> For the complete documentation index, see [llms.txt](https://ocx.gitbook.io/ocx-doc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ocx.gitbook.io/ocx-doc/ocx/reference/error-codes.md).

# error codes

Every non-2xx response has a JSON body:

```json
{
  "error":   "MarginError",
  "code":    "INSUFFICIENT_MARGIN",
  "message": "Insufficient initial margin",
  "details": { "requiredIm": "100.00", "availableIm": "80.00" }
}
```

* **`error`** — the error family.
* **`code`** — stable machine-readable code. Match on this in client logic.
* **`message`** — human-readable description.
* **`details`** — optional context (may be omitted).

## HTTP status families

| Status | Family   | Meaning                                                       |
| ------ | -------- | ------------------------------------------------------------- |
| 200    | —        | Success                                                       |
| 400    | Request  | Malformed request, schema validation failure                  |
| 401    | Auth     | Missing, invalid, or expired credential                       |
| 403    | Auth     | Credential valid but insufficient scope or IP not allowed     |
| 404    | Resource | Resource not found or not owned by caller                     |
| 409    | Conflict | Duplicate client order ID, market halted, revoked key, etc.   |
| 422    | Business | Rule rejection (insufficient margin, post-only crosses, etc.) |
| 429    | Rate     | Rate limited — see `retryAfterMs`                             |
| 500    | Server   | Unexpected server error                                       |
| 503    | Upstream | Upstream degraded; retry shortly                              |

## Full error code table

### AuthError

| Code                 | Status | Meaning                                      |
| -------------------- | ------ | -------------------------------------------- |
| `MISSING_CREDENTIAL` | 401    | No cookie and no Authorization header        |
| `INVALID_TOKEN`      | 401    | JWT or API key invalid or expired            |
| `KEY_REVOKED`        | 401    | API key has been revoked                     |
| `NONCE_MISMATCH`     | 400    | SIWE nonce doesn't match an issued nonce     |
| `INVALID_SIGNATURE`  | 401    | SIWE signature verification failed           |
| `IP_NOT_ALLOWED`     | 403    | Caller's IP is not in the key's allowlist    |
| `SCOPE_INSUFFICIENT` | 403    | The key's scope doesn't permit this endpoint |

### OrderError

| Code                        | Status | Meaning                                                             |
| --------------------------- | ------ | ------------------------------------------------------------------- |
| `INVALID_MARKET_ID`         | 400    | Unknown market or malformed ID                                      |
| `INVALID_QUANTITY`          | 400    | Quantity ≤ 0, below minimum, or not a multiple of step size         |
| `INVALID_PRICE`             | 400    | Price not a tick multiple, outside price band, or missing for limit |
| `INVALID_TIME_IN_FORCE`     | 400    | `timeInForce` not one of `gtc`, `ioc`, `fok`                        |
| `POST_ONLY_WOULD_CROSS`     | 422    | `postOnly: true` order would have crossed the book                  |
| `REDUCE_ONLY_VIOLATION`     | 422    | `reduceOnly: true` order would increase position                    |
| `MARKET_HALTED`             | 409    | Market is not active                                                |
| `DUPLICATE_CLIENT_ORDER_ID` | 409    | `clientOrderId` already used (idempotency — returns existing order) |

### MarginError

| Code                  | Status | Meaning                                                             |
| --------------------- | ------ | ------------------------------------------------------------------- |
| `INSUFFICIENT_MARGIN` | 422    | Not enough initial margin for this order                            |
| `MISSING_MARK_PRICE`  | 422    | No mark available for the market (rare; may indicate a data outage) |
| `STALE_MARK_PRICE`    | 422    | Mark price is older than the staleness threshold                    |
| `NO_RISK_ARRAY`       | 422    | Risk-array cache miss — margin check cannot proceed                 |

### AccountError

| Code                      | Status | Meaning                                            |
| ------------------------- | ------ | -------------------------------------------------- |
| `ACCOUNT_NOT_FOUND`       | 404    | Account not yet initialized (make a deposit first) |
| `BALANCE_NOT_INITIALIZED` | 409    | Internal balance row missing                       |
| `INSUFFICIENT_BALANCE`    | 422    | Bucket lacks the funds for the action              |
| `SAME_BUCKET`             | 400    | Transfer `from == to`                              |

### MarketError

| Code                | Status | Meaning                          |
| ------------------- | ------ | -------------------------------- |
| `MARKET_NOT_FOUND`  | 404    | Unknown `marketId`               |
| `PRODUCT_NOT_FOUND` | 404    | Related product metadata missing |

### RateLimitError

| Code               | Status | Meaning                            |
| ------------------ | ------ | ---------------------------------- |
| `ORDER_RATE_LIMIT` | 429    | Per-user order-rate limit exceeded |

Response body when rate-limited:

```json
{
  "error": "RateLimitError",
  "code": "ORDER_RATE_LIMIT",
  "message": "Order rate limit exceeded",
  "retryAfterMs": 1000
}
```

### InternalError

| Code                | Status | Meaning                                                     |
| ------------------- | ------ | ----------------------------------------------------------- |
| `INTERNAL_ERROR`    | 500    | Unexpected error. Include `X-Correlation-Id` when reporting |
| `UPSTREAM_DEGRADED` | 503    | An upstream dependency is temporarily unavailable           |

## Handling errors

```ts
async function placeOrder(body) {
  const res = await fetch('/perps/orders', { method: 'POST', body: JSON.stringify(body) });
  if (!res.ok) {
    const err = await res.json();
    switch (err.code) {
      case 'DUPLICATE_CLIENT_ORDER_ID':
        // Fine — we've already submitted this
        return err.details.existingOrder;
      case 'INSUFFICIENT_MARGIN':
        // Shrink size, or top up the perps bucket
        throw new NeedMoreMargin(err.details);
      case 'ORDER_RATE_LIMIT':
        await sleep(err.retryAfterMs);
        return placeOrder(body);
      default:
        throw new Error(`${err.error}/${err.code}: ${err.message}`);
    }
  }
  return res.json();
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ocx.gitbook.io/ocx-doc/ocx/reference/error-codes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
