# Errors

> The error envelope, the codes you'll see, and which ones are safe to retry.

Source: https://tickwire.io/docs/errors/

## The envelope

Every error has the same shape:

```json
{ "error": "not_supported_on_mt4", "message": "Market depth isn't available on MetaTrader 4.",
  "requestId": "4f6c1d2e9a8b4c7d8e9f0a1b2c3d4e5f",
  "docUrl": "https://tickwire.io/docs/errors/not_supported_on_mt4" }
```

- `error` is the machine-readable code. `docUrl` opens a short page about it, and
  [All error codes](#all-error-codes) below lists every code by area.
- `requestId` identifies the request. It's also in the `X-Request-ID` header of **every** response,
  successful or not. Quote it when you contact support: it finds the request in our logs and in
  the account's audit trail.

You can send your own `X-Request-ID` (1 to 128 characters from `A-Z a-z 0-9 . _ : -`). We keep it
end to end, from the moment we receive it to the account's terminal. Anything else is replaced with an id we
generate.

On the `/users/current/…` routes, `error` holds the error name that existing clients for that API
style already read (such as `NotFoundError`). Our code is in `code`, and `requestId` and `docUrl` are
added:

```json
{ "id": 1, "error": "NotFoundError", "code": "account_not_found",
  "message": "Account acc_7Qx not found",
  "requestId": "4f6c1d2e9a8b4c7d8e9f0a1b2c3d4e5f",
  "docUrl": "https://tickwire.io/docs/errors/account_not_found" }
```

The `/v1` routes and the terminal routes (`/{mt4|mt5}/{id}/…`) keep their
`{error, message, command_id}` shape and add the same two fields.

## HTTP status codes

| Status | `error` examples | Retry? |
|---|---|---|
| 400 | `ValidationError`, `not_supported_on_mt4` | No. Fix the request |
| 401 | `unauthorized` | No. Check the key |
| 403 | `read_only_key`, `ForbiddenError` | No |
| 404 | `not_found`, `symbol_not_found` | No |
| 409 | `idempotency_conflict` | No. You reused a key with a different body |
| 429 | `TooManyRequestsError` | Yes, after `Retry-After`. See [Rate limits](https://tickwire.io/docs/rate-limits/) |
| 502 | `backend_error` (the terminal returned an error) | Reads yes; mutations only with the same idempotency key |
| 503 | `not_connected` (no broker link right now) | Yes, with backoff. Check [health](https://tickwire.io/docs/health/) |
| 202 | `AcceptedError` (account creation still running) | Yes: same request, same `transaction-id` |

## Provisioning errors

Account creation can fail with a `details` code:

| Code | Meaning | What to do |
|---|---|---|
| `E_SRV_NOT_FOUND` | We don't know that server name | Use a suggested name from `details.serversByBrokers`, or upload a server file |
| `E_AUTH` | Login, password or server is wrong, or the account is disabled | Check the credentials. We don't charge for these |
| `E_SERVER_TIMEZONE` | Couldn't detect the broker's time settings | Create a [provisioning profile](https://tickwire.io/docs/broker-onboarding/) |
| `ERR_OTP_REQUIRED` | The broker wants a one-time password | Disable OTP for API access at the broker |
| `E_PASSWORD_CHANGE_REQUIRED` | The broker demands a password change | Change it in a terminal, then update the account |

## Trade results

`POST /trade` returns **HTTP 200** even when the broker rejects the order, so existing clients keep
working. Read
`numericCode` and `stringCode`:

| Codes | Meaning |
|---|---|
| 10008, 10009, 10010, 10025 | Success: placed, done, partially done, no changes needed |
| 10004 requote, 10006 rejected, 10013–10016 invalid request/volume/price/stops, 10018 market closed, 10019 no money | Refused. Nothing happened |
| 10031 connection, and 10011, 10012, 10028, 10039 | **May have executed.** Check `outcome`; see [Idempotency](https://tickwire.io/docs/idempotency/) |
| MT4: 128 trade timeout, 146 trade context busy | Same care as 10031 |

MT4 accounts return MT5-style codes too, so you only handle one set; the original MT4 code is kept
in the message.

## Streaming errors

On the WebSocket, errors arrive as frames:

```json
{ "type": "error", "seq": 88, "error": "invalid_subscription", "message": "timeframe is required for candles" }
```

## All error codes


Each entry gives the HTTP status and whether a retry can help. **Retry: yes** means nothing happened (or it was a read), so repeat the request, after `Retry-After` when there is one. **Retry: no** means the same request gets the same answer, so change something first. **Retry: same key only** means the first attempt may have run, so repeat it only with the same `Idempotency-Key` (or `clientId`). Trading errors also say whether anything reached the broker.

Each code also has its own short page at `/docs/errors/<code>/`, which is where `docUrl` points.

### Requests and validation

Problems with the request itself. Any endpoint can return these.

#### `accepted`

The same request is still being processed.
*HTTP 202 · Retry: same key only*

**Likely cause:** A create request with the same transaction-id is still running.

**What to do:** Repeat the same request after Retry-After seconds.

#### `api_version_unsupported`

The API version to pin is older than the oldest supported version.
*HTTP 400 · Retry: no*

**Likely cause:** The version is past its sunset, or predates every published version.

**What to do:** Pin one of metadata.supportedVersions. The pin was not changed.

#### `bad_path`

The request path is malformed.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely causes:**

- The path does not start with '/'.
- It has empty, '.' or '..' segments.

**What to do:** Fix the URL.

#### `bad_request`

The request is malformed.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** An invalid header, parameter or body value.

**What to do:** Fix the request; the message says what is wrong.

#### `body_too_large`

The request body exceeds the limit for this endpoint.
*HTTP 413 · Retry: no*

**Likely cause:** A hold release body over the size limit.

**What to do:** Send a smaller body.

#### `conflict`

The request conflicts with the current state.
*HTTP 409 · Retry: no*

**Likely cause:** The resource changed or already exists.

**What to do:** Re-read the resource, then decide.

#### `duplicate`

This already exists.
*HTTP 409 · Retry: no*

**Likely causes:**

- The trading account is already linked.
- A token id is already in use.

**What to do:** Use the existing resource.

#### `http_error`

An HTTP error with no more specific code.
*HTTP 404, 405 · Retry: no*

**Likely causes:**

- Status page API: the path or method does not exist (a typo in the URL, or the wrong HTTP method).
- MCP tool result: the Tickwire API answered with an HTTP error whose body had no code of its own; `message` gives the HTTP status.

**What to do:** Check the path and the method. From an MCP tool, read `message` and retry only if the status says it is temporary (429 or 5xx).

#### `invalid_api_version`

The Tickwire-Version header, or the apiVersion to pin, names no published API version.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely causes:**

- The value is not a date in YYYY-MM-DD form.
- The date is not one of the published versions (the message and metadata.supportedVersions list them).

**What to do:** Send a published version from the versioning page, or omit the header to use your organisation's pinned version. Nothing was sent or changed.

#### `invalid_cursor`

The pagination cursor is not one this list issued.
*HTTP 400 · Retry: no*

**Likely causes:**

- The cursor was edited, truncated, or taken from a different list endpoint.
- The filters changed between pages (a cursor is bound to the list and filters it came from).

**What to do:** Start again without cursor, or pass the next_cursor value from the previous page unchanged with the same filters.

#### `invalid_parameter`

A query or path parameter is invalid; `details` names it.
*HTTP 400 · Retry: no*

**Likely cause:** A value out of range or of the wrong type.

**What to do:** Fix the parameter.

#### `invalid_request`

The request body is invalid.
*HTTP 400 · Retry: no*

**Likely cause:** Not a JSON object, or a field fails validation.

**What to do:** Fix the body; the message names the field.

#### `method_not_allowed`

This HTTP method is not supported on this path.
*HTTP 405 · Retry: no · Nothing reached the broker*

**Likely cause:** For example POST on a read-only route.

**What to do:** Check the API reference.

#### `missing_header`

A required header is missing.
*HTTP 400 · Retry: no*

**Likely causes:**

- X-Actor, X-Reason or Idempotency-Key is missing on a mutating call.
- X-Actor, X-Reason or Idempotency-Key is missing when changing the API version pin.

**What to do:** Send the headers named in the message.

#### `not_found`

No such endpoint or resource.
*HTTP 404 · Retry: no*

**Likely cause:** A wrong path or id.

**What to do:** Check the URL and ids.

#### `not_implemented`

This is not implemented here.
*HTTP 501 · Retry: no*

**Likely cause:** A feature this backend or platform does not provide.

**What to do:** Check the platform notes.

#### `payload_too_large`

The request body is too large.
*HTTP 413 · Retry: no · Nothing reached the broker*

**Likely cause:** A body over our size limit.

**What to do:** Send less data per request.

#### `rate_limited`

Too many requests.
*HTTP 429 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- The per-key, per-account, per-account trade or per-IP budget is used up (metadata.bucket names it).
- A heavy call (history, batch, stream connect) costs more than one unit.
- Too many sandbox sign-ups from one address in an hour or a day, or from everyone in an hour (POST /v1/sandbox/signup).

**What to do:** Wait for Retry-After, then retry. A refused trade carries sent: false: it was not sent, so it is safe to resend. See the rate-limits page.

#### `request_body_timeout`

The request body did not arrive in time; nothing was forwarded.
*HTTP 408 · Retry: yes · Nothing reached the broker*

**Likely cause:** The client sent the body too slowly (inbound provider webhooks allow 10 seconds by default).

**What to do:** Send the whole body promptly, then retry.

#### `unsupported_media_type`

The request body has an unsupported content type.
*HTTP 415 · Retry: no · Nothing reached the broker*

**Likely cause:** Not application/json.

**What to do:** Send JSON with Content-Type: application/json.

#### `validation_error`

The request failed validation.
*HTTP 400, 422 · Retry: no · Nothing reached the broker*

**Likely cause:** A missing field, a wrong type or an out-of-range value.

**What to do:** Fix the fields named in `details` or the message.

#### `window_too_large`

The time window is too large for one call.
*HTTP 400 · Retry: no*

**Likely cause:** More than 31 days requested.

**What to do:** Page by time.

### Keys, tokens and permissions

The key or token is missing, wrong, or not allowed to do this. See [Authentication](https://tickwire.io/docs/authentication/).

#### `account_forbidden`

This token is scoped to other accounts.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely cause:** An account-scoped token was used for an account outside its scope.

**What to do:** Use a token that covers this account.

#### `agent_key_not_found`

No agent key with this id exists in your organisation.
*HTTP 404 · Retry: no*

**Likely cause:** Wrong id, or the key was deleted.

**What to do:** List agent keys and use an existing id.

#### `api_key_expired`

The API token that sent this order has expired.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** The token passed its expiry while the order was pending.

**What to do:** Create a new token. Nothing was sent.

#### `api_key_revoked`

The API key or token that sent this order was revoked or deleted.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** The key was revoked, possibly while the order was held.

**What to do:** Use a current key. Nothing was sent.

#### `forbidden`

Your credential may not do this.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely causes:**

- A read-only key on a mutating route.
- Your role lacks the permission.

**What to do:** Use a credential with the needed access.

#### `invalid_actor`

X-Actor names a server-assigned identity that is not the calling credential's own. Nothing was changed.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** The value starts with api-key:, user: or firebase: but is not api-key:<your own key id> (or, for a signed-in user, your own firebase: id).

**What to do:** Send a free-text label (for example copier-7 or ops:jane), your own api-key:<id>, or no X-Actor. The audit log always records the calling credential's key_id separately.

#### `invalid_agent_key`

The agent key is missing, invalid or revoked.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** No X-Tickwire-Agent-Key header, or a revoked key.

**What to do:** Send a valid agent key (twag_...).

#### `invalid_token`

The token is invalid, expired or revoked.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** A typo, or the token was revoked or expired.

**What to do:** Use a current token. Repeated bad tokens from one IP are rate limited.

#### `ip_not_allowed`

The token is valid, but it has an IP allowlist and this request came from an address outside it. The message never shows the allowlist.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely causes:**

- The call came from a server or network that is not on the token's allowlist.
- Your public IP changed (a new office line, VPN, NAT or cloud egress address).
- The allowlist was narrowed; open WebSockets using the token are closed (code 1008) at the next re-check.

**What to do:** Call from an address on the allowlist, or create a token whose allowlist includes this address. Retrying from the same address will not help.

#### `method_access`

This token may not call this method.
*HTTP 403 · Retry: no*

**Likely causes:**

- An account-access token on an account-management method.
- A token without the needed application or role.

**What to do:** Use an owner credential or a token with the role.

#### `missing_agent_key`

The MCP endpoint needs a Tickwire agent key, and none was sent.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely causes:**

- No `Authorization: Bearer …` header.
- The bearer value is not an agent key (agent keys start with `twag_`).

**What to do:** Create an agent key in the dashboard and send it as `Authorization: Bearer twag_…`.

#### `missing_credentials`

The MCP endpoint was called without a sign-in or an agent key.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely causes:**

- The AI app has not signed in with Tickwire yet.
- No `Authorization: Bearer …` header (or `X-Tickwire-Agent-Key`) was sent.

**What to do:** Sign in with Tickwire from the AI app (it follows the `WWW-Authenticate` header in this answer), or send an agent key as `Authorization: Bearer twag_…`.

#### `missing_token`

No token was sent.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** No auth-token, X-API-Key, Authorization: Bearer or ?auth-token= (WebSockets).

**What to do:** Send your token.

#### `not_revealable`

Key values are shown only once, at creation (or rotation), and stored hashed.
*HTTP 400 · Retry: no*

**Likely cause:** An attempt to reveal an existing full, read-only or sandbox key: only a digest is stored.

**What to do:** Create a new key if you lost it, or rotate a read-only key to get a new value.

#### `read_only_key`

This key is read-only; it can't trade.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely cause:** A read-only API key or agent key sent an order.

**What to do:** Use a full key.

#### `read_only_token`

This token is read-only.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely cause:** A read-only token on a mutating route.

**What to do:** Use a token with write access.

#### `too_many_connections`

Too many concurrent WebSockets for this token.
*HTTP 429 · Retry: yes*

**Likely cause:** More connections than the per-token limit.

**What to do:** Close unused connections or multiplex subscriptions over one.

#### `unauthorized`

Missing or invalid credentials.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** No key, a wrong key, or a revoked key.

**What to do:** Send a valid key.

### Accounts and terminals

The trading account or its terminal can't serve the request right now. See [Health](https://tickwire.io/docs/health/).

#### `account_deploying`

The account's terminal is starting.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** The account was just deployed or redeployed.

**What to do:** Retry after Retry-After. The request was not sent.

#### `account_not_deployed`

The account exists but has no running terminal.
*HTTP 404 · Retry: no · Nothing reached the broker*

**Likely cause:** The account is undeployed, parked, or its deployment failed.

**What to do:** Deploy the account, then retry. Its health routes still answer (200, status down, with the state and reason code), so read GET /users/current/accounts/{id}/health to see why it is not running.

#### `account_not_found`

No trading account with this id belongs to you.
*HTTP 404 · Retry: no · Nothing reached the broker*

**Likely causes:**

- A typo in the account id.
- The account belongs to another tenant (we answer 404, not 403, so ids are not disclosed).
- The account was deleted.

**What to do:** List your accounts and use one of their ids.

#### `account_unreachable`

The account's terminal could not be reached.
*HTTP 502, 503 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- The terminal is down or restarting.
- The account is not deployed.

**What to do:** Retry after Retry-After. We only return this when nothing was sent.

#### `backend_error`

The terminal returned an error for this operation; the MetaTrader code is in `code` or `mt5Code`.
*HTTP 500, 502 · Retry: same key only · May have executed*

**Likely causes:**

- The terminal refused or failed the call.
- The connection to the terminal failed mid-call.

**What to do:** Reads: retry. Trades: do not resend blindly; repeat only with the same Idempotency-Key, which answers with the real outcome.

#### `broker_offset_unknown`

The broker's UTC offset is not known yet, so UTC times can't be given.
*HTTP 503 · Retry: yes*

**Likely cause:** No live ticks since the terminal started (weekend, closed market).

**What to do:** Retry shortly, or omit timeBasis=utc to get broker time.

#### `broker_time_unknown`

The broker's server time is not known yet.
*HTTP 503 · Retry: yes*

**Likely cause:** No live ticks since the terminal started.

**What to do:** Retry after Retry-After.

#### `not_connected`

The terminal has no broker connection right now.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- The terminal is starting or reconnecting.
- The broker server is unreachable or rejected the login.

**What to do:** Retry with backoff and check the account's health. A trade answered with this was not sent.

#### `terminal_timeout`

The terminal did not answer a read in time.
*HTTP 504 · Retry: yes*

**Likely cause:** The terminal is busy or stuck.

**What to do:** Retry the read.

#### `unknown_account`

An account id in the request is not yours.
*HTTP 400 · Retry: no*

**Likely cause:** A wrong id in account_ids.

**What to do:** Use ids from your account list.

#### `unknown_host`

No Tickwire service is served at this hostname.
*HTTP 404 · Retry: no · Nothing reached the broker*

**Likely cause:** A wrong base URL.

**What to do:** Use the hostnames from the docs.

#### `unknown_region`

This region is not served.
*HTTP 404 · Retry: no · Nothing reached the broker*

**Likely cause:** A wrong region in a regional API hostname.

**What to do:** Use a served region.

### Orders and idempotency

Order checks and `Idempotency-Key` handling. See [Idempotency](https://tickwire.io/docs/idempotency/).

#### `idempotency_conflict`

This Idempotency-Key was already used with a different request body.
*HTTP 409 · Retry: no · Nothing reached the broker*

**Likely cause:** A key was reused for a new request.

**What to do:** Use a new key for a new request; the earlier one stands.

#### `idempotency_in_flight`

A request with this Idempotency-Key is still running. This request was not sent (`sent: false`); the earlier one may still execute.
*HTTP 409 · Retry: same key only · May have executed*

**Likely cause:** A retry arrived before the first attempt finished, for example after a gateway timeout (504) or a client-side timeout.

**What to do:** Wait Retry-After seconds, then repeat with the same key: once the first attempt finishes you get its stored result. Never switch to a new key while the outcome is open, and check positions and orders (or the trade status by key) first.

#### `idempotency_key_required`

Every trade needs an Idempotency-Key.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** The header is missing or too long.

**What to do:** Send a unique Idempotency-Key.

#### `idempotency_key_reused`

This Idempotency-Key was used for a different order, account or batch; this request was not sent.
*HTTP 409, 422 · Retry: no · Nothing reached the broker*

**Likely cause:** A key was reused for a new request.

**What to do:** Use a new key; the earlier request stands.

#### `idempotency_unavailable`

Idempotency can't be guaranteed right now, so nothing was sent.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- The idempotency store is unavailable.
- The idempotency store is full of requests that are still running.

**What to do:** Retry after Retry-After, or drop the header to trade without idempotency.

#### `invalid_batch`

The batch request is invalid; nothing was sent.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** A malformed leg or a missing field.

**What to do:** Fix the batch; the message names the field.

#### `invalid_idempotency_key`

The Idempotency-Key (or `transaction-id` header) is longer than 255 characters or holds non-printable characters. Nothing was changed.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** A key built from long ids, or one carrying control characters, tabs or bidi marks.

**What to do:** Send 1 to 255 printable characters. Keys are never truncated, so two long keys can't collide.

#### `invalid_order`

The order is invalid; nothing was sent.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** A missing or malformed order field.

**What to do:** Fix the order; the message names the field.

#### `order_not_found`

No pending order with this id.
*HTTP 404 · Retry: no*

**Likely cause:** It filled, was cancelled, or the id is wrong.

**What to do:** Check history for it.

#### `position_not_found`

No open position with this id.
*HTTP 404 · Retry: no*

**Likely cause:** It was closed, or the id is wrong.

**What to do:** Check history for it.

#### `reason_required`

Every trade needs a reason (X-Reason).
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely causes:**

- The X-Reason header is missing.
- An MCP trading tool was called without a `reason`.

**What to do:** Say why in X-Reason (or the MCP tool's `reason`).

#### `reason_too_long`

The reason is too long.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** X-Reason over the limit.

**What to do:** Shorten it.

#### `request_log_not_found`

There is no request log entry with this id in your organisation.
*HTTP 404 · Retry: no*

**Likely causes:**

- The id is mistyped, or belongs to another organisation.
- The entry is older than 14 days, or was removed to keep the organisation within its request-log limits.

**What to do:** List recent calls with GET /v1/request-logs (filter by requestId) and use an id from there.

#### `request_not_found`

No trade was recorded under this Idempotency-Key.
*HTTP 404 · Retry: no*

**Likely cause:** Nothing was sent with it, or it was refused before sending.

**What to do:** It is safe to send the order with this key.

#### `timeout`

No answer in time: a batch leg's status, or a pre-trade hook's `error` in webhook events. For a batch leg the order may have executed.
*Not an HTTP error · Retry: same key only · May have executed*

**Likely causes:**

- The terminal or broker did not reply in time.
- Your pre-trade hook did not answer in time.

**What to do:** Batch legs: never resend blindly; repeat the batch with the same Idempotency-Key, or check positions and history. Hooks: make the hook answer faster.

#### `unreachable_account`

Some accounts in the batch have no terminal; nothing was sent.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** Accounts undeployed or their terminals down.

**What to do:** Deploy them or drop them from the batch.

### Pre-trade hooks, holds and kill switches

Your own pre-trade hook, orders it holds, and the kill switch.

#### `approval_belongs_to_the_hook`

Only the pre-trade hook that placed a hold can approve it.
*HTTP 403 · Retry: no*

**Likely cause:** An owner or another hook tried to approve the hold.

**What to do:** Approve from the holding hook, or reject as the owner.

#### `hold_expired`

A held order expired before a decision; it was not and will not be executed.
*HTTP 409 · Retry: no · Nothing reached the broker*

**Likely cause:** The pre-trade hook did not approve in time.

**What to do:** Send a new order with a new Idempotency-Key if still wanted.

#### `hold_not_found`

No such hold.
*HTTP 404 · Retry: no*

**Likely cause:** Wrong hold id.

**What to do:** Check the id.

#### `hold_rejected`

A held order was rejected by the pre-trade hook or the owner; nothing was sent.
*HTTP 409 · Retry: no · Nothing reached the broker*

**Likely cause:** The hook or owner rejected the hold.

**What to do:** Read the message for why.

#### `hook_denied`

The account owner's pre-trade hook denied this order; nothing was sent.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely cause:** The order broke a rule the hook enforces.

**What to do:** Read `hook_message`.

#### `hook_exists`

This account or agent key already has a pre-trade hook.
*HTTP 409 · Retry: no*

**Likely cause:** A second hook was created.

**What to do:** Update or delete the existing hook.

#### `hook_not_found`

No such pre-trade hook.
*HTTP 404 · Retry: no*

**Likely cause:** Wrong id.

**What to do:** Check the id.

#### `hook_not_holding`

This hook did not place this hold.
*HTTP 403 · Retry: no*

**Likely cause:** A different hook tried to release the hold.

**What to do:** Release from the hook that holds it.

#### `hook_unavailable`

The pre-trade hook didn't give a valid answer. The hook fails closed, so the order wasn't sent.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** The hook URL is down, slow or answered invalidly.

**What to do:** Fix the hook, then send the order again.

#### `invalid_hook`

The pre-trade hook definition is invalid.
*HTTP 400 · Retry: no*

**Likely cause:** A hook on an agent key must apply to agents only.

**What to do:** Fix the hook body.

#### `invalid_hook_url`

The pre-trade hook URL is not allowed.
*HTTP 400 · Retry: no*

**Likely cause:** Not https, or it points to a private address.

**What to do:** Use a public https URL.

#### `invalid_signature`

A hold release was not signed with the holding hook's secret.
*HTTP 401 · Retry: no*

**Likely cause:** Wrong secret, missing headers, or a stale timestamp.

**What to do:** Sign with X-Tickwire-Hook-Id, X-Tickwire-Timestamp and X-Tickwire-Signature.

#### `kill_switch_engaged`

The kill switch for this account or agent key is engaged; nothing was sent.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely cause:** The owner engaged the kill switch (`scope`, `kill_reason`).

**What to do:** The owner must release the kill switch.

#### `pretrade_unavailable`

The pre-trade check (pre-trade hooks) could not be completed, so the order was not sent.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** Our API or the pre-trade hook service is unreachable or slow.

**What to do:** Retry after Retry-After with the same Idempotency-Key.

#### `TICKWIRE_HELD`

A pre-trade hook is holding this order; nothing has been sent yet (`stringCode` of a streaming `TradeError`).
*WebSocket error frame · Retry: no · Nothing reached the broker*

**Likely cause:** Your pre-trade hook answered with a hold instead of allow or deny.

**What to do:** Do not resend. Wait for the hook to release or reject the hold before `metadata.expiresAt`; `metadata.holdId` identifies it.

#### `unknown_actor`

The original caller of a held order can't be verified; nothing was sent.
*HTTP 401 · Retry: no · Nothing reached the broker*

**Likely cause:** The credential that sent it no longer exists.

**What to do:** Send the order again with a current credential.

### Market data and platform support

Prices, candles and depth, and things one MetaTrader version can't do.

#### `candle_unavailable`

No candle is available for this symbol and timeframe.
*HTTP 404 · Retry: yes*

**Likely cause:** The symbol has no history in the terminal for that timeframe yet.

**What to do:** Retry later or use another timeframe.

#### `depth_unavailable`

The terminal has no market depth for this symbol.
*HTTP 404 · Retry: no*

**Likely cause:** The broker does not publish depth for the symbol.

**What to do:** Use quotes instead.

#### `not_supported_on_mt4`

MetaTrader 4 doesn't support this.
*HTTP 400, 501 · Retry: no · Nothing reached the broker*

**Likely cause:** Market depth, tick history, stop-limit orders, OrderCheck and other MT5-only features.

**What to do:** Use an MT5 account for this feature. Nothing was sent.

#### `not_supported_on_mt5`

MetaTrader 5 can't do this.
*HTTP 400, 501 · Retry: no · Nothing reached the broker*

**Likely cause:** An MT4-only feature asked of an MT5 account.

**What to do:** Use the MT5 equivalent. Nothing was sent.

#### `symbol_not_found`

The terminal doesn't know this symbol.
*HTTP 404 · Retry: no · Nothing reached the broker*

**Likely cause:** A typo, or the broker names it differently (suffixes such as .m or -ECN).

**What to do:** List symbols and use the broker's exact name.

#### `too_many_requests`

All concurrent market-data request slots for this account are in use.
*HTTP 429 · Retry: yes*

**Likely cause:** Too many parallel history or market-data calls.

**What to do:** Wait for Retry-After, or send fewer calls at once.

### Streaming

Most of these arrive as WebSocket error frames (`type: error`), not HTTP statuses. See [Streaming](https://tickwire.io/docs/streaming/).

#### `invalid`

The subscription frame on the terminal WebSocket (`/{mt4|mt5}/{id}/ws`) is invalid.
*WebSocket error frame · Retry: no*

**Likely causes:**

- Unknown topic.
- A prices or bars topic without a symbol.

**What to do:** Fix the subscription frame.

#### `invalid_message`

A WebSocket frame is not a valid message (sent as an error frame, not an HTTP status).
*WebSocket error frame · Retry: no*

**Likely cause:** Not JSON, not an object, an unknown type, or a bad field.

**What to do:** Fix the frame; the message names the field.

#### `invalid_subscription`

A market-data subscription is invalid (sent as an error frame).
*WebSocket error frame · Retry: no*

**Likely cause:** Unknown market data type, or a bad interval or timeframe.

**What to do:** Fix the subscription.

#### `market_depth_unavailable`

The terminal gave no market depth for the symbol (stream error frame).
*WebSocket error frame · Retry: no*

**Likely cause:** The broker does not publish depth for it.

**What to do:** Unsubscribe from marketDepth for this symbol.

#### `not_subscribed`

Unsubscribe without a subscription (stream error frame).
*WebSocket error frame · Retry: no*

**Likely cause:** An unsubscribe before subscribe.

**What to do:** Subscribe first.

#### `not_supported`

This terminal backend does not provide the requested stream (stream error frame).
*WebSocket error frame · Retry: no*

**Likely cause:** Market depth on a backend without it.

**What to do:** Unsubscribe from it.

#### `NotAuthenticatedError`

Error name in a streaming error: the account's terminal is not connected, or the account is not subscribed on this connection.
*WebSocket error frame · Retry: yes*

**Likely causes:**

- The terminal is starting, reconnecting or stopped.
- A request for an account this connection has not subscribed to.

**What to do:** Subscribe to the account first. If it is subscribed, wait for the terminal to connect (see [Health](https://tickwire.io/docs/health/)) and retry.

#### `TimeoutError`

Error name in a streaming error: the terminal did not answer in time.
*WebSocket error frame · Retry: same key only*

**Likely causes:**

- The terminal or the broker is slow.
- A wait for synchronization ran past its timeout.

**What to do:** Repeat reads. Repeat a trade only with the same `requestId`, which returns the real outcome.

#### `too_many_sessions`

Too many stream sessions for this account (error frame, then the socket closes).
*WebSocket error frame · Retry: yes*

**Likely cause:** More sessions than allowed per account.

**What to do:** Reuse one session.

#### `TradeError`

Error name in a streaming error: the trade was refused or is being held. `stringCode` says which, and `metadata.sent` says whether anything reached the broker.
*WebSocket error frame · Retry: no*

**Likely causes:**

- A pre-trade hook is holding the order (`stringCode: TICKWIRE_HELD`).
- The order failed a check or the broker refused it.

**What to do:** Read `stringCode`, `message` and `metadata`, fix the order if needed, and send a new one only if `metadata.sent` is false.

#### `upstream_refused`

The upstream refused the WebSocket.
*HTTP 400, 401, 403, 404, 409, 429, 502 · Retry: no*

**Likely cause:** The upstream answered the upgrade with an error status.

**What to do:** Check the path and the subscription.

### Webhooks

Webhook endpoints, test events and deliveries. Some are values in a delivery record rather than HTTP errors. See [Webhooks](https://tickwire.io/docs/webhooks/).

#### `endpoint_deleted`

A webhook delivery was abandoned because its endpoint was deleted (the delivery's `error`, not an HTTP status).
*Not an HTTP error · Retry: no*

**Likely cause:** The webhook endpoint was deleted while deliveries were still queued.

**What to do:** Nothing to do. Create a new endpoint to receive future events.

#### `endpoint_disabled`

A webhook delivery was abandoned because its endpoint is disabled (the delivery's `error`, not an HTTP status).
*Not an HTTP error · Retry: no*

**Likely causes:**

- You disabled the endpoint.
- We disabled it after it kept failing (for example 72 hours of failures).

**What to do:** Fix the endpoint, re-enable it, then resend the events you missed while they are inside the resend window.

#### `event_expired`

A webhook delivery was abandoned because the event is past its retention (the delivery's `error`, not an HTTP status).
*Not an HTTP error · Retry: no*

**Likely cause:** The endpoint stayed down longer than events are kept.

**What to do:** Reconcile from the API (positions, orders, history) instead of waiting for the event.

#### `invalid_url`

The webhook URL is not allowed. The message is the same whatever rule refused it.
*HTTP 400 · Retry: no*

**Likely causes:**

- Not https://.
- The host is localhost, *.localhost, *.internal or *.local, or the URL carries a username or password.
- The host is, or resolves to, a non-public address (loopback, private, link-local, carrier-grade NAT, multicast, reserved), or cannot be resolved.
- Re-enabling an endpoint whose URL breaks these rules.

**What to do:** Use an https URL whose host resolves only to public internet addresses.

#### `resend_window_expired`

The webhook event is too old to resend.
*HTTP 410 · Retry: no*

**Likely cause:** A resend was asked for an event outside the resend window.

**What to do:** Reconcile from the API instead of resending.

#### `unknown_event_type`

The webhook event type is not in the event catalog.
*HTTP 400 · Retry: no*

**Likely causes:**

- Triggering a test event (POST /v1/webhook-events/trigger) with a type that does not exist.
- Filtering a webhook listener's events with a type or family that does not exist.

**What to do:** Use a type from GET /v1/webhook-event-types, or a family wildcard such as trade.*.

#### `url_not_allowed`

A webhook delivery was not sent because the endpoint's URL failed our egress rules at send time (the delivery's `lastError`, and an endpoint's `disabledReason`; not an HTTP status).
*Not an HTTP error · Retry: no*

**Likely causes:**

- The host now resolves to a non-public address (for example after a DNS change).
- The URL was registered before the rules applied and is not https://, or names an internal host or address; such endpoints are disabled.

**What to do:** Point the endpoint at an https URL that resolves only to public addresses, re-enable it, then resend missed events inside the resend window.

#### `webhook_trigger_sandbox_only`

On a live key only webhook.test can be triggered; other test events are sandbox-only.
*HTTP 403 · Retry: no*

**Likely cause:** POST /v1/webhook-events/trigger with a live key and an event type other than webhook.test.

**What to do:** Use a sandbox key (POST /v1/sandbox/keys) to trigger other event types, so a test trade never reaches a production handler.

#### `webhook_unavailable`

The test event was published, but the webhook pipeline could not store or queue it, so your endpoints will not receive it.
*HTTP 503 · Retry: yes*

**Likely cause:** POST /v1/webhook-events/trigger while webhook storage or the delivery queue is failing.

**What to do:** Retry later with a new Idempotency-Key. Running tickwire listen sessions still received the event. If it persists, check the status page and contact support with the requestId.

### Outages and timeouts

Something inside Tickwire, or between Tickwire and the terminal, failed or was slow. The [status page](https://tickwire.io/status/) shows known incidents.

#### `agent_gateway_misconfigured`

The agent API is not available right now.
*HTTP 503 · Retry: no · Nothing reached the broker*

**Likely cause:** A required server setting is missing.

**What to do:** Contact support with the requestId.

#### `authorization_server_unreachable`

The MCP endpoint could not reach the Tickwire sign-in service to check the access token.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** A network problem, or the sign-in service is restarting.

**What to do:** Retry after a few seconds. The [status page](https://tickwire.io/status/) shows known incidents.

#### `authorization_unavailable`

The Tickwire sign-in service could not check the access token right now.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** The sign-in service answered with an unexpected error.

**What to do:** Retry after a few seconds. If it keeps happening, sign in again from the AI app.

#### `bad_gateway`

An upstream service gave an invalid answer.
*HTTP 502 · Retry: same key only · May have executed*

**Likely cause:** A service between our API and the terminal failed.

**What to do:** Reads: retry. Mutations: repeat only with the same Idempotency-Key.

#### `bad_response`

The Tickwire API answered the MCP tool without a JSON body.
*MCP tool result · Retry: same key only*

**Likely cause:** Something in front of the API answered instead of it, for example during a restart.

**What to do:** Repeat reads. For a trade, call `get_trade_status` with the same `idempotency_key` before sending it again.

#### `controlplane_unavailable`

We could not check your token or route the request.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** An internal service is restarting or overloaded.

**What to do:** Retry after Retry-After. Nothing was sent.

#### `docs_unavailable`

The MCP docs search could not fetch the Tickwire docs.
*MCP tool result · Retry: yes*

**Likely cause:** The docs site was unreachable or slow.

**What to do:** Try the search again later, or read the docs at [/docs/](https://tickwire.io/docs/).

#### `execution_unavailable`

A trade sent over streaming could not reach the execution service, so it was not sent (`stringCode` of a streaming `InternalError`).
*WebSocket error frame · Retry: yes · Nothing reached the broker*

**Likely cause:** A short outage inside Tickwire.

**What to do:** Retry with the same `requestId`.

#### `gateway_timeout`

An upstream did not answer in time.
*HTTP 504 · Retry: same key only · May have executed*

**Likely cause:** The terminal or a service is slow or stuck.

**What to do:** Reads: retry. Mutations: the outcome is unknown; repeat only with the same Idempotency-Key.

#### `internal_error`

An unexpected failure inside Tickwire.
*HTTP 500 · Retry: same key only · May have executed*

**Likely cause:** A bug or an unexpected state.

**What to do:** Reads: retry. Mutations: repeat only with the same Idempotency-Key. If the answer has created_id, a credential (key, token or hook secret) was created before the failure: revoke or rotate it by that id. Report the requestId if it persists.

#### `outcome_unknown`

A trade sent over streaming got no answer from the execution service; it may have executed (`stringCode` of a streaming `InternalError`).
*WebSocket error frame · Retry: same key only · May have executed*

**Likely cause:** The execution service or the terminal did not answer in time.

**What to do:** Retry with the same `requestId` to learn the real outcome. Never send a new trade blindly.

#### `service_unavailable`

The service is not available right now.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** Not available yet, or restarting.

**What to do:** Retry later. Nothing was sent.

#### `temporarily_unavailable`

The service could not answer just now; nothing was changed.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- A read could not reach the database, even after one retry on a fresh connection (listings such as connected apps and dedicated IPs).
- OAuth (Sign in with Tickwire): client registration is at capacity.

**What to do:** Retry after Retry-After seconds (when sent), or later.

#### `tickwire_no_reply`

The MCP tool sent the request but got no reply. If it was a trade, it may have executed.
*MCP tool result · Retry: same key only · May have executed*

**Likely cause:** The connection dropped or timed out after the request was sent.

**What to do:** For a trade, call `get_trade_status` with the same `idempotency_key` before doing anything else. Reads can be repeated.

#### `tickwire_unreachable`

The MCP tool could not reach the Tickwire API, so nothing was sent.
*MCP tool result · Retry: yes · Nothing reached the broker*

**Likely causes:**

- A network problem between the MCP server and the API.
- A wrong API address in a locally run MCP server's settings.

**What to do:** Retry the tool. If it keeps happening, check the [status page](https://tickwire.io/status/), and for a local MCP server check the API address it uses.

#### `unavailable`

A dependent feature isn't running right now.
*HTTP 503 · Retry: yes*

**Likely cause:** Webhook delivery is not running.

**What to do:** Try again later.

#### `upstream_misconfigured`

The account's terminal refused our API.
*HTTP 502 · Retry: no · Nothing reached the broker*

**Likely cause:** An internal credential mismatch.

**What to do:** Contact support with the requestId.

#### `upstream_timeout`

The upstream did not answer in time. For a mutating request the outcome is unknown (`metadata.outcome: unknown`): it may have executed.
*HTTP 504 · Retry: same key only · May have executed*

**Likely cause:** The terminal or broker is slow.

**What to do:** Never resend a trade blindly: repeat it with the same Idempotency-Key (or clientId) to learn the real outcome. Reads can be retried.

#### `upstream_unavailable`

A Tickwire service is unreachable; the request was not sent.
*HTTP 502 · Retry: yes · Nothing reached the broker*

**Likely cause:** The service is restarting.

**What to do:** Retry after Retry-After.

#### `waking_up`

Tickwire was paused to save cost while nobody was using it, and your request started it again. Starting takes about 2 minutes.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely cause:** The first request after a quiet period (only before launch: the automatic sleep is off once trading customers are live).

**What to do:** Retry after Retry-After (30 seconds). In a browser the page reloads itself.

### Billing, slots and dedicated IPs

Payments, discount codes, account slots and dedicated IPs.

#### `already_subscribed`

The organisation already has a subscription.
*HTTP 409 · Retry: no*

**Likely cause:** A second checkout was started.

**What to do:** Use Manage billing to change the plan.

#### `bad_discount_code`

The discount code is unknown or can't be used here.
*HTTP 400 · Retry: no*

**Likely cause:** A typo, an expired code, or a code for another product.

**What to do:** Check the code.

#### `bad_signature`

A billing webhook's signature did not verify.
*HTTP 400 · Retry: no*

**Likely causes:**

- Wrong webhook secret.
- The body was changed in transit.

**What to do:** Check the webhook secret configured at the payment provider.

#### `checkout_unavailable`

Payments are not available right now.
*HTTP 503 · Retry: yes*

**Likely cause:** The payment provider is not configured or not reachable.

**What to do:** Try again later.

#### `dedicated_ip_refused`

The dedicated-IP change can't be made.
*HTTP 409 · Retry: no*

**Likely causes:**

- The requested count is above what the plan allows.
- An IP is still in use by an account.

**What to do:** Read the message; change the count or free the IP.

#### `discount_too_large`

The discount code gives more off than allowed.
*HTTP 400 · Retry: no*

**Likely cause:** The code takes more off than we accept for this order.

**What to do:** Use another code.

#### `insufficient_balance`

The wallet balance does not cover this purchase.
*HTTP 402 · Retry: no*

**Likely cause:** Not enough prepaid balance; `shortfall_eur_cents` says how much is missing.

**What to do:** Top up, then repeat.

#### `ip_allocation_refused`

The dedicated-IP operation can't be done in the IP's current state.
*HTTP 409 · Retry: no*

**Likely cause:** For example, confirming cancellation before release.

**What to do:** Follow the order in the message.

#### `ip_previously_owned`

This IP has had an owner before; a dedicated IP is never reassigned.
*HTTP 409 · Retry: no*

**Likely cause:** An attempt to allocate a previously held IP.

**What to do:** Allocate a fresh IP.

#### `no_subscription`

There is no subscription to manage yet.
*HTTP 409 · Retry: no*

**Likely causes:**

- Manage billing was opened before any subscription.
- Cancel or resume was asked for without a subscription to cancel.

**What to do:** Start a subscription first.

#### `payment_provider_error`

The payment provider failed the call.
*HTTP 502 · Retry: yes*

**Likely cause:** The provider is down or refused; `provider_code` has its code.

**What to do:** Try again later.

#### `server_limit_reached`

The maximum number of private servers is reached.
*HTTP 409 · Retry: no*

**Likely cause:** A plan limit.

**What to do:** Delete a server or raise the limit.

#### `slots_full`

Every purchased slot is taken.
*HTTP 409 · Retry: no*

**Likely cause:** Linking an account without a free slot.

**What to do:** Buy a slot or unlink an account.

### Sandbox

Limits on sandbox keys and accounts. See [Sandbox](https://tickwire.io/docs/sandbox/).

#### `sandbox_account_limit`

Your sandbox already holds its maximum number of accounts (5 by default).
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely cause:** Creating a sixth account with a sandbox (twsk_) key.

**What to do:** Delete a sandbox account you no longer need, then create the new one.

#### `sandbox_key_limit`

Your sandbox already holds its maximum number of active credentials of this kind (5 sandbox keys; 20 each of API tokens, read-only keys and agent keys by default).
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely causes:**

- Creating another sandbox key (dashboard quickstart or POST /v1/sandbox/keys) while the sandbox holds the maximum.
- Creating another API token, read-only key or agent key with a sandbox key or token while the sandbox holds the maximum of that kind.

**What to do:** Revoke a sandbox credential of that kind you no longer need on the dashboard, then create the new one.

#### `sandbox_key_required`

Server Tickwire-Sandbox is only for sandbox keys; sandbox and live never mix.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** Creating an account on server Tickwire-Sandbox with a live key.

**What to do:** Get a twsk_ key with POST /v1/sandbox/keys (or /v1/sandbox/signup) and use it.

#### `sandbox_key_restricted`

This sandbox key came from anonymous sign-up: it may manage its sandbox accounts, trade, stream and read, nothing else.
*HTTP 403 · Retry: no · Nothing reached the broker*

**Likely causes:**

- Creating a webhook, pre-trade hook, agent key, API key or token, or another sandbox key with a key from POST /v1/sandbox/signup.
- Creating, changing or deleting a risk-management tracker or guard rule (or resetting trading statistics) with such a key. Reading trackers, statistics and metrics stays allowed.
- A sandbox key whose restrictions we could not confirm, on a risk-management write (it fails closed).

**What to do:** Sign in, then create a sandbox key with POST /v1/sandbox/keys using your live key; it has full rights inside your sandbox.

#### `sandbox_server_required`

A sandbox key can only create simulated accounts.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** Creating an account on a real broker server with a sandbox (twsk_) key.

**What to do:** Use server Tickwire-Sandbox and a magic login, or use a live key for a broker account.

### Sign-in and organisations

Dashboard sign-in, invites and organisation members.

#### `already_member`

The person is already a member of the organisation.
*HTTP 409 · Retry: no*

**Likely cause:** An invite was sent to an existing member.

**What to do:** Nothing to do.

#### `email_taken`

An account with this email already exists.
*HTTP 409 · Retry: no*

**Likely cause:** Sign-up with a registered email.

**What to do:** Sign in instead.

#### `invalid_credentials`

Email or password is wrong.
*HTTP 401 · Retry: no*

**Likely cause:** Wrong password.

**What to do:** Check the credentials, or use a magic link.

#### `invalid_email`

The email address is not valid.
*HTTP 400 · Retry: no*

**Likely cause:** A typo.

**What to do:** Enter a valid email address.

#### `invalid_link`

The sign-in or invite link is invalid or expired.
*HTTP 401 · Retry: no*

**Likely cause:** The link was used already or is too old.

**What to do:** Request a new link.

#### `last_owner`

An organisation needs at least one owner.
*HTTP 409 · Retry: no*

**Likely cause:** Removing or demoting the last owner.

**What to do:** Promote another member first.

#### `no_org`

No organisation is selected.
*HTTP 400 · Retry: no*

**Likely cause:** A dashboard session without an active organisation.

**What to do:** Select or create an organisation.

#### `sign_in_required`

This invite link only works once you are signed in with the invited email address.
*HTTP 403 · Retry: no*

**Likely cause:** An invite link shared by hand was opened while signed out.

**What to do:** Sign in or sign up with the invited address, then open the link again.

#### `too_many_attempts`

Too many failed sign-ins or link requests.
*HTTP 429 · Retry: yes*

**Likely cause:** Repeated wrong passwords, or too many magic links in an hour.

**What to do:** Wait, or use a magic link.

#### `weak_password`

The password is too weak.
*HTTP 400 · Retry: no*

**Likely cause:** Too short or too common.

**What to do:** Choose a longer, less common password.

### Sign in with Tickwire (OAuth)

The OAuth flow that lets an AI app connect to a Tickwire account.

#### `access_denied`

The user pressed Cancel on the consent page (sent to the app's redirect URI).
*HTTP 302 · Retry: no*

**Likely cause:** The user declined to connect the app.

**What to do:** Nothing was shared. Ask the user whether they want to connect, then start again.

#### `connected_app_not_found`

No such connected app in this organisation.
*HTTP 404 · Retry: no*

**Likely cause:** Wrong id, or the app belongs to another organisation.

**What to do:** List connected apps with GET /v1/connected-apps.

#### `consent_token_invalid`

The approval did not come from the consent page this session opened (CSRF protection).
*HTTP 403 · Retry: no*

**Likely causes:**

- The page was reloaded (a new token was issued) or the request was forged.
- Another session or organisation tried to answer the request.

**What to do:** Reload the consent page and answer again.

#### `interaction_required`

The user has to approve the app on the consent page (shown on the sign-in error page).
*HTTP 400 · Retry: no*

**Likely cause:** The app sent prompt=none.

**What to do:** Start the sign-in without prompt=none.

#### `invalid_client`

The client could not be identified or authenticated.
*HTTP 400, 401 · Retry: no*

**Likely causes:**

- Unknown client_id: register the app first (DCR) or use a valid client metadata document URL.
- Wrong or missing client secret for a confidential client, or a secret sent by a public client.
- The client metadata document could not be loaded or is invalid.
- The app sent a token exchange request with credentials we do not accept.

**What to do:** Check the client registration and its credentials; re-register if needed.

#### `invalid_client_metadata`

The dynamic client registration request is not acceptable.
*HTTP 400 · Retry: no*

**Likely causes:**

- The body is not a JSON object.
- Unsupported grant_types, response_types or token_endpoint_auth_method.

**What to do:** Register with response_types ["code"], grant_types authorization_code (+ refresh_token), and auth method none, client_secret_post or client_secret_basic.

#### `invalid_grant`

The authorization code, refresh token or access token is not valid for this request.
*HTTP 400 · Retry: no*

**Likely causes:**

- The code or refresh token was already used, expired or revoked.
- A rotated-out refresh token was reused, so the whole token chain was revoked.
- PKCE verification failed, or redirect_uri differs from the authorization request.
- The connection was disconnected, expired, or its agent key was revoked.
- The access token was issued for another resource (MCP server URL).

**What to do:** Start the sign-in again from the app (a new authorization request). Never retry with the same refresh token.

#### `invalid_redirect_uri`

A registered redirect URI is not allowed.
*HTTP 400 · Retry: no*

**Likely causes:**

- Not https, not loopback http, and not a reverse-domain app scheme.
- It has a fragment, user info, backslashes or spaces, or is longer than 2000 characters.
- More than 10 redirect URIs.

**What to do:** Use exact https redirect URIs (or http on 127.0.0.1, [::1] or localhost).

#### `invalid_scope`

The requested scope is wider than what was granted.
*HTTP 400 · Retry: no*

**Likely cause:** A refresh request asked for a scope the user did not grant (for example tickwire.trade on a read-only connection).

**What to do:** Refresh without scope, or ask the user to connect again with the wider access.

#### `invalid_target`

The resource parameter is not the Tickwire MCP server.
*HTTP 400 · Retry: no*

**Likely causes:**

- resource names another server, or several resources were requested.
- At the token endpoint, resource differs from the one the grant was issued for.

**What to do:** Send resource set to the Tickwire MCP server URL: the same address you gave the app, shown on the AI agents page.

#### `oauth_not_configured`

Sign-in for AI apps is not enabled on this deployment.
*HTTP 404 · Retry: no*

**Likely cause:** Sign in with Tickwire is switched off or not fully set up on this server.

**What to do:** Use an agent key instead, or contact support.

#### `request_belongs_to_someone_else`

This sign-in request was opened by another signed-in user first.
*HTTP 403 · Retry: no*

**Likely cause:** The sign-in link was opened by someone else (or in another organisation) before you.

**What to do:** Start connecting again from the app.

#### `request_expired`

The sign-in request is older than 10 minutes.
*HTTP 410 · Retry: no*

**Likely cause:** The consent page was left open too long.

**What to do:** Start connecting again from the app.

#### `request_not_supported`

Request objects (request, request_uri) are not supported (shown on the sign-in error page).
*HTTP 400 · Retry: no*

**Likely cause:** The authorization request carried a request or request_uri parameter.

**What to do:** Send the authorization parameters in the query string.

#### `request_used`

The sign-in request was already answered.
*HTTP 409 · Retry: no*

**Likely cause:** Allow or Cancel was pressed twice, or in two tabs.

**What to do:** Start connecting again from the app if it did not connect.

#### `unsupported_grant_type`

The token endpoint only supports authorization_code and refresh_token.
*HTTP 400 · Retry: no*

**Likely cause:** grant_type is password, client_credentials, implicit or another type.

**What to do:** Use the authorization code flow with PKCE.

#### `unsupported_response_type`

Only response_type=code is supported (shown on the sign-in error page).
*HTTP 400 · Retry: no*

**Likely cause:** The app used the implicit flow or another response type.

**What to do:** Use response_type=code with PKCE (S256).

### Compatibility error names

On the `/users/current/…` routes, `error` holds one of these names so existing clients keep working, and our own code is in `code`. The terminal routes (`/{mt4|mt5}/{id}/…`) use the three names that start with `MRPC`.

#### `AcceptedError`

Error name for a request that is still being processed (`/users/current/…` routes only).
*HTTP 202 · Retry: same key only*

**Likely cause:** Account creation for the same transaction-id is still running.

**What to do:** Repeat the same request, with the same transaction-id, after Retry-After seconds.

#### `ApiError`

Error name on the `/users/current/…` routes for a client error that has no more specific name.
*HTTP 400, 405, 413, 415, 422 · Retry: no*

**Likely cause:** See the Tickwire code in the `code` field.

**What to do:** Read `code` and open its page; fix the request.

#### `ConflictError`

Error name for a conflict (409) on the `/users/current/…` routes.
*HTTP 409 · Retry: no*

**Likely causes:**

- The resource already exists.
- An Idempotency-Key or clientId was reused for a different request.

**What to do:** Read the Tickwire code in `code` (or `tickwireError`) for the exact reason.

#### `ForbiddenError`

Error name for a forbidden request (403) on the `/users/current/…` routes.
*HTTP 403 · Retry: no*

**Likely cause:** The token may not do this (read-only, account-scoped, or missing a role).

**What to do:** Use a token with the needed access. The Tickwire code is in `code`.

#### `InternalError`

Error name for a server-side failure on the `/users/current/…` routes.
*HTTP 500, 502, 503, 504 · Retry: same key only · May have executed*

**Likely causes:**

- The terminal returned an error.
- A Tickwire service failed.

**What to do:** Read the Tickwire code in `code` or `tickwireError`. Reads can be retried; repeat a trade only with the same clientId or Idempotency-Key.

#### `MRPC`

Terminal route error (`/{mt4|mt5}/{id}/…`): the terminal processed the request but the operation failed.
*HTTP 500 · Retry: same key only · May have executed*

**Likely causes:**

- Unknown symbol (`reason: symbol_not_found`).
- The terminal returned an error (`reason: backend_error`, MT code in `code`).

**What to do:** Check `reason`. Fix the symbol, or retry a read. Repeat a trading call only with the same Idempotency-Key.

#### `MRPC_NOT_CONNECTED`

Terminal route error (`/{mt4|mt5}/{id}/…`): the terminal has no broker connection right now.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- The terminal is starting or reconnecting.
- The broker server is down or rejected the login.

**What to do:** Retry with backoff and check the account's health endpoint.

#### `MRPC_VALIDATION`

Terminal route error (`/{mt4|mt5}/{id}/…`): a parameter is missing or invalid.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely causes:**

- A required query or body field is missing.
- A value has the wrong type or range.

**What to do:** Fix the request; the message names the parameter.

#### `NotConnectedError`

Error name on the `/users/current/…` routes: the terminal is not connected to the broker.
*HTTP 503 · Retry: yes · Nothing reached the broker*

**Likely causes:**

- The terminal is starting or reconnecting.
- The broker server is unreachable.

**What to do:** Retry after Retry-After; check account health.

#### `NotFoundError`

Error name for a missing resource on the `/users/current/…` routes.
*HTTP 404 · Retry: no*

**Likely cause:** See the Tickwire code in `code` or `tickwireError`.

**What to do:** Check the id or symbol in the path.

#### `NotSynchronizedError`

Error name on the `/users/current/…` routes: the terminal is not synchronized enough to answer (for example the broker time is not known yet).
*HTTP 503 · Retry: yes*

**Likely cause:** No live ticks have arrived since the terminal started.

**What to do:** Retry after Retry-After.

#### `PaymentRequiredError`

Error name for a request that needs payment (402) on the `/users/current/…` routes.
*HTTP 402 · Retry: no*

**Likely cause:** The wallet balance does not cover the purchase.

**What to do:** Top up, then repeat.

#### `TooManyRequestsError`

Error name for a rate limit on the `/users/current/…` routes; `metadata.recommendedRetryTime` says when to retry.
*HTTP 429 · Retry: yes*

**Likely causes:**

- Too many requests for this token, account or IP.
- All concurrent market-data request slots are in use.

**What to do:** Wait for Retry-After (or recommendedRetryTime), then retry.

#### `UnauthorizedError`

Error name for a missing or invalid token on the `/users/current/…` routes.
*HTTP 401 · Retry: no*

**Likely causes:**

- No auth-token header.
- The token is invalid, expired or revoked.

**What to do:** Send a valid token.

#### `ValidationError`

Error name for an invalid request on the `/users/current/…` routes; `details` lists the parameters.
*HTTP 400 · Retry: no · Nothing reached the broker*

**Likely cause:** A field is missing or has the wrong type or value.

**What to do:** Fix the fields named in `details`.

### Status page API

Errors from the status page's API, including its admin API.

#### `admin_disabled`

The admin API is switched off.
*HTTP 403 · Retry: no*

**Likely cause:** The status service's admin API is not enabled.

**What to do:** Not for customers: only Tickwire staff use this API.

#### `canary_not_found`

No webhook-delivery canary has this id.
*HTTP 404 · Retry: no*

**Likely causes:**

- The canary endpoint URL has a typo.
- The canary probe is not configured (its secret is unset).

**What to do:** Point the canary webhook at /api/v1/canary/webhooks/<probe id> of a configured webhook_receipt probe.

#### `component_not_found`

No component with this id.
*HTTP 404 · Retry: no*

**Likely cause:** A typo in the component id.

**What to do:** Read /api/v1/status.json for the list of component ids.

#### `email_notifications_unavailable`

Email updates are not available yet.
*HTTP 503 · Retry: no*

**Likely cause:** Email updates aren't switched on yet.

**What to do:** Follow the RSS, Atom or JSON feed instead; the message has their URLs.

#### `incident_closed`

The incident or maintenance is already resolved or completed.
*HTTP 409 · Retry: no*

**Likely cause:** An update was posted to a closed incident.

**What to do:** Open a new incident; a closed one takes no more updates.

#### `incident_not_closed`

A postmortem can only be written once the incident is resolved.
*HTTP 409 · Retry: no*

**Likely cause:** The incident is still open.

**What to do:** Resolve the incident first, then write the postmortem.

#### `incident_not_found`

No incident with this id.
*HTTP 404 · Retry: no*

**Likely cause:** A typo in the incident id.

**What to do:** List incidents with /api/v1/incidents.

#### `invalid_incident_status`

That status doesn't belong to this kind of event.
*HTTP 422 · Retry: no*

**Likely cause:** An incident given a maintenance status (scheduled, in_progress, completed), or the reverse.

**What to do:** Incidents use investigating, identified, monitoring, resolved; maintenance uses scheduled, in_progress, completed.

#### `invalid_maintenance`

The maintenance window is invalid.
*HTTP 422 · Retry: no*

**Likely causes:**

- scheduled_for is missing.
- scheduled_until is not after scheduled_for.

**What to do:** Send a start time, and an end time after it.

#### `postmortem_not_found`

This incident has no postmortem.
*HTTP 404 · Retry: no*

**Likely cause:** None was written, or it was deleted.

**What to do:** Write one with PUT /api/v1/incidents/{id}/postmortem.

#### `unknown_component`

An incident names a component that doesn't exist.
*HTTP 422 · Retry: no*

**Likely cause:** A typo in a component id.

**What to do:** Use ids from /api/v1/status.json.
