# Making an outbound call

**Availability: Preview**

This guide shows how to place a single outbound call: choosing the agent and destination, passing variables the agent can use and metadata you get back, making the request safe to retry, and handling the reasons a call can be refused.

You will send one request, get back a call in status `queued`, and learn exactly what that response does and does not promise. Placing a call is a [chargeable operation](#charges) in live mode, so the guide is careful about retries.

## Before you begin

- An API key with `calls:write` (and `calls:read` to follow the call afterwards). Start with a test key: test mode never dials anyone and never charges.
- A ready agent. In test mode you can use the built-in Sample agent, `agent_1ZltiWzxrXmMprGD6q63`; for your own agent see [Creating and preparing agents](/guides/creating-and-preparing-agents).
- Your key in place of `<token>` in the examples. Every request goes to `https://voice-api.relationsai.com/v1`.
- For the cURL examples in these guides: bash, `jq`, and curl 7.76 or newer (for `--fail-with-body`).

In test mode the destination number chooses what happens, so you can try every outcome on purpose:

| `to` | Simulated result |
|---|---|
| `+12025550100` | Answered and completed, with transcript, recording and analysis |
| `+12025550101` | No answer |
| `+12025550102` | Busy |
| `+12025550103` | Failed (network error) |
| `+12025550104` | Answered, nobody spoke (`no_response`, how most voicemail greetings appear) |
| `+12025550105` | Completed after a longer conversation |
| `+12025550106` | Completed, the person hangs up early (short duration) |
| any other number | Completed |

## Create the call

Send `POST /calls` with the agent and the destination in E.164 format (`+` and country code, no spaces).

Every request you send is executed: sending the same request twice places two calls. Before retrying a call whose answer you did not see, check whether it exists (see [Make it safe to retry](#make-it-safe-to-retry)).

Request body:

| Field | Required | Notes |
|---|---|---|
| `agent_id` | yes | The agent that makes the call (`agent_...`). The API never picks one for you. |
| `to` | yes | The number to call, E.164. |
| `phone_number_id` | no | The workspace number to call from (`pn_...`). Default: the agent's outbound number. Required when the agent has more than one. |
| `variables` | no | Values the agent can use in this conversation. |
| `metadata` | no | Your own data, stored with the call and returned in reads and events. |

Request bodies are strict: an unknown field is a `400 validation_error`, never silently ignored.

[`POST /calls`](/api/calls#place-a-call)

```bash
curl -X POST https://voice-api.relationsai.com/v1/calls \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "agent_id": "agent_1ZltiWzxrXmMprGD6q63",
  "to": "+12025550100",
  "variables": {
    "first_name": "Asha",
    "amount_due": "4,200"
  },
  "metadata": {
    "crm_record_id": "L-1042"
  }
}'
```
```js
const response = await fetch("https://voice-api.relationsai.com/v1/calls", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "agent_id": "agent_1ZltiWzxrXmMprGD6q63",
    "to": "+12025550100",
    "variables": {
      "first_name": "Asha",
      "amount_due": "4,200"
    },
    "metadata": {
      "crm_record_id": "L-1042"
    }
  }),
});
const call = await response.json();
```
```python

response = requests.post(
    "https://voice-api.relationsai.com/v1/calls",
    headers={
        "Authorization": "Bearer <token>",
    },
    json={
        "agent_id": "agent_1ZltiWzxrXmMprGD6q63",
        "to": "+12025550100",
        "variables": {
            "first_name": "Asha",
            "amount_due": "4,200",
        },
        "metadata": {
            "crm_record_id": "L-1042",
        },
    },
    timeout=30,
)
call = response.json()
```

Complete runnable example: [cURL script](/examples/guides/making-an-outbound-call/create-call.sh) · [JavaScript](/examples/guides/making-an-outbound-call/create-call.mjs) · [Python](/examples/guides/making-an-outbound-call/create-call.py)

## What the response means

A successful request answers **`202 Accepted`** with the call object:

```json
{
  "id": "call_6Vb3Nm8Qw2Er5Ty9Ui4Op7",
  "object": "call",
  "livemode": false,
  "status": "queued",
  "outcome": null,
  "end_reason": null,
  "direction": "outbound",
  "source": "api",
  "agent_id": "agent_1ZltiWzxrXmMprGD6q63",
  "campaign_id": null,
  "phone_number_id": "pn_6Vhm3sFrB5UpmXAnsvmDu",
  "from": "+12025550150",
  "to": "+12025550100",
  "metadata": { "crm_record_id": "L-1042" },
  "created_at": "2026-10-21T09:30:00.000Z",
  "ended_at": null,
  "duration_seconds": null,
  "billable_minutes": null,
  "cost": null,
  "artifacts": {
    "transcript": { "state": "pending" },
    "recording": { "state": "pending" },
    "analysis": { "state": "pending" }
  },
  "settled": false,
  "settled_at": null
}
```

`202` means the call **passed every check** (agent readiness, account, balance and spend limits, the do-not-call list, consent, calling hours and the per-number daily limit) and was handed to the telephony network, or is being handed over. It does **not** mean anyone answered, and nothing about the conversation is known yet: `outcome`, `duration_seconds`, `cost` and every artifact are still empty.

Store `id`. It is how you match later webhook events and reads to this call.

## What happens next

The call runs after your request returns. It moves through these statuses:

1. **`queued`**: accepted and being placed.
2. **`ringing`**: not reported today. The workspace's `capabilities.call_ringing_status` is `false`, so a call goes from `queued` straight to `in_progress` or to `ended`. Do not wait for `ringing`.
3. **`in_progress`**: the person answered and the conversation is happening.
4. **`ended`**: `outcome`, `end_reason` and `duration_seconds` are known. The `call.ended` event is sent.
5. **Artifacts ready**: the transcript, recording and analysis become `ready` (or `absent` when there is none, for example a call nobody answered).
6. **Settled**: `settled` becomes `true` when outcome, duration, artifacts and the charge are all final, and the `call.settled` event is sent. Treat `outcome`, `duration_seconds` and `cost` as final only now.

In test mode a call reaches `ended` within about 20 seconds and settles within about 30. In live mode artifacts are usually ready within a minute of the end and sometimes several minutes later.

Follow the call with webhooks (recommended) or by polling: see [Tracking call progress](/guides/tracking-call-progress).

> **A start that cannot be confirmed:** In rare cases the telephony network does not confirm that a call started. You still get `202` with the call `queued`, because the call may be starting. The API then resolves it on its own: either the call proceeds normally, or it ends within about two minutes with `outcome: failed` and `end_reason: not_confirmed`: the start was never confirmed, and the call is not charged. Do not place the call again yourself; follow the existing call id.

## Variables and metadata

`variables` are values the agent can use in this conversation, for example `{"first_name": "Asha"}`. Up to 50 keys; keys are lowercase letters, digits and `_`, starting with a letter, up to 64 characters; values are strings up to 500 characters. When `to` matches one of your [contacts](/guides/managing-contacts), the contact's name and custom fields are added as variables automatically, and the values you send win.

`metadata` is yours: up to 16 keys (letters, digits, `_`, `-`, `.`, up to 40 characters) with string values up to 500 characters. It is stored with the call and returned unchanged in every read and in `call.ended` and `call.settled`. It is never sent to the person called and never used by the agent. That makes it the right place for your own ids, such as a CRM record id. Do not put secrets or anything you would not log in it.

## Choosing the caller number

Without `phone_number_id`, the call uses the agent's outbound number and the response shows it in `phone_number_id` and `from`. If the agent is assigned to several outbound numbers the API will not guess, and refuses with [`phone_number_required`](/errors/phone_number_required). List your numbers with [`GET /phone-numbers`](/api/phone-numbers#list-phone-numbers) and send the `pn_...` id of one whose `capabilities.outbound` is `true`.

## Make it safe to retry

Every request is executed, so a blind retry after a timeout can place a **second call**. Put your own id in `metadata`, and if you did not see the answer (a timeout, a dropped connection, a `5xx`), first list calls with `to` and `created_after` and look for your `metadata`; send the request again only if the call is not there. Answers that say nothing happened (refusals with `retry: "no"`, `429`, `503`) can be resent without a check.

For extra safety you can also send an optional `Idempotency-Key` header and reuse it on every retry of that call: a retry of a request that already succeeded then returns the original response with `Idempotent-Replayed: true` instead of placing another call. See [Idempotency](/fundamentals/idempotency) and the full pattern in [Retry and reliability practices](/guides/retry-and-reliability).

## Charges

Live calls are charged per started minute of talk time at your workspace rate, from the workspace balance. A call that is not answered is not charged. `billable_minutes` and `cost` (in minor units; for INR that is paise, so an amount of `600` would be ₹6.00) are final once the call is settled. Test-mode calls always cost zero.

Before dialling, the API checks the balance and any spend limit, and in live mode counts the call against the workspace's daily limit of calls placed through the API (`limits.daily_call_limit` on [`GET /workspace`](/api/workspace#retrieve-the-workspace); resets at 00:00 UTC). A refused call is not dialled and not charged.

## Responses

Response 202 (Accepted and queued (test mode)):

```json
{
  "id": "call_6Vb3Nm8Qw2Er5Ty9Ui4Op7",
  "object": "call",
  "livemode": false,
  "status": "queued",
  "outcome": null,
  "end_reason": null,
  "direction": "outbound",
  "source": "api",
  "agent_id": "agent_1ZltiWzxrXmMprGD6q63",
  "campaign_id": null,
  "phone_number_id": "pn_6Vhm3sFrB5UpmXAnsvmDu",
  "from": "+12025550150",
  "to": "+12025550100",
  "metadata": {
    "crm_lead_id": "L-20931"
  },
  "created_at": "2026-10-21T09:30:00.000Z",
  "ended_at": null,
  "duration_seconds": null,
  "billable_minutes": null,
  "cost": null,
  "artifacts": {
    "transcript": {
      "state": "pending"
    },
    "recording": {
      "state": "pending"
    },
    "analysis": {
      "state": "pending"
    }
  },
  "settled": false,
  "settled_at": null
}
```

Response 400 (validation_error):

```json
{
  "error": {
    "code": "validation_error",
    "message": "One or more fields are invalid.",
    "details": {
      "errors": [
        {
          "field": "agent_id",
          "issue": "required",
          "message": "agent_id is required."
        }
      ]
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/validation_error"
  }
}
```

Response 400 (invalid_json):

```json
{
  "error": {
    "code": "invalid_json",
    "message": "The request body is not valid JSON.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/invalid_json"
  }
}
```

Response 400 (invalid_idempotency_key):

```json
{
  "error": {
    "code": "invalid_idempotency_key",
    "message": "The Idempotency-Key header must be 1 to 255 printable ASCII characters with no spaces.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/invalid_idempotency_key"
  }
}
```

Response 400 (invalid_phone_number):

```json
{
  "error": {
    "code": "invalid_phone_number",
    "message": "The phone number is not a valid E.164 number.",
    "details": {
      "field": "to"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/invalid_phone_number"
  }
}
```

Response 401 (authentication_required):

```json
{
  "error": {
    "code": "authentication_required",
    "message": "Send your API key as a Bearer token in the Authorization header.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/authentication_required"
  }
}
```

Response 401 (invalid_api_key):

```json
{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key is not valid.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/invalid_api_key"
  }
}
```

Response 401 (api_key_expired):

```json
{
  "error": {
    "code": "api_key_expired",
    "message": "This API key has expired.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/api_key_expired"
  }
}
```

Response 401 (api_key_revoked):

```json
{
  "error": {
    "code": "api_key_revoked",
    "message": "This API key has been revoked.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/api_key_revoked"
  }
}
```

Response 401 (legacy_api_key_not_supported):

```json
{
  "error": {
    "code": "legacy_api_key_not_supported",
    "message": "Legacy API keys cannot be used with the v1 API. Create a scoped key in the console.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/legacy_api_key_not_supported"
  }
}
```

Response 402 (insufficient_balance):

```json
{
  "error": {
    "code": "insufficient_balance",
    "message": "The workspace balance is too low to start this call. Top up to continue.",
    "details": {
      "balance": {
        "amount": 250,
        "currency": "INR"
      },
      "required": {
        "amount": 600,
        "currency": "INR"
      }
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/insufficient_balance"
  }
}
```

Response 402 (spend_limit_reached):

```json
{
  "error": {
    "code": "spend_limit_reached",
    "message": "This workspace has reached the monthly spend limit set by its admin.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/spend_limit_reached"
  }
}
```

Response 403 (insufficient_scope):

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key does not have the scope this operation requires.",
    "details": {
      "required_scopes": [
        "calls:write"
      ]
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/insufficient_scope"
  }
}
```

Response 403 (insufficient_permissions):

```json
{
  "error": {
    "code": "insufficient_permissions",
    "message": "The member who created this API key no longer has permission for this operation.",
    "details": {
      "required_scopes": [
        "campaigns:write"
      ]
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/insufficient_permissions"
  }
}
```

Response 403 (api_access_not_enabled):

```json
{
  "error": {
    "code": "api_access_not_enabled",
    "message": "API access is not enabled for this workspace.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/api_access_not_enabled"
  }
}
```

Response 403 (ip_not_allowed):

```json
{
  "error": {
    "code": "ip_not_allowed",
    "message": "Requests from this IP address are not allowed for this workspace.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/ip_not_allowed"
  }
}
```

Response 403 (live_key_not_allowed_in_browser):

```json
{
  "error": {
    "code": "live_key_not_allowed_in_browser",
    "message": "Live API keys cannot be used from a browser. Use a test key, or call the API from your server.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/live_key_not_allowed_in_browser"
  }
}
```

Response 403 (account_inactive):

```json
{
  "error": {
    "code": "account_inactive",
    "message": "This workspace cannot place calls because its account is not active.",
    "details": {
      "reason": "trial_expired"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/account_inactive"
  }
}
```

Response 404 (resource_not_found):

```json
{
  "error": {
    "code": "resource_not_found",
    "message": "No such resource.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/resource_not_found"
  }
}
```

Response 409 (idempotency_key_reused):

```json
{
  "error": {
    "code": "idempotency_key_reused",
    "message": "This Idempotency-Key was already used with a different request.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/idempotency_key_reused"
  }
}
```

Response 409 (idempotency_key_in_progress):

```json
{
  "error": {
    "code": "idempotency_key_in_progress",
    "message": "A request with this Idempotency-Key is still being processed.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "after",
    "doc_url": "https://docs.relationsai.com/errors/idempotency_key_in_progress"
  }
}
```

Response 409 (idempotency_outcome_unknown):

```json
{
  "error": {
    "code": "idempotency_outcome_unknown",
    "message": "An earlier request with this Idempotency-Key did not finish, and its outcome is unknown.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/idempotency_outcome_unknown"
  }
}
```

Response 413 (payload_too_large):

```json
{
  "error": {
    "code": "payload_too_large",
    "message": "The request body is too large.",
    "details": {
      "limit_bytes": 1048576
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/payload_too_large"
  }
}
```

Response 415 (unsupported_media_type):

```json
{
  "error": {
    "code": "unsupported_media_type",
    "message": "Request bodies must be sent as application/json.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/unsupported_media_type"
  }
}
```

Response 422 (agent_not_ready):

```json
{
  "error": {
    "code": "agent_not_ready",
    "message": "This agent cannot place calls right now.",
    "details": {
      "agent_id": "agent_3hT9kQ2mVx7RbN4pLc8Wz1",
      "reasons": [
        "agent_not_published"
      ]
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/agent_not_ready"
  }
}
```

Response 422 (calling_not_configured):

```json
{
  "error": {
    "code": "calling_not_configured",
    "message": "Calling is not set up for this workspace yet.",
    "details": {
      "reason": "no_outbound_assignment"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/calling_not_configured"
  }
}
```

Response 422 (outside_calling_hours):

```json
{
  "error": {
    "code": "outside_calling_hours",
    "message": "This is outside the permitted calling window for this workspace. Nothing was dialled.",
    "details": {
      "window": "09:00-21:00",
      "timezone": "Asia/Kolkata"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/outside_calling_hours"
  }
}
```

Response 422 (phone_number_not_usable):

```json
{
  "error": {
    "code": "phone_number_not_usable",
    "message": "This phone number cannot place outbound calls for this agent.",
    "details": {
      "phone_number_id": "pn_5Lq8Xc2Rk9TzW3mBv7Np4d"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/phone_number_not_usable"
  }
}
```

Response 422 (phone_number_required):

```json
{
  "error": {
    "code": "phone_number_required",
    "message": "This agent has more than one outbound number. Choose one with phone_number_id.",
    "details": {
      "agent_id": "agent_3hT9kQ2mVx7RbN4pLc8Wz1"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/phone_number_required"
  }
}
```

Response 422 (dnc_listed):

```json
{
  "error": {
    "code": "dnc_listed",
    "message": "This number is on your do-not-call list, so the call was not placed.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/dnc_listed"
  }
}
```

Response 422 (consent_required):

```json
{
  "error": {
    "code": "consent_required",
    "message": "This contact has no recorded consent, and consent is required for calls in this workspace.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/consent_required"
  }
}
```

Response 422 (contact_daily_limit_reached):

```json
{
  "error": {
    "code": "contact_daily_limit_reached",
    "message": "This number has already been called the maximum number of times allowed in 24 hours.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/contact_daily_limit_reached"
  }
}
```

Response 422 (call_not_accepted):

```json
{
  "error": {
    "code": "call_not_accepted",
    "message": "The call could not be placed. Nothing was dialled and nothing was charged.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "no",
    "doc_url": "https://docs.relationsai.com/errors/call_not_accepted"
  }
}
```

Response 429 (rate_limited):

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after the number of seconds in the Retry-After header.",
    "details": {
      "limit_class": "write"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "after",
    "doc_url": "https://docs.relationsai.com/errors/rate_limited"
  }
}
```

Response 429 (daily_call_limit_reached):

```json
{
  "error": {
    "code": "daily_call_limit_reached",
    "message": "This workspace has reached its daily limit of calls placed through the API.",
    "details": {
      "limit": 500,
      "requested": 120,
      "remaining": 37,
      "resets_at": "2026-10-22T00:00:00.000Z"
    },
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "after",
    "doc_url": "https://docs.relationsai.com/errors/daily_call_limit_reached"
  }
}
```

Response 429 (calling_capacity_reached):

```json
{
  "error": {
    "code": "calling_capacity_reached",
    "message": "Too many calls are being started right now. Retry after the number of seconds in the Retry-After header.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "after",
    "doc_url": "https://docs.relationsai.com/errors/calling_capacity_reached"
  }
}
```

Response 500 (internal_error):

```json
{
  "error": {
    "code": "internal_error",
    "message": "Something went wrong on our side.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "yes",
    "doc_url": "https://docs.relationsai.com/errors/internal_error"
  }
}
```

Response 503 (service_unavailable):

```json
{
  "error": {
    "code": "service_unavailable",
    "message": "The service is temporarily unavailable. Retry after the number of seconds in the Retry-After header.",
    "details": {},
    "request_id": "req_4k7m2qat5xvz3c6vqnwpbdfh",
    "retry": "after",
    "doc_url": "https://docs.relationsai.com/errors/service_unavailable"
  }
}
```

## What can go wrong

Every error has `error.code`, `error.retry` and `error.request_id`. Branch on the code, and log the request id. Most refusals with `retry: "no"` happened before anything was dialled, so after fixing the cause you can send the same request again. If you send an idempotency key, two codes are different: [`idempotency_outcome_unknown`](/errors/idempotency_outcome_unknown) means an earlier attempt may have placed the call (find out whether it exists first), and [`idempotency_key_reused`](/errors/idempotency_key_reused) means the key belongs to a different request (use a new key).

**Fix the request** (nothing was done):

- [`validation_error`](/errors/validation_error) (400): a field is missing, unknown or out of range; `details.errors` lists each one.
- [`invalid_phone_number`](/errors/invalid_phone_number) (400): `to` is not E.164. Send `+` and the country code.
- [`resource_not_found`](/errors/resource_not_found) (404): `agent_id` or `phone_number_id` is not in this workspace and mode. Use ids read with a key of the same workspace and mode.
- [`idempotency_key_reused`](/errors/idempotency_key_reused) (409): only if you send an idempotency key: this key was used for a different request. Use a new key for a new call.

**Fix the setup** (an admin acts in the console, then you send the same request again):

- [`agent_not_ready`](/errors/agent_not_ready) (422): `details.reasons` says why ([readiness](/guides/creating-and-preparing-agents)).
- [`phone_number_required`](/errors/phone_number_required) (422): the agent has several outbound numbers; send `phone_number_id`.
- [`phone_number_not_usable`](/errors/phone_number_not_usable) (422): that number cannot place outbound calls for this agent; pick another or omit it.
- [`calling_not_configured`](/errors/calling_not_configured) (422): calling setup is incomplete; `details.reason` names the step.
- [`insufficient_balance`](/errors/insufficient_balance) (402), [`spend_limit_reached`](/errors/spend_limit_reached) (402), [`account_inactive`](/errors/account_inactive) (403): top up, raise the limit, or upgrade.

**Respect the refusal** (the rules protecting the person; see [Consent and DNC](/guides/consent-and-dnc)):

- [`dnc_listed`](/errors/dnc_listed) (422): the number is on your do-not-call list. Do not call it.
- [`consent_required`](/errors/consent_required) (422): the contact has no recorded consent and the workspace requires it. Record consent only if it was really given.
- [`outside_calling_hours`](/errors/outside_calling_hours) (422): outside the calling window in `details.window` and `details.timezone`. Retry inside it.
- [`contact_daily_limit_reached`](/errors/contact_daily_limit_reached) (422): this number was called the maximum times in 24 hours.
- [`call_not_accepted`](/errors/call_not_accepted) (422): the network definitively refused this call; nothing was dialled. Check the number; contact support if it repeats.

**Retry** (if you sent an idempotency key, send the same one):

- [`idempotency_key_in_progress`](/errors/idempotency_key_in_progress) (409): only with an idempotency key: the first attempt is still running. Wait `Retry-After`.
- [`calling_capacity_reached`](/errors/calling_capacity_reached) (429): too many call starts right now; nothing was dialled. Wait `Retry-After`.
- [`rate_limited`](/errors/rate_limited) (429): over the `dial` rate limit. Wait `Retry-After`.
- [`service_unavailable`](/errors/service_unavailable) (503): a dependency is down; nothing was done. Wait `Retry-After`.
- [`internal_error`](/errors/internal_error) (500): unexpected, and the call **may** have been accepted. Check first: list calls with `to` and `created_after` and match your `metadata`; send it again only if the call is not there.

**Stop and check first**:

- [`idempotency_outcome_unknown`](/errors/idempotency_outcome_unknown) (409): only with an idempotency key: an earlier attempt with this key was interrupted. Check whether the call exists before using a new key ([how](/guides/retry-and-reliability#when-the-outcome-is-unknown)).
- [`daily_call_limit_reached`](/errors/daily_call_limit_reached) (429): the workspace's daily API call limit is reached. Wait for the reset, or contact support.

All codes, with causes and fixes, are in the [error-code reference](/fundamentals/error-codes).

## Check it works in test mode

1. Send the request above with the Sample agent and `"to": "+12025550100"`. You get `202` and `"status": "queued"`.
2. List calls with `to` set to `+12025550100`: your call is there with your `metadata`. (Sending the request a second time would place a second call, unless you send it with the same optional idempotency key, which replays the first answer.)
3. Try `TO=+12025550101` and follow the call: it ends with `outcome: no_answer` and `artifacts.recording.state: absent`.
4. Add `+12025550107` to the do-not-call list ([Consent and DNC](/guides/consent-and-dnc)) and call it: `422 dnc_listed`, exactly as in live mode.

## Next steps

[Tracking call progress](/guides/tracking-call-progress).
