> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.telekesher.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.telekesher.dev/_mcp/server.

# 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 — a UUID is ideal. 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": "+972527121102", "from": "+972747713001", "ring_timeout_sec": 30, "max_duration_sec": 3600 }
```

An in-progress record initially expires **1 hour** after the request acquires the key. When the request finishes, the gateway stores the final response with a fresh **1-hour** 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 body, 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, accepted response available                   | the original accepted response                                | Replayed — including the same Operation/resource identity when the endpoint returns one.                                                                                          |
| 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 gateway binds the key to the HTTP method, canonical route, normalized 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 gateway 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.