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.
Create a call
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
| Field | Type | Notes |
|---|---|---|
external_call_id | string | Your own id for the call. Used for deduplication — see below. |
rep_name | string | Sales rep / agent name. |
rep_email | string | Rep email; used to attribute the call to a user. |
customer_name | string | Prospect / customer name. |
customer_phone | string | Customer phone number. |
customer_email | Validated as an email when present. | |
call_started_at | string | ISO 8601 timestamp the call began. |
call_ended_at | string | ISO 8601 timestamp the call ended. |
duration_seconds | integer | Call length in seconds. |
recording_url | url | Publicly fetchable audio URL. Validated as a URL. |
source_system | string | Where the call came from (e.g. gohighlevel, my_app). Defaults to generic. |
crm_contact_id | string | Contact id in your CRM, for write-back. |
crm_deal_id | string | Deal / opportunity id in your CRM. |
metadata | object | Free-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
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
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 queued →
transcribing → analyzing → complete (or failed).
A call not belonging to your company returns 404 {"error":"not_found"}.
Get the 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
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
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."}— thefilepart was missing.422 {"error":"invalid_file","message":"…"}— the file failed validation (type, size, or it could not be stored). Themessageexplains why.