# Errors

> Every error the REST API and the MCP server return today, what each means and how to fix it.

An error answers with a JSON body whose `error` field is a stable code. Match
on the code, not on the `message`, which is written for people and can
change.

```json
{ "error": "forbidden_scope", "message": "Key does not have the 'leads.manage' scope.", "scopes": ["leads.manage"] }
```

**Coming soon:** one envelope for every endpoint, with a `type`, the `code`,
the `param` at fault, a `request_id` and a `doc_url` pointing at this page.
Until then, each endpoint returns the codes below.

## Any authenticated request

### 401 invalid_api_key

The `Authorization` header is missing, or the key is unknown, revoked or
expired. The body is the same whatever the reason, on purpose.

**Fix:** send `Authorization: Bearer <key>`. If the key was revoked, ask an
admin for a new one.

### 403 forbidden_scope

The key is valid but lacks a scope this endpoint needs. The body's `scopes`
lists every missing scope, and so does the header:

```http
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="leads.manage"
```

**Fix:** ask an admin to create a key with that scope. See
[Scopes](https://dev.funnelone.ai/get-started/scopes/).

### 429 rate_limited

Too many requests in this minute. Wait the number of seconds in
`Retry-After`. See [Limits](https://dev.funnelone.ai/rest/limits/).

## POST /api/v1/leads

| Status | Code | Means |
|---|---|---|
| 400 | `invalid_lead` | The lead cannot be saved; `message` says why (for example, it has neither an email nor a name). |
| 409 | `duplicate_request` | This `Idempotency-Key` was already used. No second lead was created. |
| 500 | `ingest_failed` | Something failed on our side. Retry with the same `Idempotency-Key`. |

## POST /api/v1/motions/{id}/start

| Status | Code | Means |
|---|---|---|
| 400 | `idempotency_key_required` | Send an `Idempotency-Key` header. |
| 400 | `invalid_request` | The body is not valid; `message` says why. |
| 404 | `motion_not_found` | No such motion in this workspace. |
| 404 | `company_not_found` | No such company in this workspace. |
| 409 | `api_trigger_missing` | The motion has no API call trigger. Add one in the motion's entry settings. |
| 409 | `idempotency_key_reused` | This key was already used for a different company. |
| 409 | `engine_off` | The workspace's agents are switched off. Nothing was queued. |
| 500 | `start_failed` | Something failed on our side. Retry with the same `Idempotency-Key`. |

## Preview: new credentials and the MCP server

These come with OAuth sign-in and the MCP server, which are not live yet.

| Status | Code | Means |
|---|---|---|
| 401 | `invalid_token` | The MCP server got no token, or one that is unknown, expired, revoked or issued for a different resource. The `WWW-Authenticate` header tells the client where to sign in. |
| 403 | `api_disabled` | The REST API is turned off for this workspace. An admin turns it on. |
| 403 | `mcp_disabled` | The MCP server is turned off for this workspace. An admin turns it on. |
| 403 | `insufficient_scope` | An MCP tool needs a scope the connection lacks. `data.scopes` in the JSON-RPC error lists them, and so does `WWW-Authenticate`. |
| 429 | `rate_limited` | As above, with `scope` saying whether the credential's or the workspace's limit was hit. |

Source: https://dev.funnelone.ai/rest/errors/
