> ## 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

<Note>
  This page is reference material.

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

  * [Editing with AI](/storefront/editing-with-ai)
  * [Visual themes](/storefront/visual-themes)

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

## 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, for example 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 does not exist                           |
| `422` | Validation Error  | Invalid field values                              |
| `429` | Too Many Requests | You went over the rate limit                      |
| `500` | Server Error      | Something went wrong on our end                   |

***

## Error codes reference

### Authentication errors

#### UNAUTHORIZED

**HTTP 401.** The 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.

#### FORBIDDEN (missing scope)

**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.

#### FORBIDDEN (shop suspended)

**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 through 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.

### Validation errors

#### VALIDATION\_ERROR

**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.

### What each request error means

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`. 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. A `500` means something broke on the server. Check the error and retry.

### Resource errors

#### NOT\_FOUND

**HTTP 404.** The requested resource does not 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.

#### PRODUCT\_NOT\_FOUND

**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"
  }
}
```

#### INVOICE\_NOT\_FOUND

**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"
  }
}
```

#### PAYMENT\_NOT\_FOUND

**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"
  }
}
```

#### CUSTOMER\_NOT\_FOUND

**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"
  }
}
```

#### CATEGORY\_NOT\_FOUND

**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"
  }
}
```

#### COUPON\_NOT\_FOUND

**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"
  }
}
```

#### SUBSCRIPTION\_NOT\_FOUND

**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"
  }
}
```

#### TICKET\_NOT\_FOUND

**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"
  }
}
```

#### REVIEW\_NOT\_FOUND

**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"
  }
}
```

#### ESCROW\_NOT\_FOUND

**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"
  }
}
```

### License errors

#### LICENSE\_NOT\_FOUND

**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"
  }
}
```

#### LICENSE\_INVALID

**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"
  }
}
```

#### LICENSE\_EXPIRED

**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"
  }
}
```

#### LICENSE\_HWID\_MISMATCH

**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"
  }
}
```

### Coupon errors

#### COUPON\_EXPIRED

**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"
  }
}
```

#### COUPON\_MAX\_USES\_REACHED

**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"
  }
}
```

### Idempotency errors

#### IDEMPOTENCY\_CONFLICT

**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 are retrying a failed request, make sure the body matches the original. See the [Idempotency section](/developers/api-overview#idempotency) for details.

### Rate limiting

#### RATE\_LIMITED

**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

Rate limits are plan-dependent, so treat any numeric values you see elsewhere as illustrative. Check the response headers from your own requests for your current limits.

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

***

## 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 is 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>
