Errors
Stable envelope and the full code catalog.
Every non-2xx response from /api/v1/* carries the same JSON envelope:
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Balance below required 150 credits for type=all",
"doc_url": "https://docs.citeflow.io/errors/insufficient-credits",
"required": 150
},
"request_id": "req_01jbq6t8tjk0vfgz5gd9p4d4qa"
}Code against error.code, not the HTTP status. Status mappings may
relax (e.g., we might add a new 4xx) but codes are versioned with the
API path (/api/v1).
The request_id is echoed back in the X-Request-Id response header.
Include it in support tickets.
Code catalog
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_URL | Body isn't JSON or a URL field isn't http(s). |
| 400 | INVALID_TYPE | type not in all/seo/aeo/geo. |
| 400 | INVALID_TIER | tier not in starter/growth/scale. |
| 400 | CONSENT_REQUIRED | URL requires consent the partner did not grant. |
| 401 | UNAUTHORIZED | Missing Authorization header. |
| 401 | INVALID_API_KEY | Key not recognized. |
| 401 | KEY_REVOKED | Key revoked in the dashboard. |
| 402 | INSUFFICIENT_CREDITS | Balance below the required amount. Includes error.required. |
| 403 | IP_NOT_ALLOWED | Caller IP outside the key's allowlist. |
| 403 | SCOPE_INSUFFICIENT | Endpoint requires a scope the key lacks. |
| 404 | AUDIT_NOT_FOUND | Audit id missing or not owned by the key. |
| 404 | WEBHOOK_DELIVERY_NOT_FOUND | Delivery id missing or not owned. |
| 409 | AUDIT_CANNOT_CANCEL | Cancel attempted in an invalid state (reserved — terminal states currently no-op). |
| 409 | IDEMPOTENCY_CONFLICT | Same Idempotency-Key reused with a different body. |
| 409 | WEBHOOK_ENDPOINT_DISABLED | Replay refused — endpoint disabled in dashboard. |
| 413 | BODY_TOO_LARGE | Body > 1MB. |
| 429 | RATE_LIMITED | Per-key per-endpoint rate limit hit. Includes error.retryAfter. |
| 500 | INTERNAL_ERROR | Unexpected server error. Quote request_id to support. |
| 503 | ENGINE_BUSY | Engine saturated. Honor Retry-After (default 30s). |
Distinguishing failures
404 is returned for both "audit does not exist" and "audit belongs
to a different workspace" — CiteFlow doesn't leak existence of other
partners' resources.
Retry policy
429,503: respectRetry-After/error.retryAfter. Exponential back-off if missing.5xx(other): retry up to 3 times with jittered exponential back-off (250ms, 1s, 4s).4xx: do not retry — the request is wrong; fix and resubmit.
Both official SDKs implement this policy.
Common pitfalls
INSUFFICIENT_CREDITSafter a top-up — Stripe webhook may lag the buyer redirect by a few seconds. Show "processing" until your nextGET /balancereflects the new total.IDEMPOTENCY_CONFLICTon the same payload — concurrent in-flight request raced with the same key. Sleep ~100ms and retry; the original call will have completed.AUDIT_NOT_FOUNDimmediately afterPOST /audit— replication lag (rare). Sleep 250ms and retry.