Chat completions
POST /api/v1/chat/completions is the OpenAI-compatible chat completions endpoint.
It works with the existing OpenAI SDKs (set base_url=https://api.cloakapi.io/api/v1) and
supports streaming (Server-Sent Events) and safe-to-retry Idempotency-Key semantics.
Idempotency-Key
A flaky connection that triggers a client-side retry must not double-bill the user or duplicate
the receipt-chain entry. CloakAPI implements the IETF-draft Idempotency-Key header
(draft-ietf-httpapi-idempotency-key-header).
curl https://api.cloakapi.io/api/v1/chat/completions \ -H "Authorization: Bearer $CLOAKAPI_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "model": "claude-haiku-4-5-20251001", "messages": [{"role":"user","content":"Hello"}] }'Contract
| Aspect | Behaviour |
|---|---|
| Header name | Idempotency-Key |
| Recommended format | UUID v4 |
| Max length | 128 chars |
| Scope | Per API key (different keys may reuse the same value) |
| TTL | 24 hours; an expired key is treated as fresh |
| What gets cached | Only 2xx responses (errors are not memoised so a transient outage can be retried) |
| Replay header | X-CloakAPI-Idempotent-Replay: true (legacy alias: Idempotency-Replayed: true) |
| Conflict response | 409 Conflict when the same key is reused with a different request body |
Concurrent retries — if the original call is still in flight when a duplicate arrives, the duplicate blocks for up to 3 seconds on the in-flight slot and then returns the cached response. There is no race window in which two upstream calls can fire for the same key.
Receipt / chain semantics — a replayed call returns the original receipt (same JTI,
same chain_seq). The chain sequence does not advance and the user’s balance is not
debited a second time. Verify this for yourself by chaining receipts:
curl -sk -X POST https://api.cloakapi.io/api/v1/receipts/verify \ -H 'Content-Type: application/json' \ -d '{"receipt":"<x-cloakapi-receipt-v2 header from the replayed call>"}'Streaming (SSE)
Pass "stream": true to receive an OpenAI-style Server-Sent Events stream.
curl -N https://api.cloakapi.io/api/v1/chat/completions \ -H "Authorization: Bearer $CLOAKAPI_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-haiku-4-5-20251001", "messages": [{"role":"user","content":"Count to three"}], "stream": true }'Response is Content-Type: text/event-stream; charset=utf-8 with frames of the shape:
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"1"}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":" 2"}}]}
data: [DONE]
event: receipt-v2data: {"alg":"ES256","jti":"...","hash_upstream_response":"...","input_tokens":...,"output_tokens":...,...}The terminator is data: [DONE]\n\n. After it, the gateway emits a single
event: receipt-v2 frame carrying the same signed receipt envelope you would receive in the
X-CloakAPI-Receipt-V2 header for a non-streamed call. Pass the envelope’s data: payload
to /api/v1/receipts/verify to confirm the signature, the upstream-response hash, and the
chain position — the verifier endpoint does not care whether the receipt originated from a
streamed or non-streamed call.
Streaming + Idempotency-Key
Streamed calls honour Idempotency-Key too. On the first call, the gateway records the
verbatim wire transcript (every SSE frame, in order, terminator and receipt-v2 trailer
included). On a retry with the same key + same body, the gateway replays the cached
transcript byte-for-byte without touching the upstream provider:
KEY=$(uuidgen)curl -N -H "Idempotency-Key: $KEY" ... # first call: real upstream streamcurl -N -H "Idempotency-Key: $KEY" ... # retry: cached transcript, no debitThe retry response carries X-CloakAPI-Idempotent-Replay: true and X-CloakAPI-Streamed: true.
A different request body with the same key returns 409, exactly like the non-streamed path.
Edge buffering
api.cloakapi.io advertises Cache-Control: no-cache, no-store, must-revalidate and
X-Accel-Buffering: no on streamed responses, and the front nginx is configured with
proxy_buffering off for the chat-completions location. This means individual chunks reach
the client as soon as they arrive from the upstream provider — there is no edge-layer buffer
to drain.
If you are running CloakAPI behind your own reverse proxy (Cloudflare, AWS ALB, etc.), make
sure that proxy also passes SSE through unbuffered. For Cloudflare you typically need a Page
Rule with Cache Level: Bypass for /api/v1/chat/completions.
Reference matrix
| Behaviour | Non-stream | Stream |
|---|---|---|
Idempotency-Key honoured | yes | yes |
| Replay header | X-CloakAPI-Idempotent-Replay: true | same |
| Conflict (different body) | 409 | 409 |
| Receipt delivery | X-CloakAPI-Receipt-V2 header | event: receipt-v2 SSE frame |
| Verifier endpoint | /api/v1/receipts/verify | /api/v1/receipts/verify |
| Chain seq advances on replay | no | no |
| Balance debited on replay | no | no |
Related
- Receipts protocol — receipt envelope format
- Webhooks — outbound delivery with its own
Idempotency-Keycontract