Error reference — HTTP status codes, error envelope, and stable codes
CitationBench uses standard HTTP status codes plus a single error envelope with stable `code`, `message`, `details`, and `requestId` across REST and MCP. Branch on `code` in your client.
CitationBench uses standard HTTP status codes and a single error envelope across REST and MCP. Every error response carries a stable error code you can branch on, a human-readable message, structured details, and a requestId to include in support tickets.
Envelope
{
"type": "https://citationbench.com/errors/insufficient_credits",
"code": "insufficient_credits",
"message": "Need 150 credits to invoke bootstrap_brand; you have 42.",
"details": {
"required": 150,
"available": 42,
"skill": "bootstrap_brand"
},
"requestId": "req_01HVZ..."
}| Field | Notes |
|---|---|
type | Stable URL (also resolves to this page section); use to link from your own UI |
code | Branch on this in code |
message | Safe to surface to end users |
details | Per-error structured info |
requestId | Include in any support email or ticket |
For agentic endpoints, errors land in the invocation's error field rather than in the top-level response. The invocation transitions to FAILED. The shape is the same.
Status-code overview
| HTTP | When |
|---|---|
200 | OK, sync result |
202 | Accepted, async job started; returns an invocation handle |
204 | OK, no content (deletes) |
400 | Validation, schema, or argument errors |
401 | Auth missing, invalid, or expired (and endpoint doesn't allow demo mode) |
402 | Insufficient credits |
403 | Forbidden — workspace / scope / agency mismatch |
404 | Resource not found |
409 | Conflict — idempotency replay or concurrent modification |
422 | Unprocessable — schema valid but business rule violated |
429 | Rate limited |
500 | Server bug; include requestId when reporting |
503 | External dependency unavailable (DataForSEO, Ahrefs, GSC, Apollo, Anthropic, etc.) |
Common error codes
Auth & access
| Code | HTTP | Cause | What to do |
|---|---|---|---|
unauthorized | 401 | Missing / invalid Authorization header on an endpoint that doesn't allow demo mode | Check key; if exploring, try without the header to get a demo-mode response |
key_revoked | 401 | Key has been revoked | Create a new key |
key_expired | 401 | Key reached its expiresAt | Rotate to a new key |
forbidden | 403 | Key lacks the scope needed for the resource | Check the key's scope field |
workspace_forbidden | 403 | Key can't act on the requested X-Workspace-Id | Use the correct workspace, or use an agency master key |
agency_key_required | 403 | Endpoint requires an agency master key | Use an agency key |
Validation
| Code | HTTP | Cause | What to do |
|---|---|---|---|
validation_error | 400 | Request body failed schema validation | Inspect details.errors for per-field issues |
unknown_skill | 400 | skill is not in the registry (or not visible to your workspace) | List skills with GET /v1/agent/skills |
unknown_action | 400 | action on a bulk-action call is not a registered tool | Inspect inventory |
invalid_url | 400 | URL malformed for an indexing / publish / outreach endpoint | Validate before sending |
domain_not_verified | 403 | URL is on a domain not in the connected GSC's verified list | Verify the domain in GSC first |
Resources
| Code | HTTP | Cause | What to do |
|---|---|---|---|
not_found | 404 | Resource doesn't exist (or is archived) | Check the ID; consider ?includeArchived=true |
keyword_not_found | 404 | — | — |
pillar_not_found | 404 | — | — |
workspace_not_found | 404 | — | — |
account_not_found | 404 | Link-building account not found | — |
relationship_not_found | 404 | Link-building relationship not found | — |
Conflict / concurrency
| Code | HTTP | Cause | What to do |
|---|---|---|---|
idempotency_replay | 409 | Same Idempotency-Key was used recently | Returns the prior result; safe to treat as success |
concurrent_modification | 409 | Another invocation is currently modifying this resource | Wait briefly and retry, or query the in-flight invocation |
duplicate_keyword | 409 | Exact-match keyword already exists in this workspace | Look it up and update instead |
duplicate_pending_indexing | 409 | URL already has a pending indexing submission today | No action needed |
Business rules
| Code | HTTP | Cause | What to do |
|---|---|---|---|
insufficient_credits | 402 | Reserved cost exceeds remaining credits | Top up credits or pass ?dryRun=true to estimate |
budget_too_low | 422 | budget.maxCredits is below the skill's minimum | Raise the budget |
no_drafts_produced | 422 | All SERP results were filtered out | Loosen filters |
no_keywords_in_scope | 422 | Search filter matched zero keywords | Broaden the filter |
content_too_long | 422 | targetWordCount exceeds the model's safe window | Pick another model or lower the target |
outreach_platform_not_configured | 403 | No Instantly connection in this workspace | Connect Instantly first |
gsc_not_connected | 403 | No GSC connection | Run the OAuth flow at /v1/indexing/gsc-index/config |
gsc_captcha_required | 503 | Cookie session needs reCAPTCHA | Admin must re-auth |
Rate limiting
| Code | HTTP | Cause | What to do |
|---|---|---|---|
rate_limited | 429 | Per-key rate exceeded | Back off, respect Retry-After |
concurrency_limited | 429 | Too many simultaneous invocations | Stagger via mode: "BACKGROUND" + queue management |
External
| Code | HTTP | Cause | What to do |
|---|---|---|---|
external_unavailable | 503 | Upstream provider down | Back off; partial results may still have been saved |
skill_unavailable | 503 | Skill depends on an external service that's down | Try a different skill, or wait |
dataforseo_unavailable | 503 | DataForSEO failure | Same |
ahrefs_unavailable | 503 | Ahrefs failure | Same |
apollo_unavailable | 503 | Apollo failure | Same |
model_unavailable | 503 | Selected LLM is upstream-degraded | Pass another model |
Server
| Code | HTTP | Cause | What to do |
|---|---|---|---|
internal_error | 500 | Server bug | Don't retry. Report requestId to support. |
Retry guidance
| Status | Retry? | How |
|---|---|---|
| 4xx (except 408, 429) | No | Fix the request |
| 408 | Yes | One immediate retry |
| 429 | Yes | Exponential backoff respecting Retry-After header |
| 500 | No | Report the requestId |
| 502 / 503 / 504 | Yes | Exponential backoff, max 5 retries, jitter |
For agentic endpoints, the invocation itself handles retries of its own internal tool calls (configurable via budget.maxRetries). You retry the invocation only when the request to start it fails — once you have an invocationId, the agent persists.
Idempotency
Every write endpoint accepts Idempotency-Key:
curl -X POST .../v1/agent/invoke \
-H "Idempotency-Key: $(uuidgen)" \
-d '{...}'Same key replayed within 24h returns the same response — useful for resumable scripts and at-least-once delivery patterns. The replay sets code: "idempotency_replay" only when the request body changed; if the body is identical, you get a 200/202 with the original result.
Demo mode + errors
Demo mode endpoints can return error envelopes too — for example, validation_error if you send malformed JSON. They just won't return insufficient_credits (demo runs are free), external_unavailable (no real third parties touched), or anything tied to live state.
A short list of endpoints do not respond in demo mode and will return unauthorized if called without a live key: real publishing to a connected CMS, real outreach email send, real GSC indexing submission, real Apollo contact discovery. See Authentication.
Webhook delivery errors
Outbound webhooks have their own retry policy:
- 4 retries with exponential backoff (1s, 5s, 30s, 5m)
- Final failure → event moves to the dead-letter queue, queryable via
GET /v1/webhooks/dead-letter - Replay via
POST /v1/webhooks/dead-letter/{id}/replay
Related
Inventory
Master catalog of every CitationBench REST and MCP endpoint, grouped by pillar (agent, research, production, indexing, link building) with links to full request and response samples.
Invoke
POST /v1/agent/invoke is the universal entry point for running the CitationBench agent. Pick a skill, pass its input, get an invocation handle to stream events, approve, cancel, or read results.