Calls Live
Calls are conversations with channel = "call". Each call carries an event timeline (ringing, answered, ended, voicemail_left) plus the standard conversation fields. AI features (Smart Hold, AI Receptionist) layer on top.
Real-time call control happens over Socket.io call events. The REST endpoints here exist for backend automation, audit, and history.
List calls
Cursor-paginated list. Filters: status, assigneeId, departmentId, phoneNumberId, visitorId.
Code samples
curl 'https://app.message.com/api/v1/calls?status=open' \
-H 'Authorization: Bearer YOUR_WORKSPACE_JWT'const res = await fetch('https://app.message.com/api/v1/calls?status=open', {
headers: { Authorization: 'Bearer ' + token }
});
const { calls } = await res.json();import requests
r = requests.get(
"https://app.message.com/api/v1/calls",
headers={"Authorization": f"Bearer {token}"},
params={"status": "open"},
)
calls = r.json()["calls"]require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/calls?status=open")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
http.request(req)
end
calls = JSON.parse(res.body)["calls"]<?php
$ctx = stream_context_create([
"http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$calls = json_decode(file_get_contents("https://app.message.com/api/v1/calls?status=open", false, $ctx), true)["calls"];Get one call
Returns the call record plus the full event timeline.
{
"id": "uuid",
"version": 4,
"channel": "call",
"status": "open",
"fromE164": "+14155550100",
"toE164": "+14155550199",
"assignedAgentId": "uuid",
"phoneNumberId": "uuid",
"voicemailId": null,
"events": [
{ "eventType": "ringing", "createdAt": "2026-05-13T15:00:00Z", "fromE164": "+14155550100" },
{ "eventType": "answered", "agentId": "uuid", "createdAt": "2026-05-13T15:00:08Z" },
{ "eventType": "ended", "createdAt": "2026-05-13T15:01:32Z", "duration_ms": 84000, "recordingUrl": "https://..." }
],
"createdAt": "2026-05-13T15:00:00Z"
}Event types
| Type | Meaning |
|---|---|
ringing | Carrier delivered the inbound call; queue is being notified. |
answered | An agent picked up. Carries agentId. |
on_hold | Agent put the caller on hold. |
resumed | Hold released. |
transferred | Call moved to another agent or department. |
ended | Call ended. Carries duration_ms and recordingUrl (if recording enabled). |
voicemail_left | Caller left a voicemail; voicemailId links to the voicemail record. |
ai_engaged | Smart Hold or AI Receptionist took the call. Planned |
Answer a call
Claims the call for the calling agent. The carrier-side answer happens over WebRTC in the dashboard; this endpoint records the claim and returns one of four 409 call_unavailable reasons if it loses the race: already_claimed, call_ended, agent_busy, or stale_version.
Hang up
Ends the call cleanly. The conversation status moves to solved. An ended event is appended.
Hold and resume
One endpoint toggles both directions: there is no separate /resume path. Body { "hold": true } (or an empty body) places the caller on hold; { "hold": false } resumes. Holding triggers the configured hold music or, above the Smart Hold threshold, the AI hold experience.
Transfer
Mirrors assign-to-agent under the calls path so a phones-focused integration can use one consistent set of paths. Same handshake semantics as conversation transfer.
{
"agentId": "uuid",
"expectedVersion": 4
}Recording
There is no mid-call recording start/stop action. Recording is a per-phone-number setting (recordingEnabled on PATCH /api/v1/phone-numbers/:id): if it's on for the number a call comes in on, the whole call is recorded automatically. Recordings are stored in DigitalOcean Spaces; the URL appears on the ended event.
Call recording is regulated. Two-party consent jurisdictions require notice. We provide a configurable spoken disclosure on the inbound greeting; ensure it is enabled before recording in those regions.
Errors
| Code | When |
|---|---|
400 invalid_body | Payload validation failed. |
403 not_assigned | Caller is not the assigned agent and not an admin. |
404 not_found | Call does not exist in this workspace. |
409 call_unavailable | Answer lost the race; see reason for which of the four cases. |
Common pitfalls
- Polling for incoming calls. Subscribe to call events on the agent namespace. The REST list endpoint is for backfill only.
- Looking for a
/resumepath. There isn't one; call hold again with{ "hold": false }. - Assuming recording is always on. Recording is per-phone-number. Check
recordingEnabledon the phone-number record before relying on a recording URL. - Confusing call status with event type. Status is the conversation state (
open,solved). Event type is a specific transition in the call timeline.