For Developers
Calls

Get transcripts and recordings

Track a call by its ID, then read its transcript and summary and download its recording.

All of these routes need an API key with read permission. The JavaScript samples use top-level await, so save them as .mjs files and run them with Node 18 or later.

  1. Wait for the call to finish

    POST /v1/calls returns a callId. Pass it to GET /v1/calls/{id} until status is completed or failed.

    A call can wait overnight for calling hours, so don't treat a long queued as a failure. This loop checks every 30 seconds for up to an hour. Polling counts toward your rate limits.

    const API = 'https://backend.synthesys.app/api/v1';
    const headers = { SYNTH_API_KEY: 'YOUR_API_KEY' };
    const callId = '9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d';
    
    async function waitForCall() {
      for (let check = 0; check < 120; check += 1) {
        const response = await fetch(`${API}/calls/${callId}`, { headers });
        const { data: call } = await response.json();
        if (['completed', 'failed'].includes(call.status)) return call;
        await new Promise((resolve) => setTimeout(resolve, 30_000));
      }
      throw new Error('The call has not finished yet');
    }
    
    const call = await waitForCall();
    console.log(call.status, call.failure_code, call.failure_message);

    A failed call says why. If provider_call_id is null, the call never went out, so there is no log or recording to read. This example is trimmed.

    JSON
    {
      "success": true,
      "data": {
        "id": "7c6d5e4f-3a2b-4c1d-8e0f-9a8b7c6d5e4f",
        "status": "failed",
        "failure_code": "outside_calling_hours",
        "failure_message": "The call could not be placed within the recipient's calling hours.",
        "provider_call_id": null,
        "contact_phone": "+12025550123",
        "duration_seconds": null,
        "created_at": "2026-10-31T14:02:12.103Z",
        "completed_at": "2026-11-02T01:02:12.103Z"
      }
    }
  2. Find the call log

    A call that ran has a call log, with its own call_id. Its directCallId matches your callId, so filter on that field to find it.

    curl -G "https://backend.synthesys.app/api/v1/call-logs" \
      -H "SYNTH_API_KEY: YOUR_API_KEY" \
      --data-urlencode "directCallId=9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"

    Logs come back newest first. If an agent has more than 100 calls in the range, request further pages with page. The log can appear shortly after the call finishes, so check again if there's no match. This example is trimmed.

    JSON
    {
      "success": true,
      "data": [
        {
          "call_id": "5b9e2d70-1c34-4a8f-9e62-7d1a0c3f4b25",
          "provider_call_id": "2e4f6a8c-1b3d-4f5a-9c7e-0d2b4f6a8c1e",
          "directCallId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
          "contactPhoneNumber": "+12025550123",
          "agent_id": "3f0c1a52-7b7e-4c7a-9a55-1f2d3e4c5b6a",
          "agentName": "Sales outreach",
          "status": "ended",
          "ended_reason": "customer-ended-call",
          "duration": 142,
          "start_time": "2026-10-31T14:02:11+00:00",
          "end_time": "2026-10-31T14:04:33+00:00",
          "summary": "Jane asked to upgrade her plan next month.",
          "hasTranscript": true,
          "created_at": "2026-10-31T14:02:09+00:00",
          "updated_at": "2026-10-31T14:04:40+00:00"
        }
      ],
      "pagination": { "total": 1, "page": 1, "limit": 100, "totalPages": 1 }
    }
  3. Get the transcript and summary

    Pass the call_id to GET /v1/call-logs/:id. The final transcript can arrive shortly after the status changes. If hasTranscript is false, check again a minute later.

    curl "https://backend.synthesys.app/api/v1/call-logs/5b9e2d70-1c34-4a8f-9e62-7d1a0c3f4b25" \
      -H "SYNTH_API_KEY: YOUR_API_KEY"

    The response has the log, the transcript as a list of turns, and any timed messages. This example is trimmed.

    JSON
    {
      "success": true,
      "data": {
        "callLog": {
          "call_id": "5b9e2d70-1c34-4a8f-9e62-7d1a0c3f4b25",
          "directCallId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
          "status": "ended",
          "ended_reason": "customer-ended-call",
          "duration": 142,
          "summary": "Jane asked to upgrade her plan next month.",
          "outcome_summary": "Jane asked to upgrade her plan next month.",
          "key_points": null,
          "hasTranscript": true
        },
        "transcriptText": [
          { "role": "assistant", "message": "Hi Jane, I am calling about your growth plan." },
          { "role": "user", "message": "Thanks. I would like to upgrade next month." }
        ],
        "messages": []
      }
    }
  4. Find the recording

    Pass the same call_id as call_log_id to GET /v1/recordings. A recording can be downloaded when its status is ready. The list is empty when the call has no recording yet.

    curl "https://backend.synthesys.app/api/v1/recordings?call_log_id=5b9e2d70-1c34-4a8f-9e62-7d1a0c3f4b25" \
      -H "SYNTH_API_KEY: YOUR_API_KEY"

    This example is trimmed.

    JSON
    {
      "success": true,
      "data": [
        {
          "id": "e41f7c09-6a2b-4d53-b8c1-90d2f3a4e5b6",
          "call_log_id": "5b9e2d70-1c34-4a8f-9e62-7d1a0c3f4b25",
          "status": "ready",
          "duration": 142,
          "format": "mp3",
          "customer_phone": "+12025550123",
          "call_status": "ended",
          "call_ended_reason": "customer-ended-call",
          "recording_url": null,
          "stereo_recording_url": null,
          "created_at": "2026-10-31T14:04:45+00:00"
        }
      ],
      "pagination": { "total": 1, "page": 1, "limit": 50, "totalPages": 1 }
    }

    recording_url and stereo_recording_url are always null. The next step gets you a link.

  5. Download the audio

    GET /v1/recordings/:id/download-url returns a signed link. It works without an API key until expiresAt, which is one hour away. Download the file straight away, and request a new link if it expires.

    url=$(curl -s "https://backend.synthesys.app/api/v1/recordings/e41f7c09-6a2b-4d53-b8c1-90d2f3a4e5b6/download-url" \
      -H "SYNTH_API_KEY: YOUR_API_KEY" | jq -r '.data.url')
    curl -o recording.mp3 "$url"
    JSON
    {
      "success": true,
      "data": {
        "url": "https://example.com/signed/recording.mp3?token=SIGNED_TOKEN",
        "type": "mono",
        "expiresInSeconds": 3600,
        "expiresAt": "2026-10-31T15:10:00.000Z",
        "disposition": "attachment"
      }
    }

    A recording that isn't ready returns 409 with the code RECORDING_NOT_READY. Add ?type=stereo for separate channels, and ?disposition=inline to play it in place.

