> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revcenter.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> The error shape, every code the API returns, how to handle each, and which requests are safe to retry.

> Agents: the full documentation index is at [/llms.txt](https://docs.revcenter.ai/llms.txt).

Every error has the same shape. Branch on `error.code`, never on the message, which can change.

```json theme={null}
{
  "error": { "code": "insufficient_scope", "message": "API key requires scope 'searches:write'." },
  "meta": { "request_id": "6f1c2b7e-9a3d-4c5f-8e21-0b7d4a9c1e33" }
}
```

Quote `meta.request_id` when you contact support. Some responses also carry `meta.correlation_id`; treat it
as the same value.

## Codes

| Status | Code | Meaning | What to do |
| - | - | - | - |
| `400` | `invalid_criteria` | The criteria failed validation; the message lists every problem | Fix the fields named, using `GET /v1/reference/criteria` |
| `400` | `criteria_required` | Neither `criteria` nor a `jd_text` of 40+ characters was sent | Send one of them |
| `400` | `idempotency_key_required` | A search launch without an `Idempotency-Key` header of 8 to 160 printable characters | Add the header |
| `400` | `needs_new_search` | A scoring change tried to change titles or places | Launch a new search instead |
| `400` | `candidate_ids_required` | Adding to a shortlist without `candidate_ids` | Send the ids from `/searches/{id}/people` |
| `400` | `invalid_name` | A project name that is empty or over 120 characters | Fix the name |
| `401` | `missing_api_key` | No `Authorization: Bearer` header | Send the key |
| `401` | `invalid_api_key` | The key is invalid, expired or revoked | Use a current key; see [authentication](/authentication) |
| `402` | `plan_required` | The workspace has no active plan | Choose a plan in **Settings → Plan & billing** |
| `402` | `no_active_plan` · `shortlist_allocation_exhausted` | No shortlist credit left for a new search or expansion | Add credits; see [billing](/guides/billing) |
| `403` | `api_access_disabled` | API access isn't on for this workspace | Ask your Revcenter contact |
| `403` | `insufficient_scope` | The key lacks the scope this call needs | Use a key with that scope |
| `404` | `project_not_found` · `search_not_found` · `candidate_not_found` | No such id in this workspace, or not a valid id | Check the id |
| `409` | `idempotency_key_reused` | The `Idempotency-Key` was already used for a different launch (another project or body) | Use a new key for a new search; see [idempotency keys](#idempotency-keys) |
| `409` | `search_running` | A search on the project is still running | Wait for it to finish ([async jobs](/guides/async-jobs)) |
| `409` | `search_not_finished` · `no_search_to_expand` · `expansion_limit` | The project can't be expanded now (2 expansions per project at most) | Wait, or launch a new search |
| `424` | `search_unavailable` | Live search is unavailable for this workspace | Retry later, or use `database_only` |
| `429` | `rate_limited` · `concurrent_search_limit` · `daily_search_limit` · `network_busy` | A limit was reached | Wait for `Retry-After`; see [rate limits](/guides/rate-limits) |
| `500` | `internal_error` | Something went wrong on our side | Retry with backoff, then contact support with the `request_id` |

## CLI and MCP

The CLI prints `revcenter: <code>: <message>` and exits `1`; the MCP server returns the same code and message
as an error result. The CLI's own codes are in [CLI basics](/concepts/cli-basics).

## Which requests are safe to retry

| Request | Safe to retry? |
| - | - |
| Any `GET` | Yes |
| `POST /projects/{id}/searches` | Yes, **with the same `Idempotency-Key`**: you get the same search and are never charged twice |
| `POST /searches/{id}/scoring/preview` | Yes; it changes nothing |
| `POST /searches/{id}/scoring/apply` · `/database-pass` | Yes; neither uses credits, and repeating one re-scores the same people |
| `POST /projects/{id}/shortlist/members` · `DELETE …/members/{candidateId}` | Yes; someone already on the list is counted in `already`, and removing someone not on it returns `removed: false` |
| `POST /projects` | No: each call makes a new project |
| `POST /projects/{id}/expand` | No: each call that succeeds is a new expansion and uses a credit. Check `GET /projects/{id}/searches` before retrying |

### Idempotency keys

An `Idempotency-Key` is 8 to 160 printable characters, belongs to the workspace, and is kept for good. Revcenter
remembers the request it was first used for: the route, the project and the body (the order of keys in the JSON
doesn't matter).

| Later launch with the same key | Answer |
| - | - |
| Same project, same body | `200` with the first search and `"reused": true`. No second credit, and a job description is not read again. |
| Another project, or a different body | `409 idempotency_key_reused`. Nothing is launched or charged. |

```json theme={null}
{
  "error": { "code": "idempotency_key_reused", "message": "This Idempotency-Key was already used for a different request. Use a new key for a new search." },
  "meta": { "request_id": "6f1c2b7e-9a3d-4c5f-8e21-0b7d4a9c1e33" }
}
```

Use a key that names one piece of work, such as `nightly-<requisition id>`, and a new key for a new search.
Two attempts sent at the same moment with the same key make one search.

Retry `429` after `Retry-After` and `5xx` with backoff. Don't retry other `4xx` unchanged: fix the request.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.