Proxy

Idempotency and budgets

How retries, replay, admission holds, and usage accounting differ across proxy, memory, and workers.

IBEX uses more than one idempotency mechanism. They do not have the same store, the same failure mode, or the same guarantee. Choose the contract that matches the surface you call.

Provisional Ledger class: provisional
Verified 2026-10-21Chat idempotency is implemented; budget reconciliation remains open

Surface comparison

SurfaceHeader / keyStoreDuplicate behaviorStore outage
Chat completionsIdempotency-KeyRedisReplay stored responseFail open; no dedupe
Memory writeX-Idempotency-KeyPostgreSQL ledgerReplay stored 2xxFail closed with 503
Worker enqueueIdempotency-KeyPostgreSQL ledgerReplay original task IDFail closed with 503
Budget hard capreservation keyPostgreSQL ledgerTyped owned/in-progress/conflictFail closed with 503

The difference is deliberate: a missed memory write or admission hold is a correctness failure, while a missed chat dedupe is a cost and duplicate-response risk.

Chat idempotency

POST /v1/chat/completions accepts an opaque Idempotency-Key of at most 256 characters. The key is scoped by organization and is not forwarded upstream.

CaseResult
Same key, same body fingerprintReplay stored status and body; no second provider call
Same key, different body409 IDEMPOTENCY_KEY_REUSE
Claim still active409 IDEMPOTENCY_IN_PROGRESS; retry after backoff
Transient upstream 429/5xxRelease the claim so the key can be retried
Redis unavailable or timed outRequest proceeds without dedupe
stream=true with a key400 VALIDATION_ERROR

A replay does not create a second session checkpoint or trace.

Memory idempotency

POST /v1/memories uses a PostgreSQL ledger scoped by (org, agent, key).

CaseResult
Same key, same bodyReplay the stored 2xx
Same key, different body409 IDEMPOTENCY_CONFLICT
Claim active409 IDEMPOTENCY_IN_PROGRESS plus Retry-After
PostgreSQL unavailable503 IDEMPOTENCY_UNAVAILABLE
Row committed, claim not finalized503; never report success over an unfinished claim

Worker enqueue

The extraction enqueue endpoint requires both a static bearer and an Idempotency-Key. The full normalized payload, including every turn, is hashed before dispatch. A changed payload under the same key conflicts. A dispatch failure releases the claim. If dispatch succeeds but finalization fails, the endpoint returns 503 and the 120-second lease governs the retry window.

Budget admission

For an organization with an active hard_cap period, the proxy claims a durable hold before provider work.

OutcomeBehavior
ownedAdmit; finalize or release after the provider call
exceeded402 BUDGET_EXCEEDED; no provider call
in_progressRetry after backoff; do not dispatch twice
conflictReject the reused key with a different fingerprint
no_capContinue without a hold
Ledger unavailable503 BUDGET_UNAVAILABLE; fail closed

A provider response without usage must finalize as estimated, not complete.

What is not guaranteed

Chat and worker enqueue deduplicate admission. They do not make provider or Celery execution exactly-once. Reconciliation can still find drift, and budget caps can overshoot within a bounded window.
  • No exactly-once provider call.
  • No exactly-once Celery execution.
  • No final reconciliation evidence for extraction usage.
  • No hosted/HA replay evidence.
  • No cross-language canonicalization acceptance.

Use a stable key per logical operation, retain the key with your request record, and retry only transient failures.