Quickstart
Upload your first call and read the transcript.
Upload a recording, wait for it to process, and read the result. You need an API key — create one in the
app under Organization → Settings → API keys (see authentication) — and a
recording; the examples use call.wav. The shell steps use curl 7.76 or newer and
jq to pick ids out of the responses.
1. Set your key and pick a project
export AFTERTALK_KEY="at_live_..."
export BASE="https://app.aftertalk.co/api"
# The first project in your organization. GET /v1/projects lists them all.
export PROJECT_ID=$(curl -sS --fail-with-body "$BASE/v1/projects" \
-H "X-API-Key: $AFTERTALK_KEY" | jq -r '.[0].id')
echo "$PROJECT_ID"2. Upload the recording
UPLOAD=$(curl -sS -X POST "$BASE/v1/projects/$PROJECT_ID/calls/upload" \
-H "X-API-Key: $AFTERTALK_KEY" \
-F "file=@call.wav;type=audio/wav")
export JOB_ID=$(echo "$UPLOAD" | jq -r '.jobId // .existingJobId')
if [ "$JOB_ID" = null ]; then
echo "Upload failed: $(echo "$UPLOAD" | jq -r '.message')"
else
echo "$JOB_ID"
fiThe format check reads the part's Content-Type, so set it with ;type= — curl would otherwise send
application/octet-stream, which is rejected with 400. If the same audio is already in the project,
the upload returns 409 with the original job as existingJobId, which the jq filter falls back to.
Any other error has neither, so the check prints the API's message instead of carrying on with null.
{ "jobId": "8f14e45f-ea0f-4e21-9c1a-2b3c4d5e6f70", "status": "PENDING", "submittedAt": "2026-07-25T10:00:00Z" }3. Wait for processing
Poll the job every few seconds until it leaves PENDING and PROCESSING:
while :; do
JOB=$(curl -sS "$BASE/v1/projects/$PROJECT_ID/calls/jobs/$JOB_ID" \
-H "X-API-Key: $AFTERTALK_KEY")
STATUS=$(echo "$JOB" | jq -r '.status')
[ "$STATUS" = PENDING ] || [ "$STATUS" = PROCESSING ] || break
sleep 5
done
case "$STATUS" in
COMPLETED | BILLING_FAILED)
export TRANSCRIPT_ID=$(echo "$JOB" | jq -r '.transcriptId')
echo "$STATUS $TRANSCRIPT_ID" ;;
FAILED) echo "Processing failed: $(echo "$JOB" | jq -r '.errorMessage')" ;;
*) echo "Request failed: $(echo "$JOB" | jq -r '.message')" ;;
esacCOMPLETED and BILLING_FAILED both have results to read. FAILED has no transcript —
errorMessage says why. A failed request (a wrong key, an unknown job) returns the API's error body,
whose status is the HTTP code, so it ends the loop too. See getting results for the full status list, or
webhooks to skip polling entirely.
4. Read the result
curl -sS --fail-with-body "$BASE/v1/projects/$PROJECT_ID/calls/$TRANSCRIPT_ID" \
-H "X-API-Key: $AFTERTALK_KEY" \
| jq '{title, durationS, summary: .summary.summary.executiveSummary}'The full response has the transcript as speaker-attributed turns, plus whatever analysis your project
has configured: summary, classification, and templateAnalysis. Each of those can be missing on a
successful call, so read them defensively.
The whole flow as a script
The same four steps, ready to save and run. A failed request stops the script with curl's status line and the API's error body:
#!/usr/bin/env bash
set -euo pipefail
BASE="https://app.aftertalk.co/api"
AUTH="X-API-Key: $AFTERTALK_KEY"
# 1. The first project in your organization.
PROJECT_ID=$(curl -sS --fail-with-body "$BASE/v1/projects" -H "$AUTH" | jq -r '.[0].id')
# 2. Upload. A 409 means this audio is already in the project; carry on with that job.
UPLOAD=$(curl -sS -X POST "$BASE/v1/projects/$PROJECT_ID/calls/upload" -H "$AUTH" \
-F "file=@call.wav;type=audio/wav")
JOB_ID=$(echo "$UPLOAD" | jq -r '.jobId // .existingJobId')
if [ "$JOB_ID" = null ]; then
echo "Upload failed: $(echo "$UPLOAD" | jq -r '.message')" >&2
exit 1
fi
# 3. Poll until the job leaves PENDING / PROCESSING.
while :; do
JOB=$(curl -sS --fail-with-body "$BASE/v1/projects/$PROJECT_ID/calls/jobs/$JOB_ID" -H "$AUTH")
STATUS=$(echo "$JOB" | jq -r '.status')
[ "$STATUS" = PENDING ] || [ "$STATUS" = PROCESSING ] || break
sleep 5
done
if [ "$STATUS" = FAILED ]; then
echo "Processing failed: $(echo "$JOB" | jq -r '.errorMessage')" >&2
exit 1
fi
# 4. COMPLETED and BILLING_FAILED both have results.
TRANSCRIPT_ID=$(echo "$JOB" | jq -r '.transcriptId')
curl -sS --fail-with-body "$BASE/v1/projects/$PROJECT_ID/calls/$TRANSCRIPT_ID" -H "$AUTH" \
| jq -r '.summary.summary.executiveSummary // "(no summary for this call)"'Next
- Uploading calls — formats, speaker roles, duplicate handling
- Getting results — job statuses and partial analysis
- Webhooks — get told when a call is ready instead of polling
- Errors — what the status codes mean