{
  "_type": "export",
  "__export_format": 4,
  "__export_date": "2026-05-25T00:00:00.000Z",
  "__export_source": "tickerall.docs:v1",
  "resources": [
    {
      "_id": "wrk_tickerall",
      "_type": "workspace",
      "name": "TickerAll API",
      "description": "Connect and automate your own MT4/MT5 broker accounts. Set the apiKey + accountId environment variables (Manage Environments), then send. Generated from https://tickerall.com/docs.",
      "scope": "collection"
    },
    {
      "_id": "env_tickerall_base",
      "_type": "environment",
      "parentId": "wrk_tickerall",
      "name": "Base environment",
      "data": {
        "baseUrl": "https://api.staging.tickerall.com",
        "wsBaseUrl": "wss://api.staging.tickerall.com",
        "apiKey": "cf_api_xxxxxxxxxxxxxxxxxxxx",
        "accountId": "acc_8Kd3...",
        "ticket": "4072808150",
        "symbol": "EURUSD",
        "setId": "cs_7Qa2...",
        "followerId": "cf_3Zb9..."
      },
      "dataPropertyOrder": {
        "&": [
          "baseUrl",
          "wsBaseUrl",
          "apiKey",
          "accountId",
          "ticket",
          "symbol",
          "setId",
          "followerId"
        ]
      },
      "metaSortKey": 1
    },
    {
      "_id": "fld_sessions",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Sessions",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 0
    },
    {
      "_id": "req_post_v1_sessions",
      "_type": "request",
      "parentId": "fld_sessions",
      "name": "Open a broker session",
      "description": "Connect a broker account. We authenticate and warm a live connection, then hand back an accountId — the handle for THIS broker connection. One API key can hold several broker accounts, so the id lives in this response (and in GET /v1/accounts), NOT in the key. Use it in every later /v1/accounts/:id call. By default your password is held in memory only while the connection is live, never saved to disk; the one exception is the opt-in remembered-credentials setting (Pro and Enterprise, PATCH /v1/remember-password), which stores it encrypted solely to restore the connection after a restart.\n\n**Body fields**\n- `broker` (\"mt4\" | \"mt5\") — required — Which platform your broker server runs.\n- `server` (string) — required — Broker server name, e.g. \"Exness-MT5Trial14\".\n- `account` (number | string) — required — Your numeric broker login — the account number your broker assigns (e.g. 12345678). NOT your TickerAll email.\n- `password` (string) — required — Broker (investor or master) password. Used once to authenticate; never persisted unless you have opted in to remembered credentials (Pro and Enterprise), in which case it may be omitted on reconnect.\n- `terminalType` (\"MOBILE\" | \"WEB\") — Which client the connection presents AS — MOBILE (default) or WEB. Both expose the full surface (account, quotes, positions, history). Omit for MOBILE. Choosing WEB (or CLIENT) requires a Pro or Enterprise plan.\n- `webTerminalUrl` (string) — The broker’s web-terminal URL, e.g. https://mt5.yourbroker.com. REQUIRED when terminalType is \"WEB\" — web terminals are per-broker-domain, so the URL must be supplied. Ignored for MOBILE.\n- `webEndpoint` (string) — Advanced, optional: an explicit WebSocket endpoint override (e.g. wss://host/path) for the rare broker whose WS host/path differs from the webTerminalUrl derivation. Ignored for MOBILE.\n\n**Notes**\n- On Free, demo accounts plus one live account in the Terminal; a live account via the API, SDKs or MCP returns FREE_TIER_LIVE_REJECTED.\n- The connection stays warm for a while (see expiresAt). If it cools, calls return BROKER_ACCOUNT_NOT_HOT — just POST /v1/sessions again to reconnect.\n- A broker login is linked once. If another of your API keys already holds it where this key cannot see it (an isolated key’s account, or a shared-pool account when this key is isolated), you get 409 BROKER_ACCOUNT_HELD_BY_ANOTHER_KEY and nothing is connected: use the key that owns it, or move it first with POST /v1/accounts/:id/migrate-key. The accountId you get back is always one this key can use.\n- If another TickerAll customer already has the login linked, you get 409 BROKER_ACCOUNT_ALREADY_LINKED. It stays linked to them until they remove the account, whether or not it is connected right now.\n- On an account with a remembered password you may send no password. If the broker then refuses the remembered one, the 401 BROKER_AUTH_FAILED carries details: { reason: \"CREDENTIAL_REJECTED\", credentialRejectedAt: \"<time>\" }, and the account shows the same time in credentialRejectedAt on the next read. A password you send yourself and get wrong is a plain 401 and changes nothing on the account.\n- A 401 BROKER_AUTH_FAILED whose body carries details: { reason: \"ACCOUNT_NOT_READY\", credentialRejected: false } is not a refused password: the broker accepted the login, but the account did not become ready in time. Nothing is marked on the account and a remembered password is kept. The answer says when to retry: details.retryAfterSeconds and a Retry-After header. The wait is 30 seconds the first time and doubles each time the account is again not ready, up to 15 minutes; it starts over once a login succeeds. A connect for the same account sent sooner gets the same answer with the time left, and no new login is attempted; a connect with a different password is tried at once. If it keeps happening, check that the account is active at the broker. The same details are returned by POST /v1/accounts/:id/reconnect and POST /v1/discover.\n- Connecting with the current password is how a rejected remembered password is fixed: it replaces the stored one and resets credentialRejectedAt to null on the account.\n\n**Example response** — 200 OK\n```json\n{\n  \"accountId\": \"acc_8Kd3...\",\n  \"isDemo\": true,\n  \"status\": \"connected\",\n  \"expiresAt\": \"2026-05-22T18:42:10.000Z\"\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/sessions",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"broker\": \"mt5\",\n  \"server\": \"Exness-MT5Trial14\",\n  \"account\": 12345678,\n  \"password\": \"your-broker-password\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 0,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_delete_v1_sessions_accountid",
      "_type": "request",
      "parentId": "fld_sessions",
      "name": "Disconnect a session",
      "description": "Disconnect a broker account and release its connection. Returns no body.\n\n**Path parameters**\n- `accountId` (string) — required — The accountId returned by POST /v1/sessions.\n\n**Notes**\n- A read-only key may call this for any account it can see: a shared read-only key for accounts in the shared pool, an isolated read-only key for the accounts it owns.",
      "method": "DELETE",
      "url": "{{ _.baseUrl }}/v1/sessions/{{ _.accountId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_accounts",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Accounts",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 1000
    },
    {
      "_id": "req_get_v1_accounts",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "List connected accounts",
      "description": "List every broker account attached to your API key, with connection state.\n\n**Notes**\n- credentialRejectedAt is always present. null means nothing is wrong. A timestamp means the broker refused the account’s remembered password at that time (for example the password was changed at the broker) — on an automatic reconnect, or on a connect of yours that sent no password — and we stopped retrying it. Connect once with the current password (POST /v1/sessions) to fix it; the field is null again on the next read.\n- accountNumber is masked to the last 4 digits.\n- hot is the live connection state: true only while the account is connected right now and can be read and traded; false once the connection has cooled or you disconnected it (DELETE /v1/sessions/:accountId) — POST /v1/sessions to reconnect. This is the field to check before a call.\n- status on this list is NOT the live connection state. It is the account’s registration state on your roster: \"CONNECTED\" means the account has connected successfully at least once and is on your roster, and it stays \"CONNECTED\" while the connection is cold and after you disconnect the session; \"PENDING\" appears only while a first connection is still in progress. For \"is it connected now?\" read hot here, or status on GET /v1/accounts/:id, which is the live one (\"online\" / \"offline\").\n- marginMode is the broker’s margin model for the account — \"netting\" (one aggregate position per symbol), \"hedging\" (independent positions per symbol), or \"exchange\". It is read from the live warm connection, so it is null when the account is cold/offline or on MT4.\n- group is the broker’s own account group (e.g. demo\\Standard, real\\Raw-USD), the best account-type signal available. It is cached per account from the first connection or trade the broker reports it on; null only for an account that has never once been connected.\n- Each row carries group and serverTimeOffsetSeconds as last measured on the account (cached across sessions and restarts); null means never seen yet. MT4 accounts carry serverTimeOffsetSeconds too: the value last measured from a live price on the account.\n- readOnly is true when the broker reports the login as read-only (an investor password): reads work, and an order, close, modify or cancel answers 409 BROKER_READ_ONLY. It is read from the live connection, so it is null when the account is cold. On MT4 it is true or null — null there means \"not known to be read-only\".\n- Only accounts that connected at least once are listed: a failed connection attempt (unknown server, wrong password, unreachable broker) leaves no entry and does not count toward your plan’s cap.\n\n**Example response** — 200 OK\n```json\n[\n  {\n    \"id\": \"acc_8Kd3...\",\n    \"broker\": \"mt5\",\n    \"server\": \"Exness-MT5Trial14\",\n    \"accountNumber\": \"****5678\",\n    \"isDemo\": true,\n    \"group\": \"demo\\\\Standard\",\n    \"status\": \"CONNECTED\",\n    \"hot\": true,\n    \"marginMode\": \"netting\",\n    \"alwaysHot\": false,\n    \"credentialRejectedAt\": null,\n    \"lastHotAt\": \"2026-05-22T18:30:01.000Z\",\n    \"createdAt\": \"2026-05-20T09:11:55.000Z\"\n  }\n]\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Get account info + positions",
      "description": "Live account info — balance, equity, margin, leverage — plus the current open positions.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Notes**\n- status here is the live state: \"online\" when the account is connected and its live figures were read (account and positions are present); \"offline\" when the connection has cooled or the session was disconnected — the account and positions blocks are then absent, and hot is false; POST /v1/sessions to reconnect. (\"offline\" with hot: true means the account is connected but its live figures could not be read for this one response — read again.) It is a different field from status on GET /v1/accounts, which is the account’s registration state and reads \"CONNECTED\" for a cold account too.\n- credentialRejectedAt is always present, here and on GET /v1/accounts. null means nothing is wrong. A timestamp means the broker refused the account’s remembered password at that time; the account is then offline, and its data calls (positions, history, candles, …) answer 409 BROKER_ACCOUNT_NOT_HOT with details.reason \"CREDENTIAL_REJECTED\". One POST /v1/sessions with the current password reconnects it and sets the field back to null.\n- Money fields (equity, margin, freeMargin, marginLevel) are null on an MT4 account whenever they cannot be computed at that moment (for example while the account’s open trades are not known yet) — null means \"not available\", never a misleading 0. credit is null on MT4.\n- MT4 accounts, on every login — a trading (master) password and a read-only (investor) password alike: equity, freeMargin and marginLevel, and each open position’s currentPrice and profit, are computed from live prices, on this object, on GET /v1/accounts/:id/positions and in the WebSocket account_update / position_update events. A read-only login shows the same figures as a trading login. They are null (the key is present; never 0) only while a position cannot be valued, for one of two reasons. (1) No price has arrived yet for its symbol — a moment after connect or after the position opens: its currentPrice and profit are null. (2) Its profit cannot be computed — the symbol’s profit currency, or the rate to convert it to the account currency, is not known, or the symbol is a futures-type contract: its profit is null (currentPrice can still be present). A symbol with no profitCurrency on GET /v1/accounts/:id/symbol-specs is always in this case. While any open position is in either state the account’s equity, freeMargin and marginLevel are null too — in case (2) for as long as that position stays open. balance, margin, credit and every other field are unaffected.\n- credit: broker-granted credit / deposit bonus in the account currency, already included in equity and freeMargin. A number when the broker reports it (0 means no credit); null only when the broker has not reported a credit figure for the account.\n- isCent / cent: whether the account is denominated in a cent unit. The broker reports a cent account’s deposit currency as a cent code — currency \"USC\" means US cents — and every money field (balance, equity, margin, profit, deal P/L) is in that unit. isCent true comes with cent: { baseCurrency: \"USD\", unitsPerBase: 100 } (divide by unitsPerBase for dollars); a whole-currency account reads isCent false, cent null; isCent null means the deposit currency is not known yet (no snapshot).\n- Each open position carries brokerOpenTime — the same instant as openTime in the broker’s server-local time (RFC 3339 with offset) — on this object and on GET /v1/accounts/:id/positions, whose envelope also carries serverTimeOffsetSeconds. The offset is derived from the account’s open positions as well as its closed deals, so it is known from the first session on any account holding a position.\n- serverTimeOffsetSeconds is the broker’s server-local offset from UTC (e.g. 10800 for GMT+3), measured from the broker’s own records. null means it could not be derived yet (no deal or open position on the account); 0 means a UTC server.\n- On an MT4 account the connection learns serverTimeOffsetSeconds by itself as it connects — you do not subscribe to anything for it — so on most brokers it is known on the first read. It is learned from the connection’s first price: on some brokers that arrives a few seconds after connect, and on a connection made while the market is closed (a weekend, for example) no price arrives, so it stays null. While the connection does not know it, this object and GET /v1/accounts show the value last measured on an earlier connection (null if there has never been one), and the row lists carry serverTimeOffsetSeconds: null. In that state a time is never the broker’s clock labelled as UTC: open positions read openTime: null with brokerOpenTime set to the broker’s wall-clock time with no offset suffix (e.g. \"2026-11-12T11:31:16\"), pending orders read setTime: null with brokerSetTime the same way, and the rows of /history, /balance-operations and /orders-history read their UTC times as null with the broker* twin set the same way (those responses carry timeBasis: \"broker\"). Candles keep their timestamps on the broker’s server clock and say so with timeBasis: \"broker\". Once the offset is known (0 counts as known) everything is UTC with twins, like MT5.\n- group is the broker’s own account group (e.g. demo\\Raw-USD, real\\Standard) — the best account-type signal the broker exposes. The broker sends it on an account’s first connection and on trade events; TickerAll caches it per account from that point, so it is present on the account object and the WebSocket snapshot on every later session and survives reconnects and maintenance. The only time it is null is an account that has never once been connected — never a made-up value.\n\n**Example response** — 200 OK (status: \"online\")\n```json\n{\n  \"id\": \"acc_8Kd3...\",\n  \"broker\": \"mt5\",\n  \"server\": \"Exness-MT5Trial14\",\n  \"accountNumber\": \"****5678\",\n  \"isDemo\": true,\n  \"credentialRejectedAt\": null,\n  \"status\": \"online\",\n  \"account\": {\n    \"name\": \"Demo Account 12345678\",\n    \"accountType\": \"demo\",\n    \"group\": \"demo\\\\Standard\",\n    \"leverage\": 500,\n    \"balance\": 9871.42,\n    \"currency\": \"USD\",\n    \"equity\": 9863.10,\n    \"margin\": 142.50,\n    \"freeMargin\": 9720.60,\n    \"marginLevel\": 6921.5,\n    \"credit\": null,\n    \"isCent\": false,\n    \"cent\": null,\n    \"serverTimeOffsetSeconds\": 0\n  },\n  \"positions\": [\n    {\n      \"ticket\": 4072808150,\n      \"symbol\": \"BTCUSD\",\n      \"side\": \"BUY\",\n      \"volume\": 0.10,\n      \"entryPrice\": 77512.73,\n      \"stopLoss\": 71000,\n      \"takeProfit\": 84000,\n      \"currentPrice\": 77640.10,\n      \"profit\": 12.74,\n      \"swap\": 0,\n      \"commission\": 0,\n      \"comment\": \"my-strategy\",\n      \"magic\": 0,\n      \"openTime\": \"2026-05-22T17:55:03.000Z\",\n      \"brokerOpenTime\": \"2026-05-22T17:55:03+00:00\"\n    }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_positions",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "List open positions",
      "description": "The account’s current open positions — the dedicated positions feed. Each row carries entry price, volume, live P&L and SL/TP; it is the same array GET /v1/accounts/:id embeds, with a brokerOpenTime twin per row and serverTimeOffsetSeconds on the envelope.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Notes**\n- An empty positions array is valid data — it means no open positions, never an error.\n- entryPrice is the position’s actual fill price; currentPrice and profit are live and move with the market. profit is in the account’s deposit currency.\n- MT4 accounts: currentPrice and profit are computed from live prices on every login — a read-only (investor password) login shows the same figures as a trading login — and are normally there on the first read after connect and on the first read after a position opens. Either is null (the key is present; never 0) only while the position cannot be valued: no price has arrived yet for its symbol (a moment after connect or after the open), or its profit cannot be computed (the symbol’s profit currency, or the rate to convert it to the account currency, is not known, or the symbol is a futures-type contract) — then profit is null while currentPrice can still be present; a symbol with no profitCurrency on GET /v1/accounts/:id/symbol-specs is always in this case. While any open position is in that state the account’s equity, freeMargin and marginLevel are null as well. Every other field of the row is as usual.\n- magic is the EA (Expert Advisor) magic number the trade was opened with — the integer a bot stamps its orders with; 0 means a manual or non-EA trade (0 is a real value, not \"missing\"). The same field is on pending orders.\n- openTime is UTC; brokerOpenTime is the same instant in the broker’s server-local time (RFC 3339 with offset). serverTimeOffsetSeconds on the envelope is the broker’s offset from UTC (e.g. 10800 for GMT+3); null means it could not be derived yet, 0 means a UTC server.\n- MT4 only, while serverTimeOffsetSeconds is null (the first few seconds after connect on some brokers, or a connection made while the market is closed): openTime is null — it is never the broker’s clock labelled as UTC — and brokerOpenTime carries the broker’s wall-clock time with no offset suffix, e.g. \"2026-11-12T11:31:16\". Once the offset is known (0 counts as known) openTime is UTC and brokerOpenTime has its offset again. The WebSocket position_update events follow the same rule.\n- The same positions are also on GET /v1/accounts/:id alongside the money fields — use this lighter call when you only need positions, and subscribe to the positions WebSocket channel for live updates.\n\n**Example response** — 200 OK\n```json\n{\n  \"accountId\": \"acc_8Kd3...\",\n  \"positions\": [\n    {\n      \"ticket\": 4072808150,\n      \"symbol\": \"BTCUSD\",\n      \"side\": \"BUY\",\n      \"volume\": 0.10,\n      \"entryPrice\": 77512.73,\n      \"stopLoss\": 71000,\n      \"takeProfit\": 84000,\n      \"currentPrice\": 77640.10,\n      \"profit\": 12.74,\n      \"swap\": 0,\n      \"commission\": 0,\n      \"comment\": \"my-strategy\",\n      \"magic\": 0,\n      \"openTime\": \"2026-05-22T17:55:03.000Z\",\n      \"brokerOpenTime\": \"2026-05-22T17:55:03+00:00\"\n    }\n  ],\n  \"serverTimeOffsetSeconds\": 0\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/positions",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1002,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_delete_v1_accounts_id",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Remove a broker account",
      "description": "Remove a broker account from your roster. We disconnect its live connection and drop it from your account list and from billing — this does NOT touch the broker account itself or any open positions. Reversible: reconnect the same login with POST /v1/sessions to re-add it.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Notes**\n- Idempotent — removing an already-removed or unknown id returns the same 200 shape (a retried cleanup never fails half-way). existed tells them apart: true when a linked account was actually removed by this call, false when nothing was linked under that id (a typo, or already gone). An id that belongs to another customer is a 404.\n- If the account had always-hot enabled, that per-connection charge stops immediately; billableCount is your remaining always-hot connection count.\n- If the account has a remembered password (see /v1/remember-password), it is destroyed before the account is removed. If our vault can’t confirm that yet, you get 503 VAULT_PURGE_PENDING and the account stays linked; retry the DELETE.\n- A read-only key may remove any account it can see: a shared read-only key the accounts in the shared pool, an isolated read-only key the accounts it owns.\n\n**Example response** — 200 OK\n```json\n{\n  \"id\": \"acc_8Kd3...\",\n  \"status\": \"DISCONNECTED\",\n  \"removed\": true,\n  \"existed\": true,\n  \"billableCount\": 0\n}\n```",
      "method": "DELETE",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1003,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_accounts_id_migrate",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "POST /v1/accounts/:id/migrate",
      "description": "Switch which terminal type the account presents AS (\"MOBILE\" or \"WEB\"). Your open positions, pending orders and balance live on the broker account, not the connection, so they are preserved across the switch.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Body fields**\n- `to` (\"MOBILE\" | \"WEB\") — required — The transport to switch to.\n\n**Notes**\n- status is \"noop\" when the account is already on the requested transport.\n- Switching runs only when the account is idle (no in-flight trade — otherwise 409).\n- Both terminal types expose the full surface (account, quotes, positions, history).\n- The switch is zero-gap — the new transport is warmed before the old session is dropped — so open positions, orders and balance carry over untouched.\n\n**Example response** — 200 OK\n```json\n{\n  \"id\": \"acc_8Kd3...\",\n  \"terminalType\": \"MOBILE\",\n  \"status\": \"noop\"\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/migrate",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"to\": \"2026-05-29\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1004,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_accounts_id_migrate_key",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Move an account between the shared pool and an isolated key",
      "description": "Move a broker account between the shared pool and an isolated API key, in place. Pass an isolated API key id to move the account under that key (only that key, plus the dashboard, then sees it), or null to move it back to the shared pool. The connection is never dropped — a warm account stays warm, positions/orders/balance untouched.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Body fields**\n- `ownerApiKeyId` (string | null) — required — An isolated API key id to move the account into that key, or null for the shared pool.\n\n**Notes**\n- ownerApiKeyId in the response is the new owner: null = shared pool, else the isolated key id. isolated mirrors it.\n- migrated is false when the account was already where you asked (a no-op).\n- hot reflects the live connection — migration never drops it, so a warm account stays warm.\n- Only the shared pool ↔ an isolated key are valid moves; moving directly between two isolated keys is rejected (migrate to the shared pool first).\n- The account now also carries ownerApiKeyId on GET /v1/accounts and GET /v1/accounts/:id.\n- Requires a full (non read-only) key; via an API key, the only isolated target is the calling key itself — naming another key id requires the dashboard.\n\n**Example response** — 200 OK\n```json\n{\n  \"id\": \"acc_8Kd3...\",\n  \"ownerApiKeyId\": \"key_7Qf2...\",\n  \"isolated\": true,\n  \"hot\": true,\n  \"migrated\": true\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/migrate-key",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"ownerApiKeyId\": \"...\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1005,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_accounts_id_always_hot",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Toggle always-hot on one account",
      "description": "Keep one account’s connection warm 24/7: skips the idle cool-down so the session stays up between your calls. Broker-side drops reconnect automatically on every account regardless; always-hot only removes the idle timeout. Off by default. Trader ($0.99/mo per connection) or Pro / Enterprise (included).\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Body fields**\n- `enabled` (boolean) — required — true to pin the connection warm, false to let it cool after the idle timeout.\n\n**Notes**\n- effective is the resolved state (this flag OR the account-wide switch, on an eligible plan). appliedLive is true when the account was warm and the change took effect on the live session immediately; a cold account applies it on its next connect.\n- What always-hot does NOT do: it does not store your password. If TickerAll itself restarts, the account returns as needs-rearm (see GET /v1/always-hot/pending) until you POST /v1/sessions again — unless you opt in to remembered credentials.\n- 403 ALWAYS_HOT_TIER_REQUIRED on the Free plan.\n- A read-only key may switch it, on or off, for any account it can see: a shared read-only key for accounts in the shared pool, an isolated read-only key for the accounts it owns.\n\n**Example response** — 200 OK\n```json\n{\n  \"id\": \"acc_8Kd3...\",\n  \"alwaysHot\": true,\n  \"effective\": true,\n  \"appliedLive\": true,\n  \"billableCount\": 1\n}\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/always-hot",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"enabled\": \"...\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1006,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_always_hot",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Toggle always-hot for all accounts",
      "description": "Account-wide always-hot: every eligible connection defaults to warm. Same semantics as the per-account toggle, applied live to all held sessions.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `enabled` (boolean) — required — true to keep every connection warm, false to return to per-account flags.\n\n**Notes**\n- 403 ALWAYS_HOT_TIER_REQUIRED on the Free plan.\n\n**Example response** — 200 OK\n```json\n{\n  \"accountWide\": true,\n  \"billableCount\": 3\n}\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/always-hot",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"enabled\": \"...\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1007,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_always_hot_pending",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Accounts that need a credentials re-arm",
      "description": "Always-hot accounts that currently have no live connection and need a credentials refresh — typically after a TickerAll maintenance restart dropped the in-memory password. Empty means nothing needs attention.\n\n**Notes**\n- Re-arm an account by calling POST /v1/sessions with its credentials again. The TypeScript and Python SDKs do this for you: sessions.keepAlive() / keep_alive() caches the credentials in your process and re-arms a cold account on its next call.\n- If you cannot keep credentials on your side at all, see PATCH /v1/remember-password — with it on, accounts that are always-hot re-arm themselves server-side and never appear here.\n\n**Example response** — 200 OK\n```json\n{\n  \"pending\": [\n    { \"id\": \"acc_8Kd3...\", \"broker\": \"mt5\", \"server\": \"Exness-MT5Trial14\", \"accountNumber\": \"****5678\" }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/always-hot/pending",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1008,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_remember_password",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Remembered-credentials status",
      "description": "Status of remembered credentials — the one opt-in exception to TickerAll’s rule of never storing broker passwords. Available on Pro and Enterprise, for customers who cannot keep credentials on their own side.\n\n**Notes**\n- remembered = accounts whose password is sealed in the vault; armed = opted in but not yet captured (the password is captured on each account’s next connect, never through this endpoint).\n- eligible is false on the Free and Trader plans.\n\n**Example response** — 200 OK\n```json\n{\n  \"accountWide\": false,\n  \"eligible\": true,\n  \"tier\": \"PRO\",\n  \"remembered\": 0,\n  \"armed\": 0\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/remember-password",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1009,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_remember_password",
      "_type": "request",
      "parentId": "fld_accounts",
      "name": "Toggle remembered credentials (Pro+)",
      "description": "Turn remembered credentials on or off for ALL your accounts. On: each account’s password is sealed in an encrypted vault (AES-256-GCM) on its next connect and used solely to restore the connection after a TickerAll restart; your consent text is recorded. Off: every stored password is purged immediately.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `enabled` (boolean) — required — true to opt in (account-wide), false to opt out and purge.\n- `consentText` (string) — Required when enabling: the consent wording you are agreeing to (recorded for audit).\n\n**Notes**\n- Off by default on every plan; nothing is stored until you switch it on. Turning it off returns { \"accountWide\": false, \"forgotten\": <n> } after rotating every sealed secret to an unreadable value and clearing the flags.\n- Once on, an account that is also always-hot re-arms itself server-side after a maintenance restart — no POST /v1/sessions needed. If you can supply the password yourself on reconnect, leave it off.\n- A password you pass to POST /v1/sessions on a remembered account updates the stored one; on a remembered account you may also omit password to reconnect.\n- 403 REMEMBER_PRO_REQUIRED below Pro.\n\n**Example response** — 200 OK\n```json\n{\n  \"accountWide\": true\n}\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/remember-password",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"enabled\": \"...\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 1010,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_symbols",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Symbols",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 2000
    },
    {
      "_id": "req_patch_v1_accounts_id_remember_password",
      "_type": "request",
      "parentId": "fld_symbols",
      "name": "PATCH /v1/accounts/:id/remember-password",
      "description": "Choose remembered credentials for ONE account, overriding the account-wide setting. on: remember this account’s password even with the account-wide switch off. off: never remember it, even with the switch on, and destroy what is stored now. inherit: follow the account-wide switch again.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `choice` ('on' | 'off' | 'inherit') — required — The choice for this account.\n- `consentText` (string) — Required for on: the consent wording you are agreeing to (recorded for audit).\n\n**Notes**\n- effective = whether this account is remembered once both settings are applied. stored = a password is sealed in the vault right now (for on, it is captured on the account’s next connect, never through this endpoint).\n- pendingPurge = 1 when the vault could not confirm the password was destroyed; retry the same call. off is available on every plan; on is Pro and Enterprise (403 REMEMBER_PRO_REQUIRED).\n- Turning the account-wide switch off resets every on to inherit, so it purges everything. An off set here survives turning the switch back on.\n\n**Example response** — 200 OK\n```json\n{\n  \"id\": \"cmabc123\",\n  \"rememberChoice\": \"off\",\n  \"effective\": false,\n  \"stored\": false,\n  \"pendingPurge\": 0\n}\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/remember-password",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"choice\": \"...\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 2000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_symbols",
      "_type": "request",
      "parentId": "fld_symbols",
      "name": "List tradeable symbols",
      "description": "List tradeable symbols on the account, in the broker’s native names (pass-through, no normalization). symbols is the full catalog; watched is the subset that is actively streaming live ticks right now.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Notes**\n- Use a name from symbols when placing orders; subscribe to a name to start it ticking on the WebSocket.\n- symbols is never legitimately empty on a connected account. If TickerAll cannot read the account’s instrument list you get 503 SYMBOL_CATALOG_UNAVAILABLE rather than an empty array — so an empty list can never be mistaken for “no symbols” or for an account-type verdict.\n\n**Example response** — 200 OK\n```json\n{\n  \"symbols\": [\"BTCUSD\", \"ETHUSD\", \"EURUSD\", \"GOLD\", \"USOILm\", \"...\"],\n  \"watched\": [\"BTCUSD\", \"ETHUSD\", \"EURUSD\"]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/symbols",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 2001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_symbol_specs",
      "_type": "request",
      "parentId": "fld_symbols",
      "name": "GET /v1/accounts/:id/symbol-specs",
      "description": "Per-symbol trading specs for the account: the volume constraints (min / max / step) for validating an order size before placing it, the price geometry (digits, point, tickSize) and contract maths (contractSize, tickValue, tickValueAccount) for sizing risk per tick, the broker’s stop and freeze levels, and the base, profit (quote) and margin currency for each instrument. Available on MT4 and MT5; see the note on which MT4 values are typical rather than broker-published.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Notes**\n- volumeStep is the lot increment — round an order size down to it before placing; volumeMin / volumeMax bound the size.\n- profitCurrency is the quote currency (the currency P&L accrues in). Use it — not the account currency — to denote an instrument.\n- On MT4, profitCurrency is the quote currency of the symbol (BTCUSD → \"USD\", GBPJPY → \"JPY\"); for an instrument that is not a currency pair it is the margin currency. marginCurrency is the currency the broker holds margin in (BTCUSD → \"BTC\"). MT4 brokers do not publish volume limits, so volumeMin / volumeMax / volumeStep are typical values for the instrument type and specSource is \"derived\".\n- MT4 rows also carry executionMode — \"market\", \"instant\" or \"request\" (how the broker executes market orders on the symbol), or null when it is not known. Market orders and closes on an \"instant\" or \"request\" symbol cannot be placed through the API yet: they answer 422 TRADE_DISABLED and nothing is sent to the broker; pending orders and SL/TP changes are unaffected. The field is absent on MT5.\n- specSource is \"broker\" (authoritative) or \"derived\" (a best-effort fallback when the broker did not supply the spec). Currency fields are absent when unknown.\n- Price geometry: digits is the quote precision, point is the smallest price increment (10^-digits), and tickSize is the smallest price change the broker accepts — equal to point on every instrument we serve. Use tickSize, not a hard-coded 0.0001 / 0.01, to step a price.\n- Tick value: tickValue is the profit-currency value of a one-point move on 1.00 lot (contractSize x point). tickValueAccount is that same figure converted into the ACCOUNT’s deposit currency, and tickValueCurrency tells you which currency it is in plus where it came from — source \"wire\" (read from the broker), \"heuristic\" (derived by us) or \"override\".\n- Size risk off tickValueAccount, not tickValue: risk = |entry - stop| / tickSize * tickValueAccount * lots. tickValue is in the instrument’s profit currency, which differs from your deposit currency on many symbols — using it directly overstates or understates risk by the cross-rate.\n- Absent means UNKNOWN, never zero. Any of digits, point, tickSize, contractSize, tickValue, tickValueAccount and the currency fields is omitted entirely when the broker’s record did not carry it, so you can tell \"unknown\" from a real 0 and fall back deliberately. Do NOT default a missing tickValueAccount to contractSize * tickSize — that is the profit-currency figure and is wrong whenever the profit currency is not your deposit currency.\n- stopLevel and freezeLevel are in POINTS, and 0 is real data meaning no restriction (common on floating-spread symbols) — not a missing value. stopLevel is the minimum distance an SL / TP must keep from the current price; inside freezeLevel the broker refuses modifications. Checking stopLevel before a PATCH turns most 422 BROKER_REJECTED stop-distance rejections into a client-side check.\n\n**Example response** — 200 OK\n```json\n{\n  \"specs\": [\n    {\n      \"name\": \"EURUSD\",\n      \"volumeMin\": 0.01,\n      \"volumeMax\": 200.0,\n      \"volumeStep\": 0.01,\n      \"specSource\": \"broker\",\n      \"digits\": 5,\n      \"point\": 0.00001,\n      \"tickSize\": 0.00001,\n      \"contractSize\": 100000,\n      \"tickValue\": 1.0,\n      \"tickValueAccount\": 1.0,\n      \"tickValueCurrency\": { \"code\": \"USD\", \"source\": \"wire\" },\n      \"stopLevel\": 0,\n      \"freezeLevel\": 0,\n      \"baseCurrency\": \"EUR\",\n      \"profitCurrency\": \"USD\",\n      \"marginCurrency\": \"EUR\"\n    }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/symbol-specs",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 2002,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_symbols_symbol_spec",
      "_type": "request",
      "parentId": "fld_symbols",
      "name": "GET /v1/accounts/:id/symbols/:symbol/spec",
      "description": "The full trading spec for ONE symbol — the same object GET /v1/accounts/:id/symbol-specs returns per entry, for a single instrument. Cheaper than the full list when you only need the contract maths for the symbol you are about to trade: a warm account answers from its cached catalog. Available on MT4 and MT5.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n- `symbol` (string) — required — Broker-native symbol name, e.g. \"EURUSD\". Discover names via GET /v1/accounts/:id/symbols.\n\n**Notes**\n- Field meanings, units and the absent-means-unknown contract are identical to GET /v1/accounts/:id/symbol-specs — see its notes, including the risk formula and the warning against defaulting tickValueAccount.\n- A symbol the account has never touched is primed once and then read, so the first call for it can be slower than subsequent ones.\n- An unknown symbol on this account is a 404 — distinct from a known symbol with fields missing, which is a 200 with those fields absent.\n\n**Example response** — 200 OK\n```json\n{\n  \"spec\": {\n    \"name\": \"EURUSD\",\n    \"volumeMin\": 0.01,\n    \"volumeMax\": 200.0,\n    \"volumeStep\": 0.01,\n    \"specSource\": \"broker\",\n    \"digits\": 5,\n    \"point\": 0.00001,\n    \"tickSize\": 0.00001,\n    \"contractSize\": 100000,\n    \"tickValue\": 1.0,\n    \"tickValueAccount\": 1.0,\n    \"tickValueCurrency\": { \"code\": \"USD\", \"source\": \"wire\" },\n    \"stopLevel\": 0,\n    \"freezeLevel\": 0,\n    \"baseCurrency\": \"EUR\",\n    \"profitCurrency\": \"USD\",\n    \"marginCurrency\": \"EUR\"\n  }\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/symbols/{{ _.symbol }}/spec",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 2003,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_symbols_symbol_quote",
      "_type": "request",
      "parentId": "fld_symbols",
      "name": "GET /v1/accounts/:id/symbols/:symbol/quote",
      "description": "The current live bid and ask for one symbol. Candles are bid-only, so this is the way to read the ASK over REST — needed to compute a spread or to place a limit relative to the offer. MT5 only; an MT4 account returns 404.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n- `symbol` (string) — required — Broker-native symbol name, e.g. \"EURUSD\".\n\n**Notes**\n- bid and ask are null — never 0 — when the symbol exists but is not currently quoted, e.g. a closed market. That is an honest \"no live price\", distinct from an unknown symbol (404).\n- A symbol the account is not already streaming is subscribed once and polled briefly for its first tick, so the first call can take a moment; a watched symbol answers from the last tick immediately.\n- The response carries no tick time — it is the latest tick as of the request. If you need to stamp a price, use your own receive time, or the WebSocket stream.\n- For continuous prices use the WebSocket rather than polling this endpoint.\n\n**Example response** — 200 OK\n```json\n{\n  \"symbol\": \"EURUSD\",\n  \"bid\": 1.07241,\n  \"ask\": 1.07249\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/symbols/{{ _.symbol }}/quote",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 2004,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_candles_history",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Candles & history",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 3000
    },
    {
      "_id": "req_get_v1_accounts_id_candles",
      "_type": "request",
      "parentId": "fld_candles_history",
      "name": "Historical OHLC candles (authed)",
      "description": "Fetch historical OHLC bars from your connected broker, for any symbol the broker streams on this account. Two modes: a look-back window (hours) returns the most-recent N hours of bars, or a date range (from + to, ISO-8601) returns the exact [from, to] window. Coarser timeframes (H4, D1, W1, MN1) reach much further back — daily bars typically cover years of history. Every response reports how much of the requested window was actually served (coverage / truncated).\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Query parameters**\n- `symbol` (string) — required — Broker-native symbol name, e.g. \"BTCUSD\". Discover names via GET /v1/accounts/:id/symbols.\n- `hours` (number) — Look-back mode: how many hours of data to return, counted backwards from now. Defaults to 24, capped at ~5 years. Ignored when from and to are supplied.\n- `from` (ISO-8601 string | Unix seconds | Unix milliseconds) — Date-range mode: start of the window. Accepts ISO-8601 (\"2026-11-09T00:00:00Z\"; a bare date \"2026-11-09\" means 00:00:00 UTC), Unix seconds (1794182400) or Unix milliseconds (1794182400000). Pass BOTH from and to (an alternative to hours) to fetch the exact [from, to] window. Supplying only one is a 400 invalid_range.\n- `to` (ISO-8601 string | Unix seconds | Unix milliseconds) — Date-range mode: end of the window, same formats as from. Required with from and must be after it. When both are present, hours is ignored.\n- `timeframe` (\"M1\"|\"M5\"|\"M15\"|\"M30\"|\"H1\"|\"H4\"|\"D1\"|\"W1\"|\"MN1\") — Bar interval. Defaults to \"M5\". Coarser timeframes go back further for the same window.\n\n**Notes**\n- Authed — needs your API key (same as the rest of the customer API). Works for any symbol your broker exposes on the connected account.\n- Two modes: pass hours for a look-back window, OR pass both from and to for an exact date range. When from and to are present, hours is ignored, and the response echoes from/to (the requested window, as ISO-8601) at the top level instead of hours. Supplying only one of from/to, a value that cannot be read, or from ≥ to returns 400 invalid_range; the message names the accepted formats and gives an example.\n- from / to formats, on every account type: ISO-8601, Unix seconds, or Unix milliseconds (a digits-only value below 1,000,000,000,000 is read as seconds). A digits-only value shorter than 9 digits is not read as a time and is a 400 — a year or a date written without separators (2026, 20261109) would otherwise be taken for a moment in 1970; write the date as 2026-11-09. The one exception is 0, the Unix epoch. Worked example — the same 12-hour window three ways: ?symbol=EURUSD&timeframe=M5&from=2026-11-09T00:00:00Z&to=2026-11-09T12:00:00Z, or &from=1794182400&to=1794225600, or &from=1794182400000&to=1794225600000. A bare date is the start of that day in UTC, so from=2026-11-09&to=2026-11-10 is the whole of 9 November.\n- An empty candles list with 200 means there are no bars in that range — it is not an error. The usual cause is a closed market: hours=24 on a Sunday has no bars for a symbol that last traded on Friday. The range you asked for is the range you get; older bars are never substituted. Ask for a wider window (hours=96) or an explicit from/to to reach the last session.\n- MT4 accounts serve the bars the connection holds: M5 bars from the broker’s recent chart data where the broker provides it; otherwise bars (M1 to D1) built from the symbol’s live prices. Those start at the first live price received after the symbol begins to be watched on this connection — which happens when you subscribe to its ticks on the WebSocket, or on your first candles request for it — not when the account connected. So the first candles request for a symbol nobody is streaming returns no bars and starts the collection; later requests return the bars formed since. hours is counted back from the broker’s current time. Any window, short or long, that starts before the oldest held bar returns what exists with coverage \"partial\", truncated true and stopReason \"unsupported\"; a window the held bars fully cover is \"complete\". W1 and MN1 are not available on MT4.\n- MT4 only: when the account’s serverTimeOffsetSeconds is not known yet, the response adds timeBasis: \"broker\" and a timeBasisNote — the timestamps are then in the broker’s server time and the hours window was counted from UTC. The field is absent in every other case.\n- Each candle is { timestamp, open, high, low, close, bid, tickVolume, spread }. timestamp is the bar OPEN time in Unix seconds, in UTC: the broker stamps bars on its own server clock and TickerAll has already subtracted the broker’s clock offset, so do not subtract serverTimeOffsetSeconds yourself (the response carries no offset field). The offset applied is the one in force now — bars from before the broker’s last daylight-saving change are one hour off — and until it is known on a session (no price received yet) bars come back on the broker’s server clock, which an MT4 account marks with timeBasis: \"broker\". The same holds for MT4 and MT5 accounts. bid mirrors close. tickVolume (tick count) and spread (price units) are present on recent bars; deep-history bars are bid-only and may omit them (null/absent) — don’t rely on a guaranteed volume.\n- Every response also reports completeness: served ({ from, to } actually returned in ISO-8601, or null when empty), count (number of candles), coverage (\"complete\" when the whole window was served, otherwise \"floor\"/\"partial\"), truncated (boolean — true when the whole requested window was NOT served; the authoritative completeness signal), and stopReason (why the walk stopped, e.g. \"floor\" = no deeper data exists, \"deadline\" = worth retrying, \"unsupported\", \"error\"). A note field is added when stopReason is \"unsupported\".\n- A range larger than the per-request cap (~5 years, or ~100,000 bars at the chosen timeframe) is rejected with 400 range_too_large ({ maxBars, estimatedBars, maxWindowDays }) — narrow the range or coarsen the timeframe.\n- Timeframes: M1, M5, M15, M30, H1, H4, D1, W1, MN1. Daily and coarser reach back years; intraday (M1–H4) covers recent months. One request returns as much history as fits in a few seconds. Deep look-backs are isolated onto a dedicated history connection — a big walk never disturbs your live tick stream.\n- If the broker returns no decodable bars for the symbol/range (e.g. an illiquid pair), the response is a 200 with an empty candles array (served null, truncated true) — never a 500.\n\n**Example response** — 200 OK\n```json\n{\n  \"symbol\": \"BTCUSD\",\n  \"hours\": 17520,\n  \"timeframe\": \"D1\",\n  \"candles\": [\n    { \"timestamp\": 1747699200, \"open\": 104200.10, \"high\": 107840.55, \"low\": 103520.00, \"close\": 106910.73, \"bid\": 106910.73 },\n    { \"timestamp\": 1747785600, \"open\": 106910.73, \"high\": 108100.00, \"low\": 105480.20, \"close\": 107512.40, \"bid\": 107512.40 }\n  ],\n  \"served\": { \"from\": \"2025-05-20T00:00:00.000Z\", \"to\": \"2025-05-21T00:00:00.000Z\" },\n  \"count\": 2,\n  \"coverage\": \"complete\",\n  \"truncated\": false,\n  \"stopReason\": \"floor\"\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/candles",
      "body": {},
      "parameters": [
        {
          "name": "symbol",
          "value": "BTCUSD",
          "description": "Broker-native symbol name, e.g. \"BTCUSD\". Discover names via GET /v1/accounts/:id/symbols.",
          "disabled": false
        },
        {
          "name": "hours",
          "value": "8760",
          "description": "Look-back mode: how many hours of data to return, counted backwards from now. Defaults to 24, capped at ~5 years. Ignored when from and to are supplied.",
          "disabled": false
        },
        {
          "name": "from",
          "value": "2026-05-01",
          "description": "Date-range mode: start of the window. Accepts ISO-8601 (\"2026-11-09T00:00:00Z\"; a bare date \"2026-11-09\" means 00:00:00 UTC), Unix seconds (1794182400) or Unix milliseconds (1794182400000). Pass BOTH from and to (an alternative to hours) to fetch the exact [from, to] window. Supplying only one is a 400 invalid_range.",
          "disabled": false
        },
        {
          "name": "to",
          "value": "2026-05-29",
          "description": "Date-range mode: end of the window, same formats as from. Required with from and must be after it. When both are present, hours is ignored.",
          "disabled": false
        },
        {
          "name": "timeframe",
          "value": "D1",
          "description": "Bar interval. Defaults to \"M5\". Coarser timeframes go back further for the same window.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 3000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_public_candles",
      "_type": "request",
      "parentId": "fld_candles_history",
      "name": "Historical OHLC candles (public demo)",
      "description": "Unauthenticated read of the always-on demo feed — powers the sparklines and chart on tickerall.com. Limited to TickerAll’s demo symbol list and only callable from tickerall.com (origin-gated). For arbitrary broker symbols from your own code, use GET /v1/accounts/:id/candles above.\n\n**Query parameters**\n- `symbol` (string) — required — One of the demo feed’s symbols (e.g. BTCUSD, ETHUSD, EURUSD, GOLD).\n- `hours` (number) — How many hours of data to return. Defaults to 24, capped at ~5 years.\n- `timeframe` (\"M1\"|\"M5\"|\"M15\"|\"M30\"|\"H1\"|\"H4\"|\"D1\"|\"W1\"|\"MN1\") — Bar interval. Defaults to \"M5\". Coarser timeframes go back further for the same hours value.\n\n**Notes**\n- No API key needed, but origin-gated: this endpoint accepts requests from tickerall.com only and exists to drive the public demo widgets. For programmatic access from your own code use GET /v1/accounts/:id/candles instead — it works on any symbol your broker exposes.\n- Each candle is { timestamp, open, high, low, close, bid }. timestamp is the bar OPEN time in Unix seconds, in UTC: the broker stamps bars on its own server clock and TickerAll has already subtracted the broker’s clock offset (the one in force now, so bars from before the broker’s last daylight-saving change are one hour off). bid mirrors close.\n- Only the demo feed’s symbol list is available here. For any other symbol, connect a broker account and call GET /v1/accounts/:id/candles instead.\n\n**Example response** — 200 OK\n```json\n{\n  \"symbol\": \"BTCUSD\",\n  \"hours\": 17520,\n  \"timeframe\": \"D1\",\n  \"candles\": [\n    { \"timestamp\": 1747699200, \"open\": 104200.10, \"high\": 107840.55, \"low\": 103520.00, \"close\": 106910.73, \"bid\": 106910.73 },\n    { \"timestamp\": 1747785600, \"open\": 106910.73, \"high\": 108100.00, \"low\": 105480.20, \"close\": 107512.40, \"bid\": 107512.40 }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/public/candles",
      "body": {},
      "parameters": [
        {
          "name": "symbol",
          "value": "BTCUSD",
          "description": "One of the demo feed’s symbols (e.g. BTCUSD, ETHUSD, EURUSD, GOLD).",
          "disabled": false
        },
        {
          "name": "hours",
          "value": "8760",
          "description": "How many hours of data to return. Defaults to 24, capped at ~5 years.",
          "disabled": false
        },
        {
          "name": "timeframe",
          "value": "D1",
          "description": "Bar interval. Defaults to \"M5\". Coarser timeframes go back further for the same hours value.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {},
      "metaSortKey": 3001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_orders",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Orders",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 4000
    },
    {
      "_id": "req_post_v1_accounts_id_orders",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "Place an order",
      "description": "Place a market order (fills immediately) or a pending limit/stop order (rests until price is hit).\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Body fields**\n- `type` (\"market\" | \"limit\" | \"stop\") — required — The base order type — market (fills now) or limit / stop (rests until price is hit). Do NOT pass MetaTrader-style combined names like \"BUY_STOP\" here: those are only ever returned by GET /orders. On input, split the intent into type + side — e.g. a buy-stop is type:\"stop\" + side:\"BUY\".\n- `symbol` (string) — required — Broker-native symbol, e.g. \"BTCUSD\".\n- `side` (\"BUY\" | \"SELL\") — required — Trade direction — uppercase BUY or SELL. Combined with type it expresses the order, e.g. type:\"limit\" + side:\"SELL\" is a sell-limit.\n- `volume` (number) — required — Lots, e.g. 0.10. Must be positive.\n- `price` (number) — Trigger price. Required for limit and stop; ignored for market.\n- `stopLoss` (number) — Stop-loss price. Omit for none; 0 means the same thing.\n- `takeProfit` (number) — Take-profit price. Omit for none; 0 means the same thing.\n- `comment` (string (≤ 31 chars)) — Optional strategy tag stored on the order.\n\n**Notes**\n- Requires an Idempotency-Key header (see Conventions). Re-sending the same key returns the original response without placing a second order.\n- Pending orders are expressed as two separate fields, not one combined name: a buy-stop is type:\"stop\" + side:\"BUY\"; a sell-limit is type:\"limit\" + side:\"SELL\". The write API does NOT accept MetaTrader-style combined names (BUY_STOP, SELL_LIMIT, …) — passing type:\"BUY_STOP\" is a 400 VALIDATION_ERROR. GET /v1/accounts/:id/orders reports that combined name in its own type field for readability, but that is a read-only convenience: do not echo it back here — use its orderType (LIMIT/STOP) lowercased as type, plus side.\n- status is \"open\" for market orders and \"pending\" for limit/stop orders.\n- A broker rejection (bad volume, market closed, stop-level too tight, insufficient margin) comes back as 422 BROKER_REJECTED with the broker’s reason in message.\n- Pending orders are placed good-till-cancelled and there is no expiry field on this call. An expiry you cannot set is better than one silently ignored: nothing here accepts an expiration time, so a pending order rests until it triggers or you cancel it with DELETE /v1/accounts/:id/orders/:ticket.\n\n**Example response** — 201 Created\n```json\n{\n  \"ticket\": 4072808150,\n  \"symbol\": \"BTCUSD\",\n  \"side\": \"BUY\",\n  \"type\": \"market\",\n  \"volume\": 0.10,\n  \"price\": null,\n  \"stopLoss\": 71000,\n  \"takeProfit\": 84000,\n  \"comment\": \"my-strategy\",\n  \"status\": \"open\",\n  \"timestamp\": \"2026-05-22T17:55:03.000Z\"\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/orders",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"type\": \"market\",\n  \"symbol\": \"BTCUSD\",\n  \"side\": \"BUY\",\n  \"volume\": 0.1,\n  \"stopLoss\": 71000,\n  \"takeProfit\": 84000\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_orders",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "List pending orders",
      "description": "List the account’s working pending orders — LIMIT and STOP orders resting until their trigger price is hit. A market order is never pending: it becomes an open position immediately (see GET /v1/accounts/:id/positions). Returns an empty list when nothing is resting.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Notes**\n- magic is the EA (Expert Advisor) magic number the order was placed with — the integer a bot stamps its orders with; 0 means a manual or non-EA order (0 is a real value, not \"missing\"). comment is the order comment set at placement, \"\" when none.\n- Working (pending) orders only. When a pending order triggers it leaves this list and becomes an open position — find it under GET /v1/accounts/:id/positions.\n- MT4 and MT5 accounts return the same row. MT4 has limit and stop orders only (no stop-limit), so limitPrice is always null there.\n- MT4 only, while the account’s serverTimeOffsetSeconds is null (the first few seconds after connect on some brokers, or a connection made while the market is closed): setTime is null and the row carries brokerSetTime — the broker’s wall-clock time with no offset suffix, e.g. \"2026-11-12T11:31:16\". An order that has an expiration then reads expirationTime: null with brokerExpirationTime set the same way, so in that state check brokerExpirationTime before reading null as good-till-cancelled. Once the offset is known (0 counts as known) setTime and expirationTime are UTC and the two broker fields are absent. The WebSocket order_update events carry the same rows.\n- type is one of BUY_LIMIT, SELL_LIMIT, BUY_STOP, SELL_STOP, BUY_STOP_LIMIT, SELL_STOP_LIMIT; orderType collapses that to LIMIT / STOP / STOP_LIMIT and side to BUY / SELL. ticket is a string; price is the trigger (activation) level; limitPrice is set only on STOP_LIMIT variants; expirationTime is null for good-till-cancelled.\n- Expiry is READ-ONLY today. Every pending order placed through this API is good-till-cancelled, so expirationTime is null on anything you placed here; a non-null value means the order was created elsewhere (your terminal) with an expiry. There is no expiry field on POST /v1/accounts/:id/orders or PATCH /v1/accounts/:id/orders/:ticket — expiry is not accepted and therefore never silently dropped. Cancel explicitly with DELETE /v1/accounts/:id/orders/:ticket when you need a time-bounded order.\n- This combined type (e.g. BUY_STOP) is READ-ONLY. To place or modify an order, use the split form — type (market/limit/stop) + side (BUY/SELL) — from POST /v1/accounts/:id/orders; the write API does not accept BUY_STOP-style names.\n- For live updates instead of polling, subscribe to the orders channel over the WebSocket (see the WebSocket section) — it pushes the full pending-order list on subscribe and on every change.\n- Optional ?waitMs=<0–10000> adds a settle window for a just-warmed connection’s first pending snapshot; omit for an immediate read.\n- Also reachable at the alias GET /v1/accounts/:id/orders/pending.\n\n**Example response** — 200 OK\n```json\n{\n  \"orders\": [\n    {\n      \"ticket\": \"4072809988\",\n      \"symbol\": \"BTCUSD\",\n      \"type\": \"BUY_LIMIT\",\n      \"side\": \"BUY\",\n      \"orderType\": \"LIMIT\",\n      \"volume\": 0.10,\n      \"price\": 68000,\n      \"limitPrice\": null,\n      \"stopLoss\": 66000,\n      \"takeProfit\": 72000,\n      \"setTime\": \"2026-08-14T09:12:44.000Z\",\n      \"expirationTime\": null,\n      \"comment\": \"\",\n      \"magic\": 0\n    }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/orders",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_delete_v1_accounts_id_orders_ticket",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "Cancel a pending order",
      "description": "Cancel a resting pending order (LIMIT or STOP) by its ticket.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n- `ticket` (number) — required — Ticket of the pending order to cancel (from GET /v1/accounts/:id/orders).\n\n**Notes**\n- Requires an Idempotency-Key header.\n- ticket is the pending order’s ticket from GET /v1/accounts/:id/orders — not a position. If the order has already triggered it is now an open position; close it with DELETE /v1/accounts/:id/positions/:ticket instead.\n- If the ticket is not a resting pending order on this account you get 404 TICKET_NOT_FOUND.\n- Works on MT4 and MT5 accounts. ticket is the number in the order’s ticket field (a string in GET /v1/accounts/:id/orders, a number in the URL).\n\n**Example response** — 200 OK\n```json\n{\n  \"ticket\": 4072809988,\n  \"symbol\": \"BTCUSD\",\n  \"side\": \"BUY\",\n  \"cancelled\": true,\n  \"timestamp\": \"2026-08-14T09:12:44.000Z\"\n}\n```",
      "method": "DELETE",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/orders/{{ _.ticket }}",
      "body": {},
      "parameters": [],
      "headers": [
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4002,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_accounts_id_orders_ticket",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "Modify a pending order",
      "description": "Modify a resting pending order’s trigger price, stop-loss or take-profit. Any field you omit is preserved at its current value.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n- `ticket` (number) — required — Ticket of the pending order to modify (from GET /v1/accounts/:id/orders).\n\n**Body fields**\n- `price` (number) — New trigger (activation) price. Omit to keep the current trigger.\n- `stopLoss` (number) — New stop-loss. Send 0 to REMOVE it. Omit to keep the current SL.\n- `takeProfit` (number) — New take-profit. Send 0 to REMOVE it. Omit to keep the current TP.\n\n**Notes**\n- Requires an Idempotency-Key header.\n- Provide at least one of price, stopLoss or takeProfit — an empty body is a 400. Omitted fields keep their current value.\n- If the ticket is not a resting pending order on this account you get 404 TICKET_NOT_FOUND.\n- As on an open position, send stopLoss or takeProfit as 0 to REMOVE that level rather than move it; omitting the field keeps it. The trigger price is different — price must be a positive number, since a resting order has nowhere to trigger without one.\n- This call changes the trigger price, SL and TP only — it cannot set or remove an expiry. See GET /v1/accounts/:id/orders on expiry being read-only.\n\n**Example response** — 200 OK\n```json\n{\n  \"ticket\": 4072809988,\n  \"symbol\": \"BTCUSD\",\n  \"side\": \"BUY\",\n  \"price\": 66000,\n  \"stopLoss\": 64000,\n  \"takeProfit\": 72000,\n  \"timestamp\": \"2026-08-14T09:12:44.000Z\"\n}\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/orders/{{ _.ticket }}",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"stopLoss\": 71000,\n  \"takeProfit\": 84000\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4003,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_accounts_id_orders_cancel_all",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "POST /v1/accounts/:id/orders/cancel-all",
      "description": "Cancel every resting pending order on the account in one call, optionally narrowed by symbol, side and/or type. The server lists the pending orders once and cancels the matches in parallel.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Body fields**\n- `symbol` (string) — Only cancel pending orders on this broker-native symbol (e.g. BTCUSDm). Omit to cancel on every symbol. Case-insensitive.\n- `side` (string) — Only cancel BUY or SELL pending orders. Omit for both sides.\n- `type` (string) — Only cancel this order type — limit, stop or stopLimit. Omit for all types.\n\n**Notes**\n- Requires an Idempotency-Key header.\n- Body (all fields optional): { \"symbol\": \"EURUSD\", \"side\": \"BUY\", \"type\": \"limit\" }. type is one of limit / stop / stopLimit; side is BUY / SELL.\n- Single-account cancel — for a ticket you already hold use DELETE /v1/accounts/:id/orders/:ticket; to cancel across many accounts at once use POST /v1/bulk/orders/cancel.\n- Always 200. No matching pending orders is an empty success — results: [] with summary { requested: 0, ok: 0, failed: 0 } — never a 404 or an error.\n- Partial success — each order’s outcome is in results: status \"ok\" (cancelled true) or \"failed\" (with a code and reason). summary tallies requested / ok / failed.\n- Works on MT4 and MT5 accounts.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"ticket\": 4072809988, \"symbol\": \"BTCUSD\", \"side\": \"BUY\", \"status\": \"ok\", \"cancelled\": true },\n    { \"ticket\": 4072809991, \"symbol\": \"BTCUSD\", \"side\": \"SELL\", \"status\": \"failed\", \"code\": \"BROKER_REJECTED\", \"reason\": \"Broker rejected the cancel.\" }\n  ],\n  \"summary\": { \"requested\": 2, \"ok\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/orders/cancel-all",
      "body": {},
      "parameters": [],
      "headers": [
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4004,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_delete_v1_accounts_id_positions_ticket",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "Close a position",
      "description": "Close an open position. Omit volume for a full close, or pass a smaller volume for a partial close.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n- `ticket` (number) — required — Ticket of the position to close (from the order response or GET /v1/accounts/:id).\n\n**Body fields**\n- `volume` (number) — Partial-close volume in lots. If omitted, the whole position is closed.\n\n**Notes**\n- Requires an Idempotency-Key header. Re-using a key with a different body or ticket is a 409 IDEMPOTENCY_KEY_REUSED — nothing is replayed or executed.\n- If the ticket is not an open position on this account you get 404 TICKET_NOT_FOUND.\n- closePrice, dealTicket, profit, swap and commission are the booked closing deal — the same values the OUT row in /deals-history carries (dealTicket is its ticket there; money in the account currency). They are null when the broker has not booked the deal by the time the response is sent (rare — read /deals-history then); 0 is a real 0. A partial close reports the deal for the closed part.\n- On MT4, closePrice, profit, swap and commission come from the broker’s record of the closed trade (profit is before swap and commission). dealTicket is always null — an MT4 trade has one ticket, the one in the URL. If the broker’s record does not arrive within about 2.5 seconds, those four are null; GET /v1/accounts/:id/history lists the closed trade once it has.\n- On MT4 a PARTIAL close ends the ticket you closed: the part still open continues under a NEW ticket. The response then carries remainderTicket — that new ticket, or null when it is not known yet (read GET /v1/accounts/:id/positions; the broker marks it with the comment \"from #<old ticket>\"). The field is absent on a full close and on MT5, where a position keeps its ticket.\n- volume is what was actually closed. Asking for more than is open closes the whole position (the contract is unchanged) and the response then also carries requestedVolume, warningCode \"VOLUME_CLAMPED\" and a warning message; these three fields are absent otherwise.\n\n**Example response** — 200 OK\n```json\n{\n  \"ticket\": 4072808150,\n  \"symbol\": \"BTCUSD\",\n  \"side\": \"BUY\",\n  \"volume\": 0.10,\n  \"closed\": true,\n  \"timestamp\": \"2026-05-22T18:10:44.000Z\",\n  \"closePrice\": 81320.15,\n  \"dealTicket\": \"1191098644\",\n  \"profit\": -0.40,\n  \"swap\": 0,\n  \"commission\": 0\n}\n```",
      "method": "DELETE",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/positions/{{ _.ticket }}",
      "body": {},
      "parameters": [],
      "headers": [
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4005,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_accounts_id_positions_close_all",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "POST /v1/accounts/:id/positions/close-all",
      "description": "Flatten the account — close every open position in one call, optionally narrowed by symbol and/or side. The server reads the account’s open positions once and closes the matches in parallel.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Body fields**\n- `symbol` (string) — Only close positions on this broker-native symbol (e.g. BTCUSDm). Omit to close on every symbol. Case-insensitive.\n- `side` (string) — Only close BUY or SELL positions. Omit to close both sides.\n\n**Notes**\n- Requires an Idempotency-Key header.\n- Body (all fields optional): { \"symbol\": \"EURUSD\", \"side\": \"BUY\" }. side is BUY / SELL.\n- Each \"ok\" result carries the booked closing deal (closePrice, dealTicket, profit, swap, commission), null when not yet booked — the same fields as a single DELETE.\n- Single-account flatten — for a ticket you already hold use DELETE /v1/accounts/:id/positions/:ticket; to flatten across many accounts at once use POST /v1/bulk/positions/close.\n- Always 200. No matching positions is an empty success — results: [] with summary { requested: 0, ok: 0, failed: 0 } — never a 404 or an error.\n- Partial success — each position’s outcome is in results: status \"ok\" (closed true) or \"failed\" (with a code and reason). summary tallies requested / ok / failed.\n- Each \"ok\" result carries the booked close deal (closePrice, dealTicket, profit, swap, commission), as on the single close; null until the broker books it.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"ticket\": 4072808150, \"symbol\": \"BTCUSD\", \"side\": \"BUY\", \"status\": \"ok\", \"closed\": true, \"closePrice\": 81320.15, \"dealTicket\": \"1191098644\", \"profit\": -0.40, \"swap\": 0, \"commission\": 0 },\n    { \"ticket\": 4072809002, \"symbol\": \"BTCUSD\", \"side\": \"SELL\", \"status\": \"failed\", \"code\": \"BROKER_REJECTED\", \"reason\": \"Broker rejected the close.\" }\n  ],\n  \"summary\": { \"requested\": 2, \"ok\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/positions/close-all",
      "body": {},
      "parameters": [],
      "headers": [
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4006,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_accounts_id_positions_ticket",
      "_type": "request",
      "parentId": "fld_orders",
      "name": "Modify stop-loss / take-profit",
      "description": "Modify the stop-loss and/or take-profit of an open position, or remove one. Provide at least one of stopLoss / takeProfit; send 0 to clear a level rather than move it.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n- `ticket` (number) — required — Ticket of the position to modify.\n\n**Body fields**\n- `stopLoss` (number) — New stop-loss price. Send 0 to REMOVE the stop-loss. Omit to leave it unchanged.\n- `takeProfit` (number) — New take-profit price. Send 0 to REMOVE the take-profit. Omit to leave it unchanged.\n\n**Notes**\n- Requires an Idempotency-Key header.\n- You must supply at least one of stopLoss or takeProfit; sending neither is a VALIDATION_ERROR.\n- Some brokers enforce a minimum stop distance (stop level). Too-tight values come back as 422 BROKER_REJECTED. GET /v1/accounts/:id/symbol-specs reports that distance as stopLevel, in points, so you can check it before sending.\n- To CLEAR a protective level, send it as 0 — that removes it. 0 and \"omitted\" are different instructions: 0 means remove this level, omitting the field means leave it exactly as it is. So { \"stopLoss\": 0 } removes the stop-loss and keeps the take-profit untouched, and { \"stopLoss\": 0, \"takeProfit\": 0 } removes both. 0 also satisfies the at-least-one-field rule, so clearing is never an empty body.\n- The response and GET /v1/accounts/:id/positions both report a removed level as 0, not null — the same 0 you send. A position that never had an SL or TP reads 0 as well, so 0 consistently means \"no level\" in both directions.\n\n**Example response** — 200 OK\n```json\n{\n  \"ticket\": 4072808150,\n  \"symbol\": \"BTCUSD\",\n  \"side\": \"BUY\",\n  \"volume\": 0.10,\n  \"stopLoss\": 72000,\n  \"takeProfit\": 85000,\n  \"timestamp\": \"2026-05-22T18:12:09.000Z\"\n}\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/positions/{{ _.ticket }}",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"stopLoss\": 71000,\n  \"takeProfit\": 84000\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 4007,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_trade_history",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Trade history",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 5000
    },
    {
      "_id": "req_get_v1_accounts_id_history",
      "_type": "request",
      "parentId": "fld_trade_history",
      "name": "Closed-trade history",
      "description": "Closed-trade history for the account — executed trades paired into round-trips (entry + exit), the equivalent of MT5’s history_deals_get. Returns the recent window your broker provides on connect plus any trades closed live during the session. Filter by symbol and close-time range.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Query parameters**\n- `symbol` (string) — Narrow to one broker-native symbol, e.g. \"ETHUSD\". Omit for all symbols.\n- `from` (ISO-8601 | epoch seconds) — Only trades closed at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.\n- `to` (ISO-8601 | epoch seconds) — Only trades closed at/before this time.\n- `limit` (number) — Rows per page, newest-first. Defaults to 500, capped at 5000.\n- `cursor` (string) — The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.\n- `waitMs` (number) — How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.\n\n**Notes**\n- openTime / closeTime are UTC. brokerOpenTime / brokerCloseTime are the same instants in the broker’s server-local time (RFC 3339 with offset), and serverTimeOffsetSeconds is that offset, measured from the broker’s own records. null means it could not be derived yet (no deal or open position on the account); 0 means a UTC server.\n- Returns what the broker provides for the account on connect plus trades closed live during the current session — how far back that reaches is set by the broker. An account connected with terminalType \"CLIENT\" (desktop terminal) reaches back roughly 11 days. from/to filter those rows; they do not fetch beyond what the broker provides.\n- MT4 accounts: the list is the account’s closed trades for the whole account, on the first read after the account connects; a trade that closes while it is connected is in the list from that moment. An account with no closed trades answers 200 with an empty list. After a reconnect the list is again the whole account.\n- MT4 accounts: coverage: \"partial\" appears when the list is not known to be the whole account, with coverageReason saying why — \"history_still_loading\" in the first moments of a connection while the history is still arriving (read again shortly: the list is then either complete, with no coverage key, or \"history_load_failed\"), \"history_load_failed\" when the history could not be read in (the list holds what was read, possibly nothing; reconnect the account — an account with more than 299,593 closed trades and balance entries is beyond what can be loaded and stays in this state), or \"broker_session_window\" when history loading for MT4 accounts has been limited to a recent window (the list covers that window). Both keys are absent otherwise. Each row is one whole closed trade (complete: true); pending orders that were cancelled are not here — GET /v1/accounts/:id/orders-history lists them together with the closed trades. Differences from MT5 rows: closeTicket is null (an MT4 trade has one ticket), profit is before swap and commission (net = profit + swap + commission), and there are no partial rows — on MT4 a partial close is its own closed trade and the remainder continues under a new ticket.\n- MT4 only, while the account’s serverTimeOffsetSeconds is null (the first few seconds after connect on some brokers, or a connection made while the market is closed): openTime and closeTime are null on every row — never the broker’s clock labelled as UTC — and brokerOpenTime / brokerCloseTime carry the broker’s wall-clock time with no offset suffix, e.g. \"2026-11-12T18:06:12\". The response adds timeBasis: \"broker\" and a timeBasisNote, and the rows are ordered, paged and from / to-filtered on that broker clock meanwhile — so a date-filtered or paged read made then can differ from the same read made later. Once the offset is known (0 counts as known) openTime and closeTime are UTC, the twins carry their numeric offset (\"2026-11-12T18:06:12+02:00\") and timeBasis is absent; read again then.\n- Each row is a round-trip: ticket is the open/position ticket; closeTicket is the closing deal. On a row not yet matched to its closing deal (complete: false) closeTicket is null or repeats ticket. profit is realised P/L in the account currency.\n- stopLoss / takeProfit are the position’s stop-loss and take-profit levels when it closed — the levels on its closing deal (closeTicket). 0 means no level was set. The levels the position was opened with are on its opening (\"IN\") deal in GET /v1/accounts/:id/deals-history.\n- A position closed in several partial closes is ONE row here, and it carries the levels of its last closing deal; each partial close’s own levels are on its \"OUT\" row in GET /v1/accounts/:id/deals-history. While part of the position is still open (a partial row), the levels are those of the latest partial close.\n- Right after connecting, until the account’s deal history has loaded, stopLoss / takeProfit can still show their earlier value (the opening order’s level, or 0). A read waits for that load by default (waitMs), so you only see it if you skip the wait or it runs out.\n- Open positions are never listed here — they live under GET /v1/accounts/:id/positions. complete=true is a confirmed round-trip with trustworthy P/L; complete=false marks a closed row we could not pair with its opening deal, where profit is reported as null rather than a misleading 0.\n- A partially closed position that still has volume open is listed as one row with partial: true: volume is the closed volume so far, remainingVolume is the volume still open, profit/swap/commission are the broker-booked figures for the closed part, and complete is false. When the rest closes, that row is replaced by a single row with the same ticket, the full volume, complete: true and no partial or remainingVolume fields.\n- swap is provided: the swap the broker booked on the position, in the account currency (non-zero for positions held across a rollover). commission is the commission the broker charged on the round-trip (opening plus closing deal), in the account currency, stored as the broker books it: negative for a charge, 0 on account types that charge none. A trade opened and closed within the current session shows its commission within about 30 seconds of the close (the ledger is re-read on the next history read after a balance change)\n- A cheap read that never disturbs your live tick stream. On a connection that has cooled you get 409 BROKER_ACCOUNT_NOT_HOT — POST /v1/sessions to reconnect.\n- MT5 accounts: if the envelope carries coverage: \"partial\", the list is not the account’s whole history. coverageReason says why: \"history_still_loading\" (the account’s history has not finished loading on this connection — read again shortly: the list is then either whole, or \"history_load_failed\"), \"history_load_failed\" (it could not be loaded on this connection, or the broker did not answer the request for it within 30 seconds; it loads again when the account reconnects), or \"broker_session_window\" (the account is connected with terminalType \"CLIENT\": such a connection is given only a recent window of the account’s history, so the list is complete for that window and not for the account — connect it as \"MOBILE\" for the whole history). When the list is whole, coverage and coverageReason are absent. The same two keys and values apply to /deals-history, /orders-history and /balance-operations.\n\n**Example response** — 200 OK\n```json\n{\n  \"trades\": [\n    {\n      \"ticket\": \"4072808150\",\n      \"symbol\": \"ETHUSD\",\n      \"side\": \"BUY\",\n      \"volume\": 0.10,\n      \"openPrice\": 2500.50,\n      \"closePrice\": 2510.25,\n      \"openTime\": \"2026-05-20T10:00:00.000Z\",\n      \"closeTime\": \"2026-05-20T12:30:00.000Z\",\n      \"brokerOpenTime\": \"2026-05-20T13:00:00+03:00\",\n      \"brokerCloseTime\": \"2026-05-20T15:30:00+03:00\",\n      \"profit\": 0.98,\n      \"swap\": -0.12,\n      \"commission\": 0,\n      \"stopLoss\": 0,\n      \"takeProfit\": 0,\n      \"closeTicket\": \"4072808151\",\n      \"complete\": true\n    }\n  ],\n  \"count\": 1,\n  \"limit\": 500,\n  \"hasMore\": false,\n  \"nextCursor\": null,\n  \"serverTimeOffsetSeconds\": 10800\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/history",
      "body": {},
      "parameters": [
        {
          "name": "symbol",
          "value": "BTCUSD",
          "description": "Narrow to one broker-native symbol, e.g. \"ETHUSD\". Omit for all symbols.",
          "disabled": false
        },
        {
          "name": "from",
          "value": "2026-05-01",
          "description": "Only trades closed at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.",
          "disabled": false
        },
        {
          "name": "to",
          "value": "2026-05-29",
          "description": "Only trades closed at/before this time.",
          "disabled": false
        },
        {
          "name": "limit",
          "value": "50",
          "description": "Rows per page, newest-first. Defaults to 500, capped at 5000.",
          "disabled": false
        },
        {
          "name": "cursor",
          "value": "...",
          "description": "The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.",
          "disabled": false
        },
        {
          "name": "waitMs",
          "value": "4000",
          "description": "How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 5000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_deals_history",
      "_type": "request",
      "parentId": "fld_trade_history",
      "name": "Deal history (individual fills)",
      "description": "Individual deal fills for the account — one row per executed deal, the equivalent of MT5’s \"Deals\" view (while /history is the aggregated one-row-per-position \"Positions\" view). Each entry — the open, and every PARTIAL and full close — is its own row with its own ticket, time, price and volume. Filter by symbol and time range.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Query parameters**\n- `symbol` (string) — Narrow to one broker-native symbol, e.g. \"BTCUSDm\". Omit for all symbols.\n- `from` (ISO-8601 | epoch seconds) — Only deals executed at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.\n- `to` (ISO-8601 | epoch seconds) — Only deals executed at/before this time.\n- `limit` (number) — Rows per page, newest-first. Defaults to 500, capped at 5000.\n- `cursor` (string) — The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.\n- `waitMs` (number) — How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.\n\n**Notes**\n- One row per DEAL (fill), not per position: a position opened and then closed in two parts is three rows — the open (entry \"IN\") and the two closing deals (entry \"OUT\"), each with its own ticket, time, price and volume. Use this when you need the individual exits; use GET /v1/accounts/:id/history for the aggregated one-row-per-position view (an MT5 terminal shows both the same way, as \"Deals\" and \"Positions\").\n- entry is the deal’s role: \"IN\" opened/added, \"OUT\" closed/reduced, \"INOUT\" a reversal, \"OUT_BY\" closed by an opposing position. positionId ties every deal to the position it belongs to; ticket is the deal’s own id.\n- stopLoss / takeProfit are the stop-loss and take-profit levels on the deal (MT5’s DEAL_SL / DEAL_TP). On an \"IN\" deal they are the levels the position was opened with; on an \"OUT\" deal they are the position’s levels when that deal closed it. So a stop that was moved shows its original value on the opening deal and the moved value on the closing deal. 0 means no level was set.\n- profit / swap / commission are the broker-booked realised figures on a CLOSE (\"OUT\") fill, in the account currency — so a partial exit shows its own exact realised P/L. They are ABSENT (not 0) on an \"IN\" / open fill, which books no P/L — treat absent as \"not booked\", never as 0.\n- time is UTC; brokerTime is the same instant in the broker’s server-local time (RFC 3339 with offset), and serverTimeOffsetSeconds is that offset — null if not derivable yet, 0 for a UTC server.\n- Trade fills only — deposits, withdrawals and other balance-ledger entries are NOT here; they live on GET /v1/accounts/:id/balance-operations.\n- MT5 only. MT4 keeps one record per closed trade, not one per fill, so an MT4 account answers 501 MT4_DEALS_HISTORY_UNAVAILABLE — use GET /v1/accounts/:id/history there.\n- Paged: while hasMore is true, call again with cursor=nextCursor (same filters) for the next older page; every row comes back exactly once, in order. nextCursor is null on the last page.\n- MT5 accounts: if the envelope carries coverage: \"partial\", the list is not the account’s whole history: retry shortly. coverageReason says why (the values are listed under GET /v1/accounts/:id/history).\n\n**Example response** — 200 OK\n```json\n{\n  \"deals\": [\n    {\n      \"ticket\": \"1952757330\",\n      \"positionId\": \"2075644599\",\n      \"symbol\": \"BTCUSDm\",\n      \"side\": \"SELL\",\n      \"entry\": \"OUT\",\n      \"volume\": 0.05,\n      \"price\": 81390.12,\n      \"stopLoss\": 81400,\n      \"takeProfit\": 83000,\n      \"profit\": -9.27,\n      \"swap\": 0,\n      \"commission\": -0.40,\n      \"time\": \"2026-10-18T14:21:10.000Z\",\n      \"brokerTime\": \"2026-10-18T17:21:10+03:00\"\n    }\n  ],\n  \"count\": 1,\n  \"limit\": 500,\n  \"hasMore\": false,\n  \"nextCursor\": null,\n  \"serverTimeOffsetSeconds\": 10800\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/deals-history",
      "body": {},
      "parameters": [
        {
          "name": "symbol",
          "value": "BTCUSD",
          "description": "Narrow to one broker-native symbol, e.g. \"BTCUSDm\". Omit for all symbols.",
          "disabled": false
        },
        {
          "name": "from",
          "value": "2026-05-01",
          "description": "Only deals executed at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.",
          "disabled": false
        },
        {
          "name": "to",
          "value": "2026-05-29",
          "description": "Only deals executed at/before this time.",
          "disabled": false
        },
        {
          "name": "limit",
          "value": "50",
          "description": "Rows per page, newest-first. Defaults to 500, capped at 5000.",
          "disabled": false
        },
        {
          "name": "cursor",
          "value": "...",
          "description": "The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.",
          "disabled": false
        },
        {
          "name": "waitMs",
          "value": "4000",
          "description": "How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 5001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_orders_history",
      "_type": "request",
      "parentId": "fld_trade_history",
      "name": "Order history (one row per order)",
      "description": "Historical ORDERS for the account — one row per order that reached a final state (filled, cancelled, rejected, expired), the equivalent of MT5’s history orders. Distinct from /deals-history (the individual fills) and /history (the aggregated round-trips): this is the order-lifecycle view. On MT4 it lists each closed trade as one row plus the cancelled pending orders. Filter by symbol and time range.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Query parameters**\n- `symbol` (string) — Narrow to one broker-native symbol, e.g. \"BTCUSDm\". Omit for all symbols.\n- `from` (ISO-8601 | epoch seconds) — Only orders at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.\n- `to` (ISO-8601 | epoch seconds) — Only orders at/before this time.\n- `limit` (number) — Rows per page, newest-first. Defaults to 500, capped at 5000.\n- `cursor` (string) — The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.\n- `waitMs` (number) — How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.\n\n**Notes**\n- One row per ORDER. orderTicket is the order’s own ticket (distinct from the position ticket); positionId links it to the position it opened or acted on. volume is the order’s volume and price its request / fill price.\n- On an account connected with the default terminalType (\"MOBILE\"), the first read after connecting returns the account’s full order history, back to its first order, including cancelled, rejected and expired orders. An account connected with terminalType \"CLIENT\" (desktop terminal) lists roughly the last 11 days. from / to narrow the list; without them you page through all of it, newest first.\n- state is how the order ended: \"FILLED\", \"CANCELED\", \"REJECTED\", \"EXPIRED\" or \"PARTIAL\". Each is the broker’s own verdict: a rejected order is \"REJECTED\" and an expired one \"EXPIRED\", never \"CANCELED\". \"PARTIAL\" is an order that filled in part and whose record still says so; it always has dealCount above 0. An order that filled in part and was then cancelled or expired reads \"CANCELED\" / \"EXPIRED\" with its fills (volume is the filled volume, dealCount above 0). \"UNKNOWN\" is reserved for an order whose state could not be read; treat it as not filled.\n- An order that never filled has positionId \"0\" and dealCount 0. dealCount is how many deal fills the order produced (a market order is normally 1).\n- fillingType is the order’s filling type, by MT5’s names: \"FOK\", \"IOC\", \"RETURN\" or \"BOC\". It is a separate fact from state — an order placed with \"RETURN\" can end filled, cancelled, rejected or expired. It is set on cancelled, rejected and expired orders and null on orders that have fills.\n- time is UTC; brokerTime is the same instant in the broker’s server-local time (RFC 3339 with offset), and serverTimeOffsetSeconds is that offset — null if not derivable yet, 0 for a UTC server.\n- For the individual fills an order produced, use GET /v1/accounts/:id/deals-history (join on positionId); for the aggregated round-trip with realised P/L, GET /v1/accounts/:id/history. Trade orders only — no balance-ledger entries.\n- MT4 accounts: the list holds each closed trade as ONE row and each pending order that left the book without filling. An MT4 trade is one record from open to close, so its row is state \"FILLED\" with orderTicket = the trade’s ticket (positionId is the same ticket, and so is ticket on /history), type \"BUY\" or \"SELL\", price = the open price, and the close on the same row: closePrice, closeTime, profit, swap and commission, plus openTime, stopLoss, takeProfit, comment and magic as on /history. A pending order that left the book without filling is state \"CANCELED\" — on MT4 the state is only ever \"FILLED\" or \"CANCELED\": a pending order that expired also reads \"CANCELED\", because MT4 does not tell an expiry from a cancel (its expirationTime is on the row). A pending order that triggers is not a cancelled row: it becomes a position, and once that position closes it is one \"FILLED\" row. A cancelled row has type \"BUY_LIMIT\", \"SELL_LIMIT\", \"BUY_STOP\" or \"SELL_STOP\", price = the order’s price, with setTime (when it was placed), expirationTime (null = none), stopLoss, takeProfit, comment and magic; it never filled, so positionId is \"0\" and dealCount 0. time is when the row became history — the trade’s close, the order’s removal — and it is what from / to and the newest-first order use. fillingType is null on MT4 rows, and dealCount is null on a closed trade (MT4 has no deal records). Every time field has a broker-clock twin (brokerTime, brokerOpenTime, brokerCloseTime, brokerSetTime, brokerExpirationTime). Open positions and resting pending orders are not history and are not listed (GET /positions, GET /orders). On MT4 this list is more complete than /history, which lists closed trades only; /deals-history is not available on MT4.\n- MT4 accounts: coverage / coverageReason, serverTimeOffsetSeconds and timeBasis behave exactly as on GET /v1/accounts/:id/history. While serverTimeOffsetSeconds is null, every UTC time a row has (time, openTime, closeTime, setTime, expirationTime) is null and its broker* twin carries the broker’s wall-clock time with no offset suffix, e.g. \"2026-11-12T18:06:12\"; once it is known the UTC fields are filled and the twins carry their numeric offset. A cancel is in the list on the first read after it, and the list is the same after a reconnect. An MT4 account whose connection cannot list its cancelled pending orders answers 501 MT4_ORDERS_HISTORY_UNAVAILABLE instead of an incomplete list.\n- Paged: while hasMore is true, call again with cursor=nextCursor (same filters) for the next older page; every row comes back exactly once, in order. nextCursor is null on the last page.\n- MT5 accounts: if the envelope carries coverage: \"partial\", the list is not the account’s whole history: retry shortly. coverageReason says why (the values are listed under GET /v1/accounts/:id/history).\n\n**Example response** — 200 OK\n```json\n{\n  \"orders\": [\n    {\n      \"orderTicket\": \"2075644598\",\n      \"positionId\": \"2075644599\",\n      \"symbol\": \"BTCUSDm\",\n      \"side\": \"BUY\",\n      \"volume\": 0.10,\n      \"price\": 81555.00,\n      \"state\": \"FILLED\",\n      \"fillingType\": null,\n      \"dealCount\": 1,\n      \"time\": \"2026-10-18T14:20:02.000Z\",\n      \"brokerTime\": \"2026-10-18T17:20:02+03:00\"\n    },\n    {\n      \"orderTicket\": \"2075601177\",\n      \"positionId\": \"0\",\n      \"symbol\": \"BTCUSDm\",\n      \"side\": \"BUY\",\n      \"volume\": 0.10,\n      \"price\": 79000.00,\n      \"state\": \"CANCELED\",\n      \"fillingType\": \"RETURN\",\n      \"dealCount\": 0,\n      \"time\": \"2026-10-09T08:02:41.000Z\",\n      \"brokerTime\": \"2026-10-09T11:02:41+03:00\"\n    }\n  ],\n  \"count\": 2,\n  \"limit\": 500,\n  \"hasMore\": false,\n  \"nextCursor\": null,\n  \"serverTimeOffsetSeconds\": 10800\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/orders-history",
      "body": {},
      "parameters": [
        {
          "name": "symbol",
          "value": "BTCUSD",
          "description": "Narrow to one broker-native symbol, e.g. \"BTCUSDm\". Omit for all symbols.",
          "disabled": false
        },
        {
          "name": "from",
          "value": "2026-05-01",
          "description": "Only orders at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.",
          "disabled": false
        },
        {
          "name": "to",
          "value": "2026-05-29",
          "description": "Only orders at/before this time.",
          "disabled": false
        },
        {
          "name": "limit",
          "value": "50",
          "description": "Rows per page, newest-first. Defaults to 500, capped at 5000.",
          "disabled": false
        },
        {
          "name": "cursor",
          "value": "...",
          "description": "The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.",
          "disabled": false
        },
        {
          "name": "waitMs",
          "value": "4000",
          "description": "How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 5002,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_accounts_id_balance_operations",
      "_type": "request",
      "parentId": "fld_trade_history",
      "name": "Balance operations (deposits / withdrawals)",
      "description": "Balance operations for the account — money moving in or out (deposits, withdrawals, credits, charges, corrections, bonuses), kept separate from trades so a deposit is never summed as P/L. The equivalent of MT5’s DEAL_TYPE_BALANCE family. Newest-first, like every history route — reverse it to fold into an equity curve.\n\n**Path parameters**\n- `id` (string) — required — accountId.\n\n**Query parameters**\n- `from` (ISO-8601 | epoch seconds) — Only operations at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.\n- `to` (ISO-8601 | epoch seconds) — Only operations at/before this time.\n- `limit` (number) — Rows per page, newest-first (limit=1 is the most recent operation). Defaults to 500, capped at 5000.\n- `cursor` (string) — The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.\n- `waitMs` (number) — How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.\n\n**Notes**\n- type is one of deposit, withdrawal, credit, charge, correction, bonus, commission, other. deposit / withdrawal are derived from the sign of amount; the rest map the broker’s own ledger kinds. dealType is the broker’s raw ENUM_DEAL_TYPE, passed through so an unrecognised kind is never silently flattened.\n- amount is signed and in the account currency, exactly as the broker booked it: positive = money in, negative = money out.\n- From v1.41.0 every row also carries comment, profit, symbol and positionId; an account on a dedicated environment gets them once that environment is updated. comment is the broker’s own text for the entry (for example \"Deposit\" or \"DV p=1234567890, XOP.NYSE\"); null when the broker wrote none. profit is the amount as the broker booked it and is always equal to amount; 0 is a real value.\n- symbol and positionId are filled only when the broker’s entry carries them, and brokers generally do not: a dividend or adjustment tied to a position usually names the position and instrument only in comment, so expect null on both. positionId is a string (ids can exceed 2^53) and is null, never \"0\", when the entry belongs to no position. Both are always null on an MT4 account.\n- Paged: while hasMore is true, call again with cursor=nextCursor (same filters) for the next older page; every row comes back exactly once, in order. nextCursor is null on the last page.\n- time is UTC; brokerTime is the same instant in the broker’s server-local time (RFC 3339 with offset), and serverTimeOffsetSeconds is that offset. null means it could not be derived yet (no deal or open position on the account); 0 means a UTC server.\n- MT4 accounts: the list is the account’s deposits, withdrawals and broker credit for the whole account, on the first read after the account connects; an entry booked while it is connected is in the list from that moment. An account with none answers 200 with an empty list.\n- MT4 accounts: coverage: \"partial\" appears when the list is not known to be the whole account, with the same coverageReason values as GET /v1/accounts/:id/history (\"history_still_loading\", \"history_load_failed\", \"broker_session_window\"); both keys are absent otherwise. type is \"deposit\" or \"withdrawal\" by the sign of amount, or \"credit\" for a broker credit; dealType is null.\n- MT4 only, while the account’s serverTimeOffsetSeconds is null (the first few seconds after connect on some brokers, or a connection made while the market is closed): time is null on every row — never the broker’s clock labelled as UTC — and brokerTime carries the broker’s wall-clock time with no offset suffix, e.g. \"2026-11-12T18:06:12\". The response adds timeBasis: \"broker\" and a timeBasisNote, and the rows are ordered, paged and from / to-filtered on that broker clock meanwhile — so a date-filtered or paged read made then can differ from the same read made later. Once the offset is known (0 counts as known) time is UTC, brokerTime carries its numeric offset (\"2026-11-12T18:06:12+02:00\") and timeBasis is absent; read again then.\n- Returns what the broker provides for the account on connect — the same rule as /history. An empty list means no operations in what the broker provided, not that the account was never funded; treat the oldest row as a floor, not the account’s opening.\n- No symbol filter: balance operations are account-level. Realtime: the WebSocket account channel pushes an account_update on every balance change, including deposits and withdrawals — read this endpoint on that push to learn what moved the balance. A new operation is refreshed from the broker on the first read after the balance moved, so it is readable within seconds of the push.\n- A cheap read that never disturbs your live tick stream. On a connection that has cooled you get 409 BROKER_ACCOUNT_NOT_HOT — POST /v1/sessions to reconnect.\n- MT5 accounts: if the envelope carries coverage: \"partial\", the list is not the account’s whole history: retry shortly. coverageReason says why (the values are listed under GET /v1/accounts/:id/history).\n\n**Example response** — 200 OK\n```json\n{\n  \"operations\": [\n    {\n      \"ticket\": \"4138294581\",\n      \"type\": \"deposit\",\n      \"dealType\": 2,\n      \"amount\": 500,\n      \"profit\": 500,\n      \"symbol\": null,\n      \"positionId\": null,\n      \"time\": \"2026-09-12T15:44:08.000Z\",\n      \"brokerTime\": \"2026-09-12T18:44:08+03:00\",\n      \"account\": 434221754,\n      \"comment\": \"Deposit\"\n    }\n  ],\n  \"count\": 1,\n  \"limit\": 500,\n  \"hasMore\": false,\n  \"nextCursor\": null,\n  \"serverTimeOffsetSeconds\": 10800,\n  \"windowed\": true\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/accounts/{{ _.accountId }}/balance-operations",
      "body": {},
      "parameters": [
        {
          "name": "from",
          "value": "2026-05-01",
          "description": "Only operations at/after this time. A value that does not parse is 400 INVALID_DATE — including a digits-only value shorter than 9 digits such as 20261109 (write 2026-11-09); 0 means the Unix epoch.",
          "disabled": false
        },
        {
          "name": "to",
          "value": "2026-05-29",
          "description": "Only operations at/before this time.",
          "disabled": false
        },
        {
          "name": "limit",
          "value": "50",
          "description": "Rows per page, newest-first (limit=1 is the most recent operation). Defaults to 500, capped at 5000.",
          "disabled": false
        },
        {
          "name": "cursor",
          "value": "...",
          "description": "The nextCursor from the previous page, passed back unchanged to get the next (older) page. Omit for the newest page. A malformed cursor, or one from another endpoint, is 400 INVALID_CURSOR.",
          "disabled": false
        },
        {
          "name": "waitMs",
          "value": "4000",
          "description": "How long to wait (ms) for history to populate on a just-connected account. Defaults to 4000; pass 0 to skip the wait on a warm connection.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 5003,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_bulk",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Bulk",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 6000
    },
    {
      "_id": "req_post_v1_bulk_orders",
      "_type": "request",
      "parentId": "fld_bulk",
      "name": "Bulk place orders",
      "description": "Place orders across many of your accounts in one request — one call fans out to every account you name, and each order’s outcome is reported separately. An account can appear more than once, so one call can send several legs to the same account. Requires a Pro or Enterprise plan.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `orders` (object[]) — required — The orders to place — 1 to 50, one object per target: { accountId, type?, symbol, side, volume, price?, stopLoss?, takeProfit?, comment? }. accountId is the account to trade; type defaults to \"market\" (\"limit\"/\"stop\" need a price for a pending order); symbol, side and volume are required; stopLoss / takeProfit / comment are optional. Each entry mirrors the single-account POST /v1/accounts/:id/orders body.\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403.\n- Requires an Idempotency-Key header (see Conventions). Re-sending the same key replays the original response without placing any order a second time.\n- Partial success — a mix of filled and failed accounts still returns 200. Read each account’s outcome in results: status is \"filled\" (with its ticket and fill price) or \"failed\" (with a code and human-readable reason). summary tallies total / filled / failed.\n- Works on both live and demo accounts — there is no account-type restriction beyond the Pro / Enterprise plan requirement above.\n- Several legs on one account — repeat its accountId. results come back in the order you sent them. On one account, market orders run in parallel and pending orders are placed one after another.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"accountId\": \"acc_8Kd3...\", \"status\": \"filled\", \"ticket\": 4072808150, \"price\": 77512.73, \"symbol\": \"BTCUSD\" },\n    { \"accountId\": \"acc_9Lm4...\", \"status\": \"failed\", \"symbol\": \"BTCUSD\", \"code\": \"BROKER_REJECTED\", \"reason\": \"Not enough money\" }\n  ],\n  \"summary\": { \"total\": 2, \"filled\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/bulk/orders",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"orders\": [\n    {\n      \"accountId\": \"acc_8Kd3...\",\n      \"type\": \"market\",\n      \"symbol\": \"BTCUSD\",\n      \"side\": \"BUY\",\n      \"volume\": 0.1\n    },\n    {\n      \"accountId\": \"acc_9Lm4...\",\n      \"type\": \"market\",\n      \"symbol\": \"BTCUSD\",\n      \"side\": \"BUY\",\n      \"volume\": 0.1\n    }\n  ]\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 6000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_bulk_positions_close",
      "_type": "request",
      "parentId": "fld_bulk",
      "name": "Bulk close positions",
      "description": "Close positions across many accounts in one request — either an explicit list of positions, or a per-account intent that flattens each account (optionally narrowed by its own broker-native symbol or side). Requires a Pro or Enterprise plan.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `items` (object[]) — Explicit mode: the exact positions to close, one object each: { accountId, ticket, volume? }. Omit volume for a full close, or pass a smaller volume for a partial close. Use this OR the intent fields below — not both.\n- `targets` (object[]) — Intent mode: a per-account list, one object each: { accountId, symbol?, side? }. Every open position on that account is closed unless narrowed by its own symbol and/or side. Each target carries its own broker-native symbol, so a mixed-broker roster (Exness EURUSDm + XM EURUSD) works in one call. Use this OR items.\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403.\n- Requires an Idempotency-Key header. Re-sending the same key replays the original response without closing anything twice; re-using it with a different body is a 409 IDEMPOTENCY_KEY_REUSED.\n- Each \"ok\" result carries the booked closing deal (closePrice, dealTicket, profit, swap, commission), null when not yet booked.\n- Two modes: pass items for an explicit per-position list, OR targets for a per-account intent (each { accountId, symbol?, side? }, with its own broker-native symbol). Supplying neither is a 400.\n- Partial success — each position’s outcome is in results: status \"ok\" (closed true) or \"failed\" (with a code and reason). summary tallies total / ok / failed.\n- Each \"ok\" result carries the booked close deal (closePrice, dealTicket, profit, swap, commission), as on the single close; null until the broker books it.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"accountId\": \"acc_8Kd3...\", \"ticket\": 4072808150, \"status\": \"ok\", \"symbol\": \"BTCUSD\", \"side\": \"BUY\", \"volume\": 0.10, \"closed\": true, \"closePrice\": 81320.15, \"dealTicket\": \"1191098644\", \"profit\": -0.40, \"swap\": 0, \"commission\": 0 },\n    { \"accountId\": \"acc_9Lm4...\", \"ticket\": 4072809002, \"status\": \"failed\", \"symbol\": \"BTCUSD\", \"side\": \"SELL\", \"volume\": 0.20, \"closed\": false, \"code\": \"TICKET_NOT_FOUND\", \"reason\": \"Position not open\" }\n  ],\n  \"summary\": { \"total\": 2, \"ok\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/bulk/positions/close",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"items\": [\n    {\n      \"accountId\": \"acc_8Kd3...\",\n      \"ticket\": 4072808150\n    }\n  ]\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 6001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_bulk_positions_modify",
      "_type": "request",
      "parentId": "fld_bulk",
      "name": "Bulk modify positions",
      "description": "Modify the stop-loss and/or take-profit on positions across many accounts in one request. Requires a Pro or Enterprise plan.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `items` (object[]) — required — The positions to modify, one object each: { accountId, ticket, stopLoss?, takeProfit? }. Provide at least one of stopLoss / takeProfit per item; an omitted field keeps its current value.\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403.\n- Requires an Idempotency-Key header.\n- Each item must carry at least one of stopLoss / takeProfit; an omitted field is preserved at its current value, and a field sent as 0 REMOVES that level — the same meaning as on the single-account PATCH this fans out to. So { accountId, ticket, stopLoss: 0 } clears the stop on that position and leaves its take-profit alone.\n- Partial success — per-position outcome in results: status \"ok\" (modified true) or \"failed\" (with a code and reason). summary tallies total / ok / failed.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"accountId\": \"acc_8Kd3...\", \"ticket\": 4072808150, \"status\": \"ok\", \"symbol\": \"BTCUSD\", \"side\": \"BUY\", \"modified\": true },\n    { \"accountId\": \"acc_9Lm4...\", \"ticket\": 4072809002, \"status\": \"failed\", \"symbol\": \"BTCUSD\", \"side\": \"SELL\", \"modified\": false, \"code\": \"BROKER_REJECTED\", \"reason\": \"Invalid stops\" }\n  ],\n  \"summary\": { \"total\": 2, \"ok\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/bulk/positions/modify",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"items\": [\n    {\n      \"accountId\": \"acc_8Kd3...\",\n      \"ticket\": 4072808150\n    }\n  ]\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 6002,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_bulk_orders_cancel",
      "_type": "request",
      "parentId": "fld_bulk",
      "name": "Bulk cancel pending orders",
      "description": "Cancel resting pending orders (LIMIT / STOP) across many accounts in one request — an explicit list, or a per-account intent that cancels the pending orders on each account (optionally narrowed by its own broker-native symbol). Requires a Pro or Enterprise plan.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `items` (object[]) — Explicit mode: the pending orders to cancel, one object each: { accountId, ticket }. Use this OR the intent fields below.\n- `targets` (object[]) — Intent mode: a per-account list, one object each: { accountId, symbol? }. Cancels every resting pending order on that account, narrowed by its own broker-native symbol. Use this OR items.\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403.\n- Requires an Idempotency-Key header.\n- Two modes: items for an explicit list, OR targets for a per-account intent (each { accountId, symbol? }, with its own broker-native symbol). Supplying neither is a 400.\n- Partial success — per-order outcome in results: status \"ok\" (cancelled true) or \"failed\" (with a code and reason). summary tallies total / ok / failed.\n- Works on MT4 and MT5 accounts.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"accountId\": \"acc_8Kd3...\", \"ticket\": 4072809988, \"status\": \"ok\", \"cancelled\": true },\n    { \"accountId\": \"acc_9Lm4...\", \"ticket\": 4072809991, \"status\": \"failed\", \"cancelled\": false, \"code\": \"TICKET_NOT_FOUND\", \"reason\": \"Not a resting pending order\" }\n  ],\n  \"summary\": { \"total\": 2, \"ok\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/bulk/orders/cancel",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"items\": [\n    {\n      \"accountId\": \"acc_8Kd3...\",\n      \"ticket\": 4072808150\n    }\n  ]\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 6003,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_bulk_orders_modify",
      "_type": "request",
      "parentId": "fld_bulk",
      "name": "Bulk modify pending orders",
      "description": "Modify resting pending orders (trigger price, stop-loss, take-profit) across many accounts in one request. Requires a Pro or Enterprise plan.\n\nRequires an `Idempotency-Key` header — a unique string per logical action, so a retried request never double-fires.\n\n**Body fields**\n- `items` (object[]) — required — The pending orders to modify, one object each: { accountId, ticket, price?, stopLoss?, takeProfit? }. Provide at least one of price / stopLoss / takeProfit per item; an omitted field keeps its current value.\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403.\n- Requires an Idempotency-Key header.\n- Each item must carry at least one of price / stopLoss / takeProfit; an omitted field is preserved at its current value, and stopLoss or takeProfit sent as 0 REMOVES that level. price is the exception — it must be positive, since a resting order needs a trigger.\n- Partial success — per-order outcome in results: status \"ok\" (modified true) or \"failed\" (with a code and reason). summary tallies total / ok / failed.\n\n**Example response** — 200 OK\n```json\n{\n  \"results\": [\n    { \"accountId\": \"acc_8Kd3...\", \"ticket\": 4072809988, \"status\": \"ok\", \"modified\": true },\n    { \"accountId\": \"acc_9Lm4...\", \"ticket\": 4072809991, \"status\": \"failed\", \"modified\": false, \"code\": \"TICKET_NOT_FOUND\", \"reason\": \"Not a resting pending order\" }\n  ],\n  \"summary\": { \"total\": 2, \"ok\": 1, \"failed\": 1 }\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/bulk/orders/modify",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"items\": [\n    {\n      \"accountId\": \"acc_8Kd3...\",\n      \"ticket\": 4072808150\n    }\n  ]\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        },
        {
          "name": "Idempotency-Key",
          "value": "{% uuid 'v4' %}"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 6004,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_bulk_accounts",
      "_type": "request",
      "parentId": "fld_bulk",
      "name": "Bulk read accounts",
      "description": "Read live state for many accounts in one request — connection status, margin mode, read-only flag, account financials and open positions for your whole roster (or a subset) in a single call. Each account comes back as the same object GET /v1/accounts/:id returns. Requires a Pro or Enterprise plan.\n\n**Query parameters**\n- `ids` (string) — Comma-separated accountIds to read, e.g. \"acc_8Kd3...,acc_9Lm4...\". Omit to read every account on your key.\n- `include` (string) — Comma-separated list of what to include per account: \"account\", \"positions\". Defaults to both — e.g. include=account to skip positions and get a smaller response.\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403.\n- Not idempotent and takes no body — pass ids and include as query-string parameters.\n- One round-trip for your whole roster. Each element of accounts is the object GET /v1/accounts/:id would return for that account at that moment — the same fields, values and null rules — so you never need a per-account call to learn an account’s connection status, margin mode or read-only flag. include only removes the account and/or positions blocks.\n- status is the live connection status, with the same two values as on GET /v1/accounts/:id. \"online\": the account is connected and its live figures were read (its account and positions blocks are present, per include). \"offline\": the account is not connected (hot is false — POST /v1/sessions to reconnect), or it is connected but its live figures could not be read for this one response (hot is true — read again); the account/positions blocks are omitted. It is not the registration status that GET /v1/accounts lists (\"CONNECTED\" / \"PENDING\").\n- marginMode is the account’s margin model: \"netting\" (one aggregate position per symbol), \"hedging\" (independent positions per symbol) or \"exchange\". null means it is not known right now — the account is not connected, it connected only a few seconds ago, or it is an MT4 account.\n- readOnly is true when the broker reports the login as read-only (an investor password): reads work, and an order, close, modify or cancel answers 409 BROKER_READ_ONLY. false means full trading rights. null means not known — the account is not connected or connected only a few seconds ago; on MT4 it is true or null, and null there means \"not known to be read-only\".\n- credentialRejectedAt is null when nothing is wrong. A timestamp means the broker refused the account’s remembered password at that time: the row stays \"offline\" until one POST /v1/sessions with the current password, which sets the field back to null.\n- serverTimeOffsetSeconds is the broker’s server-local offset from UTC in seconds, as last measured on the account: 0 means a UTC server, null means not measured yet. group is the broker’s own account group, null only for an account that has never connected. Both are kept between connections, so an offline row still carries them.\n- ownerApiKeyId is the isolated API key the account belongs to; null means the shared pool. lastHotAt is when the account last became connected, null if it has not connected yet.\n- include controls the per-account payload: \"account\" adds the financials block, \"positions\" adds open positions; both are included by default. Drop one to make the response smaller.\n- Any ids you pass that are not on your key — or that belong to an account that was removed or refused — come back in notFound. summary tallies total / online / offline.\n\n**Example response** — 200 OK\n```json\n{\n  \"accounts\": [\n    {\n      \"id\": \"acc_8Kd3...\",\n      \"broker\": \"mt5\",\n      \"server\": \"Exness-MT5Trial14\",\n      \"accountNumber\": \"****5678\",\n      \"externalRef\": null,\n      \"isDemo\": true,\n      \"group\": \"demo\\\\Standard\",\n      \"serverTimeOffsetSeconds\": 0,\n      \"hot\": true,\n      \"poolId\": \"pool_3\",\n      \"ownerApiKeyId\": null,\n      \"readOnly\": false,\n      \"marginMode\": \"hedging\",\n      \"terminalType\": \"MOBILE\",\n      \"alwaysHot\": false,\n      \"credentialRejectedAt\": null,\n      \"lastHotAt\": \"2026-08-29T09:30:01.000Z\",\n      \"createdAt\": \"2026-08-20T09:11:55.000Z\",\n      \"status\": \"online\",\n      \"account\": { \"group\": \"demo\\\\Standard\", \"balance\": 9871.42, \"currency\": \"USD\", \"equity\": 9863.10, \"margin\": 142.50, \"freeMargin\": 9720.60, \"marginLevel\": 6921.5, \"credit\": null, \"leverage\": 500, \"serverTimeOffsetSeconds\": 0 },\n      \"positions\": [\n        { \"ticket\": 4072808150, \"symbol\": \"BTCUSD\", \"side\": \"BUY\", \"volume\": 0.10, \"entryPrice\": 77512.73, \"currentPrice\": 77640.10, \"profit\": 12.74, \"openTime\": \"2026-08-29T09:41:10.000Z\", \"brokerOpenTime\": \"2026-08-29T09:41:10+00:00\" }\n      ]\n    },\n    {\n      \"id\": \"acc_9Lm4...\",\n      \"broker\": \"mt5\",\n      \"server\": \"ICMarketsSC-Demo\",\n      \"accountNumber\": \"****3311\",\n      \"externalRef\": null,\n      \"isDemo\": true,\n      \"group\": \"demo\\\\Raw\",\n      \"serverTimeOffsetSeconds\": 7200,\n      \"hot\": false,\n      \"poolId\": null,\n      \"ownerApiKeyId\": null,\n      \"readOnly\": null,\n      \"marginMode\": null,\n      \"terminalType\": \"WEB\",\n      \"alwaysHot\": false,\n      \"credentialRejectedAt\": \"2026-08-29T08:05:44.000Z\",\n      \"lastHotAt\": \"2026-08-28T21:15:09.000Z\",\n      \"createdAt\": \"2026-08-21T14:02:10.000Z\",\n      \"status\": \"offline\"\n    }\n  ],\n  \"notFound\": [],\n  \"summary\": { \"total\": 2, \"online\": 1, \"offline\": 1 }\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/bulk/accounts",
      "body": {},
      "parameters": [
        {
          "name": "ids",
          "value": "acc_8Kd3...,acc_9Lm4...",
          "description": "Comma-separated accountIds to read, e.g. \"acc_8Kd3...,acc_9Lm4...\". Omit to read every account on your key.",
          "disabled": false
        },
        {
          "name": "include",
          "value": "account,positions",
          "description": "Comma-separated list of what to include per account: \"account\", \"positions\". Defaults to both — e.g. include=account to skip positions and get a smaller response.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 6005,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "fld_copy_trading",
      "_type": "request_group",
      "parentId": "wrk_tickerall",
      "name": "Copy Trading",
      "description": "",
      "environment": {},
      "environmentPropertyOrder": null,
      "metaSortKey": 7000
    },
    {
      "_id": "req_post_v1_copy_sets",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Create a copy set",
      "description": "Create a copy set — one master account whose trades are mirrored to your follower accounts, each scaled and risk-clamped to its own size. All accounts are your own. Requires a Pro or Enterprise plan.\n\n**Body fields**\n- `name` (string) — required — A label for the set.\n- `masterAccountId` (string) — required — The account whose trades are copied. Must be one of your own accounts (and never also a follower).\n- `failurePolicy` (string) — What to do when a follower copy fails: \"retry\" (a few backoff attempts, the default) or \"skip\".\n- `webhookUrl` (string) — Optional. A public https:// URL that receives an HMAC-signed POST for every mirror outcome (see \"Copy-event delivery\" below).\n- `webhookSecret` (string) — Optional HMAC-SHA256 signing key (16+ chars) for the X-Tickerall-Signature header. Write-only — never returned.\n- `followers` (object[]) — Optional followers to attach now, one object each: { followerAccountId, ...config }. Per-follower settings, all optional: sizingMethod (\"proportional\" — scale by the follower÷master equity ratio, the default | \"multiplier\" | \"fixed\" | \"risk_percent\") and sizingValue (the multiplier factor, fixed lots, or risk %); minLot / maxLot (clamp + snap the copied volume to the follower broker’s lot step); maxOpenTrades / maxExposureLots (caps); symbolAllow / symbolBlock (string arrays of follower symbols); dailyLossStop (auto-pause on loss); reverse (inverse copy — sell when the master buys); maxSlippagePips (skip if the follower price moved too far from the master fill); minMasterLot (ignore the master’s tiny trades); copyDelayMs; symbolOverrides (a { \"MASTERSYMBOL\": \"followerSymbol\" } map for cross-broker names the auto-normalizer can’t resolve).\n\n**Notes**\n- Requires a Pro or Enterprise plan — on the Free and Trader plans this call returns 403 (COPY_REQUIRES_PRO).\n- Self-copy only — the master and every follower must be your own accounts. A follower cannot also be the master.\n- A new set is created paused (enabled false). Arm it with PATCH /v1/copy/sets/:setId { \"enabled\": true } once you’re happy with the followers.\n- While live copying is being rolled out, mirrored trades run on demo followers; managing sets and reading stats works on all accounts.\n\n**Example response** — 201 Created\n```json\n{\n  \"id\": \"cset_7Hb2...\",\n  \"ownerId\": \"usr_9Kd3...\",\n  \"name\": \"My desk\",\n  \"masterAccountId\": \"acc_master\",\n  \"enabled\": false,\n  \"failurePolicy\": \"retry\",\n  \"webhookUrl\": null,\n  \"hasWebhookSecret\": false,\n  \"createdAt\": \"2026-08-29T12:00:00.000Z\",\n  \"updatedAt\": \"2026-08-29T12:00:00.000Z\",\n  \"followers\": [\n    { \"id\": \"cf_1\", \"followerAccountId\": \"acc_1\", \"sizingMethod\": \"proportional\", \"reverse\": false, \"enabled\": true }\n  ]\n}\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/copy/sets",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"name\": \"My desk\",\n  \"masterAccountId\": \"acc_master...\",\n  \"followers\": [\n    {\n      \"followerAccountId\": \"acc_1...\",\n      \"sizingMethod\": \"proportional\"\n    },\n    {\n      \"followerAccountId\": \"acc_2...\",\n      \"sizingMethod\": \"multiplier\",\n      \"sizingValue\": 0.5\n    }\n  ]\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7000,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_copy_sets",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "List copy sets",
      "description": "List your copy sets, each with a follower count. Requires a Pro or Enterprise plan.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n\n**Example response** — 200 OK\n```json\n{\n  \"sets\": [\n    { \"id\": \"cset_7Hb2...\", \"name\": \"My desk\", \"masterAccountId\": \"acc_master\", \"enabled\": true, \"failurePolicy\": \"retry\", \"_count\": { \"followers\": 3 }, \"createdAt\": \"2026-08-29T12:00:00.000Z\", \"updatedAt\": \"2026-08-29T12:00:00.000Z\" }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/copy/sets",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7001,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_copy_sets_setid",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Get a copy set",
      "description": "Get one copy set with its full follower list + config. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- Returns 404 if the set is not yours.\n\n**Example response** — 200 OK\n```json\n{\n  \"id\": \"cset_7Hb2...\",\n  \"name\": \"My desk\",\n  \"masterAccountId\": \"acc_master\",\n  \"enabled\": true,\n  \"failurePolicy\": \"retry\",\n  \"followers\": [\n    { \"id\": \"cf_1\", \"followerAccountId\": \"acc_1\", \"sizingMethod\": \"multiplier\", \"sizingValue\": 0.5, \"maxLot\": 1, \"reverse\": false, \"enabled\": true }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7002,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_copy_sets_setid",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Update / arm / pause a set",
      "description": "Update a set — rename, change the failure policy, or arm / pause it. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n\n**Body fields**\n- `name` (string) — New label.\n- `failurePolicy` (string) — \"retry\" or \"skip\".\n- `enabled` (boolean) — Arm (true) — start mirroring — or pause (false). Pausing leaves existing follower positions untouched.\n- `webhookUrl` (string | null) — Set the copy-event webhook (public https:// URL), or null to clear it.\n- `webhookSecret` (string | null) — Set the HMAC signing key (16+ chars), or null to clear it. Write-only.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- Provide at least one field. Arming is just enabled: true.\n\n**Example response** — 200 OK\n```json\n{ \"id\": \"cset_7Hb2...\", \"name\": \"My desk\", \"enabled\": true, \"failurePolicy\": \"retry\", \"followers\": [ ... ] }\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7003,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_delete_v1_copy_sets_setid",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Delete a copy set",
      "description": "Delete a copy set (and its followers, position map, and log). Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- Open follower positions are NOT closed — deleting a set only stops future mirroring.",
      "method": "DELETE",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7004,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_post_v1_copy_sets_setid_followers",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Add a follower",
      "description": "Add a follower to a set. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n\n**Body fields**\n- `followerAccountId` (string) — required — The account to mirror to (one of your own; not the master, not already a follower).\n- `...config` (object) — Per-follower settings, all optional: sizingMethod (\"proportional\" — scale by the follower÷master equity ratio, the default | \"multiplier\" | \"fixed\" | \"risk_percent\") and sizingValue (the multiplier factor, fixed lots, or risk %); minLot / maxLot (clamp + snap the copied volume to the follower broker’s lot step); maxOpenTrades / maxExposureLots (caps); symbolAllow / symbolBlock (string arrays of follower symbols); dailyLossStop (auto-pause on loss); reverse (inverse copy — sell when the master buys); maxSlippagePips (skip if the follower price moved too far from the master fill); minMasterLot (ignore the master’s tiny trades); copyDelayMs; symbolOverrides (a { \"MASTERSYMBOL\": \"followerSymbol\" } map for cross-broker names the auto-normalizer can’t resolve).\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- The follower must be your own account, not the master, and not already in the set.\n\n**Example response** — 201 Created\n```json\n{ \"id\": \"cf_4\", \"copySetId\": \"cset_7Hb2...\", \"followerAccountId\": \"acc_4\", \"sizingMethod\": \"proportional\", \"reverse\": true, \"maxSlippagePips\": 3, \"enabled\": true }\n```",
      "method": "POST",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}/followers",
      "body": {
        "mimeType": "application/json",
        "text": "{\n  \"followerAccountId\": \"...\"\n}"
      },
      "parameters": [],
      "headers": [
        {
          "name": "Content-Type",
          "value": "application/json"
        }
      ],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7005,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_patch_v1_copy_sets_setid_followers_followerid",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Update a follower",
      "description": "Update a follower’s config — only the fields you send change. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n- `followerId` (string) — required — The follower id (cf_...), from the set’s followers list.\n\n**Body fields**\n- `...config` (object) — Any subset of Per-follower settings, all optional: sizingMethod (\"proportional\" — scale by the follower÷master equity ratio, the default | \"multiplier\" | \"fixed\" | \"risk_percent\") and sizingValue (the multiplier factor, fixed lots, or risk %); minLot / maxLot (clamp + snap the copied volume to the follower broker’s lot step); maxOpenTrades / maxExposureLots (caps); symbolAllow / symbolBlock (string arrays of follower symbols); dailyLossStop (auto-pause on loss); reverse (inverse copy — sell when the master buys); maxSlippagePips (skip if the follower price moved too far from the master fill); minMasterLot (ignore the master’s tiny trades); copyDelayMs; symbolOverrides (a { \"MASTERSYMBOL\": \"followerSymbol\" } map for cross-broker names the auto-normalizer can’t resolve). Send a field as null to clear it.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n\n**Example response** — 200 OK\n```json\n{ \"id\": \"cf_4\", \"copySetId\": \"cset_7Hb2...\", \"followerAccountId\": \"acc_4\", \"sizingMethod\": \"fixed\", \"sizingValue\": 0.02, \"maxLot\": 1 }\n```",
      "method": "PATCH",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}/followers/{{ _.followerId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7006,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_delete_v1_copy_sets_setid_followers_followerid",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Remove a follower",
      "description": "Remove a follower from a set. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n- `followerId` (string) — required — The follower id (cf_...).\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- Open positions the follower already holds are NOT closed.",
      "method": "DELETE",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}/followers/{{ _.followerId }}",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7007,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_copy_sets_setid_stats",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Copy set stats",
      "description": "Dashboard stats for a set — totals, replication rate, and per-follower rollups. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- Derived from the copy log: totals tally ok / skipped / failed; replicationRate is ok ÷ (ok + failed). Per follower: outcome counts, current open copied positions, and average fill latency.\n\n**Example response** — 200 OK\n```json\n{\n  \"set\": { \"id\": \"cset_7Hb2...\", \"name\": \"My desk\", \"enabled\": true, \"masterAccountId\": \"acc_master\", \"failurePolicy\": \"retry\", \"followerCount\": 3 },\n  \"totals\": { \"ok\": 128, \"skipped\": 4, \"failed\": 2, \"replicationRate\": 0.9846 },\n  \"byAction\": { \"open\": 66, \"close\": 60, \"modify\": 8 },\n  \"followers\": [\n    { \"followerAccountId\": \"acc_1\", \"enabled\": true, \"sizingMethod\": \"proportional\", \"ok\": 44, \"skipped\": 1, \"failed\": 0, \"openPositions\": 2, \"avgLatencyMs\": 138 }\n  ]\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}/stats",
      "body": {},
      "parameters": [],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7008,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "req_get_v1_copy_sets_setid_log",
      "_type": "request",
      "parentId": "fld_copy_trading",
      "name": "Copy set log",
      "description": "The copy log for a set — every mirrored action and its outcome, newest first. Requires a Pro or Enterprise plan.\n\n**Path parameters**\n- `setId` (string) — required — The copy set id.\n\n**Query parameters**\n- `limit` (number) — Query param. Page size, 1–200 (default 50).\n- `before` (string) — Query param. An ISO timestamp — return entries older than this. Use the response’s nextBefore to page.\n\n**Notes**\n- Requires a Pro or Enterprise plan.\n- Newest first. limit and before are query-string parameters; page by passing the previous response’s nextBefore back as before (null means the end).\n- result is \"ok\", \"skipped\" (with a reason — e.g. filtered by a rule), or \"failed\" (with a reason). action is open / close / partial_close / modify / pending_place / pending_cancel / pending_modify.\n\n**Example response** — 200 OK\n```json\n{\n  \"entries\": [\n    { \"id\": \"clg_9\", \"followerAccountId\": \"acc_1\", \"action\": \"open\", \"masterTicket\": \"4072808150\", \"followerTicket\": \"5510022931\", \"symbol\": \"EURUSDm\", \"mappedSymbol\": \"EURUSD\", \"volume\": 0.01, \"result\": \"ok\", \"latencyMs\": 132, \"createdAt\": \"2026-08-29T12:34:56.000Z\" },\n    { \"id\": \"clg_8\", \"followerAccountId\": \"acc_2\", \"action\": \"open\", \"symbol\": \"XAUUSDm\", \"volume\": null, \"result\": \"skipped\", \"reason\": \"XAUUSD is on the block list\", \"createdAt\": \"2026-08-29T12:34:56.000Z\" }\n  ],\n  \"nextBefore\": \"2026-08-29T12:34:56.000Z\"\n}\n```",
      "method": "GET",
      "url": "{{ _.baseUrl }}/v1/copy/sets/{{ _.setId }}/log",
      "body": {},
      "parameters": [
        {
          "name": "limit",
          "value": "50",
          "description": "Query param. Page size, 1–200 (default 50).",
          "disabled": false
        },
        {
          "name": "before",
          "value": "...",
          "description": "Query param. An ISO timestamp — return entries older than this. Use the response’s nextBefore to page.",
          "disabled": false
        }
      ],
      "headers": [],
      "authentication": {
        "type": "bearer",
        "token": "{{ _.apiKey }}"
      },
      "metaSortKey": 7009,
      "settingStoreCookies": true,
      "settingSendCookies": true,
      "settingDisableRenderRequestBody": false,
      "settingEncodeUrl": true,
      "settingRebuildPath": true,
      "settingFollowRedirects": "global"
    },
    {
      "_id": "ws_tickerall_stream",
      "_type": "websocket_request",
      "parentId": "wrk_tickerall",
      "name": "Stream (ticks / positions / account)",
      "description": "Realtime data over a single long-lived WebSocket.\n\nConnect to `{{ _.wsBaseUrl }}/v1/stream` and authenticate with the same bearer token — either as an `Authorization: Bearer <key>` header on the upgrade, or `?token={{ _.apiKey }}` in the URL where headers are awkward (browser clients).\n\nAfter connecting, send a subscribe frame listing the channels you want:\n```json\n{\n  \"type\": \"subscribe\",\n  \"channels\": [\n    {\n      \"kind\": \"ticks\",\n      \"accountId\": \"{{ _.accountId }}\",\n      \"symbols\": [\n        \"BTCUSD\",\n        \"ETHUSD\"\n      ]\n    },\n    {\n      \"kind\": \"positions\",\n      \"accountId\": \"{{ _.accountId }}\"\n    },\n    {\n      \"kind\": \"account\",\n      \"accountId\": \"{{ _.accountId }}\"\n    }\n  ],\n  \"correlationId\": \"sub-1\"\n}\n```\n\nThe server streams `tick`, `position_update`, and `account_update` frames, plus a `subscribed` acknowledgement. It also broadcasts `event` frames — service notices (maintenance, all-clears, news, changelog, announcements) sent to every connected client and replayed newest-first on connect, so a client joining mid-maintenance catches up; a client that does not handle them simply ignores them. Send `{ \"type\": \"ping\" }` to get a `pong` and keep the connection alive.",
      "url": "{{ _.wsBaseUrl }}/v1/stream?token={{ _.apiKey }}",
      "headers": [
        {
          "name": "Authorization",
          "value": "Bearer {{ _.apiKey }}"
        }
      ],
      "authentication": {},
      "parameters": [],
      "metaSortKey": 8000
    }
  ]
}