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

# Call-completed callback

> AlloMia sends one POST request to your server when an outbound call you started reaches its final status.

When a call you started with [Create outbound call](/api-reference/endpoint/create-outbound-call) reaches a final status, AlloMia sends one `POST` request with the result to the `callbackUrl` you gave for that call. This saves you from polling [Get outbound call status](/api-reference/endpoint/get-outbound-call-status).

## Set the callback URL

Send `callbackUrl` in the body of each `POST /api/outbound` request:

```json theme={null}
{
  "phoneNumber": "+15145550187",
  "assistantId": "34042575-74d3-4588-a3d9-8773aab96a9b",
  "phoneNumberId": "d5d3c6cb-d0ac-4068-ace4-1b5af3fbad20",
  "callbackUrl": "https://partner.example.com/allomia/call-completed"
}
```

* The URL is set per call. There's no account-wide setting, and each call can use a different URL.
* Use an HTTPS URL.
* You can't change the URL once the call has started.
* Without a `callbackUrl`, no callback is sent. Use the status endpoint instead.

## When it's sent

AlloMia sends the callback once per call, as soon as the status becomes final: `completed`, `busy`, `no-answer` or `failed`.

It isn't sent:

* When the create request already answered `"status": "failed"`. The call was never placed.
* When a call stays `calling` and never reaches a final status. This is rare.

The callback is sent as soon as the end of the call is recorded, before the call's summary and transcript are ready. See [Reading the conversation](/api-reference/outbound-calls#reading-the-conversation) to get them.

Callbacks for different calls aren't sent in any guaranteed order.

## The request

AlloMia sends `POST` to your URL with the header `Content-Type: application/json` and this body:

<ResponseField name="event" type="string">
  Always `call.completed`, whatever the final status.
</ResponseField>

<ResponseField name="outboundCallId" type="string">
  The call's ID, as returned by Create outbound call.
</ResponseField>

<ResponseField name="contactId" type="string">
  The ID of the person called, as returned by Create outbound call.
</ResponseField>

<ResponseField name="status" type="string">
  The final status: `completed`, `busy`, `no-answer` or `failed`. See [Statuses](/api-reference/outbound-calls#statuses).
</ResponseField>

<ResponseField name="timestamp" type="string">
  When AlloMia prepared the callback, in ISO 8601 UTC. It's the same on every retry.
</ResponseField>

<ResponseField name="callDetails" type="object">
  How the call ended. The same fields as in [Get outbound call status](/api-reference/endpoint/get-outbound-call-status). Any of them can be `null`.

  <Expandable title="properties">
    <ResponseField name="startedAt" type="string | null">
      When the call started, in ISO 8601 UTC.
    </ResponseField>

    <ResponseField name="endedAt" type="string | null">
      When the call ended, in ISO 8601 UTC.
    </ResponseField>

    <ResponseField name="duration" type="integer | null">
      Length of the call in whole seconds. `0` for a call nobody answered.
    </ResponseField>

    <ResponseField name="endedReason" type="string | null">
      Why the call ended, such as `customer-ended-call`. See [How a call can end](/api-reference/endpoint/get-call#how-a-call-can-end).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="dynamicVariables" type="object | null">
  The `dynamicVariables` you sent when you created the call. `null` if you sent none, or an empty object.
</ResponseField>

```json Answered call theme={null}
{
  "event": "call.completed",
  "outboundCallId": "6ba6543b-8071-4228-a168-eaadb4f27144",
  "contactId": "cd773470-1e07-49a1-beb9-ed02c3fcb129",
  "status": "completed",
  "timestamp": "2026-10-01T14:32:33.271Z",
  "callDetails": {
    "startedAt": "2026-10-01T14:30:02.118Z",
    "endedAt": "2026-10-01T14:32:31.904Z",
    "duration": 150,
    "endedReason": "customer-ended-call"
  },
  "dynamicVariables": {
    "first_name": "Jane",
    "appointment_date": "Tuesday, October 14 at 2:00 PM"
  }
}
```

```json Busy line theme={null}
{
  "event": "call.completed",
  "outboundCallId": "6ba6543b-8071-4228-a168-eaadb4f27144",
  "contactId": "cd773470-1e07-49a1-beb9-ed02c3fcb129",
  "status": "busy",
  "timestamp": "2026-10-01T14:30:44.902Z",
  "callDetails": {
    "startedAt": "2026-10-01T14:30:41.556Z",
    "endedAt": "2026-10-01T14:30:41.556Z",
    "duration": 0,
    "endedReason": "customer-busy"
  },
  "dynamicVariables": null
}
```

## Delivery and retries

* Answer with any `2xx` status to confirm you received the callback. AlloMia ignores the response body.
* AlloMia waits up to **10 seconds** for your answer.
* Any other answer, a timeout, or a connection error counts as a failed try. AlloMia tries again after waiting 1 second, then 5 seconds, then 30 seconds: **4 tries in all**.
* After the fourth failed try, AlloMia stops. That callback isn't sent again. Use the status endpoint to get the result.
* Every try sends exactly the same body.

## Handle it safely

<Steps>
  <Step title="Answer quickly">
    Answer with a `2xx` as soon as you receive the request, then do the work. If you take longer than 10 seconds, AlloMia counts the try as failed and sends the callback again.
  </Step>

  <Step title="Make handling idempotent">
    You can receive the same callback more than once, for example when your server handled it but answered too late. Use `outboundCallId` as the key, and skip a callback you've already handled.
  </Step>

  <Step title="Check that the call is yours">
    Only accept an `outboundCallId` that your system started and is still waiting on. Ignore anything else.
  </Step>

  <Step title="Confirm before you act">
    Before you change your own records, confirm the result with [Get outbound call status](/api-reference/endpoint/get-outbound-call-status), using a clinic key from the clinic that owns the call. Act on what the status endpoint returns.
  </Step>

  <Step title="Don't rely on the callback alone">
    If no callback arrives in the time you expect, check the call with the status endpoint.
  </Step>
</Steps>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.