> For complete lifecycle-event payload schemas and examples, use the canonical overview: Session /api/sessions; Room lifecycle, members, and room playback /api/rooms; Session playback /api/playback; Recording /api/recording; DTMF /api/keypad-input; Messages /api/messages; WebSocket Session /api/web-socket. Use /api/event-delivery for transport behavior. Endpoint pages name relevant events, but these overviews are the canonical references.

# Idempotency

Network failures happen. A request can succeed on the server while the response is lost in transit, leaving your client unsure whether the operation ran. Retrying blindly is dangerous: a retried `POST /sessions:dial` would place a **second real outbound call**. An **idempotency key** makes a retry safe while its record exists: a finalized operation is not re-executed and the retry returns the original result.

**Optional, but recommended for every mutation.** It is supported by every `POST`, `PUT`, `PATCH`, and `DELETE` endpoint.

## Sending a Key

Attach an `Idempotency-Key` request header with a value you generate, such as a UUID. Use **one key per logical operation**, and reuse the *same* key when retrying that operation.

```http
POST /v1/sessions:dial
Authorization: Bearer <app_uuid>:<api_key>
Idempotency-Key: 9f1c8e7a-2b3d-4f56-8a90-1c2d3e4f5a6b
Content-Type: application/json

{ "to": "+972500000000", "from": "+972740000000", "ring_timeout_sec": 30, "max_duration_sec": 3600 }
```

An idempotency record moves through two timing states:

* While the request is in progress, the record expires **1 hour** after the request acquires the key.
* When the request finishes, the API stores the final response with a fresh **1-hour** time-to-live (TTL), so the replay window runs from finalization.

Replays do not refresh that TTL. After the record expires, reusing the same key starts a brand-new operation — for `POST /sessions:dial` that means a second real outbound call.

Allowed characters: letters, digits, dot, hyphen, and underscore. The value must be **1 to 128 characters**. Omitting the header, or sending it with an empty value, disables idempotency for that request; any non-empty value that breaks the character or length rules is rejected with `400`.

## Response Behavior

| Scenario                                                               | You get                                                       | Meaning                                                                                                                                                                           |
| ---------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| New key                                                                | the endpoint's normal response                                | Handled once; its status and body are stored for replay.                                                                                                                          |
| Same key + same request, original finished                             | the **same** HTTP status code and JSON body as the first call | Replayed — not re-executed. Only the status and body are replayed; the original response headers are not reproduced, and the replay always sets `Content-Type: application/json`. |
| Same key + same request, original returned an accepted response        | the original accepted response                                | Replayed with the same operation or resource identifier.                                                                                                                          |
| Same key + a **different** method, route, query, content type, or body | `422 Unprocessable Entity`                                    | A key maps to one request. Use a new key.                                                                                                                                         |
| Store unavailable before execution                                     | `503 Service Unavailable`                                     | The request was **not** processed. Safe to retry.                                                                                                                                 |
| Malformed key                                                          | `400 Bad Request`                                             | Check the character/length rules above.                                                                                                                                           |

## Reusing a Key

A key identifies one specific request within your authenticated app and must be unique across all supported endpoints. Always generate a fresh key for a new operation, even when calling a different endpoint.

The API binds the key to the HTTP method, route, query, `Content-Type`, and raw request body. If you retry with different whitespace, property ordering, or other byte-level body changes, it rejects the request with `422`. Reuse the original request exactly as sent.

## When Idempotency Is Unavailable

If the API cannot acquire or read the idempotency record before execution, it returns `503` without executing the operation. Retry the same request with the same key.

## Best Practices

* **One key per operation.** Generate a fresh key for each distinct action.
* **Persist the key before sending.** Store it client-side first, so a crash-then-retry reuses the same key.
* **Reuse the same key for retries of the same operation.** If the request outcome is unclear, retry the original request with its original key while the replay window is still open.
* **Treat a replay like the original.** A replayed response is the real outcome of your one operation.