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

# Error Handling

> Understanding and handling API errors

<Info>
  This page is reference material.

  If you are troubleshooting a theme workflow, start with the workflow pages first:

  * [Build and Customize a Theme with AI](/themes/build-and-customize-with-ai)
  * [Editing Themes](/themes/editing)

  Then come back here if you need the exact error shape or status-code meaning.
</Info>

## Error Response Format

All errors follow a consistent structure:

```json theme={"system"}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email address",
    "doc_url": "https://docs.shoppex.io/api-reference/errors",
    "details": [
      { "field": "email", "message": "must be a valid email" }
    ]
  }
}
```

| Field     | Type   | Description                              |
| --------- | ------ | ---------------------------------------- |
| `code`    | string | Machine-readable error code              |
| `message` | string | Human-readable description               |
| `doc_url` | string | Link to relevant documentation           |
| `details` | array  | Field-level validation errors (optional) |

***

## HTTP Status Codes

| Code  | Name              | Description                                  |
| ----- | ----------------- | -------------------------------------------- |
| `200` | OK                | Request succeeded                            |
| `201` | Created           | Resource created successfully                |
| `204` | No Content        | Success with no response body (e.g., DELETE) |
| `400` | Bad Request       | Domain-specific or business rule error       |
| `401` | Unauthorized      | Missing or invalid API key                   |
| `403` | Forbidden         | Valid key but insufficient permissions       |
| `404` | Not Found         | Resource doesn't exist                       |
| `422` | Validation Error  | Invalid field values                         |
| `429` | Too Many Requests | Rate limit exceeded                          |
| `500` | Server Error      | Something went wrong on our end              |

***

## Error Codes Reference

### Authentication Errors

