> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shilo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /api/v1/calls — Submit a Call for AI Analysis

> Submit a new call recording to Shilo for AI processing. Provide a recording URL, agent details, and contact details. Returns a call_event_id for polling.

Use this endpoint to submit a call recording to Shilo for asynchronous AI processing. Shilo will download the audio from the URL you provide, transcribe it, resolve speaker identities, and run your configured AI coaching models against it. Because processing happens in the background, the response is `202 Accepted` — you receive a `call_event_id` that you can use to poll for the call object and its analysis once processing is complete.

<Note>
  This endpoint requires a **write-enabled** API key. Requests made with a read-only key will receive a `403 Forbidden` response.
</Note>

## Endpoint

```text theme={null}
POST https://api.shilo.ai/api/v1/calls
```

## Request Body

The request body must be a JSON object conforming to `CreateCallDto`. The following fields are required on every submission.

### Required Fields

<ParamField body="recording_id" type="string" required>
  Your system's unique external identifier for this recording. Shilo stores this as `external_recording_id` on the Call object and you can use it later to retrieve the call via `external_recording_id:{value}`.
</ParamField>

<ParamField body="recording_url" type="string" required>
  A publicly accessible URL pointing to the audio file for this call. Shilo's servers must be able to fetch this URL without authentication. Supported formats include MP3, WAV, M4A, and OGG.
</ParamField>

<ParamField body="duration_seconds" type="number" required>
  Total length of the call in seconds. This value is stored on the call and used to contextualize AI analysis outputs.
</ParamField>

<ParamField body="ai_category" type="string" required>
  The analysis category that determines which AI coaching rubric is applied to this call (e.g. `"sales"`). Contact your Shilo account team to confirm the categories available for your organization.
</ParamField>

<ParamField body="external_user_id" type="string" required>
  Your system's unique identifier for the agent on this call. Shilo uses this to match the call to an existing Shilo user record. If no match is found, Shilo will use `user_name` and `user_email` to create or locate the user.
</ParamField>

<ParamField body="user_name" type="string" required>
  Display name of the agent on this call. Used for speaker labeling in the transcript and coaching output.
</ParamField>

<ParamField body="user_email" type="string" required>
  Email address of the agent on this call. Used to look up or create the corresponding Shilo user account.
</ParamField>

<ParamField body="external_contact_id" type="string" required>
  Your system's unique identifier for the contact (lead or prospect) on this call. Shilo uses this to associate the call with an existing contact record.
</ParamField>

<ParamField body="contact_name" type="string" required>
  Full display name of the contact on this call. Used for speaker labeling in the transcript and analysis output.
</ParamField>

### Optional Fields

<ParamField body="occurred_at" type="string">
  ISO-8601 timestamp representing when the call actually took place (e.g. `"2030-09-24T14:00:00Z"`). If omitted, Shilo records the ingestion time instead.
</ParamField>

<ParamField body="from_number" type="string">
  The phone number the call was placed from, in any standard format. Nullable.
</ParamField>

<ParamField body="to_number" type="string">
  The phone number the call was placed to, in any standard format. Nullable.
</ParamField>

<ParamField body="incoming" type="boolean">
  Set to `true` if this was an inbound call received by the agent; `false` if the agent placed the call. Nullable.
</ParamField>

<ParamField body="ai_models" type="array">
  Override the default AI models applied to this call. Each element must be an object with `model` (string) and `type` (string) properties. If omitted, Shilo uses the models configured for your organization. Nullable.
</ParamField>

<ParamField body="user_timezone" type="string">
  IANA timezone string for the agent (e.g. `"America/New_York"`). Used to localize timestamps in coaching reports. Nullable.
</ParamField>

<ParamField body="user_picture" type="string">
  URL of the agent's profile picture. Used for display in the Shilo dashboard. Nullable.
</ParamField>

<ParamField body="contact_first_name" type="string">
  First name of the contact. If provided alongside `contact_last_name`, Shilo uses these in place of `contact_name` for structured display. Nullable.
</ParamField>

<ParamField body="contact_last_name" type="string">
  Last name of the contact. Nullable.
</ParamField>

<ParamField body="contact_phone" type="string">
  Phone number of the contact. Nullable.
</ParamField>

<ParamField body="contact_emails" type="string[]">
  Array of email addresses associated with the contact. Nullable.
</ParamField>

<ParamField body="contact_stage" type="string">
  Human-readable sales stage name for the contact at the time of this call (e.g. `"Nurture"`). Nullable.
</ParamField>

<ParamField body="contact_stage_id" type="string">
  Your system's identifier for the contact's sales stage. Nullable.
</ParamField>

<ParamField body="contact_source" type="string">
  Human-readable lead source name for the contact (e.g. `"Website"`). Nullable.
</ParamField>

<ParamField body="contact_source_id" type="string">
  Your system's identifier for the contact's lead source. Nullable.
</ParamField>

<ParamField body="contact_tags" type="string[]">
  Array of tag strings to apply to the contact (e.g. `["hot-lead", "referral"]`). Nullable.
</ParamField>

## Response

A successful submission returns HTTP `202 Accepted` with a `CreateCallResponse` body.

<ResponseField name="call_event_id" type="string">
  UUID that tracks this submission through Shilo's async processing pipeline. Save this value — you can pass it as `call_event_id:{uuid}` to `GET /calls/{identifier}` and `GET /calls/{identifier}/analysis` to poll for results.
</ResponseField>

<ResponseField name="status" type="string">
  Current processing status of the submission. On initial response this is always `"QUEUED"`. The call progresses through internal states before analysis becomes available.
</ResponseField>

### Example Response

```json theme={null}
{
  "call_event_id": "ee6f2136-b94a-4438-9335-3acf5b2a0d31",
  "status": "QUEUED"
}
```

<Note>
  Save the `call_event_id` returned in this response. To retrieve the processed call or its AI analysis, use this value as the `identifier` path parameter in the format `call_event_id:{uuid}` — for example: `GET /api/v1/calls/call_event_id:ee6f2136-b94a-4438-9335-3acf5b2a0d31`.
</Note>

## Example Request

```bash theme={null}
curl --request POST \
  --url "https://api.shilo.ai/api/v1/calls" \
  --header "x-api-key: YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "recording_id": "rec-20300924-001",
    "recording_url": "https://storage.example.com/recordings/rec-20300924-001.mp3",
    "duration_seconds": 183,
    "ai_category": "sales",
    "external_user_id": "agent-432",
    "user_name": "Jordan Smith",
    "user_email": "jordan.smith@example.com",
    "external_contact_id": "lead-4534",
    "contact_name": "Alex Johnson",
    "occurred_at": "2030-09-24T14:00:00Z",
    "incoming": false,
    "from_number": "(555) 555-1234",
    "to_number": "(555) 555-5678",
    "contact_stage": "Nurture",
    "contact_source": "Website"
  }'
```

## Error Responses

**400 Bad Request** — Returned when the request body is missing required fields or contains invalid values. The response body includes a `message` field describing the problem.

```json theme={null}
{
  "statusCode": 400,
  "message": ["recording_url must be a URL address", "duration_seconds must be a number"]
}
```

**403 Forbidden** — Returned when your API key is read-only and does not have permission to create calls.

```json theme={null}
{
  "error": "forbidden",
  "message": "API key is read-only"
}
```
