Skip to Content
API ReferenceRecordingsCreate Recording

Upload Recording

Upload an audio file to create a new recording and start transcription.

POSThttps://api.identitycall.ai/api/v1/public/recordings

Upload an audio file for transcription and analysis.

Requires write permission

Request Body

Body Parameters (multipart/form-data)

NameTypeRequiredDefaultDescription
filefileYes-Audio file (MP3, WAV, M4A, OGG, FLAC, WebM)
languagestringNoenLanguage code (en, es, fr, de, etc.)
namestringNo-Display name for the recording
tags[]string[]No-Tag names to attach. Matched case-insensitively against the recording's project and created there if missing. Blank entries are ignored.
operator_idintegerNo-Id of the operator (agent) who handled the call. Get ids from GET /operators. An id outside your account is rejected with 422.

Tagging on upload

Pass tags[] once per tag. Names are resolved inside the recording’s own project, so you never need to look up ids first — an unknown name simply becomes a new tag in that project:

curl -X POST "https://api.identitycall.ai/api/v1/public/recordings" \ -H "Authorization: Bearer $IDENTITYCALL_API_KEY" \ -F "file=@call.mp3" \ -F "project_id=12" \ -F "tags[]=vip" \ -F "tags[]=callback-requested"

The attached tags come back in the response under data.tags. Because names create tags on first use, a typo becomes a real tag in that project — see Tags if you would rather pre-define the vocabulary with POST /tags and treat unknown names as a bug in your integration.

Tags are project-scoped. vip in project 12 and vip in project 13 are separate tags with separate ids.

The parameter is tags, plural — and it must be sent as tags[].

Sending singular tag is rejected with a 422 naming the correct parameter, so the mistake cannot pass silently.

The subtler trap is the bracket. Most HTTP clients encode a list under a plain tags key as repeated fields, and only the last value survives:

Sent on the wireWhat the API receives
tags=a&tags=b"b" — the first value is lost
tags[]=a&tags[]=b["a", "b"]

With one tag both forms happen to work, which is why this surfaces only once you send a second tag. In Python requests, use the bracketed key:

data = {"project_id": 12, "tags[]": ["vip", "callback-requested"]} requests.post(url, headers=headers, files={"file": (name, blob, "audio/wav")}, data=data)

Attributing the call to an operator

If your telephony platform already knows which agent took the call, send that agent’s operator_id and IdentityCall will credit them instead of trying to work it out from the audio.

Operator ids are numeric and account-wide — unlike tags, they are not created by name on first use. The usual integration shape is: fetch the roster once, map your own agent identifiers to IdentityCall ids, and create anything missing.

# 1. List the operators you already have curl "https://api.identitycall.ai/api/v1/public/operators" \ -H "Authorization: Bearer $IDENTITYCALL_API_KEY" # 2. Create one that does not exist yet (phone_number is required and unique) curl -X POST "https://api.identitycall.ai/api/v1/public/operators" \ -H "Authorization: Bearer $IDENTITYCALL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Jonas Jonaitis", "phone_number": "+37060000001"}' # 3. Upload the recording with that operator's id curl -X POST "https://api.identitycall.ai/api/v1/public/recordings" \ -H "Authorization: Bearer $IDENTITYCALL_API_KEY" \ -F "file=@call.mp3" \ -F "project_id=12" \ -F "operator_id=42"

The assignment comes back under data.operator, including a source field:

sourceMeaning
apiYou sent operator_id
voice / routingIdentityCall inferred it from voice or the dialled line
review / manualA person set it in the IdentityCall UI

An api assignment wins. Voice identification and line-based routing will not overwrite an operator you supplied, so your telephony records stay the source of truth. A person can still correct it in the UI.

The parameter is operator_id, not operator. Sending operator is rejected with a 422 naming the correct one, and an operator_id belonging to another account is rejected rather than ignored — a recording is never created with attribution silently dropped.

Request

curl -X POST "https://api.identitycall.ai/api/v1/public/recordings" \ -H "Authorization: Bearer $IDENTITYCALL_API_KEY" \ -F "file=@call-recording.mp3" \ -F "language=en" \ -F "name=Sales Call - John Smith"

Response

Response201 Created
{
"data": {
  "public_id": "018f3a00-0000-7000-8000-0000000000a5",
  "status": "pending",
  "created_at": "2025-01-15T14:22:00Z"
}
}

Response Fields

FieldTypeDescription
idintegerUnique recording ID
statusstringInitial status (always “pending”)
created_atstringISO 8601 timestamp

Transcription is asynchronous. Poll the Get Recording endpoint to check when processing is complete.

Supported Audio Formats

FormatExtensionMax Size
MP3.mp3100 MB
WAV.wav500 MB
M4A.m4a100 MB
OGG.ogg100 MB
FLAC.flac500 MB
WebM.webm100 MB

Supported Languages

IdentityCall supports 100+ languages via Google Gemini. Common language codes:

CodeLanguageCodeLanguage
enEnglishjaJapanese
esSpanishkoKorean
frFrenchzhChinese
deGermanarArabic
itItalianhiHindi
ptPortugueseruRussian
nlDutchtrTurkish
plPolishviVietnamese

See the Introduction for a complete list.

Errors

StatusErrorDescription
400“file is required”No file was uploaded
422“Invalid file type”Unsupported audio format
422“File too large”File exceeds size limit
422Unknown parameter "tag". Did you mean "tags"?The parameter is tags, plural. The upload is rejected rather than stored untagged.
422Unknown parameter "operator". Did you mean "operator_id"?The parameter is operator_id.
422operator_id N was not found in this accountThe id does not exist or belongs to another account. List valid ids with GET /operators.

Try It

Try It