CiteFlow API

Idempotency

Safe retries via `Idempotency-Key`.

POST /audit accepts an Idempotency-Key header. CiteFlow caches the response for 24 hours; sending the same key with the same body returns the original response — same audit_id, same status, only one credit deduction.

POST /api/v1/audit HTTP/1.1
Authorization: Bearer ckf_…
Idempotency-Key: ord_42-attempt-1
Content-Type: application/json

{ "url": "https://example.com", "type": "seo" }

The replay response carries an extra header so you can tell:

HTTP/1.1 202 Accepted
X-Idempotent-Replay: true

Key requirements

  • Max 255 characters, any printable ASCII.
  • Unique per logical operation in your system. UUID v4, ULID, or ${order_id}-${attempt} all work.
  • Don't reuse across different request bodies — that returns 409 IDEMPOTENCY_CONFLICT.

When to use

Always, for POST /audit. Networks lose responses; clients lose process. Retrying without an idempotency key risks duplicate charges and duplicate audits.

The official Node and Python SDKs auto-generate keys per call if the caller doesn't supply one.

Window

24 hours. After the window expires the key is forgotten and a fresh call with the same body creates a new audit.

Conflict detection

If you send the same Idempotency-Key with a different body within 24h:

{
  "error": {
    "code": "IDEMPOTENCY_CONFLICT",
    "message": "Idempotency-Key reused with a different request body",
    "doc_url": "https://docs.citeflow.io/errors/idempotency-conflict"
  }
}

Resolution: generate a fresh key for the new payload.

Concurrency

Two concurrent requests with the same key + same body — only one completes; the second receives 409 IDEMPOTENCY_CONFLICT (raced) or the cached replay (if the first finished first). Sleep ~100ms and retry; you'll get the cached replay.

What's NOT idempotent

  • POST /audit/{id}:cancel — cancellations are naturally idempotent (terminal states return current state).
  • POST /billing/topup — each call mints a fresh Stripe Checkout Session by design; the same partner can have multiple open sessions.
  • POST /webhooks/deliveries/{id}/replay — replays are naturally idempotent (re-firing the same event_id UPSERTs the delivery row).

Storage

CiteFlow stores the SHA-256 hash of partner_id + idempotency_key plus the response body in Upstash Redis with a 24h TTL. The cache lives outside the primary database to keep /audit p99 latency low.

On this page