# Error handling

> Documentation of the watsi API standard error envelope, every error code, and recommended retry strategies.

Source: https://docs.watsi.ai/en/error-handling

All watsi API surfaces — Fastify services and Next.js API routes — return a consistent error envelope.

## Standard error response

Every error response has the same envelope. The top-level `error` field is always an object with `code` and `message`:

```jsonc
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "email is required",
    "details": {}, // optional — extra context when available
  },
}
```

| Field           | Type      | Description                                              |
| --------------- | --------- | -------------------------------------------------------- |
| `error.code`    | `string`  | Machine-readable error code (e.g. `UNAUTHORIZED`)        |
| `error.message` | `string`  | Human-readable description of what went wrong            |
| `error.details` | `object?` | Optional object with field-level errors or extra context |

## Error codes

| Status | Code                  | Description                                                                            |
| ------ | --------------------- | -------------------------------------------------------------------------------------- |
| 400    | `VALIDATION_ERROR`    | The request body or query parameters failed validation. Check the message for details. |
| 400    | `BAD_REQUEST`         | The request is malformed or cannot be processed in its current state.                  |
| 401    | `UNAUTHORIZED`        | Missing or invalid authentication credentials. Include a valid API key or session.     |
| 403    | `FORBIDDEN`           | The authenticated user does not have permission to perform this action.                |
| 404    | `NOT_FOUND`           | The requested resource does not exist or is not accessible.                            |
| 405    | `METHOD_NOT_ALLOWED`  | The HTTP method is not supported for this endpoint.                                    |
| 409    | `CONFLICT`            | The request conflicts with the current state (e.g. duplicate resource).                |
| 429    | `RATE_LIMITED`        | Too many requests. Back off and retry after the Retry-After interval.                  |
| 500    | `INTERNAL_ERROR`      | An unexpected server error occurred. Retry with exponential backoff.                   |
| 502    | `EXTERNAL_API_ERROR`  | An upstream service (WhatsApp, AI provider) returned an error.                         |
| 503    | `SERVICE_UNAVAILABLE` | The service is temporarily unavailable. Retry after a short delay.                     |

## Example responses

```http Validation error (400)
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "email is required"
  }
}
```

```http Unauthorized (401)
HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Unauthorized"
  }
}
```

```http Not found (404)
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Chat not found"
  }
}
```

```http Rate limited (429)
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry after 30 seconds."
  }
}
```

```http Internal error (500)
HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal server error"
  }
}
```

## Retry strategies

| Code                  | Retryable? | Strategy                                                                                               |
| --------------------- | ---------- | ------------------------------------------------------------------------------------------------------ |
| `VALIDATION_ERROR`    | No         | Fix the request parameters before retrying.                                                            |
| `UNAUTHORIZED`        | No         | Check your API key or re-authenticate.                                                                 |
| `FORBIDDEN`           | No         | Verify you have the required permissions (admin role, etc.).                                           |
| `NOT_FOUND`           | No         | Verify the resource ID exists.                                                                         |
| `CONFLICT`            | No         | The resource already exists. Fetch the existing resource or use a different identifier.                |
| `RATE_LIMITED`        | Yes        | Wait for the duration in the `Retry-After` header, then retry. Default limit: 100 req/min per API key. |
| `INTERNAL_ERROR`      | Yes        | Retry with exponential backoff: 1s, 2s, 4s, up to 3 attempts.                                          |
| `EXTERNAL_API_ERROR`  | Yes        | An upstream provider failed. Retry with exponential backoff.                                           |
| `SERVICE_UNAVAILABLE` | Yes        | The service is temporarily down. Retry after 5–30 seconds.                                             |

> **Tip**

Only `RATE_LIMITED`, `INTERNAL_ERROR`, `EXTERNAL_API_ERROR` and `SERVICE_UNAVAILABLE` are retryable. The rest need the request or the credentials fixed before you try again.

## Handling errors in code

```javascript
const res = await fetch('https://api.watsi.ai/api/v1/conversations', {
  headers: { Authorization: `Bearer ${apiKey}` },
})

if (!res.ok) {
  const body = await res.json()
  const { code, message } = body.error

  switch (code) {
    case 'RATE_LIMITED':
      const retryAfter = res.headers.get('Retry-After') ?? '30'
      await sleep(Number(retryAfter) * 1000)
      return retry()
    case 'UNAUTHORIZED':
      throw new Error('Invalid API key')
    default:
      throw new Error(`API error [${code}]: ${message}`)
  }
}
```

## Next steps

- [Interactive API reference](/reference)
    Every endpoint, with its parameters and responses.
- [Authentication](/guides/authentication)
    API keys, scopes, and security best practices.
