message.comDevelopers

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

GET/api/v1/callsAuth: Bearer

Cursor-paginated list. Filters: status, assigneeId, departmentId, phoneNumberId, visitorId.

Code samples

cURL
curl 'https://app.message.com/api/v1/calls?status=open' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/calls?status=open', {
  headers: { Authorization: 'Bearer ' + token }
});
const { calls } = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/calls",
    headers={"Authorization": f"Bearer {token}"},
    params={"status": "open"},
)
calls = r.json()["calls"]
Ruby
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
<?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

GET/api/v1/calls/:conversationIdAuth: Bearer

Returns the call record plus the full event timeline.

200 OK
{
  "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

TypeMeaning
ringingCarrier delivered the inbound call; queue is being notified.
answeredAn agent picked up. Carries agentId.
on_holdAgent put the caller on hold.
resumedHold released.
transferredCall moved to another agent or department.
endedCall ended. Carries duration_ms and recordingUrl (if recording enabled).
voicemail_leftCaller left a voicemail; voicemailId links to the voicemail record.
ai_engagedSmart Hold or AI Receptionist took the call. Planned

Answer a call

POST/api/v1/calls/:id/answerAuth: Bearer

Marks the call as answered by the calling agent. The carrier-side answer happens over WebRTC in the dashboard; this endpoint records the metadata. Returns 200.

Hang up

POST/api/v1/calls/:id/hangupAuth: Bearer

Ends the call cleanly. The conversation status moves to solved. ended event is appended.

Hold and resume

POST/api/v1/calls/:id/holdAuth: Bearer
POST/api/v1/calls/:id/resumeAuth: Bearer

Toggle hold. Holding triggers the configured hold music or, above the Smart Hold threshold, the AI hold experience.

Transfer

POST/api/v1/calls/:id/transferAuth: Bearer

Move the call to another agent or department. Same handshake semantics as conversation transfer.

Body
{
  "agentId": "uuid",
  "expectedVersion": 4
}

Recording

POST/api/v1/calls/:id/recording/startAuth: Bearer
POST/api/v1/calls/:id/recording/stopAuth: Bearer

Mid-call recording control. Whether recording starts automatically is a per-phone-number setting. 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

CodeWhen
400 invalid_bodyPayload validation failed.
403 forbiddenAgent lacks the call channel.
404 not_foundCall does not exist.
409 stale_versionOptimistic-lock mismatch on transfer.
422 illegal_stateCannot perform this action in the current call state (e.g., hold on an unanswered call).

Common pitfalls

  • Polling for incoming calls. Subscribe to call:ringing on the agent namespace. The REST list endpoint is for backfill only.
  • Assuming recording is always on. Recording is per-phone-number. Check the recordingPolicy field on 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.