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

# Calls & recordings

> Place an outbound call, read the log, and fetch a recording.

## Two ways to place a call

|             | `POST /v1/callback`                                                      | `POST /v1/calls`      |
| ----------- | ------------------------------------------------------------------------ | --------------------- |
| **Product** | [Web Callback](/web-callback)                                            | Programmable calling  |
| **Idea**    | "Call this customer back" — often widget- or queue-driven (`from_queue`) | "Place this call now" |
| **Sandbox** | Supports the `x-wave-simulate` header + test numbers                     | Real calls only       |

Both originate from your number and, in sandbox, are locked to your signup number.
A **production** key removes that lock.

```bash theme={null}
curl -X POST https://api.wave.sa/v1/calls \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "to": "+966500000000", "caller_id_name": "Acme" }'
```

```json theme={null}
{
  "call_id": "27d6dd39-…",
  "status": "initiated",
  "direction": "outbound",
  "to": "+966 50 XXX 0000",
  "from": "+966115209300",
  "sandbox": false,
  "created_at": "2026-08-19T10:00:00.000Z"
}
```

## Read the call log

`GET /v1/calls` returns your calls newest-first with cursor pagination. Needs the
`calls:read` scope.

```bash theme={null}
curl "https://api.wave.sa/v1/calls?limit=20" \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxx"
```

```json theme={null}
{
  "data": [
    {
      "id": "27d6dd39-…",
      "type": "outbound",
      "status": "ended",
      "from": "+966115209300",
      "to_masked": "+966 50 XXX 0000",
      "duration_seconds": 42,
      "created_at": "2026-08-19T10:00:00.000Z",
      "answered_at": "2026-08-19T10:00:05.000Z",
      "ended_at": "2026-08-19T10:00:47.000Z"
    }
  ],
  "next_cursor": "eyJ…",
  "limit": 20
}
```

Pass `next_cursor` back as `?cursor=` to page. Destination numbers are masked.

## Fetch a recording

`GET /v1/calls/{call_id}/recording` returns a **time-limited download URL** (valid
\~60 minutes). Needs the `recordings:read` scope and a **production** key.

```bash theme={null}
curl https://api.wave.sa/v1/calls/27d6dd39-…/recording \
  -H "Authorization: Bearer sk_live_xxxxxxxxxxxx"
```

```json theme={null}
{
  "call_id": "27d6dd39-…",
  "url": "https://oss.example.com/…?signature=…",
  "expires_at": "2026-08-19T11:00:00.000Z"
}
```

<Note>
  A recording exists only if the call was recorded (via a `<Dial record>` or a
  background [`<Record>`](/voice/waveml#record)) **and** storage is provisioned for
  your account. Until then this returns `404 RECORDING_NOT_FOUND` or
  `503 RECORDING_SERVICE_UNAVAILABLE`. Sandbox keys get `400 SANDBOX_KEY_NOT_ALLOWED`.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Web Calling" icon="microphone" href="/voice/web-calling">
    Place calls from the browser with the SDK.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/webhooks">
    Get call events pushed to you instead of polling.
  </Card>
</CardGroup>
