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

# Error codes

> Complete reference of API error responses, status codes, and retry guidance

## How errors are returned

The Integrations API does not use a universal `error_code` field, and it does not return a `request_id`. Instead, errors are signalled by the HTTP status code and one of four response body shapes. Always branch on `response.status_code` first, then read the body shape that matches.

### Error envelope shapes

<Tabs>
  <Tab title="Domain error">
    Most domain errors return a `detail` message. Some also include a `docs_url` pointing at the relevant documentation.

    ```json theme={null}
    {
      "detail": "Asset already submitted",
      "docs_url": "https://docs.mediamagicverify.com//concepts/assets"
    }
    ```

    The `docs_url` field is optional. Authentication failures (401) use this shape with messages such as `Missing X-API-Key header`, `Invalid API key`, `This API key has expired`, or `This API key has been revoked`.
  </Tab>

  <Tab title="Validation error (422)">
    Request validation failures return a structured envelope with a per-field breakdown.

    ```json theme={null}
    {
      "error": "validation_error",
      "message": "Request payload is invalid.",
      "fields": [
        { "field": "endpointUrl", "message": "URL must use HTTPS" }
      ]
    }
    ```
  </Tab>

  <Tab title="Rate limit (429)">
    Exceeding a rate-limit bucket returns a single `error` string and a `Retry-After` header.

    ```json theme={null}
    {
      "error": "Rate limit exceeded: too many submission writes"
    }
    ```

    See [Rate limiting](/reference/rate-limiting) for the bucket model.
  </Tab>

  <Tab title="Tier not authorized (402)">
    Returned when your plan or tier does not include access to the Integrations API.

    ```json theme={null}
    {
      "error_code": "tier_not_authorized_for_integrations_api",
      "message": "Your plan does not include access to the Integrations API.",
      "bucket": "submission_write"
    }
    ```

    This is the only response that uses an `error_code` field. The `bucket` names the capability that was gated.
  </Tab>
</Tabs>

## Status codes

| Code | When                                                                                      |
| ---- | ----------------------------------------------------------------------------------------- |
| 200  | OK                                                                                        |
| 201  | Created (submission, webhook subscription)                                                |
| 202  | Accepted (asset retry)                                                                    |
| 204  | No content (delete subscription)                                                          |
| 400  | `simulationDelaySeconds` sent on a live key; subscription missing a delivery destination  |
| 401  | Missing, invalid, expired, or revoked API key                                             |
| 402  | Tier not authorized for the Integrations API (plan gating)                                |
| 403  | Sandbox-only endpoint called with a live key; invalid blob path                           |
| 404  | Resource not found in your workspace; asset not finished (topics or issues still pending) |
| 409  | Asset already submitted; asset not retryable; retry already in progress                   |
| 413  | Upload too large                                                                          |
| 415  | Unsupported or mismatched content type                                                    |
| 422  | Request validation failed                                                                 |
| 429  | Rate limit exceeded (includes `Retry-After`)                                              |
| 500  | Storage error or internal failure                                                         |
| 502  | Failed to load an issue or topic blob from storage                                        |

## Common situations

These are real conditions you will encounter, described by status code and the style of `detail` message returned. There are no error-code constants to match against.

| Status | Situation                                                                                          | Example message                       |
| ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------- |
| 401    | Missing or bad API key                                                                             | `Invalid API key`                     |
| 402    | Plan does not include API access                                                                   | see the 402 envelope above            |
| 403    | Invalid blob path, or sandbox-only endpoint hit with a live key                                    | `Invalid blob path`                   |
| 404    | Submission or asset not found in your workspace; topics/issues requested before the asset finished | `Submission not found`                |
| 409    | Asset already submitted, or a retry is not allowed                                                 | `Asset already submitted`             |
| 413    | Uploaded blob exceeds the per-category size limit                                                  | `File too large`                      |
| 415    | Content type not in the allowlist or does not match the file                                       | `Unsupported content type`            |
| 422    | Request body failed validation                                                                     | see the validation envelope above     |
| 429    | A rate-limit bucket was exceeded                                                                   | `Rate limit exceeded: ...`            |
| 500    | Storage write or internal error                                                                    | `Internal server error`               |
| 502    | Issue or topic blob could not be loaded                                                            | `Failed to load results from storage` |

## Retry guidance

| Status    | Retryable? | How                                                        |
| --------- | ---------- | ---------------------------------------------------------- |
| 429       | Yes        | Wait for the `Retry-After` header, then retry with backoff |
| 500       | Yes        | Retry with exponential backoff and jitter                  |
| 502       | Yes        | Retry with exponential backoff and jitter                  |
| Other 4xx | No         | Fix the request; retrying will not help                    |

Do not retry 400, 401, 402, 403, 404, 409, 413, 415, or 422 without changing the request. They indicate a problem with the request, the resource, or your plan that a retry alone cannot resolve.

## Handling errors in code

Branch on the status code, then read the matching body shape. Read `detail` for domain errors and the `fields` array for validation errors. Do not look for `error_code` (except on 402) or `request_id` (which does not exist).

```python theme={null}
import requests


def handle_error(response: requests.Response) -> None:
    """Inspect an error response and decide what to do."""
    status = response.status_code

    try:
        body = response.json()
    except ValueError:
        print(f"HTTP {status}: {response.text}")
        return

    if status == 422 and body.get("error") == "validation_error":
        print(f"Validation failed: {body.get('message')}")
        for field in body.get("fields", []):
            print(f"  {field['field']}: {field['message']}")
    elif status == 402:
        print(f"Plan not authorized for bucket {body.get('bucket')}: {body.get('message')}")
    elif status == 429:
        print(body.get("error", "Rate limit exceeded"))
    else:
        # Domain error envelope.
        print(f"HTTP {status}: {body.get('detail', response.text)}")
        if body.get("docs_url"):
            print(f"See {body['docs_url']}")


def is_retryable(status_code: int) -> bool:
    """429, 500, and 502 are safe to retry with backoff."""
    return status_code in {429, 500, 502}
```

For 429 responses, wait for the duration in the `Retry-After` header before retrying. See [Rate limiting](/reference/rate-limiting) for a complete backoff example.

## Need help?

* Note the HTTP status code and the `detail` or `fields` message
* Review the [Error handling guide](/guides/error-handling)
* Contact support at [https://support.mediamagicverify.com/](https://support.mediamagicverify.com/) with the status code and message
