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

# Error codes

> Every error the Inboxapp API returns and what to do about it

## The error body

Every error has the same shape:

```json theme={null}
{
  "code": "capabilityNotSupported",
  "message": "This account can't perform this operation.",
  "details": { "capability": "message:edit" },
  "requestId": "req_tz4a98xxat96iws9zmbrgj3a"
}
```

| Field | Use |
| - | - |
| `code` | Branch on this. It is the contract |
| `message` | Readable, and safe to show a person. It can change |
| `details` | `null`, or the object documented for that code |
| `requestId` | Also in the `X-Request-Id` header. Include it when you contact support |

New codes can be added within v2. Handle one you don't know by its HTTP status.

```typescript errors.ts theme={null}
class InboxError extends Error {
  constructor(
    readonly status: number,
    readonly code: string,
    message: string,
    readonly details: Record<string, unknown> | null,
    readonly requestId: string,
  ) {
    super(message);
  }
}

async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
  const response = await fetch(`https://inboxapp.com/api/v2${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.INBOX_API_TOKEN}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });

  if (response.ok) {
    return response.status === 204 ? (undefined as T) : response.json();
  }

  const body = await response.json();

  throw new InboxError(
    response.status,
    body.code,
    body.message,
    body.details,
    body.requestId,
  );
}
```

## Request errors

| Status | Code | When | Fix |
| - | - | - | - |
| 400 | `invalidRequest` | A parameter or body field is wrong | `details.issues` lists each `path` and `message` |
| 400 | `invalidCursor` | The cursor is malformed, or used with other filters | Restart from the first page |
| 401 | `unauthorized` | The token is missing, malformed or revoked | Check the `Authorization` header |
| 403 | `planRequired` | The team's plan lacks `details.feature` | Upgrade, or add the Advanced API add-on |
| 403 | `workspaceLocked` | Billing is paused or past due, or the trial ended | Resolve billing in Inboxapp |
| 403 | `platformNotAvailable` | The resource is on a platform your team can't use | Use a platform from `GET /platforms` |
| 429 | `rateLimited` | A team limit was reached | Wait for `Retry-After`. See [Rate limits](/v2/reference/rate-limits) |
| 500 | `internal` | Something failed on our side | Retry, then contact support with the `requestId` |

## Not found

`threadNotFound`, `messageNotFound`, `contactNotFound`, `accountLinkNotFound`, `memberNotFound`, `tagNotFound`, `statusNotFound`: all `404`. The ID doesn't exist in your team, or the object was deleted.

A lookup by platform identity never returns `404`. It returns an empty `data` array.

## Conflicts

| Status | Code | When | Fix |
| - | - | - | - |
| 409 | `accountLinkDisconnected` | The account is `offline` or `paused` | See `details.status`, and [Account links](/v2/configuration/account-links#status) |
| 409 | `integrationSetupRequired` | The account needs setup in Inboxapp before it can act | Finish the setup in the app |
| 409 | `sendInProgress` | A send with this idempotency key is still running | Retry shortly with the same key |
| 409 | `nameTaken` | A tag or status with this name exists | Use the existing one, or another name |

## The platform said no

| Status | Code | When | Fix |
| - | - | - | - |
| 422 | `capabilityNotSupported` | The account link lacks `details.capability` | Check `capabilities` before calling |
| 422 | `threadRestricted` | The platform disabled `details.capability` in this thread | Check the thread's `restrictions` |
| 422 | `mutationWindowClosed` | Too long since the message was sent | See the windows in [Platforms](/v2/platforms) |
| 422 | `messageNotModifiable` | This message can't be edited or deleted. See `details.reason` | — |
| 422 | `contentRejected` | The content is too long or was refused. See `details.detail` | Shorten or change it |
| 422 | `recipientUnavailable` | The person can't be messaged from this account | See `details.reason` |
| 422 | `profileAmbiguous` | The contact has several profiles on this platform | Send with a `profile` target |
| 422 | `platformNotSupported` | `platform` isn't the account link's platform | Use one in `details.supported` |
| 422 | `idempotencyKeyReused` | The key was used with a different body | Use a new key |
| 422 | `tagLimitReached` | The team has `details.max` tags | Delete unused tags |

## The platform failed

| Status | Code | When | Fix |
| - | - | - | - |
| 429 | `platformRateLimited` | The platform is limiting this account | Wait `details.retryAfterSeconds`, or use another account |
| 502 | `platformError` | The platform returned an error | Retry later |
| 502 | `sendUnconfirmed` | The platform may have delivered the message | Don't resend. `details.messageId` is the stored message |
| 503 | `platformUnavailable` | The platform can't be reached | Wait `details.retryAfterSeconds`, then retry |

<Warning>
  After `sendUnconfirmed`, never retry with a new idempotency key: the person
  may receive the message twice.
</Warning>

## What to retry

| Retry with backoff | Don't retry |
| - | - |
| `429`, `500`, `502 platformError`, `503`, `409 sendInProgress` | Every other `4xx`, `sendUnconfirmed` |

Sends are only safe to retry with an [`Idempotency-Key`](/v2/guides/messages#send-safely).


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