<AccordionGroup>
  <Accordion title="UNAUTHORIZED" icon="lock">
    **HTTP 401** - API key is missing or invalid.

    ```json theme={"system"}
    {
      "error": {
        "code": "UNAUTHORIZED",
        "message": "Invalid API key.",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```

    **Solution:** Check that your API key is correct and included in the `Authorization` header.
  </Accordion>

  <Accordion title="FORBIDDEN (missing scope)" icon="ban">
    **HTTP 403** - Your API key is valid but lacks the required scope for this endpoint.

    ```json theme={"system"}
    {
      "error": {
        "code": "FORBIDDEN",
        "message": "Missing required API scope.",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```

    **Solution:** Go to **Settings → API Keys**, check which scopes your key has, and add the missing one. Use `GET /me/capabilities` to inspect active scopes at runtime.
  </Accordion>

  <Accordion title="FORBIDDEN (shop suspended)" icon="ban">
    **HTTP 403** - Your shop has been suspended by Shoppex.

    ```json theme={"system"}
    {
      "error": {
        "code": "FORBIDDEN",
        "message": "Your shop has been suspended.",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```

    **Solution:** Reach out via the Shoppex [Discord](https://discord.gg/VjrptT6Nvx) or [Telegram](https://t.me/shoppexhq) to resolve the suspension. This is an account-level issue, not a key configuration problem.
  </Accordion>
</AccordionGroup>

### Validation Errors

<AccordionGroup>
  <Accordion title="VALIDATION_ERROR" icon="triangle-exclamation">
    **HTTP 422** - One or more fields failed validation.

    ```json theme={"system"}
    {
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "Validation failed",
        "details": [
          { "field": "email", "message": "must be a valid email" },
          { "field": "price", "message": "must be a positive number" }
        ]
      }
    }
    ```

    **Solution:** Check the `details` array for specific field errors.
  </Accordion>
</AccordionGroup>

### Request Error Semantics

Use the status code as the first signal:

* `422 VALIDATION_ERROR` means the request shape or field values are invalid
* `400` means the request was understood, but a business rule rejected it
* `404` can be generic `NOT_FOUND` or resource-specific like `PRODUCT_NOT_FOUND`

For example: a wrong email format on `POST /customers` returns `422 VALIDATION_ERROR`, an expired license on `POST /licenses/validate` returns `400 LICENSE_EXPIRED`, and a missing product on `GET /products/prod_xyz` returns `404 PRODUCT_NOT_FOUND`.

## Theme Workflow Troubleshooting

If you are using the theme automation flow:

* `401` usually means your API key is missing or invalid
* `403` usually means your key is missing `themes.read` or `themes.write`
* `422` usually means the request body or params are wrong
* `500` usually means the server failed while validating, previewing, publishing, or reading the theme

The most common mistake: `theme.inspect` fails with `403` because your key is missing `themes.read`, and `theme.apply` fails with `403` because it needs `themes.write`. A `422` usually means a bad request body or theme id, while `500` means something broke server-side — inspect the error and retry.

### Resource Errors

<AccordionGroup>
  <Accordion title="NOT_FOUND" icon="magnifying-glass">
    **HTTP 404** - The requested resource doesn't exist.

    ```json theme={"system"}
    {
      "error": {
        "code": "NOT_FOUND",
        "message": "Requested resource not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```

    **Solution:** Verify the resource ID is correct.
  </Accordion>

  <Accordion title="PRODUCT_NOT_FOUND" icon="box">
    **HTTP 404** - Specific product not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "PRODUCT_NOT_FOUND",
        "message": "Product with ID 'prod_xyz' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="INVOICE_NOT_FOUND" icon="file-invoice">
    **HTTP 404** - Specific invoice not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "INVOICE_NOT_FOUND",
        "message": "Invoice with ID 'inv_abc' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="PAYMENT_NOT_FOUND" icon="credit-card">
    **HTTP 404** - Payment not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "PAYMENT_NOT_FOUND",
        "message": "Payment with ID 'pay_xyz' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="CUSTOMER_NOT_FOUND" icon="user">
    **HTTP 404** - Customer not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "CUSTOMER_NOT_FOUND",
        "message": "Customer with ID 'cust_abc' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="CATEGORY_NOT_FOUND" icon="folder">
    **HTTP 404** - Category not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "CATEGORY_NOT_FOUND",
        "message": "Category with ID 'cat_xyz' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="COUPON_NOT_FOUND" icon="tag">
    **HTTP 404** - Coupon not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "COUPON_NOT_FOUND",
        "message": "Coupon with ID 'coup_abc' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="SUBSCRIPTION_NOT_FOUND" icon="repeat">
    **HTTP 404** - Subscription not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "SUBSCRIPTION_NOT_FOUND",
        "message": "Subscription with ID 'sub_xyz' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="TICKET_NOT_FOUND" icon="ticket">
    **HTTP 404** - Support ticket not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "TICKET_NOT_FOUND",
        "message": "Ticket with ID 'tkt_abc' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="REVIEW_NOT_FOUND" icon="star">
    **HTTP 404** - Review not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "REVIEW_NOT_FOUND",
        "message": "Review with ID 'rev_xyz' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="ESCROW_NOT_FOUND" icon="lock">
    **HTTP 404** - Escrow transaction not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "ESCROW_NOT_FOUND",
        "message": "Escrow with ID 'esc_abc' not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### License Errors

<AccordionGroup>
  <Accordion title="LICENSE_NOT_FOUND" icon="key">
    **HTTP 404** - License key not found.

    ```json theme={"system"}
    {
      "error": {
        "code": "LICENSE_NOT_FOUND",
        "message": "License key not found",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="LICENSE_INVALID" icon="key">
    **HTTP 400** - License key is invalid or malformed.

    ```json theme={"system"}
    {
      "error": {
        "code": "LICENSE_INVALID",
        "message": "Invalid license key format",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="LICENSE_EXPIRED" icon="clock">
    **HTTP 400** - License key has expired.

    ```json theme={"system"}
    {
      "error": {
        "code": "LICENSE_EXPIRED",
        "message": "License key has expired",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="LICENSE_HWID_MISMATCH" icon="desktop">
    **HTTP 400** - Hardware ID does not match the registered device.

    ```json theme={"system"}
    {
      "error": {
        "code": "LICENSE_HWID_MISMATCH",
        "message": "Hardware ID mismatch. License is bound to a different device.",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Coupon Errors

<AccordionGroup>
  <Accordion title="COUPON_EXPIRED" icon="clock">
    **HTTP 400** - Coupon has expired.

    ```json theme={"system"}
    {
      "error": {
        "code": "COUPON_EXPIRED",
        "message": "This coupon has expired",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>

  <Accordion title="COUPON_MAX_USES_REACHED" icon="ban">
    **HTTP 400** - Coupon has reached maximum uses.

    ```json theme={"system"}
    {
      "error": {
        "code": "COUPON_MAX_USES_REACHED",
        "message": "This coupon has reached its maximum number of uses",
        "doc_url": "https://docs.shoppex.io/api-reference/errors"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Idempotency Errors

<Accordion title="IDEMPOTENCY_CONFLICT" icon="fingerprint">
  **HTTP 422** - The same idempotency key was used with a different request body.

  ```json theme={"system"}
  {
    "error": {
      "code": "IDEMPOTENCY_CONFLICT",
      "message": "Idempotency key already used with a different request body",
      "doc_url": "https://docs.shoppex.io/api-reference/errors"
    }
  }
  ```

  **Solution:** Each unique request body needs its own idempotency key. If you're retrying a failed request, make sure the body matches the original. See the [Idempotency section](/api-reference/introduction#idempotency) for details.
</Accordion>

### Rate Limiting

<Accordion title="RATE_LIMITED" icon="gauge-high">
  **HTTP 429** - Too many requests.

  ```json theme={"system"}
  {
    "error": {
      "code": "RATE_LIMITED",
      "message": "Rate limit exceeded. Retry after the reset window.",
      "doc_url": "https://docs.shoppex.io/api-reference/errors"
    }
  }
  ```

  Response headers:

  * `X-RateLimit-Limit`
  * `X-RateLimit-Remaining`
  * `X-RateLimit-Reset`
  * `Retry-After` when blocked

  **Solution:** Wait for `Retry-After` or `X-RateLimit-Reset` before retrying.
</Accordion>

***

## Handling Errors in Code

Every Dev API error response also returns `X-Request-Id` in the headers. Log that value together with `error.code` when you need support or want to trace a failing request.

<CodeGroup>
  ```typescript TypeScript theme={"system"}
  async function createPayment(title: string, email: string, value: number) {
    const response = await fetch('https://api.shoppex.io/dev/v1/payments', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ title, email, value, currency: 'USD' })
    });

    const result = await response.json();

    if (!response.ok) {
      const error = result.error;

      switch (error.code) {
        case 'VALIDATION_ERROR':
          // Handle field-level errors
          error.details?.forEach(d => console.error(`${d.field}: ${d.message}`));
          break;
        case 'RATE_LIMITED':
          // Wait and retry
          const retryAfter = Number(response.headers.get('retry-after') ?? '2');
          console.log(`Rate limited. Waiting ${retryAfter}s before retry...`);
          await new Promise(r => setTimeout(r, retryAfter * 1000));
          return createPayment(title, email, value);
        case 'UNAUTHORIZED':
          console.error('Invalid API key');
          break;
        default:
          console.error(`Error: ${error.message}`);
      }

      throw new Error(error.message);
    }

    return result.data;
  }
  ```

  ```python Python theme={"system"}
  import requests
  import time

  def create_payment(title: str, email: str, value: float):
      response = requests.post(
          'https://api.shoppex.io/dev/v1/payments',
          headers={
              'Authorization': f'Bearer {API_KEY}',
              'Content-Type': 'application/json'
          },
          json={'title': title, 'email': email, 'value': value, 'currency': 'USD'}
      )

      result = response.json()

      if not response.ok:
          error = result['error']

          if error['code'] == 'VALIDATION_ERROR':
              for detail in error.get('details', []):
                  print(f"{detail['field']}: {detail['message']}")
          elif error['code'] == 'RATE_LIMITED':
              retry_after = int(response.headers.get('retry-after', '2'))
              print(f"Rate limited. Waiting {retry_after}s before retry...")
              time.sleep(retry_after)
              return create_payment(title, email, value)  # Retry

          raise Exception(error['message'])

      return result['data']
  ```
</CodeGroup>

***

## Best Practices

<CardGroup cols={2}>
  <Card title="Log Error Codes" icon="clipboard-list">
    Always log the `error.code` for debugging. It's more reliable than parsing messages.
  </Card>

  <Card title="Handle 429s Gracefully" icon="clock">
    Implement exponential backoff for rate limits. Check `X-RateLimit-Reset` header.
  </Card>

  <Card title="Validate Before Sending" icon="check">
    Validate inputs client-side to catch errors early and reduce API calls.
  </Card>

  <Card title="Use Idempotency Keys" icon="fingerprint">
    For payment creation, use idempotency keys to safely retry failed requests.
  </Card>
</CardGroup>
