Idempotency
Network failures between your backend and The Aggregator are inevitable. Without idempotency, a retry after a timeout can charge the same player twice or open the same session twice. The Idempotency-Key header solves this.
How it works
Section titled “How it works”Send a unique Idempotency-Key for each logical operation on the endpoints that require it. Keep the same key and body for retries:
POST /v1/sessions HTTP/1.1Authorization: Bearer YOUR_ISSUED_API_KEYIdempotency-Key: 8c8e8c8e-8c8e-4c8e-8c8e-8c8e8c8e8c8eContent-Type: application/json
{"game_id": "...", "player_id": "...", "balance": 10000, "currency": "EUR", "country": "DE"}The endpoints have different replay contracts:
POST /v1/sessions: once a response is cached, the same operator account and key replay that response. The session path does not compare a later request body before replay; changing the body can still return the original session. Never use this as a body-conflict check. An in-flight request can return409 idempotency_in_progresswithRetry-After.POST /v1/free-rounds: the grant record stores the key and request hash. The same key and body replay the recorded grant result; a different body returns409 idempotency_conflict. The same body while issuance is still in flight can return409 idempotency_in_progress.
After an ambiguous outcome, reconcile the original operation before starting another one. A new key starts a new logical operation and can create another session or grant.
Required on
Section titled “Required on”POST /v1/sessionsPOST /v1/free-rounds
For other operations, follow their own API contract rather than assuming every POST requires this header.
POST /v1/demo-sessions does not move money and does not currently require
Idempotency-Key.
Idempotency on GET is intrinsic (queries don’t mutate); the header has no effect there.
Key format
Section titled “Key format”- Recommended: UUID v4, generated once and persisted with the operation
- For compatibility with the published OpenAPI profile, use no more than 128 ASCII letters, digits, dashes or underscores. This is a portable client profile, not a claim that every deployed endpoint enforces the same validation limit.
- Never reuse keys across logically different operations. One key = one logical request.
Common patterns
Section titled “Common patterns”Per-operation
Section titled “Per-operation”Use identifiers from your own persisted operation state; define playerId and launchAttemptId before constructing the key:
const idempotencyKey = `launch-${playerId}-${launchAttemptId}`;Stable across retries of the same launch, unique across different logical launches. This outbound request key is separate from the scoped identity used to deduplicate inbound wallet callbacks.
Per-attempt UUID
Section titled “Per-attempt UUID”const idempotencyKey = crypto.randomUUID();// Store this in your own state so retries reuse itGenerated once when you start the operation, persisted in your DB, reused on every retry until the operation succeeds.
Anti-pattern: timestamps
Section titled “Anti-pattern: timestamps”// ❌ DO NOT do thisconst idempotencyKey = `bet-${Date.now()}`;Two retries 100ms apart get different keys. You lose the protection.
Response
Section titled “Response”The session path includes X-Idempotent-Replayed: true when serving a cached response and preserves its original status and body. For example, a replay of a successful session creation retains 201:
HTTP/1.1 201 CreatedContent-Type: application/jsonX-Idempotent-Replayed: true
{"session_id":"...","game_url":"..."}The free-round issuance path returns 200 with _replayed: true in the recorded grant body on a completed replay. Do not require the session replay header on that endpoint.
The session path caches a completed response for 24 hours when it stores that response. After expiry, the same key can be processed as a new request. Do not use expiry as permission to repeat an operation whose outcome is still ambiguous.
Free-round issuance uses its durable grant record; the session cache’s 24-hour expiry is not the grant contract. Keep the original key and body and reconcile the grant before requesting another one.
When to skip
Section titled “When to skip”Do not omit Idempotency-Key where the API operation requires it; a caller-side UPSERT does not remove that requirement. For operations that do not require the header, follow their documented contract. Inbound wallet callbacks use a separate scoped transaction identity, not this outbound request header.