Networks fail. If a request times out you cannot tell whether it ran and was billed. Send an Idempotency-Key header and the gateway guarantees that the same request is billed at most once.
Use it
Add the header to a non-streaming completion request (/v1/chat/completions or /v1/messages). Use a unique value per logical request, such as a UUID: 1 to 255 printable ASCII characters.
KEY="demo-$(date +%s)-$RANDOM"
for attempt in 1 2; do
curl -sS -D - -o /dev/null https://api.ticonai.com/v1/chat/completions \
-H "Authorization: Bearer $AIP_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d '{
"model": "your-model-id",
"max_tokens": 32,
"messages": [{ "role": "user", "content": "Say hello." }]
}'
doneThe second response has the header Idempotent-Replayed: true: it is the first response again, no model call was made, and nothing more was charged.
What the gateway does
| Situation | Result |
|---|---|
| New key | The request runs normally and its outcome is remembered for 24 hours. |
| Same key, same request, finished, and the response is still held | The original response is replayed with Idempotent-Replayed: true. No model call, no charge. |
| Same key, different request | 409 idempotency_conflict. Nothing runs. |
| Same key, first request still running | 409 idempotency_in_progress. Wait and retry. |
| Same key, finished, but the response is no longer available | 409 idempotency_replay_unavailable. See below. |
| Streaming request | The header is ignored. |
Keys are scoped to your organization. "Same request" means the same endpoint and the same body, compared exactly as sent: a body that differs only in whitespace counts as different.
When the response cannot be replayed
For privacy, the platform does not store prompts or completions in its database. A replay is therefore served from the gateway's memory, which is bounded and does not survive a restart or move between instances. When the response is gone, the gateway answers 409 idempotency_replay_unavailable. The message includes the id and the cost of the original request. The request is never run again or billed again.
What to do: treat the original request as done and billed, and send the next attempt with a new key if you still need an answer.
Failures do not use up the key
A request that fails before it is billed, for example with a 4xx, a provider error or a timeout, releases its key, so you can retry with the same key. After a request has been billed, the key is kept for the 24 hours.
In the rare case the platform cannot be sure how a request ended (for example, the billing write failed), the key answers 409 idempotency_replay_unavailable instead of risking a second charge.
Patterns
- Generate the key once, before the first attempt, and reuse it for every retry of that request.
- Retry with the same key after a timeout or a
5xx. - Never reuse a key for a different prompt.
- If you stream, handle retries yourself: a cancelled or broken stream is billed for what was produced. See Streaming.
More on retries in Best practices and Errors.