Errors
Every error — from a missing parameter to an exchange outage — comes back in one shape:
{
"error": {
"code": "INVALID_REQUEST",
"status": 400,
"message": "Parameter 'symbol' must be a valid trading pair"
},
"meta": { "requestId": "550e8400-e29b-41d4-a716-446655440001" }
}
code— machine-readable, stable, switch on this (not on the message text).status— the HTTP status, mirrored in the body.message— human-readable English; wording may improve over time.meta.requestId— quote it when contacting support.
Internals are never leaked: unexpected failures return a generic 500 message.
The reaction pattern
// JavaScript — with the reference client (e.code / e.status / e.retryAfter / e.requestId)
try {
return await lexx.request("POST", "/orders",
{ body: order, headers: { "X-Idempotency-Key": key } });
} catch (e) {
switch (e.code) {
case "RATE_LIMITED": // wait, then retry
await sleep((e.retryAfter ?? 5) * 1000); return retry();
case "INTERNAL_ERROR":
case "SERVICE_UNAVAILABLE": // backoff-retry, SAME idempotency key
await sleep(backoff()); return retry();
case "TIMESTAMP_EXPIRED": // clock drifted — resync, retry
await lexx.syncTime(); return retry();
default: // 4xx: fix the request, don't loop
console.error(e.code, e.message, e.requestId);
throw e;
}
}
The same logic in Python maps onto LexxError (e.code / e.status / e.retry_after /
e.request_id).
Code reference
Request & validation
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
INVALID_REQUEST |
400 | Malformed request, missing/invalid parameter, or unreadable body — all client validation failures use this code | Fix the request; retrying unchanged will fail identically |
CONFLICT |
409 | Operation conflicts with current state (e.g. updating a bot that is still running) | Change the state first (stop the bot), then retry |
IDEMPOTENCY_CONFLICT |
409 | Same X-Idempotency-Key with different parameters, or the original request is still processing |
See Idempotency — two distinct cases |
PAYLOAD_TOO_LARGE |
413 | Request body exceeds 100 KB | Shrink the payload |
VALIDATION_ERROR |
422 | A downstream service rejected the request as semantically invalid (rare) | Fix the input; if unclear, contact support |
Authentication — the full diagnostic checklist lives in Authentication → Troubleshooting
| Code | HTTP | Meaning |
|---|---|---|
UNAUTHORIZED |
401 | Generic auth failure: missing headers, unknown/revoked key — also returned when an identical signature is replayed (“Duplicate request signature…”) |
INVALID_KEY |
401 | Public key unknown, deactivated, or malformed |
INVALID_SIGNATURE |
401 | Ed25519 signature does not match the request |
TIMESTAMP_EXPIRED |
401 | Timestamp outside the ±5 s window |
TIMESTAMP_INVALID |
401 | Timestamp is not a valid number |
IP_NOT_WHITELISTED |
403 | Caller IP not in the key’s CIDR whitelist |
Authorization & risk shields
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
FORBIDDEN |
403 | Action not allowed for this key (e.g. read key on a write endpoint) |
Use a trade-tier key |
EXCHANGE_NOT_ALLOWED |
403 | Target exchange is outside the key’s allowedExchanges shield |
Adjust the shield in the Terminal or use an allowed exchange |
RISK_SHIELD_ERROR |
500 | A shield check failed to evaluate — the API fails closed and blocks the action | Safe to retry; report if it persists |
Resources, rate & system
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
NOT_FOUND |
404 | Resource does not exist (order, bot, run…) | Check the ID |
RATE_LIMITED |
429 | Route limit exceeded | Wait Retry-After seconds — see Rate limits |
INTERNAL_ERROR |
500 | Unexpected server-side error | Retryable (idempotency key recommended) |
SERVICE_UNAVAILABLE |
503 | Maintenance or overload | Retry with exponential backoff |
UNKNOWN_ERROR |
— | Unclassified failure | Contact support with the requestId |
What is safe to retry
| ✅ Retry (fresh signature; same idempotency key for mutations) | RATE_LIMITED (after Retry-After) · INTERNAL_ERROR · SERVICE_UNAVAILABLE · RISK_SHIELD_ERROR · network errors/timeouts |
| ⛔ Don’t retry unchanged | Every other 4xx — the same input will fail the same way. Fix the cause first |
Remember the two-layer retry rule: fresh timestamp + signature every attempt
(Authentication), same
X-Idempotency-Key per logical operation (Idempotency).
Exchange-related nuances
- Cancelling an already-filled/cancelled order is usually an idempotent no-op: the platform
detects the state and returns
200with the current order. A genuine exchange rejection is passed through with the exchange’s message — typically as400 INVALID_REQUEST. - Calling a feature an exchange does not support (see the
capability matrix) currently surfaces as
500 INTERNAL_ERRORwith a descriptive message — not a400.
Two footnotes
GET /pingreplies in the legacy v1 envelope ({ status, data }) — the only endpoint that does.- The WebSocket has its own error vocabulary (17 codes, e.g.
AUTH_TIMEOUT,CHALLENGE_MISSING) delivered asop: "error"messages; noteIP_REJECTEDthere is the same condition as REST’sIP_NOT_WHITELISTED. Full list: WebSocket.
Contacting support
File a ticket via the support portal support.lexx-trade.com
(the “Contact us” widget) or email support@lexx-trade.com, and include: the meta.requestId,
the endpoint, the timestamp, and the code you received. The
requestId lets the team locate your exact request in seconds.
Verified against API spec v2.0.0 · 2026-07-11