Call Analytics API documentation

Calls

A call is a single recording plus its metadata. You create calls, poll their processing status, and fetch the resulting report. All endpoints are company-scoped to the API key.

POST /v1/calls GET /v1/calls GET /v1/calls/{id} GET /v1/calls/{id}/report POST /v1/calls/{id}/reprocess POST /v1/uploads

Create a call

POST/v1/calls

Submit a call by reference. Provide a recording_url we can fetch, or omit it and attach audio later. Returns immediately with a call_id; processing happens asynchronously.

Body parameters

FieldTypeNotes
external_call_idstringYour own id for the call. Used for deduplication — see below.
rep_namestringSales rep / agent name.
rep_emailstringRep email; used to attribute the call to a user.
customer_namestringProspect / customer name.
customer_phonestringCustomer phone number.
customer_emailemailValidated as an email when present.
call_started_atstringISO 8601 timestamp the call began.
call_ended_atstringISO 8601 timestamp the call ended.
duration_secondsintegerCall length in seconds.
recording_urlurlPublicly fetchable audio URL. Validated as a URL.
source_systemstringWhere the call came from (e.g. gohighlevel, my_app). Defaults to generic.
crm_contact_idstringContact id in your CRM, for write-back.
crm_deal_idstringDeal / opportunity id in your CRM.
metadataobjectFree-form. metadata.high_value_opportunity (boolean) flags the call for priority review.

Request

POST /v1/calls
Authorization: Bearer cc_3a91f8e2_your_api_key
Content-Type: application/json

{
  "external_call_id": "crm-8821",
  "rep_name": "Jordan Lee",
  "rep_email": "[email protected]",
  "customer_name": "Acme Corp",
  "customer_phone": "+15125550199",
  "customer_email": "[email protected]",
  "call_started_at": "2026-06-22T14:55:00Z",
  "call_ended_at": "2026-06-22T15:21:00Z",
  "duration_seconds": 1560,
  "recording_url": "https://example.com/recordings/8821.mp3",
  "source_system": "my_app",
  "crm_contact_id": "ct_993",
  "crm_deal_id": "dl_4471",
  "metadata": { "high_value_opportunity": true }
}

Response — 201 Created

{
  "call_id": 4127,
  "status": "received",
  "processing_status_url": "/api/v1/calls/4127",
  "report_url": null
}

Deduplication — 200 OK

If you submit a call whose external_call_id matches an existing call for your company, the request is treated as a retry: no new call is created, and we return the existing call_id with "status": "duplicate" and HTTP 200 instead of 201. This makes POST /v1/calls safe to retry.

{
  "call_id": 4127,
  "status": "duplicate",
  "processing_status_url": "/api/v1/calls/4127",
  "report_url": null
}

Validation — 422

Invalid fields return 422 with per-field detail:

{
  "error": "validation_failed",
  "errors": { "recording_url": ["must be a valid URL"] }
}

List calls

GET/v1/calls

Returns recent calls, newest first. Use ?limit= (1–100, default 25).

Response — 200 OK

{
  "data": [
    {
      "call_id": 4127,
      "external_call_id": "crm-8821",
      "customer_name": "Acme Corp",
      "processing_status": "complete",
      "report_status": "ready",
      "overall_score": 78,
      "outcome": "follow_up_scheduled",
      "created_at": "2026-06-22T15:21:40Z"
    }
  ],
  "count": 1
}

Retrieve a call

GET/v1/calls/{id}

Returns a single call with its current processing state. Poll this endpoint to know when the report is ready. When report_status is ready, report_url is populated; otherwise it is null.

Response — 200 OK

{
  "call_id": 4127,
  "external_call_id": "crm-8821",
  "customer_name": "Acme Corp",
  "processing_status": "complete",
  "report_status": "ready",
  "overall_score": 78,
  "outcome": "follow_up_scheduled",
  "created_at": "2026-06-22T15:21:40Z",
  "report_url": "/api/v1/calls/4127/report"
}

processing_status moves through values such as queuedtranscribinganalyzingcomplete (or failed). A call not belonging to your company returns 404 {"error":"not_found"}.

Get the report

GET/v1/calls/{id}/report

Returns the structured report once analysis is complete. See Reports for the full schema.

Response — 200 OK

{
  "call_id": 4127,
  "report": {
    "overall_score": 78,
    "summary": "Strong discovery; pricing objection left unresolved.",
    "call_outcome": "follow_up_scheduled",
    "close_likelihood": 62,
    "...": "see /reports for the full schema"
  }
}

Not ready yet — 409 Conflict

If the report does not exist yet (still processing), you get 409 with the current processing status so you know whether to keep polling:

{
  "error": "report_not_ready",
  "status": "analyzing"
}

Reprocess a call

POST/v1/calls/{id}/reprocess

Re-runs the pipeline for a call (for example after correcting metadata or when a recording URL became reachable). Flags the call for manual reprocessing and re-queues audio download.

Response — 200 OK

{ "call_id": 4127, "status": "reprocessing" }

Upload audio

POST/v1/uploads

When you have the audio file itself rather than a URL, upload it as multipart/form-data. The file field must be named file. Optional text fields: rep_name, customer_name, duration_seconds.

Request

curl -X POST https://api.callanalyticsapi.com/v1/uploads \
  -H "Authorization: Bearer cc_3a91f8e2_your_api_key" \
  -F "file=@./call-8821.mp3" \
  -F "rep_name=Jordan Lee" \
  -F "customer_name=Acme Corp" \
  -F "duration_seconds=1560"

Response — 201 Created

{
  "call_id": 4128,
  "status": "received",
  "processing_status_url": "/api/v1/calls/4128"
}

Errors

  • 422 {"error":"no_file","message":"Expected a multipart \"file\" field."} — the file part was missing.
  • 422 {"error":"invalid_file","message":"…"} — the file failed validation (type, size, or it could not be stored). The message explains why.