> Source: https://builder-docs.ema.ai/agent-qa/api-audio-ingestion
> Title: Audio Ingestion API

# Audio Ingestion API

The Audio Ingestion API uploads an audio recording (e.g., a call recording from a telephony provider) to be processed by an Agent QA AI Employee. Once uploaded, the file is transcribed, diarized, evaluated against your QA parameters, and made available in the Audit tab and the daily export.

Use this endpoint when you have an audio file (`.mp3` or `.wav`). If you already have a text transcript, use the [Transcript Ingestion API](/legacy-docs/agent-qa/api-transcript-ingestion) instead.

## 12.1 Authentication & Headers

For instructions on generating a Bearer token from your API key, see the internal Authentication Guide for Agent QA.

All requests require the following headers:

Header

Type

Required

Description

`Authorization`

string

Yes

Bearer token (e.g., `Bearer <token>`)

`x-persona-id`

UUID

Yes

The unique identifier of the Agent QA AI Employee (persona). Found in the URL of the Ema web app, e.g., `https://staging.ema.co/ai-employees/<persona_id>`.

## 12.2 Upload File

Uploads an audio file for processing by the AI Employee.

Property

Value

URL

`{base_url}/api/v1/external/upload/file`

HTTP Method

POST

Content-Type

`multipart/form-data`

The `{base_url}` is region-specific (for example, a staging EU tenant uses `https://api.staging.tp-eu.ema.co`). Confirm the correct host for your deployment with your Ema contact.

### Form-Data Fields

Field

Type

Required

Description

`file`

file

Yes

The audio file to upload. Supported formats: `.mp3`, `.wav`.

`agent_id`

string

Yes

Identifier of the human agent associated with the recording.

`agent_email`

string

Yes

The human agent's email. Used for permissioning so agents can only view their own calls.

`resource_id`

string

Yes

External identifier (e.g., a Call ID from your telephony provider).

`language`

string

No

Language of the audio file.

`channels`

integer

Yes

Number of channels in the call (Mono = 1, Dual/Stereo = 2).

`channel_map`

JSON

Yes

For stereo calls, which channel maps to which speaker. Example: `{"Agent": 0, "Customer": 1}`.

`timestamp`

string

Yes

Call timestamp. Values without timezone information are treated as UTC.

`case_id`

string

No

Ticket/case ID associated with the call.

`customer_survey`

JSON

No

Customer satisfaction/dissatisfaction survey data.

`agent_tenure`

integer

No

Tenure of the associated agent in days.

`metadata`

JSON

No

Any additional metadata you want attached to the call record.

### Constraints

-   Maximum file size: **25 MB**.
-   Allowed MIME types: `audio/mpeg`, `audio/wav`.

### Success Response (`200 OK`)

```json
{
  "file_id": "550e8400-e29b-41d4-a716-446655440000",
  "file_name": "recording_2024_01_15.mp3",
  "status": "UPLOAD_COMPLETED"
}
```

## 12.3 Error Codes

HTTP Code

Reason

`400 Bad Request`

Missing headers, invalid UUID format, unsupported file extension, or missing `agent_id`.

`403 Forbidden`

Credentials do not have permission to access the specified AI Employee.

`413 Payload Too Large`

The uploaded file exceeds the size limit.

`429 Too Many Requests`

Rate limit exceeded. Retry with exponential backoff.

`500 Internal Server Error`

Unexpected server-side error.

## 12.4 Rate Limits

A rate limit of **10 queries per second** is enforced on the data upload API. When rate limited, the response includes a `Retry-After` header (in seconds). Clients should retry with exponential backoff.

## 12.5 Sample Python

The script below explicitly sets the MIME type to `audio/mpeg`. Use `audio/wav` if uploading WAV files. The `x-persona-id` header is the source of truth for which AI Employee the data belongs to.

```python
import requests
import time

BASE_URL = "https://api.<region>.ema.co"  # replace with your domain
AUTH_TOKEN = "YOUR_BEARER_TOKEN"
AI_EMPLOYEE_ID = "YOUR_AI_EMPLOYEE_UUID"

headers = {
    "Authorization": f"Bearer {AUTH_TOKEN}",
    "x-persona-id": AI_EMPLOYEE_ID,
}

def upload_audio_file(file_path, agent_id, agent_email, resource_id,
                     language, channels, channels_map, timestamp,
                     max_retries=3):
    """Uploads an audio file for a specific AI Employee."""
    url = f"{BASE_URL}/api/v1/external/upload/file"
    data = {
        "agent_id": agent_id,
        "agent_email": agent_email,
        "resource_id": resource_id,
        "language": language,
        "channels": channels,
        "channels_map": channels_map,
        "timestamp": timestamp,
    }
    try:
        with open(file_path, "rb") as f:
            files = {"file": (file_path, f, "audio/mpeg")}  # or audio/wav
            for attempt in range(max_retries + 1):
                response = requests.post(url, headers=headers, data=data, files=files)
                if response.status_code == 200:
                    return response.json()
                if response.status_code == 429 and attempt < max_retries:
                    retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
                    time.sleep(retry_after)
                    continue
                print(f"Failed! Status: {response.status_code}")
                print(f"Error: {response.text}")
                return None
    except FileNotFoundError:
        print(f"Error: File {file_path} not found.")
        return None

if __name__ == "__main__":
    result = upload_audio_file(
        file_path="sample-3s.mp3",
        agent_id="human_agent_001",
        agent_email="human_agent_001@email.com",
        resource_id="resource_id_123",
        language="en",
        channels=2,
        channels_map={"Agent": 0, "Customer": 1},
        timestamp=time.time(),
    )
    if result:
        print(f"New File ID: {result.get('file_id')}")
```
