> For the complete documentation index, see [llms.txt](https://docs.amigo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amigo.ai/developer-guide/platform-api/conversations/calls.md).

# Calls

The outbound call endpoint lets you place a voice call from a workspace phone number to a patient. The request validates a patient entity and includes a reason; after the voice service accepts the call, the Platform API makes a best-effort attempt to emit lineage metadata on that entity.

The request requires a workspace API key, `phone_to`, `use_case_id`, `service_id`, `reason`, exactly one patient identifier, and a valid outbound voice setup. No trigger or campaign resource is required for an on-demand call.

{% hint style="info" %}
**When to use this vs triggers.** This endpoint is for on-demand calls - a CRM event fires, a care coordinator clicks a button, or your backend decides a patient needs a callback right now. For recurring or event-driven outreach, a [Trigger](/developer-guide/platform-api/functions/triggers.md) can invoke an action that creates a scheduled outbound task. The trigger itself does not supply consent, retry, pacing, or business-hours policy; define those in the action and task workflow.
{% endhint %}

## Quick Start

```bash
curl -X POST https://api.platform.amigo.ai/v1/{workspace_id}/calls/outbound \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_to": "+18005551234",
    "use_case_id": "cccccccc-0000-0000-0000-000000000001",
    "service_id": "dddddddd-0000-0000-0000-000000000001",
    "patient_entity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "reason": "lab_results"
  }'
```

The response includes a `call_sid` for tracking. When the best-effort lineage emit succeeds, `event_id` points to that world event; otherwise it is `null`:

```json
{
  "call_sid": "CA1234567890abcdef",
  "status": "queued",
  "phone_from": "+12125559876",
  "setup_id": null,
  "event_id": "f9e8d7c6-b5a4-3210-fedc-ba0987654321"
}
```

***

## Endpoint

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/calls/outbound" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

{% hint style="warning" %}
**`patient_entity_id` and `patient_canonical_id` are mutually exclusive.** Provide exactly one. Use `patient_entity_id` when you have the workspace entity UUID, or `patient_canonical_id` to reference the patient by world model canonical ID (`source:resource_type:id`).
{% endhint %}

Although the schema currently marks `service_id` as optional, the active voice runtime requires it. Supply it on every request.

***

## How It Works

When you create an outbound call, the platform coordinates the initial request and voice runtime while exposing later states separately:

1. **Validation** - The Platform API verifies that the patient resolves to a projected `person` in the workspace.
2. **Voice preparation and dial** - The voice service validates the service and use case, selects an assigned caller ID, prepares the first agent audio, and then asks the carrier to create the call. Preparation or routing failure can reject the request before a usable call is returned.
3. **Connection** - If the destination answers and the agent joins successfully, the runtime begins the configured outbound experience. Startup latency still depends on the selected runtime and providers; there is no one-second speech guarantee.
4. **Conversation** - The service's runtime determines available context, state-machine behavior, and tools. Outbound and inbound calls share infrastructure, but they are not contractually identical and do not universally expose every tool or clinical field.
5. **Completion** - Status and post-call artifacts become available as their operational stores and asynchronous analyses update.

The initial `status` is typically `queued`; later carrier/runtime states can include `ringing`, `in-progress`, `completed`, `failed`, `no-answer`, or `busy`. Use the canonical [Runs](/developer-guide/platform-api/conversations/runs.md) inventory (`kind=conversation&channel=voice`) to browse call runs. The legacy `GET /calls` and `GET /calls/{call_id}` compatibility routes return `Deprecation: true` and `Sunset: 2026-07-01`; `POST /calls/outbound` remains current.

***

## Startup Instructions and Patient Identity

When you provide a `goal`, the Platform API turns it into a startup instruction. If `system_prompt` is also present, the goal is appended to that instruction before it is sent to the voice service. In the standard voice runtime, this instruction takes precedence over task-derived greeting context; it does not replace the agent definition or Context Graph.

The patient identifier validates that a projected `person` exists in the workspace and anchors the best-effort `outbound.initiated` lineage event. The direct call request does not pass that entity ID to the voice service as a dedicated patient-context field. Any caller lookup or ambient context therefore depends on the configured service and the runtime's own identity-resolution path.

| You provide                                   | The agent gets                                                                             |
| --------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `patient_entity_id` or `patient_canonical_id` | Patient validation and best-effort lineage anchor; not a dedicated runtime context payload |
| `goal`                                        | Startup instruction formatted as a call objective                                          |
| `system_prompt`                               | Explicit startup instruction for the call                                                  |
| `system_prompt` + `goal`                      | Goal appended to the explicit startup instruction                                          |
| Neither `system_prompt` nor `goal`            | The service's normal outbound greeting and context behavior                                |

***

## Phone Number Selection

The caller ID is always resolved from the use case. The current selector chooses one phone number at random from the numbers assigned to that outbound-voice use case. The resolved number is returned as `phone_from`; the compatibility `setup_id` field is currently `null`.

```json
{
  "phone_to": "+18005551234",
  "use_case_id": "cccccccc-0000-0000-0000-000000000001",
  "service_id": "dddddddd-0000-0000-0000-000000000001",
  "patient_entity_id": "...",
  "reason": "appointment_reminder"
}
```

Response:

```json
{
  "call_sid": "CA9876543210fedcba",
  "status": "queued",
  "phone_from": "+13105550199",
  "setup_id": null,
  "event_id": "..."
}
```

***

## Callback Chains

When a call is a follow-up to a previous conversation, pass `derived_from_call_sid` with the original call's identifier. When lineage emission succeeds, that identifier is stored in the new `outbound.initiated` event so your own queries can correlate the two calls. It does not create a world-model graph edge or prove that either call completed.

```json
{
  "phone_to": "+18005551234",
  "use_case_id": "cccccccc-0000-0000-0000-000000000001",
  "service_id": "dddddddd-0000-0000-0000-000000000001",
  "patient_entity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "reason": "follow_up",
  "goal": "Follow up on lab results discussed in the previous call",
  "derived_from_call_sid": "CA1234567890abcdef"
}
```

When the relevant initiation events are emitted and projected successfully, the patient's timeline can expose the call identifiers for correlation. Treat that timeline as asynchronous evidence of initiation, not as a guaranteed relationship view or confirmation that either call completed.

***

## External System Correlation

Use `tags` and `metadata` to connect calls to records in your other systems. When lineage emission succeeds, these values are stored in the `outbound.initiated` event. They remain queryable event data but are not guaranteed to appear in every built-in analytics view.

```json
{
  "phone_to": "+18005551234",
  "use_case_id": "cccccccc-0000-0000-0000-000000000001",
  "service_id": "dddddddd-0000-0000-0000-000000000001",
  "patient_entity_id": "...",
  "reason": "care_plan_review",
  "tags": ["chronic_care", "diabetes_management"],
  "metadata": {
    "ehr_encounter_id": "ENC-2026-5678",
    "crm_opportunity_id": "OPP-1234",
    "campaign": "annual_wellness_q2"
  }
}
```

Tags and metadata can support custom event filtering and joins back to source systems when the initiation event is available.

***

## Idempotency

Include an `idempotency_key` on requests that may need an ambiguous retry:

```bash
curl -X POST .../calls/outbound \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "phone_to": "+18005551234",
    "use_case_id": "cccccccc-0000-0000-0000-000000000001",
    "service_id": "dddddddd-0000-0000-0000-000000000001",
    "patient_entity_id": "...",
    "reason": "appointment_reminder",
    "idempotency_key": "reminder-apt-2026-1234"
  }'
```

After a call request completes successfully, its response can be reused under the supplied key for one hour. A later request with the same key can return that response instead of placing another call. This does not serialize concurrent requests and cannot deduplicate a retry made before the first successful response is recorded. If an outcome is ambiguous, reconcile by `call_sid` or the Runs inventory before retrying. If you omit the key, the service generates one internally, so separate client retries do not share a deduplication key.

***

## World Model Lineage

After the voice service accepts the outbound call, the Platform API attempts to emit an `outbound.initiated` event on the patient entity. Emission is best-effort: failure is logged, does not cancel the accepted call, and leaves `event_id` as `null`. A successfully emitted event captures:

* **Who** requested the call - the API key identifier (not the secret)
* **Why** - the `reason` and `goal` you provided
* **What** external systems are involved - your `tags` and `metadata`
* **Where** it fits in a sequence - `derived_from_call_sid` if this is a callback

The `event_id` in the response points to this event. You can use it to:

* Correlate initiation with available downstream call records by `call_sid`
* Query the patient's timeline for available initiation events
* Build custom event analysis for API-initiated calls

Once the asynchronous world projection ingests the event, it can appear in the patient's timeline; read-after-write visibility is not immediate.

***

## Error Responses

| Status | When                                                                                                                                                          | What to do                                                                                                                                                                                    |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `404`  | Patient entity does not exist in this workspace (or is not a person entity), or no phone number is available for the `use_case_id`                            | Verify the `patient_entity_id` exists via `GET /v1/{workspace_id}/world/entities/{entity_id}`. For use case 404s, check your phone number provisioning.                                       |
| `422`  | Invalid E.164 phone number, invalid UUID, missing required field, missing `service_id` at the active voice runtime, or invalid patient-identifier combination | Supply `phone_to`, `use_case_id`, `service_id`, `reason`, and exactly one patient identifier (`patient_entity_id` or `patient_canonical_id`).                                                 |
| `429`  | Rate limit exceeded                                                                                                                                           | Back off and retry. The limit applies per API key and per route (scoped to `/calls/outbound`).                                                                                                |
| `502`  | The voice infrastructure returned an error                                                                                                                    | First reconcile whether a call was created. If retry is appropriate, preserve the same `idempotency_key`; a failure before the first successful response was recorded can still be ambiguous. |
| `503`  | Voice agent or outbound calling is not configured for this workspace                                                                                          | Contact support to enable outbound calling.                                                                                                                                                   |
| `504`  | Phone number selection timed out                                                                                                                              | Retry. If persistent, check phone number provisioning for the use case.                                                                                                                       |

***

## Rate Limits

The outbound call endpoint allows 1000 requests per minute, enforced per API key and per route. This limit is scoped to `/calls/outbound` only; other Platform API routes apply their own limits.

If you exceed the limit, you receive a `429` response. Preserve the same `idempotency_key` when retrying, but reconcile an ambiguous earlier attempt because the key does not make simultaneous retries or retries sent before the first successful response duplicate-proof.

Carrier-level constraints on individual phone numbers are enforced separately by the downstream voice carrier and are independent of this API rate limit.

***

## Full Example

A CRM webhook fires when a patient's lab results are ready. Your backend calls the outbound API to notify them:

```bash
curl -X POST https://api.platform.amigo.ai/v1/ea03a2a2-1234-5678-abcd-ef1234567890/calls/outbound \
  -H "Authorization: Bearer amigo_pk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "phone_to": "+14155550123",
    "use_case_id": "cccccccc-0000-0000-0000-000000000001",
    "service_id": "dddddddd-0000-0000-0000-000000000001",
    "patient_entity_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "reason": "lab_results",
    "goal": "Inform the patient that their A1C results are ready and discuss next steps for their diabetes management plan",
    "tags": ["lab_notification", "diabetes"],
    "metadata": {
      "ehr_order_id": "ORD-2026-7890",
      "lab_type": "A1C"
    },
    "idempotency_key": "lab-notify-ORD-2026-7890"
  }'
```

The goal is appended to the prompt sent to the configured service. Patient-context availability depends on that service's runtime and tools, so validate the intended context path before relying on clinical fields during the call.

***

## Troubleshooting

| Symptom                                            | Cause                                                                                                                                                                                                                   | Fix                                                                                                                                                                                                                            |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Call connects but patient hears silence/hold music | The prepared agent audio or conference join did not become available as expected.                                                                                                                                       | Verify `service_id`, the service release, and outbound voice setup; inspect the resulting run and call status.                                                                                                                 |
| `503 Service Unavailable`                          | Outbound calling is not enabled for this workspace, or the voice infrastructure is temporarily unavailable.                                                                                                             | Contact support to verify outbound calling is configured.                                                                                                                                                                      |
| `404 Not Found` on `patient_entity_id`             | The entity doesn't exist, isn't in this workspace, or isn't a `person` entity type.                                                                                                                                     | Check the entity via `GET /v1/{workspace_id}/world/entities/{entity_id}`.                                                                                                                                                      |
| Call rings but goes to voicemail                   | The patient did not answer.                                                                                                                                                                                             | Check the [Runs](/developer-guide/platform-api/conversations/runs.md) inventory. A later call attempt should use a deliberate retry policy and a new idempotency key; `derived_from_call_sid` can record correlation metadata. |
| Agent speaks but has no patient context            | The direct endpoint validates the patient for lineage but does not pass the entity ID as a dedicated voice-runtime context field. Caller lookup, projection freshness, or service configuration may also be incomplete. | Verify the service's identity-resolution and world-tool path instead of assuming the request's patient identifier preloads context.                                                                                            |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.amigo.ai/developer-guide/platform-api/conversations/calls.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
