Skip to main content

Uploads

Three ways to bring recordings in. All of them land a call in the active project and enqueue it for analysis.

Single upload​

POST /api/uploads — multipart/form-data

PartRequiredNotes
file✓The audio recording
direction—inbound or outbound. Defaults to outbound
customer_phone—Used to attach or create the customer
customer_name—
customer_segment—Segment name, created on demand
curl -X POST https://your-host/api/uploads \
-H "Authorization: Bearer <token>" \
-F "[email protected]" \
-F "direction=outbound" \
-F "customer_phone=+918888888888" \
-F "customer_name=Priya Kumar"

Returns the created call, in status pending. Analysis runs in the background — poll GET /api/calls/{id} or watch the list.

StatusCause
400{"detail": "file is required"}

Bulk upload​

POST /api/uploads/bulk — multipart/form-data

Repeat the files part once per recording:

curl -X POST https://your-host/api/uploads/bulk \
-H "Authorization: Bearer <token>" \
-F "[email protected]" \
-F "[email protected]" \
-F "[email protected]"

Each file is reported on independently:

{
"results": [
{ "filename": "call-1.mp3", "call_id": 1042 },
{ "filename": "call-2.mp3", "call_id": 1043 },
{ "filename": "call-3.mp3", "error": "unsupported audio format" }
]
}

One bad file does not fail the batch. There is no customer metadata on this route — use CSV upload when you need it.

CSV upload​

POST /api/uploads/csv — multipart/form-data

The richest option: a manifest of calls with their metadata, plus either the audio files alongside it or URLs to fetch them from.

PartRequiredNotes
csv_file✓The manifest
audio_files—Recordings, matched to rows by filename

CSV columns​

The header row is matched case-insensitively. Every column is optional except that each row needs either filename or recording_url.

ColumnNotes
filenameMust match an uploaded audio_files part exactly
recording_urlDownloaded server-side if no matching file was uploaded
customer_name
customer_phone
customer_segment
segmentsComma-separated segment names, created on demand
directioninbound or outbound. Defaults to outbound
filename,customer_name,customer_phone,direction,segments
call-1.mp3,Priya Kumar,+918888888888,outbound,"winback,north"
call-2.mp3,Arjun Rao,+919999999999,inbound,south
curl -X POST https://your-host/api/uploads/csv \
-H "Authorization: Bearer <token>" \
-F "[email protected]" \
-F "[email protected]" \
-F "[email protected]"

Response​

{
"results": [
{ "row_index": 0, "filename": "call-1.mp3", "call_id": 1042 },
{ "row_index": 1, "filename": "call-2.mp3", "call_id": 1043, "segment_error": "failed to attach segments" },
{ "row_index": 2, "filename": "call-3.mp3", "error": "no audio file uploaded matching \"call-3.mp3\" and no recording_url provided" }
]
}

row_index is the position in the CSV, excluding the header.

Note the distinction between error and segment_error: a segment that could not be attached does not discard a successfully ingested call. The call is reported with its call_id so the row can be re-tagged later.

Rows with neither filename nor recording_url are skipped silently.

StatusCause
400{"detail": "multipart form required"}
400{"detail": "csv_file is required"}
400{"detail": "failed to parse csv: …"}
400{"detail": "csv must have a 'filename' or 'recording_url' column"}

Which to use​

SituationRoute
One call, with metadataPOST /api/uploads
A folder of recordings, metadata not neededPOST /api/uploads/bulk
An export from another systemPOST /api/uploads/csv
Recordings already hosted somewherePOST /api/uploads/csv with recording_url
A server-to-server integrationDeveloper API

After upload​

Each call is queued through River, a Postgres-backed job queue, so a restart mid-analysis resumes rather than losing the work. Track progress through the call's status — see Calls.

Customers are matched on phone number and created if unknown, so a customer's history accumulates across uploads without any linking step.