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

# Error Codes

> Machine-readable error codes, human-readable messages, and retry guidance for every Zenoo API error response.

# Error Codes

Every error response from the Zenoo API includes a machine-readable error code, a human-readable message, and a unique request ID.

## Response format

```json theme={null}
{
  "error": "VALIDATION_ERROR",
  "message": "Missing required field: company_name",
  "request_id": "req-a1b2c3d4"
}
```

| Field        | Description                                                               |
| ------------ | ------------------------------------------------------------------------- |
| `error`      | Machine-readable error code. Use this for programmatic error handling.    |
| `message`    | Human-readable description. Log this, but do not display it to end users. |
| `request_id` | Unique identifier for the request. Include this in support tickets.       |

<Tip>Always log the `request_id` from every error response. It is essential for correlating issues with Zenoo support.</Tip>

## Error code reference

| Error Code            | HTTP Status | Description                                     | Retryable | Recommended Action                                                   |
| --------------------- | ----------- | ----------------------------------------------- | --------- | -------------------------------------------------------------------- |
| `VALIDATION_ERROR`    | 400         | Missing or invalid required fields              | No        | Fix the request payload and resubmit                                 |
| `INVALID_JSON`        | 400         | Request body is not valid JSON                  | No        | Check for syntax errors in the JSON body                             |
| `UNAUTHORIZED`        | 401         | Missing or invalid API key                      | No        | Verify the `X-API-KEY` header value                                  |
| `FORBIDDEN`           | 403         | API key does not have access to this project    | No        | Contact Zenoo support for access                                     |
| `NOT_FOUND`           | 404         | Invalid project hash, endpoint, or resource     | No        | Verify the URL, project hash, and resource ID                        |
| `TOKEN_EXPIRED`       | 404         | Pull token has expired                          | No        | Initiate a new verification to get fresh tokens                      |
| `PROCESSING`          | 204         | Results still processing (pull endpoint only)   | Yes       | Retry per polling strategy in [Sync vs Async](/guides/sync-vs-async) |
| `RATE_LIMITED`        | 429         | Too many requests                               | Yes       | Wait for the duration in the `Retry-After` header                    |
| `INTERNAL_ERROR`      | 500         | Unexpected server error                         | Yes       | Retry with exponential backoff                                       |
| `PROVIDER_ERROR`      | 502         | Upstream provider failure (registry, screening) | Yes       | Retry with exponential backoff                                       |
| `SERVICE_UNAVAILABLE` | 503         | Zenoo service temporarily unavailable           | Yes       | Retry after 30 seconds                                               |

## Examples

<AccordionGroup>
  <Accordion title="VALIDATION_ERROR (400)">
    Returned when the request is missing required fields or contains invalid values.

    ```json theme={null}
    {
      "error": "VALIDATION_ERROR",
      "message": "Missing required field: company_name",
      "request_id": "req-7f3a2b1c"
    }
    ```

    ```json theme={null}
    {
      "error": "VALIDATION_ERROR",
      "message": "Invalid country code: UK. Use ISO 3166-1 alpha-2 (GB).",
      "request_id": "req-8e4d3c2a"
    }
    ```
  </Accordion>

  <Accordion title="INVALID_JSON (400)">
    Returned when the request body cannot be parsed as JSON.

    ```json theme={null}
    {
      "error": "INVALID_JSON",
      "message": "Unexpected token at position 42",
      "request_id": "req-9a5e4d3b"
    }
    ```
  </Accordion>

  <Accordion title="UNAUTHORIZED (401)">
    Returned when the `X-API-KEY` header is missing or contains an invalid key.

    ```json theme={null}
    {
      "error": "UNAUTHORIZED",
      "message": "Invalid API key",
      "request_id": "req-1b6f5e4c"
    }
    ```
  </Accordion>

  <Accordion title="FORBIDDEN (403)">
    Returned when the API key is valid but does not have access to the requested project.

    ```json theme={null}
    {
      "error": "FORBIDDEN",
      "message": "API key does not have access to project abc123",
      "request_id": "req-2c7a6f5d"
    }
    ```
  </Accordion>

  <Accordion title="NOT_FOUND (404)">
    Returned when the project hash, endpoint, or resource does not exist.

    ```json theme={null}
    {
      "error": "NOT_FOUND",
      "message": "Project not found: invalid-hash",
      "request_id": "req-3d8b7a6e"
    }
    ```
  </Accordion>

  <Accordion title="TOKEN_EXPIRED (404)">
    Returned when a pull token has expired. Pull tokens are valid for **30 days** after results become available. Start tokens (verification URLs) expire after **24 hours**.

    ```json theme={null}
    {
      "error": "TOKEN_EXPIRED",
      "message": "Pull token has expired. Initiate a new verification.",
      "request_id": "req-4e9c8b7f"
    }
    ```
  </Accordion>

  <Accordion title="RATE_LIMITED (429)">
    Returned when request volume exceeds your project's rate limit. The `Retry-After` header tells you how many seconds to wait.

    ```json theme={null}
    {
      "error": "RATE_LIMITED",
      "message": "Rate limit exceeded. Retry after 60 seconds.",
      "request_id": "req-5f0d9c8a"
    }
    ```

    **Response headers:**

    ```
    Retry-After: 60
    ```
  </Accordion>

  <Accordion title="INTERNAL_ERROR (500)">
    Returned for unexpected server errors. These are transient and should be retried.

    ```json theme={null}
    {
      "error": "INTERNAL_ERROR",
      "message": "An unexpected error occurred. Please retry.",
      "request_id": "req-6a1e0d9b"
    }
    ```
  </Accordion>

  <Accordion title="PROVIDER_ERROR (502)">
    Returned when an upstream verification provider (registry, screening) fails. Zenoo automatically retries provider failures for async flows. For sync flows, retry the request.

    ```json theme={null}
    {
      "error": "PROVIDER_ERROR",
      "message": "Upstream provider temporarily unavailable",
      "request_id": "req-7b2f1e0c"
    }
    ```
  </Accordion>

  <Accordion title="SERVICE_UNAVAILABLE (503)">
    Returned when the Zenoo service is temporarily unavailable (maintenance, capacity limits). Retry after 30 seconds.

    ```json theme={null}
    {
      "error": "SERVICE_UNAVAILABLE",
      "message": "Service temporarily unavailable. Please retry.",
      "request_id": "req-8c3a2f1d"
    }
    ```
  </Accordion>
</AccordionGroup>

## Retry logic

<Warning>Only retry errors marked as retryable in the table above. For 4xx errors, fix the request before resubmitting.</Warning>

<Note>
  Use the following exponential backoff schedule for retryable errors:

  ```
  Attempt 1: Immediate
  Attempt 2: Wait 5 seconds
  Attempt 3: Wait 25 seconds
  Attempt 4: Wait 60 seconds
  Maximum: 4 attempts
  ```
</Note>

For `RATE_LIMITED` (429), always use the `Retry-After` header value instead of the default backoff schedule.

For detailed retry implementation, see [Error Handling](/guides/error-handling).

## Next steps

* [Rate Limits](/reference/rate-limits) -- Per-endpoint rate limits and best practices
* [Error Handling](/guides/error-handling) -- Retry strategies and circuit breaker behavior
* [Glossary](/reference/glossary) -- Term definitions
