Callens
Ingesting calls

Submitting a call

POST /calls, field by field.

POST /calls accepts a call for processing and returns immediately. Transcription and analysis happen asynchronously; the result reaches you as a call.completed webhook.

Audio reaches Callens through an upload: request one, send the bytes, and present the ticket here.

{
  "mode": "upload",
  "upload_ticket": "eyJpdiI6…",
  "started_at": "2026-08-05T09:14:00Z",
  "employee_code": "EMP-1042",
  "provider": "zoom",
  "provider_call_id": "CA123",
  "idempotency_key": "CA123"
}

Required fields

FieldNotes
modeAlways upload.
upload_ticketThe ticket from POST /uploads, verbatim.
started_atWhen the call started.
employee_code or employee_idThe employee the call belongs to. Send exactly one of the two. See naming the employee.

provider_call_id (max 255 characters) and idempotency_key (max 255 characters) are also required if your key has webhooks enabled — see correlation identifiers.

Naming the employee

Every call belongs to one employee, and the request says which one. Send exactly one of these two fields. Leave the other one out.

FieldNotes
employee_codeYour organization's own code for the employee: the Code on the People page in Callens. Matched exactly, including letter case. Max 255 characters.
employee_idThe employee's Callens ID, an opaque string. Copy it from the People page in Callens. Max 64 characters.

Use employee_code when your phone system already holds your own identifier for each person. Use employee_id for an employee who has no code.

Callens refuses the request in three cases, and creates no call in any of them:

CaseResponse
The request sends neither field422 in the field-validation shape, with errors naming the fields.
The request sends both fields422 in the field-validation shape. Callens does not choose between them.
The code or ID names no employee, or names a deleted employee422 with error set to employee_not_found.

In all three cases the recording you uploaded is kept. Correct the request and submit the same upload_ticket again; you do not upload the file a second time.

Spaces before and after a code are ignored. Letter case is not: emp-1042 does not match EMP-1042.

An employee who has left your organization, and has not been deleted, is still accepted. A historical import can name the person who took the call.

Callens checks for a replay before it looks up the employee, so a retry of a call that was already accepted returns that call with 200.

identity_key is no longer read. A request that sends identity_key and neither employee field is refused, like any other request that names no employee.

Optional fields

FieldNotes
ended_atMust not be before started_at.
duration_msMinimum 1. Callens measures duration itself and prefers its own observation.
providerThe system the call came from. Max 64 characters.
channel_layoutmono_mixed, dual_agent_customer, or multi. dual_agent_customer asks us to attribute speech by channel, which only some speech-to-text providers can do — if yours cannot, the call is refused with a 422 naming the provider rather than accepted and left unprocessed. Submit without this field, or as mono_mixed, to be transcribed with speaker diarization instead.
directioninbound, outbound, or unknown.
counterparty_e164The other party's number. Max 32 characters.
prioritylive or backfill. Use backfill for historical imports so they yield to live traffic.
provider_metadataAny JSON object. Stored on the call as submitted. Not returned to you anywhere today — not in this response, not on call.completed.

Responses

{ "success": true, "message": "Call accepted.", "data": { "id": "gQ7bYx", "status": "queued" } }

201 on a new call, 200 on a replay. See idempotency.

The id is an opaque string. Callens never exposes internal numeric identifiers.

status is queued for every new call: the call is on its way through the pipeline. A replay returns the stored call's current status.

A replay of a call that was submitted without an employee, before employees became required, can still return identity_unresolved until someone in your organization assigns that call an employee in Callens.

Refusals specific to the upload are listed under uploading audio.

Modes that no longer exist

Earlier versions of this API documented mode: "pull", which took a source_url, and mode: "push", which took an audio_key. Both now return 422.

Neither could produce a transcribable call. Nothing ever fetched a source_url — the URL was validated and discarded, so the call was created with no audio and failed as soon as the pipeline reached it, having already consumed part of your ingestion allowance. push required write access to Callens storage, which is not something an API key has ever granted.

If you built against either, switch to uploading audio. Nothing that worked before has stopped working.

On this page