API
API
Back to appIntroductionAuthenticationQuickstartCore conceptsUploading callsGetting resultsWebhooksPagination and searchErrorsRate limits and billing

Uploading calls

Formats, speaker roles, size limits, and duplicate handling.

Uploads are multipart/form-data to a project:

curl -X POST "$BASE/v1/projects/$PROJECT_ID/calls/upload" \
  -H "X-API-Key: $AFTERTALK_KEY" \
  -F "file=@call.wav"

The response is 202 Accepted with a job id — processing happens in the background. See getting results.

Supported audio

FormatsMP3 (audio/mpeg), WAV (audio/wav, audio/x-wav), MP4/M4A (audio/mp4, audio/x-m4a), AAC (audio/aac), OGG (audio/ogg), WebM (audio/webm)
Max size10 MB per file

An unsupported format or an oversized file returns 400. Send the right Content-Type for the part — that's what the check uses, not the file extension.

Mapping speakers to roles

For stereo recordings where each channel is one party, pass channel_roles as a JSON object mapping channel index to a role. The transcript then attributes each segment to a named role instead of "Speaker 1":

curl -X POST "$BASE/v1/projects/$PROJECT_ID/calls/upload" \
  -H "X-API-Key: $AFTERTALK_KEY" \
  -F "file=@call.wav" \
  -F 'channel_roles={"0":"agent","1":"customer"}'

Malformed JSON here is ignored rather than rejected — the call still processes, just without role mapping. Double-check the value if roles are missing from the result.

To reuse the same mapping across a project, configure a speaker preset and skip the parameter.

Duplicate uploads

Uploads are de-duplicated per project by the audio's content hash. Re-sending bytes that already exist in the project returns 409 Conflict with the id of the original job:

{
  "existingJobId": "8f14e45f-ea0f-4e21-9c1a-2b3c4d5e6f70",
  "existingFilename": "call.wav",
  "code": "DUPLICATE_FILE"
}

This makes retries safe: if a request times out and you send it again, you get a 409 pointing at the job that already exists instead of a second charge. Treat 409 as success and continue with existingJobId:

const res = await fetch(uploadUrl, { method: 'POST', headers, body: form });

if (res.status === 409) {
  const { existingJobId } = await res.json();
  return existingJobId; // already uploaded — carry on
}
const { jobId } = await res.json();
return jobId;

A previously failed upload doesn't block a retry — you can re-send the same audio after a failure. To upload identical audio deliberately, add force=true, which skips the duplicate check and creates a separate call.

Insufficient balance

If the organization has no audio allowance left, the upload is rejected with 402 Payment Required before the file is stored. See rate limits and billing.

Core concepts

Projects, calls, categories, templates, presets, and action items.

Getting results

Track a job to completion and read the analysis.

On this page

Supported audioMapping speakers to rolesDuplicate uploadsInsufficient balance