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

# Introduction

> Get started with the platform REST API - authentication, conventions, and your first call.

<Note>
  **This API reference is in beta.** The endpoints documented here are live in
  production and safe to build against. The reference itself is still growing -
  more of the Placetel platform API will be added over time.
</Note>

The REST API lets you drive Placetel from your own systems. Today the
reference covers the **Outbound Calls API**: place AI-powered calls to your
contacts, personalize each one, and review the outcome.

**Base URL:** `https://aipro.placetel.de/api/v1/outbound`

## Authentication

Every request must send a Bearer token in the `Authorization` header:

```
Authorization: Bearer <your-api-token>
```

Generate (or rotate) your token on the **Outbound Calls** page in your dashboard
("Generate API Token"). The token is shown once - store it securely; rotating it
invalidates the previous token. Each token only reaches your own agents and data.

## Phone number format

All phone numbers must be in **E.164 international format** - a `+` followed by
digits only. Send the exact format; numbers are not reformatted for you.

```
✅  +491761234567
✅  +442071234567
❌  0176 1234567
❌  +49 176 123 4567
❌  491761234567
```

## Identifying your agent

You identify an agent by its phone number (a leading `+` is optional), or by its SIP ID (a value starting with `t`) - not by an internal database ID.

When `agent_number` is a SIP ID, you must also send `caller_number`: the caller ID
shown to the recipient, which has to be a number registered on your Placetel
account.

## Personalizing the prompt

Inject dynamic values into your agent's prompt at call time so each recipient
hears a tailored message.

<Steps>
  <Step title="Add placeholders to the agent's prompt">
    Use `{{name}}` syntax in the prompt:

    ```
    Hello {{customer_name}}, I'm calling about your order {{order_id}}.
    ```
  </Step>

  <Step title="Provide the values when placing the call">
    ```json theme={null}
    {
      "destination": "+491761234567",
      "agent_number": "+4921186942846",
      "prompt_variables": {
        "customer_name": "Max Mustermann",
        "order_id": "ORD-2026-0042"
      }
    }
    ```

    The prompt is resolved with your values before the call starts.
  </Step>
</Steps>

### Rules

* Every `{{placeholder}}` in the prompt must be provided, or the request is
  rejected with `400`.
* Extra keys not used by the prompt are accepted and ignored.
* Key names should be lower `snake_case` (letters, digits, underscores), e.g.
  `customer_name`. The `system__` and `secret__` prefixes are reserved.
* Values must be strings - send numbers and booleans as strings (`"3"`, `"true"`).
* Up to 25 keys; each value up to 500 characters.
* In bulk, use either per-destination or a shared top-level `prompt_variables`,
  not both.

## Correlating calls with your own records

Send your own identifier in `metadata.external_call_id` and it is echoed back at
the top level of the placement response, and carried through to the post-call
callback:

```json theme={null}
{
  "destination": "+491761234567",
  "agent_number": "+4921186942846",
  "metadata": { "external_call_id": "crm-8842" }
}
```

The response's `call_id` is the primary public identifier for a call. It is also
available in post-call actions as `%%call_id%%`.

## Checking results

Every call - single or bulk - belongs to a batch. Use **List batches** and **Get a
batch** (under *Batches* in the sidebar) to see whether each call connected.

Outcomes also appear in your dashboard, in your
[outbound call history](https://aipro.placetel.de/outbound/history) or in
[Conversations](https://aipro.placetel.de/conversations), together with the full
transcript. Filter by the agent you placed the calls with to find them.

## Errors

Existing outbound domain errors use this shape:

```json theme={null}
{
  "success": false,
  "error": "Description of the issue"
}
```

Messages are always in English. Limit-related errors add a machine-readable
`error_type`, and rate limits add a `detail` pointing you to where you can request
more capacity.

| Code  | Meaning                                                          |
| ----- | ---------------------------------------------------------------- |
| `400` | Bad request - missing or invalid parameters. No call was placed. |
| `401` | Unauthorized - missing or invalid API token                      |
| `403` | Forbidden - outbound calling isn't enabled for your account      |
| `404` | Not found - agent, batch, or call record not found               |
| `422` | Your daily call limit has been reached, or the account is locked |
| `429` | Too many requests - retry after the `Retry-After` period         |
| `503` | Temporarily unavailable - retry shortly                          |

<Tip>
  A **bulk** request returns `201` even when some or all destinations fail. A
  non-`201` means the whole request was rejected and nothing was dialed - check
  each entry's `success` and `status` in `results`.
</Tip>

## Upcoming customer API request limits

<Note>
  The following request-limit contract is prepared for a future rollout. These
  limits are not enabled yet. Existing outbound call limits continue to apply.
</Note>

Selected customer API operations will share a request allowance per tenant and
operation group. All users and API tokens belonging to that tenant share the
allowance. Rotating a token or changing an IP address does not reset it. Partner
API operations are excluded.

| Policy            | Customer operations                                                                  |
| ----------------- | ------------------------------------------------------------------------------------ |
| `api_read`        | Voice-agent, concern, tool, evaluation and evaluation-result reads                   |
| `api_write`       | Voice-agent, concern, tool and evaluation changes, including evaluation cancellation |
| `tool_execute`    | Tool execution                                                                       |
| `eval_start`      | Starting an evaluation run                                                           |
| `outbound_create` | Placing a single call                                                                |
| `outbound_bulk`   | Placing calls in bulk                                                                |
| `outbound_read`   | Listing and reading batches                                                          |
| `outbound_cancel` | Cancelling a consent                                                                 |

An allowance permits an initial burst and replenishes over time. Its burst size
is not a strict per-minute quota. Each accepted request uses one unit, including
a bulk request; existing limits still govern the number of calls it can place.
Cancellation has a separate allowance from starting evaluations or placing calls.

When the request allowance is exhausted, the API will return `429` with a
`Retry-After` header in seconds and this body:

```json theme={null}
{
  "error": "mf-core:rate-limit-exceeded",
  "message": "Rate limit exceeded",
  "details": { "policy": "api_write", "retry_after": 3 }
}
```

Wait at least the indicated interval before retrying and add a small random
delay when several clients retry together. Other requests can consume the newly
available allowance, so a later retry can still receive `429`.

If the API cannot check the allowance, it will return `503`:

```json theme={null}
{
  "error": "mf-core:rate-limiter-unavailable",
  "message": "API rate limiting is temporarily unavailable",
  "details": { "policy": "api_write" }
}
```

This response has no quota reset time. Retry with bounded exponential backoff.
For either of these new errors, the requested operation has not executed.
Handle the stable `error` identifier rather than matching the message text.

## Quickstart

```bash theme={null}
curl -X POST https://aipro.placetel.de/api/v1/outbound/calls \
  -H "Authorization: Bearer <your-api-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "destination": "+491761234567",
    "agent_number": "+4921186942846"
  }'
```

## Removed endpoints

`POST /api/v1/outbound/calls/place` is no longer available and responds `410
Gone`. It existed for the retired SMS-consent flow. Use **Place a call** or
**Place calls in bulk** instead.
