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

Getting results

Track a job to completion and read the analysis.

Uploading returns a job id. Either poll that job until it reaches a terminal state, or register a webhook and be told when it's done — webhooks are the better default for anything long-running, polling is simpler for a script.

curl "$BASE/v1/projects/$PROJECT_ID/calls/jobs/$JOB_ID" \
  -H "X-API-Key: $AFTERTALK_KEY"

Job statuses

StatusMeaning
PENDINGQueued, not started
PROCESSINGBeing transcribed and analysed
COMPLETEDDone — transcriptId is set
FAILEDProcessing failed; errorMessage explains why
BILLING_FAILEDResults are saved and readable, but usage couldn't be recorded

PENDING and PROCESSING are the only non-terminal states. Stop polling on anything else.

BILLING_FAILED is not a data failure: the transcript and analysis are saved and transcriptId is set, so treat it like COMPLETED for reading results. It signals a billing problem to sort out in the app, not a reason to re-upload.

A reasonable poll is every 5 seconds with a ceiling — processing time scales with audio length.

import time

TERMINAL = {"COMPLETED", "FAILED", "BILLING_FAILED"}

def wait_for_job(project_id, job_id, timeout_s=900):
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        job = requests.get(
            f"{BASE}/v1/projects/{project_id}/calls/jobs/{job_id}", headers=headers
        ).json()
        if job["status"] in TERMINAL:
            return job
        time.sleep(5)
    raise TimeoutError(f"job {job_id} still {job['status']}")

Reading the call

Once transcriptId is set:

curl "$BASE/v1/projects/$PROJECT_ID/calls/$TRANSCRIPT_ID" \
  -H "X-API-Key: $AFTERTALK_KEY"

You get the transcript segments plus the analysis your project produces — summary, sentiment, category, action items, and any template analysis. The reference has the full response schema.

Analysis fields are optional, even on success

Transcription is required; every analysis step after it is best-effort. If classification or summarisation fails for one call, the job still completes with the transcript saved and that particular field absent.

Never assume an analysis field exists just because the job is COMPLETED. Read defensively — call.summary?.text, not call.summary.text — or a single unusual call will break your integration.

Getting the audio back

GET /calls/{id}/audio returns metadata and a link for the stored recording, for a player or an archive copy.

Uploading calls

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

Webhooks

Be told when a call is ready instead of polling for it.

On this page

Job statusesReading the callAnalysis fields are optional, even on successGetting the audio back