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

# Place a call

> POST /api/v1/calls — your agent rings a customer, and says why.

```http theme={null}
POST /api/v1/calls
Authorization: Bearer ta_live_…
Content-Type: application/json
Idempotency-Key: order-10432-delivery-call
```

```json theme={null}
{
  "to": "919876543210",
  "purpose": "I'm calling from Sunrise Clinic about your appointment with Doctor Iyer tomorrow at four."
}
```

```json theme={null}
{
  "id": "out_9f2c1a44-...",
  "to": "919876543210",
  "status": "ringing"
}
```

The agent dials, and the moment the customer picks up it says your `purpose` and
then holds a normal conversation — same knowledge, same instructions, same
memory of that customer as every other channel.

## Body

<ParamField body="to" type="string" required>
  The customer's number. E.164, with or without the `+`; spaces are fine.

  Normalised the same way the contact importer does, so `+91 98765 43210` and
  `919876543210` reach the same customer rather than making a second one.
</ParamField>

<ParamField body="purpose" type="string" required>
  Why you are calling, in your own words. **This is spoken aloud** as the first
  thing the customer hears.

  Maximum 300 characters. Write it as you would say it: *"I'm calling from
  Sunrise Clinic about your appointment tomorrow at four"*, not *"appointment
  reminder"*.
</ParamField>

<ParamField body="agent" type="string">
  Which agent is calling — its name or id.

  Only needed when more than one agent in your workspace answers the phone. With
  one, it is chosen for you; with several and no `agent`, the request is refused
  rather than guessing which of your businesses is ringing the customer.
</ParamField>

<Warning>
  **`purpose` is required and it is not a label.** A call that begins "how can I
  help you?" to somebody who did not dial you is a robocall, and it is the
  fastest way to teach your customers not to answer your number.
</Warning>

## Who you may call

Two rules, both enforced, and both stricter than the messaging API.

<AccordionGroup>
  <Accordion title="They must already be a contact">
    A number that is not in your contacts is refused. There is no way to dial an
    arbitrary number through this API.

    Import them, or let them reach you once on any channel first.

    ```json theme={null}
    { "error": "That number is not one of your contacts. Import them before calling." }
    ```
  </Accordion>

  <Accordion title="They must not have opted out">
    A customer recorded as having withdrawn consent is refused, whichever
    channel they said it on. Somebody who told the agent to stop on WhatsApp has
    not agreed to be telephoned instead.

    ```json theme={null}
    { "error": "That customer has asked not to be contacted." }
    ```

    A contact nobody has ever asked is **allowed** — that is the normal state
    for someone who booked an appointment two minutes ago and would be surprised
    not to be reminded.
  </Accordion>
</AccordionGroup>

<Warning>
  **These rules are ours, not your regulator's.** Passing them does not mean a
  call is lawful where you operate. Automated and promotional calling is
  regulated in most countries — in India that means TRAI's rules, DND registers
  and DLT registration for anything promotional.

  You are responsible for having a lawful basis for each call. Reminders about
  something a customer arranged with you sit very differently from a call about
  an offer they never asked about.
</Warning>

## Responses

<ResponseField name="202 Accepted" type="object">
  The call is being placed. `status` is `ringing` — the customer has not
  answered yet.

  `id` is the call's reference. It appears in your inbox against that customer.
</ResponseField>

<ResponseField name="400" type="object">
  A missing `to` or `purpose`, a number that could not be read, a purpose over
  300 characters, or several phone agents and no `agent` named.
</ResponseField>

<ResponseField name="403" type="object">
  Not a contact, or the customer has opted out.
</ResponseField>

<ResponseField name="409" type="object">
  No agent in the workspace answers the phone — Voice is off, or no number has
  been added.

  Also returned when the only number attached is one you brought yourself and
  you have not yet added that carrier account's credentials. Outgoing calls go
  out through the account the number belongs to, and for your own number that
  account is yours rather than ours — so the call is refused here rather than
  failing at your provider one customer at a time. Answering on that number is
  unaffected. Add the Auth ID and Auth token on the **Phone Numbers** page and
  this call works from your own number too.
</ResponseField>

<ResponseField name="409" type="object">
  The same `Idempotency-Key` is still being processed. Retry in a moment.
</ResponseField>

<ResponseField name="429" type="object">
  Too many calls a minute, or the workspace already has as many calls running as
  it is allowed. Both carry `Retry-After`.
</ResponseField>

<ResponseField name="502" type="object">
  Your phone provider would not place the call — a bad number, an unverified
  destination, or no balance. The call is recorded as failed rather than left
  ringing.
</ResponseField>

## Not calling twice

Send an `Idempotency-Key`.

```
Idempotency-Key: order-10432-delivery-call
```

A repeat of that key places **no second call** and answers with the first
reply — the same `id` — alongside a header:

```
Idempotent-Replayed: true
```

<Warning>
  This is the endpoint where a duplicate is most obvious to the customer. A
  repeated message is noise; a repeated call is their phone ringing twice about
  the same thing, and both connections are charged.
</Warning>

A body field named `idempotencyKey` works too; the header wins if you send both.
Derive it from the reason for the call — the order, the appointment, the ticket
— and never from a random value, or your retry produces a new key and a new
call.

<Note>
  The replay is the **first reply**, so `status` still reads `ringing` even if
  that call has since been answered or ended. Use the `id` to find out where it
  actually got to; it is the same call either way.
</Note>

Keys are honoured for 24 hours, and a call the provider refused releases its key
straight away — so a `502` or a `503` can be retried with the same one.

## What happens after

The call appears in your inbox as its own conversation against that customer,
with its transcript, beside their chats. Nothing else is needed to see how it
went.

If nobody answers, it is recorded as missed. You are not told separately — poll
your inbox or read it later; there is no callback for the outcome yet.

## Worth doing

<CardGroup cols={2}>
  <Card title="Say the business name first" icon="building">
    "I'm calling from Sunrise Clinic…" — a customer who cannot place the number
    hangs up before your agent gets to the point.
  </Card>

  <Card title="Call about something they did" icon="calendar-check">
    Reminders, confirmations, "your part is in". These are answered. Calls about
    things they did not ask for are how a number gets blocked.
  </Card>

  <Card title="Ring at a sane hour" icon="clock">
    Nothing here checks the time. A reminder at nine at night undoes whatever
    goodwill the reminder was for.
  </Card>

  <Card title="Try it on your own number first" icon="phone">
    Hear your own `purpose` read aloud before a customer does. It almost always
    needs shortening.
  </Card>
</CardGroup>

<Note>
  Calls placed this way count toward the same concurrency ceiling as calls
  people make to you. A batch of reminders can fill it, and further calls are
  refused with `429` until some finish — so pace a batch rather than firing it
  all at once.
</Note>
