> For the complete documentation index, see [llms.txt](https://docs.forepaas.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forepaas.org/v2/getting-started/common-pitfalls.md).

# Common Pitfalls

Common mistakes and how to avoid them when integrating with the ForeGate PaaS API

Field-tested traps, in roughly the order integrators hit them.

## Signing

### 1. Uppercase header names in the signature

The gateway normalizes custom header names to **lowercase** before rebuilding the string-to-sign. If you sign `X-API-KEY:…` or declare `x-ca-signature-headers: Cookie,X-API-KEY`, the server's string and yours diverge → `400 Invalid Signature`.

* ❌ `'x-ca-signature-headers': 'X-API-KEY'`
* ✅ `'x-ca-signature-headers': 'x-api-key'`
* ✅ User WebSocket channels: `'x-ca-signature-headers': 'cookie,x-api-key'` (alphabetical: `cookie` first)

### 2. Dropping the empty lines on GET requests

Body-less requests have no `content-md5` and no `content-type`, but their **positions in the string-to-sign remain** as empty lines:

```
GET
application/json
⬅ empty content-md5 line
⬅ empty content-type line
2026-06-01 17:51:23
x-api-key:<X-API-KEY>
/markets/list?page=1&pageSize=10
```

Joining only the non-empty parts produces a valid-looking string that never matches.

### 3. Query strings not sorted (or URL-encoded)

When the request has query parameters, the path component of the string-to-sign includes them **sorted alphabetically by key** and **not URL-encoded**. `/socket/user-positions?marketId=…&userId=…` — `marketId` before `userId`, always.

### 4. Wrong `date` format or clock skew

The `date` header is `YYYY-MM-DD HH:mm:ss` in **UTC (UTC+0)** — not ISO 8601, not GMT-style `Date`. It must be within \~15 minutes of server time. Response-payload timestamps, by contrast, are ISO 8601 — the two formats coexist by design.

### 5. Hashing a different body than you send

`content-md5` = `base64(md5(raw body))` over the **exact bytes transmitted**. Serializing once for the hash and again for the request (different key order, whitespace, or unicode escaping) is a classic mismatch. Serialize once, reuse the string.

### 6. Signing with the wrong secret

The HMAC key is the **APP Secret**. The APP Key goes in `x-ca-key`, the API key in `x-api-key` — three different values. The APP Secret itself is never sent as a header.

## Sessions

### 7. Losing the Set-Cookie from registration

`POST /user/register` returns the user session via `Set-Cookie`. Every user-scoped endpoint requires that cookie in the `Cookie` header. If you receive HTTP 401 with business code `1401` and `unauthorized: Missing Cookie header`, you dropped it; if the upstream rejects an invalid or expired session, re-register to mint a fresh session.

### 8. Forgetting that WebSocket user channels sign the cookie

On REST calls the cookie is sent but **not** signed (`x-ca-signature-headers` stays `x-api-key`). On `/socket/user-info`, `/socket/user-positions`, `/socket/user-open-orders` the cookie **must** participate in the signature and be declared in `x-ca-signature-headers`. Reusing your REST signing code unchanged for user sockets fails the handshake.

## Data Types & Precision

### 9. Parsing big IDs and decimals as native numbers

Market/outcome/option IDs can exceed 2⁵³ (e.g. `415481793060297046`), so every JSON ID must remain a string. Fields documented as decimal strings—such as balances and volumes—can also exceed native precision (`396.878928000000000000`, `0E-18`); parse those with a decimal library. Keep fields documented as JSON numbers, including position quantities and costs, as numbers.

* Send IDs as strings where accepted — `/ordering/open-orders` explicitly recommends it.
* Parse decimals with `decimal.js` / `BigDecimal` / `json-bigint`.

### 10. `marchantUserId` is spelled that way on purpose

The registration field is `marchantUserId` — "marchant", not "merchant" — kept for compatibility with the upstream API. Sending `merchantUserId` fails validation. (The separate `merchantId` field *is* spelled normally.)

### 11. Use `message`, not `msg`

Gateway JSON envelopes use `code`, `message`, and `data`. Do not read a `msg` field from public API responses.

## Endpoint Quirks

### 12. `/ordering/create-order` conditional fields

The required fields change with `(orderType, direction)`:

| Combination             | Additional required fields |
| ----------------------- | -------------------------- |
| `LIMIT` + any direction | `limitPrice`, `share`      |
| `MARKET` + `BUY`        | `price` (amount to spend)  |
| `MARKET` + `SELL`       | `share` (shares to sell)   |

`positionId` is required only for `SELL` orders. Sending `price` on a MARKET SELL (or `share` on a MARKET BUY) gets a 400 naming the missing counterpart.

### 13. Cancelling one order still takes an array

`/ordering/cancel-order` accepts `orderOpenIds` as a non-empty array of strings — even for a single cancellation: `"orderOpenIds": ["1400001111"]`. Also note the batch-claim path is `/ordering/claim_all` (underscore, not a dash).

### 14. `/withdraw` limits and fixed parameters

* `/withdraw/request` amounts: **minimum 10, maximum 500**.
* `/withdraw/limit` requires the `userId` query parameter even though the session cookie identifies the user, and the gateway fixes the upstream USDT `coinId` to `"2"`. Do not send `coinId` to any public monetary endpoint.

### 15. `/search` accepts `searchBy=Market` or `searchBy=Creator`

Any other `searchBy` value returns a 400 deserialization error. An empty `search` string is treated as if the parameter were absent.

## WebSockets

### 16. Consuming the CLOB delta stream without a snapshot

`/socket/clob-price-update` sends **pure deltas** — no initial snapshot. You must seed a local book from `GET /orderbook/book` and apply deltas keyed by `(optionId, side, price)`, or your book is permanently incomplete. For strong consistency, buffer deltas before fetching the snapshot and replay the ones newer than it — the full recipe is in [Building a local order book](/v2/advanced/websocket-api.md#building-a-local-order-book-required-reading).

### 17. Treating WebSocket business times as epoch values

Known server-to-client business timestamps are normalized to ISO 8601 UTC at second precision. For example, both `/socket/clob-price-update` and `/socket/market-price` use values such as `2026-05-29T06:30:53Z`.

### 18. Expecting `asks` and `bids` in one message

Each CLOB delta message carries only one side. A simultaneous two-sided update arrives as two messages. `size: 0` with `eventType: removed` means the level was cleared.

{% hint style="success" %}
**Golden rule:** when a signed request fails, read the `x-ca-error-message` response header before changing anything else — it contains the server's string-to-sign, which pinpoints the mismatch immediately.
{% endhint %}
