View as Markdown

Health

How Tickwire decides whether an account is working.

A running terminal isn’t necessarily a working one. A terminal can be alive while logged out, stuck on a dialog, or connected to a broker whose price feed has stopped. So Tickwire measures health from work that succeeded, using three independent signals:

  1. Broker link: the connection state the terminal itself reports.
  2. Account reads: when we last read the account successfully. That shows the broker is answering, not just that a socket is open.
  3. Tick advance: whether the broker’s clock on a reference symbol is moving.

The ladder

StatusMeaningWhat to do
healthyConnected, account readable, ticks advancingNothing
staleConnected and readable, but ticks haven’t advanced within 30 sUsually the broker or a closed market. Treat prices as frozen; don’t page yourself
disconnectedTerminal up, no broker linkUsually the broker. We reconnect automatically and escalate if it persists
downWe can’t read the terminal at all, or the account isn’t runningA running account: ours to fix, and you’ll get a webhook. An account that isn’t running: reason_code says why (see below)

A health snapshot

curl -s https://api.tickwire.io/users/current/accounts/acc_7Qx/health -H "auth-token: $TW_KEY"
{
  "status": "healthy",
  "connected": true,
  "read_only": false,
  "trading_enabled": true,
  "reasons": [],
  "last_account_read_age_s": 2.1,
  "last_tick_age_s": 0.4,
  "last_action_age_s": 812.0,
  "broker_time": "2026-09-23 14:02:11.480",
  "checked_at": "2026-09-23T11:02:13Z"
}
  • trading_enabled is true only if an order could be accepted right now: connected, not an investor login, and trading switched on in the terminal.
  • reasons explains any status other than healthy in plain words.
  • The ages let you apply your own, stricter thresholds.
  • A quote older than four days never counts as “ticks advancing”. Some brokers serve a cached quote from years ago right after login.

Accounts that aren’t running

An account can exist without a terminal: its deploy failed (for example the broker asked for a one-time password), or you undeployed it. There is nothing to measure, but the answer is still known, so health says so instead of returning 404:

curl -s https://api.tickwire.io/users/current/accounts/acc_7Qx/health -H "auth-token: $TW_KEY"
{
  "status": "down",
  "connected": false,
  "read_only": null,
  "trading_enabled": false,
  "reasons": ["account is not running (state DEPLOY_FAILED)",
              "ERR_OTP_REQUIRED: the broker asks for a one-time password"],
  "last_account_read_age_s": null,
  "last_tick_age_s": null,
  "last_action_age_s": null,
  "broker_time": null,
  "checked_at": "2026-09-23T11:02:13Z",
  "fx_status": "error",
  "deployed": false,
  "state": "DEPLOY_FAILED",
  "reason_code": "ERR_OTP_REQUIRED",
  "error": "ERR_OTP_REQUIRED: the broker asks for a one-time password"
}
  • The answer is 200. state is the account’s lifecycle state, and reason_code the code at the start of its error (ERR_OTP_REQUIRED, E_AUTH, …), or null when the error has no code or there is none. error is the same text as health.error on the account object.
  • Anything we can’t know without a terminal, such as read_only and the ages, is null.
  • The terminal routes match: GET /mt5/{id}/status (and /mt4/…) is 200 with status: "down" plus deployed, state, reasonCode and error; /healthz and /livez are 503.
  • Agents: GET /v1/agent/accounts/{id}/health says disconnected, with state and reason_code in its evidence.
  • An account that is still deploying answers 503 account_deploying with Retry-After: it may be up in a moment. An account that doesn’t exist, or isn’t yours, is 404 account_not_found.
  • Other data calls on an account that isn’t running are 404 account_not_deployed. Fix the cause and redeploy it.

How it reaches you

  • Polling: GET …/health, as above.
  • Streaming: health and brokerConnectionStatus frames on the account stream.
  • Webhooks: account.health_changed on every transition, within 60 seconds. See Webhooks.
  • /users/current/… fields: connectionStatus on the account is CONNECTED only when health is healthy or stale and the broker link is up; otherwise DISCONNECTED_FROM_BROKER or DISCONNECTED.
  • /v1 and terminal-route fields: status maps healthy and stale to connected, disconnected to disconnected, and down to error.

Account snapshot

GET /users/current/accounts/{id}/snapshot (agents: GET /v1/agent/accounts/{id}/snapshot) returns everything needed to rebuild your picture of an MT4 or MT5 account in one call: account information, open positions, pending orders, health and the broker’s server time.

{
  "account_id": "acc_7Qx", "platform": "mt5",
  "as_of": "2026-09-23T11:02:13.412Z", "complete": true, "terminal_connected": true,
  "account_information": { "status": "ok", "read_at": "2026-09-23T11:02:13.380Z", "age_ms": 32, "data": { "balance": 10000, "equity": 10012.5 } },
  "positions": { "status": "ok", "read_at": "…", "age_ms": 25, "data": [] },
  "orders": { "status": "ok", "read_at": "…", "age_ms": 18, "data": [] },
  "health": { "status": "ok", "read_at": "…", "age_ms": 9, "data": { "status": "healthy" } },
  "server_time": { "status": "ok", "read_at": "…", "age_ms": 2,
                   "data": { "time": "2026-09-23T11:02:13.410Z", "brokerTime": "2026-09-23 14:02:13.410", "brokerUtcOffsetMinutes": 180 } }
}
  • Each section has its own status: ok (read from a terminal connected to its broker), stale (read, but the terminal was not connected, so values may be out of date) or unavailable (the read failed; data is null and error says why). complete is true only when every section is ok.
  • read_at and age_ms show how fresh each section is. Sections are read one after another, not atomically.
  • data uses the same shapes as the individual routes. brokerUtcOffsetMinutes is explicit and is null until measured; broker time is never assumed to be UTC.
  • The answer is 200 whenever anything could be read, and 503 only when no section could be.

Missing data shows as unknown

If a measurement is missing, the answer is unknown. The status page works the same way: a component without a recent check shows as unknown.