Call fields

Fields used on this page

idstring
The call's UUID. This is the callId.
statusstring
queued, dialing, in_progress, completed or failed.
failure_codestring | null
Why the call failed. See Failure codes.
failure_messagestring | null
A readable version of failure_code.
provider_call_idstring | null
The voice provider's ID, once the call has gone out.
duration_secondsinteger | null
Length in seconds, once known.

Call log fields

Fields used on this page

call_idstring
The log's UUID. Webhook events use the same value as call.id.
directCallIdstring
The callId this log belongs to. Left out for calls not placed with POST /v1/calls.
contactPhoneNumberstring
The contact's number. Left out when unknown.
provider_call_idstring
The voice provider's ID. Quote it when you ask about a call.
agent_idstring
UUID of the agent, or null.
agentNamestring
The agent's name. Left out when unknown.
statusstring
The call's state. ended and failed mean it has finished.
ended_reasonstring
Why the call ended, such as customer-ended-call, or null.
durationinteger
Length in seconds, or null.
start_timestring
When the call started, or null if unknown.
end_timestring
When the call ended, or null.
summarystring
The call summary, or null.
outcome_summarystring
The summary from the post-call analysis, falling back to summary.
key_pointsarray
Topics from the post-call analysis, or null.
hasTranscriptboolean
Whether a transcript exists.

Transcript fields

transcriptText is the simplest way to read a call. Each item has a role of user or assistant, and a message.

messages holds per-turn timing, but it's often empty. Each item has message_order, role, message_text, timestamp, end_timestamp, duration_ms and seconds_from_start.

Errors

  • GET /v1/calls/:id returns 404 with Call not found when the call isn't in your account.
  • GET /v1/call-logs returns 400 when page, limit, agent_id or a date is invalid.
  • GET /v1/call-logs/:id returns 404 with Call log not found when the log isn't in your account.
  • GET /v1/recordings returns 400 when call_log_id isn't a UUID. It returns an empty data list when the call has no recording yet.
  • GET /v1/recordings/:id/download-url returns 409 RECORDING_NOT_READY when there's no audio to download yet.