{"openapi":"3.1.0","info":{"title":"fxapis","version":"0.1.0","description":"fxapis is the MetaTrader 5 API. Connect an MT5 account once, then trade it, read its positions\nand its history over plain HTTP. Choose whether each account stays connected or comes online\nonly when you need it.\n\n### Conventions\n\n- Every successful response is `{ \"data\": … }`; every failure is `{ \"error\": { \"code\", \"message\" } }`.\n  Match on `code`, which is stable. `message` is written for a human and may change.\n- Every response carries `requestId`. Quote it in a support request and the entire call can be found.\n- Times are RFC 3339 in UTC. Money and prices are never floats in a request body.\n\n### Bringing an account online is not instant\n\nA broker login takes as long as the broker takes — usually seconds, occasionally much longer.\n`POST /accounts/{id}/warm` brings an account online and returns **202** with a `pollUrl` rather than\nholding the request open, so your client's timeout never becomes a question about someone else's\nnetwork. Poll `GET /accounts/{id}/status` until `state` is `ready`.\n\n### Credentials\n\nThe MT5 password you send is encrypted before it is stored, with a data key that is bound to that\none account, and is never returned by any endpoint, written to the account record or included in\na log line. We cannot show it back to you — if it is lost, it is reset at the broker."},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer fx_live_<id>_<secret>`.\n\nA key is shown once, at creation. We cannot show it again or recover it for you.\nKeys carry an environment (`live` or `test`) in the key itself, so a test key pasted into a\nproduction config fails immediately instead of at the worst possible moment.\n\nScopes are per key. A key without `accounts:write` can read accounts and nothing else."}},"schemas":{}},"paths":{"/healthz":{"get":{"summary":"Liveness","tags":["Health"],"description":"Says the API is up. Checks nothing else, and never returns anything but 200.","security":[],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}}}}}}}}},"/readyz":{"get":{"summary":"Readiness","tags":["Health"],"description":"Says the API can serve traffic right now, including storing and using account credentials. Returns 503 when it cannot.","security":[],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"checks":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"checks":{"type":"object","additionalProperties":{"type":"string"}}}}}}}}}},"/v1/accounts":{"get":{"summary":"List connected accounts","tags":["Accounts"],"description":"Every account in this workspace, newest first.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"],"description":"Yours, for telling accounts apart."},"login":{"type":"string","description":"The MT5 account number."},"server":{"type":"string","description":"The broker server name, exactly as MetaTrader shows it."},"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"]},"state":{"type":"string","enum":["created","provisioning","standby","starting","connecting","synchronizing","ready","executing","cooling","offline","degraded","reconnecting","invalid_credentials","trading_disabled","needs_2fa","needs_certificate","error"],"description":"Online: `ready` (can trade) and `executing` (an order is in flight). Connecting: `provisioning`, `starting`, `connecting`, `synchronizing`, `reconnecting` — poll until `ready`. Offline: `created`, `standby`, `cooling` (going offline) and `offline` (after a disconnect, send the account's credentials again to reconnect). `degraded`: online but misbehaving — restart the connection. Need a human: `invalid_credentials`, `needs_2fa`, `needs_certificate`, `trading_disabled`. `error`: see `stateDetail`."},"stateDetail":{"type":["null","string"],"description":"Why it is in that state, when there is more to say."},"stateChangedAt":{"type":"string","format":"date-time"},"currency":{"type":["null","string"]},"leverage":{"type":["null","integer"]},"marginMode":{"type":["null","string"]},"tradeAllowed":{"type":["null","boolean"]},"brokerName":{"type":["null","string"]},"tradingDisabled":{"type":"boolean","description":"Set by us or by the broker. No order is accepted while true."},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}},"post":{"summary":"Connect an MT5 account","tags":["Accounts"],"description":"Stores the account and encrypts its password. It does **not** log in: the account lands in `created`, and connects when you bring it online or when the first order arrives on a `warm_on_demand` account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["login","server","password"],"properties":{"platform":{"type":"string","enum":["mt5"],"default":"mt5"},"login":{"type":"string","pattern":"^\\d+$","minLength":1,"maxLength":32,"description":"The MT5 account number.","example":"26177561"},"server":{"type":"string","minLength":1,"maxLength":128,"description":"Broker server name, e.g. `ICMarkets-Demo`.","example":"VantageMarkets-Demo"},"password":{"type":"string","minLength":1,"maxLength":256,"description":"The MT5 password. Encrypted on arrival and never returned or logged. Send the *trading* password, not the investor password: an investor password can read the account but cannot place an order.","example":"your-mt5-trading-password"},"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"],"default":"cold","description":"`always_on`: the account stays connected. `warm_on_demand`: it connects on demand (the first order, or a prepare) and goes offline by itself when idle. `cold`: it connects only when you ask. Orders are fastest in that order; cost runs the other way."},"label":{"type":"string","maxLength":120,"example":"Demo — strategy A"}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"],"description":"Yours, for telling accounts apart."},"login":{"type":"string","description":"The MT5 account number."},"server":{"type":"string","description":"The broker server name, exactly as MetaTrader shows it."},"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"]},"state":{"type":"string","enum":["created","provisioning","standby","starting","connecting","synchronizing","ready","executing","cooling","offline","degraded","reconnecting","invalid_credentials","trading_disabled","needs_2fa","needs_certificate","error"],"description":"Online: `ready` (can trade) and `executing` (an order is in flight). Connecting: `provisioning`, `starting`, `connecting`, `synchronizing`, `reconnecting` — poll until `ready`. Offline: `created`, `standby`, `cooling` (going offline) and `offline` (after a disconnect, send the account's credentials again to reconnect). `degraded`: online but misbehaving — restart the connection. Need a human: `invalid_credentials`, `needs_2fa`, `needs_certificate`, `trading_disabled`. `error`: see `stateDetail`."},"stateDetail":{"type":["null","string"],"description":"Why it is in that state, when there is more to say."},"stateChangedAt":{"type":"string","format":"date-time"},"currency":{"type":["null","string"]},"leverage":{"type":["null","integer"]},"marginMode":{"type":["null","string"]},"tradeAllowed":{"type":["null","boolean"]},"brokerName":{"type":["null","string"]},"tradingDisabled":{"type":"boolean","description":"Set by us or by the broker. No order is accepted while true."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"402":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}":{"get":{"summary":"Fetch one account","tags":["Accounts"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"],"description":"Yours, for telling accounts apart."},"login":{"type":"string","description":"The MT5 account number."},"server":{"type":"string","description":"The broker server name, exactly as MetaTrader shows it."},"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"]},"state":{"type":"string","enum":["created","provisioning","standby","starting","connecting","synchronizing","ready","executing","cooling","offline","degraded","reconnecting","invalid_credentials","trading_disabled","needs_2fa","needs_certificate","error"],"description":"Online: `ready` (can trade) and `executing` (an order is in flight). Connecting: `provisioning`, `starting`, `connecting`, `synchronizing`, `reconnecting` — poll until `ready`. Offline: `created`, `standby`, `cooling` (going offline) and `offline` (after a disconnect, send the account's credentials again to reconnect). `degraded`: online but misbehaving — restart the connection. Need a human: `invalid_credentials`, `needs_2fa`, `needs_certificate`, `trading_disabled`. `error`: see `stateDetail`."},"stateDetail":{"type":["null","string"],"description":"Why it is in that state, when there is more to say."},"stateChangedAt":{"type":"string","format":"date-time"},"currency":{"type":["null","string"]},"leverage":{"type":["null","integer"]},"marginMode":{"type":["null","string"]},"tradeAllowed":{"type":["null","boolean"]},"brokerName":{"type":["null","string"]},"tradingDisabled":{"type":"boolean","description":"Set by us or by the broker. No order is accepted while true."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/status":{"get":{"summary":"Poll lifecycle state","tags":["Accounts"],"description":"The endpoint to poll after bringing an account online. Cheap enough to call every second or two; `state` reaches `ready` when the account is logged in and ready to trade.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"state":{"type":"string","enum":["created","provisioning","standby","starting","connecting","synchronizing","ready","executing","cooling","offline","degraded","reconnecting","invalid_credentials","trading_disabled","needs_2fa","needs_certificate","error"],"description":"Online: `ready` (can trade) and `executing` (an order is in flight). Connecting: `provisioning`, `starting`, `connecting`, `synchronizing`, `reconnecting` — poll until `ready`. Offline: `created`, `standby`, `cooling` (going offline) and `offline` (after a disconnect, send the account's credentials again to reconnect). `degraded`: online but misbehaving — restart the connection. Need a human: `invalid_credentials`, `needs_2fa`, `needs_certificate`, `trading_disabled`. `error`: see `stateDetail`."},"detail":{"type":["null","string"]},"since":{"type":"string","format":"date-time"},"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"]},"tradingDisabled":{"type":"boolean"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/warm":{"post":{"summary":"Bring an account online","tags":["Lifecycle"],"description":"Starts connecting the account and returns **202** immediately — the broker login is still in progress. Poll `pollUrl` until `state` is `ready`.\n\nCalling this for an account that is already online is harmless and returns `alreadyRunning: true`, so a client that is unsure can simply call it.\n\nReturns 409 when the account needs a human first (a wrong password, 2FA, a certificate, trading disabled at the broker). Retrying will not fix those, and repeated failed logins are how a broker locks an account.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"runtimeId":{"type":"string","format":"uuid"},"alreadyRunning":{"type":"boolean","description":"True when the account was already online or connecting; asking again is harmless."},"state":{"type":"string","enum":["created","provisioning","standby","starting","connecting","synchronizing","ready","executing","cooling","offline","degraded","reconnecting","invalid_credentials","trading_disabled","needs_2fa","needs_certificate","error"]},"pollUrl":{"type":"string","description":"Poll here until `state` is `ready`."}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/prepare":{"post":{"summary":"Bring several accounts online at once","tags":["Lifecycle"],"description":"Brings every account listed online and returns **202** with a result for each — the broker logins are still in progress. Built for a signal: call it when the signal goes out, and by the time people act on it their accounts are online, so the order goes straight through instead of waiting for a login.\n\nAn account already online is reported as `already_running`; one that needs a human first (`needs_operator`) is not retried. `warm_on_demand` accounts go offline again by themselves after the idle window with no orders.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accountIds"],"additionalProperties":false,"properties":{"accountIds":{"type":"array","minItems":1,"maxItems":200,"uniqueItems":true,"items":{"type":"string","format":"uuid"}}}}}}},"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","required":["accountId","result"],"properties":{"accountId":{"type":"string","format":"uuid"},"result":{"type":"string","enum":["starting","already_running","not_found","leased_elsewhere","needs_operator","no_secret","failed"],"description":"`starting`: connecting now. `already_running`: already online or connecting. `leased_elsewhere`: busy with another request; retry in a moment. `needs_operator`: needs a human first — a wrong login, 2FA or a certificate. `no_secret`: no credentials are stored; send them again. `failed`: could not be brought online; `message` says why."},"message":{"type":["null","string"]}}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/cool":{"post":{"summary":"Take an account offline","tags":["Lifecycle"],"description":"Takes the account offline and keeps it, with its stored password, in `standby` — ready to be brought online again. Open positions are untouched: a stop loss or take profit is held by the broker and keeps working while the account is offline.\n\nReturns 409 while an order is in flight. Going offline then would turn a known outcome into an unknown one, and a retry on top of that is a duplicate trade.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"stopped":{"type":"boolean","description":"False when the account was already offline."},"state":{"type":"string"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/restart":{"post":{"summary":"Restart the connection","tags":["Lifecycle"],"description":"For an account that is online but misbehaving — `degraded`, stale quotes, a stuck sync. Returns **202**; poll `pollUrl` until `state` is `ready`. Refused with 409 while an order is in flight, for the same reason as taking it offline.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"202":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"runtimeId":{"type":"string","format":"uuid"},"alreadyRunning":{"type":"boolean","description":"True when the account was already online or connecting; asking again is harmless."},"state":{"type":"string","enum":["created","provisioning","standby","starting","connecting","synchronizing","ready","executing","cooling","offline","degraded","reconnecting","invalid_credentials","trading_disabled","needs_2fa","needs_certificate","error"]},"pollUrl":{"type":"string","description":"Poll here until `state` is `ready`."}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/disconnect":{"post":{"summary":"Disconnect and erase the credential","tags":["Lifecycle"],"description":"Takes the account offline and **deletes the stored password**. The account record stays, with its history, in `offline`. Reconnecting means sending the password again.\n\nThis is the endpoint to call when a customer leaves. A disconnected account that still holds a usable password is a credential nobody is watching.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"state":{"type":"string"},"credentialsRemoved":{"type":"boolean"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/mode":{"post":{"summary":"Change how eagerly the account stays connected","tags":["Lifecycle"],"description":"`always_on`: stays connected. `warm_on_demand`: connects on demand (the first order, or a prepare) and goes offline by itself when idle. `cold`: connects only when you ask.\n\nTakes effect the next time the account is brought online, so the call stays fast and predictable. Switch to `always_on` and then bring the account online to apply it now.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mode"],"properties":{"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"]}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"mode":{"type":"string","enum":["always_on","warm_on_demand","cold"]},"restingState":{"type":"string","description":"Where the account sits when idle under this mode."}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"402":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/orders/market":{"post":{"summary":"Place a market order","tags":["Trading"],"description":"Sends a market order and waits for the broker's answer.\n\n**Send an `Idempotency-Key` header.** Without one, a request that times out leaves you unable to retry safely: you cannot tell whether the order was placed, and guessing wrong opens a second position. With one, retrying is free — the same key returns the same answer and never places a second order.\n\nThree failures are worth telling apart, and the response distinguishes them:\n\n- **`SEND_FAILED` (502)** — the order never reached the broker. `retryable: true`. Safe to resend.\n- **`ORDER_REJECTED` (422)** — the broker refused it, with a reason and its own return code. `retryable` says whether the reason was transient (a requote, a moved price) or permanent (insufficient margin, a closed market).\n- **`ORDER_UNRESOLVED` (502)** — we do not know. The order may be live at the broker. It is in `unknown` while we confirm the result with the broker. **Do not resend it**; poll the order instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["symbol","side","volume"],"properties":{"symbol":{"type":"string","minLength":1,"maxLength":32,"description":"As the broker names it, e.g. `EURUSD`.","example":"EURUSD"},"side":{"type":"string","enum":["buy","sell"]},"volume":{"type":"string","pattern":"^\\d+(\\.\\d{1,8})?$","description":"Lots, **as a string** — `\"0.10\"`, not `0.1`. A JSON number is a double, and a volume that arrives as 0.30000000000000004 is rejected by the broker.","example":"0.01"},"stopLoss":{"type":"string","description":"Absolute price, not a distance. Must be below entry for a buy.","example":"1.12900"},"takeProfit":{"type":"string","description":"Absolute price, not a distance. Must be above entry for a buy.","example":"1.14200"},"deviationPoints":{"type":"integer","minimum":0,"maximum":10000,"description":"Maximum slippage in points. Too small and a fast market returns a requote instead of a fill.","example":20},"comment":{"type":"string","maxLength":120,"description":"MT5 stores the first 31 characters.","example":"signal 42"},"clientOrderId":{"type":"string","maxLength":120,"description":"Your reference. Not required to be unique.","example":"sig-42-user-7"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":200},"example":"3f0c9a52-8a8e-4f7e-9b0e-6c1d2f5a7b10","in":"header","name":"idempotency-key","required":false,"description":"Any unique string, usually a UUID. A retry with the same key and body returns the first answer instead of placing a second order; the same key with a different body is refused."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"402":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/positions/{positionId}/close":{"post":{"summary":"Close a position","tags":["Trading"],"description":"Closes a position in whole, or partly if you send a `volume`.\n\n**Send an `Idempotency-Key`.** It matters more here than on an opening order: a retried close does not close twice, it opens an opposing position, and the account ends up with exposure nobody chose.\n\nThe three failure modes are the same as for an opening order, and `ORDER_UNRESOLVED` means the same thing — **do not resend**, poll the order. An unresolved close is the case where you might believe you are flat and not be.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"volume":{"type":"string","pattern":"^\\d+(\\.\\d{1,8})?$","description":"Lots to close, as a string. Omit to close the whole position.","example":"0.01"},"deviationPoints":{"type":"integer","minimum":0,"maximum":10000,"example":20},"comment":{"type":"string","maxLength":120,"example":"take profit early"},"clientOrderId":{"type":"string","maxLength":120,"example":"sig-42-user-7-close"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"positionId","required":true,"description":"The broker's position ticket."},{"schema":{"type":"string","maxLength":200},"example":"3f0c9a52-8a8e-4f7e-9b0e-6c1d2f5a7b10","in":"header","name":"idempotency-key","required":false,"description":"Any unique string, usually a UUID. A retry with the same key and body returns the first answer instead of placing a second order; the same key with a different body is refused."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/positions/{positionId}/modify":{"post":{"summary":"Move a position's stop loss or take profit","tags":["Trading"],"description":"**Send `null` to remove a level.** Omitting the field leaves it unchanged — an omitted field silently clearing a stop is how protection disappears without anybody choosing it.\n\nRecorded in the order history with intent `modify`, because widening a stop changes what the account risks and belongs beside the trade it protects.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stopLoss":{"type":["string","null"],"description":"Absolute price. **Send null to remove it** — omitting the field leaves it unchanged.","example":"1.13100"},"takeProfit":{"type":["string","null"],"description":"Absolute price. Send null to remove it.","example":"1.14500"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string"},"in":"path","name":"positionId","required":true,"description":"The broker's position ticket."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/orders/pending":{"post":{"summary":"Place a limit, stop or stop-limit order","tags":["Trading"],"description":"A success here means **accepted and waiting**, not filled — the broker answers `10008 PLACED` and the order sits in `accepted` until it triggers.\n\nA limit waits for a better price (buy below the market, sell above); a stop waits for a worse one and is how a breakout is traded (buy above, sell below). The wrong side is refused before anything is sent, with a message naming the order you probably meant — the broker's own `INVALID_PRICE` says nothing about which way round it should have been.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["symbol","side","kind","volume","price"],"properties":{"symbol":{"type":"string","minLength":1,"maxLength":32,"example":"EURUSD"},"side":{"type":"string","enum":["buy","sell"]},"kind":{"type":"string","enum":["limit","stop","stop_limit"],"description":"A **limit** waits for a better price: buy below the market, sell above it. A **stop** waits for a worse one and is how a breakout is traded: buy above, sell below. Getting these backwards is the commonest mistake here, so the wrong side is refused with a message saying which you meant."},"volume":{"type":"string","pattern":"^\\d+(\\.\\d{1,8})?$","example":"0.01"},"price":{"type":"string","description":"The price the order waits at.","example":"1.12800"},"stopLimitPrice":{"type":"string","description":"Required for stop_limit: the limit placed once the stop triggers.","example":"1.12780"},"stopLoss":{"type":"string","example":"1.12400"},"takeProfit":{"type":"string","example":"1.13600"},"expiresAt":{"type":"string","format":"date-time","description":"When the broker should drop it. Omit for good-till-cancelled."},"comment":{"type":"string","maxLength":120,"example":"breakout"},"clientOrderId":{"type":"string","maxLength":120,"example":"sig-43-user-7"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true},{"schema":{"type":"string","maxLength":200},"example":"3f0c9a52-8a8e-4f7e-9b0e-6c1d2f5a7b10","in":"header","name":"idempotency-key","required":false,"description":"Any unique string, usually a UUID. A retry with the same key and body returns the first answer instead of placing a second order; the same key with a different body is refused."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"402":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/orders/{id}/modify":{"post":{"summary":"Move a pending order's price or levels","tags":["Trading"],"description":"Keeps the order's ticket. Cancelling and re-placing loses its place in the broker's queue and leaves a window where the order does not exist at all — on a fast market that window is where the move happens.\n\nAn omitted field is left alone. Send `stopLoss: null` to clear a stop; omitting it keeps whatever is there.\n\nOnly an order in `working` can be moved. A `filled` order is a position — move its stop with `/positions/{id}/modify` instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"price":{"type":"string","description":"The new price the order waits at.","example":"1.12750"},"stopLoss":{"type":["string","null"],"description":"`null` clears it; omitting it keeps what is there.","example":"1.12350"},"takeProfit":{"type":["string","null"],"description":"`null` clears it; omitting it keeps what is there.","example":"1.13550"},"stopLimitPrice":{"type":"string","description":"For a stop-limit order.","example":"1.12730"},"expiresAt":{"type":"string","format":"date-time"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/orders/{id}/cancel":{"post":{"summary":"Cancel a pending order","tags":["Trading"],"description":"Removes a pending order that has not triggered. If it already has, the broker refuses with `10035` — at that point there is a position, not an order, and what you want is a close.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/calculate":{"post":{"summary":"Margin required, or profit at a price","tags":["Trading"],"description":"Opens nothing and is safe to call freely. This is how you find out an order will be refused for margin *before* sending it, rather than collecting a `10019` from the broker.\n\nThe account must be online: an offline account is refused with 409 `NO_RUNTIME`. A null `value` means the broker would not answer, which is not the same as zero.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","symbol","side","volume","price"],"properties":{"kind":{"type":"string","enum":["margin","profit"]},"symbol":{"type":"string","maxLength":32,"example":"EURUSD"},"side":{"type":"string","enum":["buy","sell"]},"volume":{"type":"string","pattern":"^\\d+(\\.\\d{1,8})?$","example":"0.10"},"price":{"type":"string","description":"The price the position opens at.","example":"1.13426"},"closePrice":{"type":"string","description":"Required for a profit calculation.","example":"1.13926"}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"kind":{"type":"string","enum":["margin","profit"]},"value":{"type":["null","string"],"description":"In the account's currency. Null means the broker would not answer — which is not the same as zero."}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/orders":{"get":{"summary":"List orders","tags":["History"],"description":"Every order this workspace has placed, newest first, across all accounts unless `accountId` narrows it. Filter by `state` to find what needs attention — `state=unknown` is the set whose result we are still confirming with the broker.\n\nPaginated by cursor rather than offset: an offset shifts as new orders arrive, so page two would silently skip rows.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"accountId","required":false,"description":"Narrow to one account."},{"schema":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"in":"query","name":"state","required":false,"description":"`unknown` is the set whose result we are still confirming with the broker."},{"schema":{"type":"string","maxLength":32},"in":"query","name":"symbol","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"since","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"until","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"The `nextCursor` from the previous page."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}},"page":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to continue."}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/orders/{id}":{"get":{"summary":"Fetch one order","tags":["History"],"description":"The authoritative record of one order: what was asked for, what the broker returned, and its own return code unmodified. Poll this after an `ORDER_UNRESOLVED` response — the order moves out of `unknown` as soon as we have confirmed the result with the broker.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"clientOrderId":{"type":["null","string"],"description":"Your own reference, echoed back."},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"type":{"type":"string","enum":["market","limit","stop","stop_limit"]},"intent":{"type":"string","enum":["open","close","modify","cancel"],"description":"What this order was for. A close is an order, with the same record and the same failed/unknown distinction as an open — `modify` covers moving a stop, which changes what the account risks."},"brokerPositionId":{"type":["null","string"],"description":"The position this acted on. Null on an opening order."},"volume":{"type":["null","string"],"description":"Requested volume, in lots."},"stopLoss":{"type":["null","string"],"description":"Absolute price, not a distance."},"takeProfit":{"type":["null","string"],"description":"Absolute price, not a distance."},"state":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"stateDetail":{"type":["null","string"]},"needsReconciliation":{"type":"boolean","description":"True while we are confirming the result with the broker. Never resend such an order; poll it."},"retcode":{"type":["null","integer"],"description":"The broker's own MT5 return code, unmodified. 10009 is a completed request."},"retcodeText":{"type":["null","string"]},"brokerOrderId":{"type":["null","string"],"description":"The broker's ticket. What their statement shows."},"brokerDealId":{"type":["null","string"]},"filledVolume":{"type":["null","string"],"description":"What actually filled, which can be less than requested."},"filledPrice":{"type":["null","string"],"description":"What it actually filled at, which is rarely the requested price."},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time","description":"When an answer arrived."},"reconciledAt":{"type":["null","string"],"format":"date-time","description":"Set when a result was confirmed with the broker after the fact."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/orders/{id}/deals":{"get":{"summary":"Deals produced by one order","tags":["History"],"description":"One order can produce several deals — a partial fill leaves more than one. These are the broker's own records of money changing hands, which is what a statement is reconciled against.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"orderId":{"type":["null","string"],"format":"uuid","description":"Null when the broker did this without us — a stop firing, a swap, a manual trade."},"brokerDealId":{"type":"string","description":"The broker's deal ticket. The identity of this record."},"brokerOrderId":{"type":["null","string"]},"brokerPositionId":{"type":["null","string"]},"symbol":{"type":"string"},"dealType":{"type":"string","description":"buy, sell, balance, credit, commission, swap-adjacent charges and corrections."},"entry":{"type":["null","string"],"description":"in, out, inout or out_by — whether exposure opened or closed."},"volume":{"type":["null","string"],"description":"Lots."},"price":{"type":["null","string"],"description":"Execution price."},"commission":{"type":["null","string"],"description":"Account currency."},"swap":{"type":["null","string"],"description":"Account currency."},"profit":{"type":["null","string"],"description":"Realised, in account currency. Negative is a loss."},"fee":{"type":["null","string"],"description":"Account currency."},"dealtAt":{"type":"string","format":"date-time","description":"The broker's timestamp, not ours."},"comment":{"type":["null","string"]}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/deals":{"get":{"summary":"An account's full deal history","tags":["History"],"description":"Everything the broker recorded against this account, **including deals no order of yours caused** — a stop loss firing, a swap charge, a deposit, or somebody trading the same login from the MetaTrader desktop.\n\nThat inclusion is the point. A history containing only your own orders would show an account whose balance moves for no visible reason. Ordered by the broker's timestamp, not ours.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"accountId","required":false,"description":"Narrow to one account."},{"schema":{"type":"string","enum":["accepted","validating","sending","working","completed","filled","partially_filled","rejected","failed","unknown","cancelled","expired"]},"in":"query","name":"state","required":false,"description":"`unknown` is the set whose result we are still confirming with the broker."},{"schema":{"type":"string","maxLength":32},"in":"query","name":"symbol","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"since","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"until","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"The `nextCursor` from the previous page."},{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"orderId":{"type":["null","string"],"format":"uuid","description":"Null when the broker did this without us — a stop firing, a swap, a manual trade."},"brokerDealId":{"type":"string","description":"The broker's deal ticket. The identity of this record."},"brokerOrderId":{"type":["null","string"]},"brokerPositionId":{"type":["null","string"]},"symbol":{"type":"string"},"dealType":{"type":"string","description":"buy, sell, balance, credit, commission, swap-adjacent charges and corrections."},"entry":{"type":["null","string"],"description":"in, out, inout or out_by — whether exposure opened or closed."},"volume":{"type":["null","string"],"description":"Lots."},"price":{"type":["null","string"],"description":"Execution price."},"commission":{"type":["null","string"],"description":"Account currency."},"swap":{"type":["null","string"],"description":"Account currency."},"profit":{"type":["null","string"],"description":"Realised, in account currency. Negative is a loss."},"fee":{"type":["null","string"],"description":"Account currency."},"dealtAt":{"type":"string","format":"date-time","description":"The broker's timestamp, not ours."},"comment":{"type":["null","string"]}}}},"page":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["null","string"],"description":"Pass as `cursor` to continue."}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/reconcile":{"post":{"summary":"Confirm pending results with the broker now","tags":["Trading"],"description":"Confirms the result of every order on this account still in `unknown`, and brings its deals and positions up to date with the broker.\n\nThis happens automatically; call it when you do not want to wait. It is safe to call at any time and safe to call twice — nothing is recorded or settled twice.\n\n`ambiguous` counts orders whose result the broker's records do not settle unambiguously. Those are left in `unknown` on purpose: a wrong guess produces a history that looks settled and is wrong, which is worse than the gap. They need a human.\n\n`skipped` is `no-runtime` when the account is offline: bring it online and call again.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"examined":{"type":"integer","description":"Orders that were in `unknown`."},"resolved":{"type":"integer","description":"How many now have a confirmed result."},"stillUnknown":{"type":"integer","description":"Still unconfirmed, including the ambiguous ones."},"ambiguous":{"type":"integer","description":"Orders the broker's records do not settle unambiguously. Left alone deliberately — a wrong guess produces a history that looks settled and is wrong. These need a human."},"historyNotReady":{"type":"integer","description":"Orders left unconfirmed only because the broker's records were not available yet — usually for about half a minute after an account comes online. An empty answer is not treated as proof an order did not execute; they are confirmed on a later attempt."},"historyReady":{"type":["null","boolean"],"description":"Whether the broker's records were available for this check. Null when that could not be told."},"dealsIngested":{"type":"integer","description":"Broker deals recorded, including ones no order caused."},"positionsOpen":{"type":"integer"},"positionsClosed":{"type":"integer","description":"Positions the broker stopped reporting."},"skipped":{"type":["null","string"],"description":"`no-runtime` when the account is offline, so there was nothing to ask; `unreachable` when the broker would not answer."}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/accounts/{id}/positions":{"get":{"summary":"Positions on an account","tags":["Trading"],"description":"What we last saw, with `observedAt` saying when. The broker is authoritative and this is a snapshot, not a ledger — `observedAt` is here so you can tell a current view from a stale one rather than having to assume.\n\nOpen positions only, unless `includeClosed=true`.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"accountId":{"type":"string","format":"uuid"},"brokerPositionId":{"type":"string"},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"volume":{"type":["null","string"],"description":"Lots."},"openPrice":{"type":["null","string"],"description":""},"currentPrice":{"type":["null","string"],"description":""},"stopLoss":{"type":["null","string"],"description":""},"takeProfit":{"type":["null","string"],"description":""},"swap":{"type":["null","string"],"description":""},"profit":{"type":["null","string"],"description":"Unrealised, at observedAt."},"openedAt":{"type":["null","string"],"format":"date-time"},"observedAt":{"type":"string","format":"date-time","description":"When this was last true. A snapshot without it would be worthless."},"closedAt":{"type":["null","string"],"format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/execution-waves":{"post":{"summary":"Place one trade on many accounts","tags":["Waves"],"description":"A multi-account order: one trade placed on many accounts at once. Every account is brought online first, then the orders are sent together — at `executeAt`, or as soon as every account is online.\n\n**A multi-account order is not all-or-nothing once sent, and cannot be.** There is no transaction across a hundred brokers: accounts fill at different prices, some are rejected for margin, some go unanswered. So it never reports a single success or failure — it reports a result for each account, and `summary` counts what landed. An account whose result is `unresolved` must not be resent; poll its order.\n\n`dispatchSpreadMs` is the gap between the first account's order being sent and the last. It is the number worth watching: as it grows, your accounts are getting different prices.\n\n**`barrierPolicy`** decides what happens when some accounts are not online in time:\n\n- `release-ready` — trade on the accounts that are online, skip the rest. A partial entry beats a late one.\n- `all-or-nothing` — trade on none unless all are online. Note this is about *sending*: a broker can still reject an account's order after it was sent.\n- `wait` — keep waiting past `executeAt` until every account is online, up to `expiresAt`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accountIds","symbol","side","volume"],"properties":{"accountIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":500,"description":"The accounts to trade on, one order each. An account listed twice is traded once."},"symbol":{"type":"string","minLength":1,"maxLength":32,"example":"EURUSD"},"side":{"type":"string","enum":["buy","sell"]},"volume":{"type":"string","pattern":"^\\d+(\\.\\d{1,8})?$","description":"Lots per account, unless a weight overrides it.","example":"0.01"},"weights":{"type":"object","additionalProperties":{"type":"string"},"description":"Per-account volume, keyed by account id. For accounts that differ in equity."},"stopLoss":{"type":"string","example":"1.12900"},"takeProfit":{"type":"string","example":"1.14200"},"comment":{"type":"string","maxLength":120,"example":"signal 42"},"label":{"type":"string","maxLength":120,"example":"London open — EURUSD long"},"clientWaveId":{"type":"string","maxLength":120,"example":"signal-42"},"barrierPolicy":{"type":"string","enum":["release-ready","all-or-nothing","wait"],"default":"release-ready","description":"What to do when some accounts are not online in time. `release-ready` trades on the ones that are; `all-or-nothing` trades on none unless all are; `wait` keeps waiting past executeAt until expiresAt."},"executeAt":{"type":"string","format":"date-time","description":"When the orders should be sent. Omit to send as soon as every account is online."},"expiresAt":{"type":"string","format":"date-time","description":"Stop waiting for accounts that are not online after this."}}}}}},"parameters":[{"schema":{"type":"string","maxLength":200},"example":"3f0c9a52-8a8e-4f7e-9b0e-6c1d2f5a7b10","in":"header","name":"idempotency-key","required":false,"description":"Any unique string, usually a UUID. A retry with the same key and body returns the first answer instead of placing a second order; the same key with a different body is refused."}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"]},"clientWaveId":{"type":["null","string"]},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"baseVolume":{"type":["null","string"]},"state":{"type":"string","enum":["planned","preparing","armed","releasing","settled","cancelled","abandoned"]},"stateDetail":{"type":["null","string"]},"barrierPolicy":{"type":"string","enum":["release-ready","all-or-nothing","wait"]},"executeAt":{"type":["null","string"],"format":"date-time"},"expiresAt":{"type":["null","string"],"format":"date-time"},"preparedAt":{"type":["null","string"],"format":"date-time"},"releasedAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"},"dispatchSpreadMs":{"type":["null","integer"],"description":"The gap between the first account's order being sent and the last, in milliseconds. As it grows, your accounts are getting different prices."},"summary":{"type":"object","description":"What landed. Deliberately not a success flag — a multi-account order is not all-or-nothing once sent.","properties":{"total":{"type":"integer"},"filled":{"type":"integer"},"rejected":{"type":"integer"},"skipped":{"type":"integer"},"unresolved":{"type":"integer","description":"Accounts whose result we are still confirming with the broker. Do not resend these."}}},"legs":{"type":"array","description":"One entry per account: the order placed there and how it went.","items":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"orderId":{"type":["null","string"],"format":"uuid","description":"The ordinary order placed on this account. Query it like any other."},"volume":{"type":["null","string"]},"state":{"type":"string","enum":["pending","preparing","ready","dispatched","filled","rejected","skipped","unresolved"],"description":"`pending`/`preparing`: being brought online. `ready`: online and waiting. `dispatched`: sent, awaiting the broker. `filled`, `rejected`: the broker's answer. `skipped`: never sent. `unresolved`: sent, and we are confirming the result with the broker — do not resend."},"stateDetail":{"type":["null","string"]},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"}}}},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"402":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}},"get":{"summary":"List multi-account orders","tags":["Waves"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"]},"clientWaveId":{"type":["null","string"]},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"baseVolume":{"type":["null","string"]},"state":{"type":"string","enum":["planned","preparing","armed","releasing","settled","cancelled","abandoned"]},"stateDetail":{"type":["null","string"]},"barrierPolicy":{"type":"string","enum":["release-ready","all-or-nothing","wait"]},"executeAt":{"type":["null","string"],"format":"date-time"},"expiresAt":{"type":["null","string"],"format":"date-time"},"preparedAt":{"type":["null","string"],"format":"date-time"},"releasedAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"},"dispatchSpreadMs":{"type":["null","integer"],"description":"The gap between the first account's order being sent and the last, in milliseconds. As it grows, your accounts are getting different prices."},"summary":{"type":"object","description":"What landed. Deliberately not a success flag — a multi-account order is not all-or-nothing once sent.","properties":{"total":{"type":"integer"},"filled":{"type":"integer"},"rejected":{"type":"integer"},"skipped":{"type":"integer"},"unresolved":{"type":"integer","description":"Accounts whose result we are still confirming with the broker. Do not resend these."}}},"legs":{"type":"array","description":"One entry per account: the order placed there and how it went.","items":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"orderId":{"type":["null","string"],"format":"uuid","description":"The ordinary order placed on this account. Query it like any other."},"volume":{"type":["null","string"]},"state":{"type":"string","enum":["pending","preparing","ready","dispatched","filled","rejected","skipped","unresolved"],"description":"`pending`/`preparing`: being brought online. `ready`: online and waiting. `dispatched`: sent, awaiting the broker. `filled`, `rejected`: the broker's answer. `skipped`: never sent. `unresolved`: sent, and we are confirming the result with the broker — do not resend."},"stateDetail":{"type":["null","string"]},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"}}}},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/execution-waves/{id}":{"get":{"summary":"Follow a multi-account order","tags":["Waves"],"description":"Poll this after creating a multi-account order. `state` moves `planned → preparing → armed → releasing → settled`: accepted, bringing accounts online, waiting for `executeAt`, sending, and every account has a result. `cancelled` and `abandoned` mean nothing was sent.\n\nEach account in `legs` carries its own state and its own order id — an account's order is an ordinary order, confirmed with the broker and audited like any other.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"]},"clientWaveId":{"type":["null","string"]},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"baseVolume":{"type":["null","string"]},"state":{"type":"string","enum":["planned","preparing","armed","releasing","settled","cancelled","abandoned"]},"stateDetail":{"type":["null","string"]},"barrierPolicy":{"type":"string","enum":["release-ready","all-or-nothing","wait"]},"executeAt":{"type":["null","string"],"format":"date-time"},"expiresAt":{"type":["null","string"],"format":"date-time"},"preparedAt":{"type":["null","string"],"format":"date-time"},"releasedAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"},"dispatchSpreadMs":{"type":["null","integer"],"description":"The gap between the first account's order being sent and the last, in milliseconds. As it grows, your accounts are getting different prices."},"summary":{"type":"object","description":"What landed. Deliberately not a success flag — a multi-account order is not all-or-nothing once sent.","properties":{"total":{"type":"integer"},"filled":{"type":"integer"},"rejected":{"type":"integer"},"skipped":{"type":"integer"},"unresolved":{"type":"integer","description":"Accounts whose result we are still confirming with the broker. Do not resend these."}}},"legs":{"type":"array","description":"One entry per account: the order placed there and how it went.","items":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"orderId":{"type":["null","string"],"format":"uuid","description":"The ordinary order placed on this account. Query it like any other."},"volume":{"type":["null","string"]},"state":{"type":"string","enum":["pending","preparing","ready","dispatched","filled","rejected","skipped","unresolved"],"description":"`pending`/`preparing`: being brought online. `ready`: online and waiting. `dispatched`: sent, awaiting the broker. `filled`, `rejected`: the broker's answer. `skipped`: never sent. `unresolved`: sent, and we are confirming the result with the broker — do not resend."},"stateDetail":{"type":["null","string"]},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"}}}},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/execution-waves/{id}/cancel":{"post":{"summary":"Cancel a multi-account order before it is sent","tags":["Waves"],"description":"Only while nothing has been sent. Once orders are going out there is no cancelling them — `cancelled` would claim nothing was sent, and that is exactly the promise we can no longer make.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":["null","string"]},"clientWaveId":{"type":["null","string"]},"symbol":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"baseVolume":{"type":["null","string"]},"state":{"type":"string","enum":["planned","preparing","armed","releasing","settled","cancelled","abandoned"]},"stateDetail":{"type":["null","string"]},"barrierPolicy":{"type":"string","enum":["release-ready","all-or-nothing","wait"]},"executeAt":{"type":["null","string"],"format":"date-time"},"expiresAt":{"type":["null","string"],"format":"date-time"},"preparedAt":{"type":["null","string"],"format":"date-time"},"releasedAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"},"dispatchSpreadMs":{"type":["null","integer"],"description":"The gap between the first account's order being sent and the last, in milliseconds. As it grows, your accounts are getting different prices."},"summary":{"type":"object","description":"What landed. Deliberately not a success flag — a multi-account order is not all-or-nothing once sent.","properties":{"total":{"type":"integer"},"filled":{"type":"integer"},"rejected":{"type":"integer"},"skipped":{"type":"integer"},"unresolved":{"type":"integer","description":"Accounts whose result we are still confirming with the broker. Do not resend these."}}},"legs":{"type":"array","description":"One entry per account: the order placed there and how it went.","items":{"type":"object","properties":{"accountId":{"type":"string","format":"uuid"},"orderId":{"type":["null","string"],"format":"uuid","description":"The ordinary order placed on this account. Query it like any other."},"volume":{"type":["null","string"]},"state":{"type":"string","enum":["pending","preparing","ready","dispatched","filled","rejected","skipped","unresolved"],"description":"`pending`/`preparing`: being brought online. `ready`: online and waiting. `dispatched`: sent, awaiting the broker. `filled`, `rejected`: the broker's answer. `skipped`: never sent. `unresolved`: sent, and we are confirming the result with the broker — do not resend."},"stateDetail":{"type":["null","string"]},"sentAt":{"type":["null","string"],"format":"date-time"},"settledAt":{"type":["null","string"],"format":"date-time"}}}},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/plans":{"get":{"summary":"The available plans","tags":["Billing"],"description":"The plans on offer now: what each includes and what it costs. Prices are in minor units — `9900` is $99.00, never a float.\n\n`accounts` is a capacity limit: you cannot exceed it and be billed for the excess. The monthly allowances can run over where the plan allows it, up to twice the allowance, after which orders are refused — a runaway strategy should not be able to generate an unbounded invoice.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"monthlyPrice":{"type":["null","integer"],"description":"Minor units. 9900 is $99.00 — never a float. Null for a plan priced by contract."},"currency":{"type":"string"},"selfServe":{"type":"boolean","description":"False for a plan arranged with us rather than chosen in the dashboard."},"overagePerOrder":{"type":"integer","description":"Minor units per order beyond the allowance."},"features":{"type":"object","description":"What the plan switches on. A feature a plan lacks is refused with `402 FEATURE_NOT_IN_PLAN` when used to open something; closing, cancelling and modifying what already exists are always allowed.","properties":{"executionWaves":{"type":"boolean","description":"`POST /execution-waves`."},"pendingOrders":{"type":"boolean","description":"`POST /accounts/{id}/orders/pending`."},"alwaysOn":{"type":"boolean","description":"The `always_on` account mode."},"webhooks":{"type":"boolean","description":"Event delivery to your endpoints."}}},"entitlements":{"type":"object","properties":{"accounts":{"type":"integer","description":"A capacity limit, not a monthly allowance: you cannot exceed it and be billed."},"ordersPerMonth":{"type":"integer"},"wavesPerMonth":{"type":"integer"},"legsPerWave":{"type":"integer","description":"Accounts in one multi-account order."},"apiCallsPerMonth":{"type":"integer"},"allowOverage":{"type":"boolean","description":"Whether the monthly allowances may be exceeded and billed, up to twice the allowance."}}}}}}}}}}}}}},"/v1/billing/usage":{"get":{"summary":"This month's usage and what it would cost","tags":["Billing"],"description":"Counted as it happens rather than derived at the end of the month, so this is the same number the invoice will use.\n\n**Over quota, opening is refused and closing is always allowed.** A billing problem must never hold you in a position you cannot exit — an order to close comes back `201` however far over the plan you are, and only opening returns `402 QUOTA_EXCEEDED`.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"plan":{"type":"string"},"planName":{"type":"string"},"currency":{"type":"string"},"subtotal":{"type":["null","integer"],"description":"The subscription, in minor units. Null on a contract-priced plan."},"overage":{"type":"integer","description":"Charged on opening orders only — never on API calls or on getting flat."},"total":{"type":["null","integer"]},"entitlements":{"type":"object","properties":{"accounts":{"type":"integer","description":"Accounts holding credentials at once. A capacity limit, not a monthly allowance: you cannot exceed it and be billed."},"ordersPerMonth":{"type":"integer","description":"Opening orders. Closing, cancelling and moving stops are never counted."},"wavesPerMonth":{"type":"integer"},"legsPerWave":{"type":"integer","description":"Accounts in one multi-account order."},"apiCallsPerMonth":{"type":"integer"},"allowOverage":{"type":"boolean","description":"Whether the monthly allowances may be exceeded and billed, up to twice the allowance."}}},"features":{"type":"object","description":"What the plan switches on. A feature a plan lacks is refused with `402 FEATURE_NOT_IN_PLAN` when used to open something; closing, cancelling and modifying what already exists are always allowed.","properties":{"executionWaves":{"type":"boolean","description":"`POST /execution-waves`."},"pendingOrders":{"type":"boolean","description":"`POST /accounts/{id}/orders/pending`."},"alwaysOn":{"type":"boolean","description":"The `always_on` account mode."},"webhooks":{"type":"boolean","description":"Event delivery to your endpoints."}}},"usage":{"type":"object","properties":{"orders":{"type":"integer"},"waves":{"type":"integer"},"api_calls":{"type":"integer"},"accounts":{"type":"integer","description":"Connected right now, not a monthly total."}}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/usage/daily":{"get":{"summary":"Usage broken down by day","tags":["Billing"],"description":"For a chart, or for checking an invoice line against the day it came from.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","description":"UTC date, as YYYY-MM-DD."},"metric":{"type":"string"},"count":{"type":"integer"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/subscription":{"get":{"summary":"What is paying for the plan","tags":["Billing"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":["null","object"],"properties":{"id":{"type":"string","format":"uuid"},"plan":{"type":"string"},"method":{"type":"string","enum":["card","crypto"]},"status":{"type":"string","enum":["pending","active","past_due","ended"],"description":"`past_due`: a card renewal failed and is being retried; the plan is kept meanwhile. When paying stops the workspace moves to Free — opening is then refused, and closing never is."},"currentPeriodEnd":{"type":["null","string"],"format":"date-time"},"cancelAtPeriodEnd":{"type":"boolean"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/invoices":{"get":{"summary":"Invoices, newest first","tags":["Billing"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"number":{"type":"string","description":"Sequential, e.g. FX-2026-000123. Never reused. Invoices issued before the fxapis name keep their OW- numbers."},"plan":{"type":"string"},"method":{"type":"string","enum":["card","crypto"]},"status":{"type":"string","enum":["open","paid","void","uncollectible"]},"currency":{"type":"string"},"amount":{"type":"integer","description":"Minor units. 9900 is $99.00."},"amountRefunded":{"type":"integer","description":"Minor units."},"periodStart":{"type":["null","string"],"format":"date-time"},"periodEnd":{"type":["null","string"],"format":"date-time"},"crypto":{"type":["null","object"],"description":"Crypto only. While the invoice is open: send exactly `amount` of `coin` on `network` to `address`, with `extraId` as the memo or tag when one is given — without it the payment cannot be matched.","properties":{"coin":{"type":["null","string"]},"network":{"type":["null","string"]},"address":{"type":["null","string"]},"amount":{"type":["null","string"],"description":"Exact, as quoted. A string, never a float."},"extraId":{"type":["null","string"]},"expiresAt":{"type":["null","string"],"format":"date-time"},"providerStatus":{"type":["null","string"],"description":"waiting, confirming, confirmed, sending, partially_paid, finished, failed, expired."},"amountReceived":{"type":["null","string"]}}},"note":{"type":["null","string"]},"paidAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/checkout":{"post":{"summary":"Start paying for a plan","tags":["Billing"],"description":"Nothing changes until the payment is confirmed. By card, the card is confirmed on the checkout page, then `POST /billing/sync` confirms the result; moving between paid plans by card starts the new one today and refunds the unused part of the old. By crypto, the customer pays a one-month invoice to the address shown on the checkout page; an upgrade mid-month is charged the difference, and a downgrade is bought once the current month ends.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["plan","method"],"additionalProperties":false,"properties":{"plan":{"type":"string","description":"A self-serve paid plan: `starter` or `scale`.","example":"starter"},"method":{"type":"string","enum":["card","crypto"]},"coin":{"type":"string","description":"Crypto only: a ticker from `GET /billing/crypto/coins`.","example":"USDT"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"next":{"type":"object","description":"`card-form`: render the card form with these values to collect and confirm the card in the browser; then call `POST /billing/sync`. `crypto-quote`: show the customer `quote` — what to send, where, until when — and poll `POST /billing/invoices/{invoiceId}/refresh`. Neither leaves your page.","properties":{"type":{"type":"string","enum":["card-form","crypto-quote"]},"publishableKey":{"type":"string"},"subscriptionId":{"type":"string"},"customerId":{"type":"string"},"quote":{"type":"object","properties":{"paymentId":{"type":"string"},"currency":{"type":"string"},"amount":{"type":"string"},"address":{"type":"string"},"extraId":{"type":["null","string"]},"network":{"type":["null","string"]},"expiresAt":{"type":["null","string"],"format":"date-time"}}}}},"invoiceId":{"type":"string"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"502":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/sync":{"post":{"summary":"Confirm a card payment","tags":["Billing"],"description":"For the browser returning from the card form. Nothing it says is believed; the payment is checked at source.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":["null","object"],"properties":{"id":{"type":"string","format":"uuid"},"plan":{"type":"string"},"method":{"type":"string","enum":["card","crypto"]},"status":{"type":"string","enum":["pending","active","past_due","ended"],"description":"`past_due`: a card renewal failed and is being retried; the plan is kept meanwhile. When paying stops the workspace moves to Free — opening is then refused, and closing never is."},"currentPeriodEnd":{"type":["null","string"],"format":"date-time"},"cancelAtPeriodEnd":{"type":"boolean"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/cancel":{"post":{"summary":"Cancel at the end of the paid period","tags":["Billing"],"description":"Nothing is taken away early and nothing is refunded; the month bought runs to its end, then the workspace moves to Free.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"endsAt":{"type":["null","string"],"format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/crypto/coins":{"get":{"summary":"Coins accepted for crypto payment","tags":["Billing"],"description":"Each with the network it must be sent on. Sending on another network loses the payment.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"ticker":{"type":"string"},"label":{"type":"string"},"network":{"type":"string","description":"The chain it must be sent on."},"stable":{"type":"boolean"},"note":{"type":"string"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/invoices/{id}":{"get":{"summary":"One invoice","tags":["Billing"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"number":{"type":"string","description":"Sequential, e.g. FX-2026-000123. Never reused. Invoices issued before the fxapis name keep their OW- numbers."},"plan":{"type":"string"},"method":{"type":"string","enum":["card","crypto"]},"status":{"type":"string","enum":["open","paid","void","uncollectible"]},"currency":{"type":"string"},"amount":{"type":"integer","description":"Minor units. 9900 is $99.00."},"amountRefunded":{"type":"integer","description":"Minor units."},"periodStart":{"type":["null","string"],"format":"date-time"},"periodEnd":{"type":["null","string"],"format":"date-time"},"crypto":{"type":["null","object"],"description":"Crypto only. While the invoice is open: send exactly `amount` of `coin` on `network` to `address`, with `extraId` as the memo or tag when one is given — without it the payment cannot be matched.","properties":{"coin":{"type":["null","string"]},"network":{"type":["null","string"]},"address":{"type":["null","string"]},"amount":{"type":["null","string"],"description":"Exact, as quoted. A string, never a float."},"extraId":{"type":["null","string"]},"expiresAt":{"type":["null","string"],"format":"date-time"},"providerStatus":{"type":["null","string"],"description":"waiting, confirming, confirmed, sending, partially_paid, finished, failed, expired."},"amountReceived":{"type":["null","string"]}}},"note":{"type":["null","string"]},"paidAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/billing/invoices/{id}/refresh":{"post":{"summary":"Check an open crypto invoice now","tags":["Billing"],"description":"For the checkout page while the customer pays. The payment is checked at source; nothing the caller sends is believed.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"number":{"type":"string","description":"Sequential, e.g. FX-2026-000123. Never reused. Invoices issued before the fxapis name keep their OW- numbers."},"plan":{"type":"string"},"method":{"type":"string","enum":["card","crypto"]},"status":{"type":"string","enum":["open","paid","void","uncollectible"]},"currency":{"type":"string"},"amount":{"type":"integer","description":"Minor units. 9900 is $99.00."},"amountRefunded":{"type":"integer","description":"Minor units."},"periodStart":{"type":["null","string"],"format":"date-time"},"periodEnd":{"type":["null","string"],"format":"date-time"},"crypto":{"type":["null","object"],"description":"Crypto only. While the invoice is open: send exactly `amount` of `coin` on `network` to `address`, with `extraId` as the memo or tag when one is given — without it the payment cannot be matched.","properties":{"coin":{"type":["null","string"]},"network":{"type":["null","string"]},"address":{"type":["null","string"]},"amount":{"type":["null","string"],"description":"Exact, as quoted. A string, never a float."},"extraId":{"type":["null","string"]},"expiresAt":{"type":["null","string"],"format":"date-time"},"providerStatus":{"type":["null","string"],"description":"waiting, confirming, confirmed, sending, partially_paid, finished, failed, expired."},"amountReceived":{"type":["null","string"]}}},"note":{"type":["null","string"]},"paidAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/api-keys":{"get":{"summary":"List this workspace's keys","tags":["API keys"],"description":"Newest first, revoked keys included — a revocation is history worth seeing.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"publicId":{"type":"string","description":"The key's public half. Safe to log and to show."},"hint":{"type":"string","description":"The key with its secret elided, e.g. `fx_live_7f3a9c21_…`, for telling keys apart."},"environment":{"type":"string","enum":["live","test"]},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["null","string"],"format":"date-time","description":"To the minute. Null if it has never been used."},"expiresAt":{"type":["null","string"],"format":"date-time"},"revokedAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}},"post":{"summary":"Create a key","tags":["API keys"],"description":"Returns the key in `key`, **once**. We cannot show a key again after creation, or recover it. A lost key is replaced, not recovered.\n\n`admin` and `keys:manage` cannot be granted here. Issue narrow keys: an integration that only reads positions should hold `trading:read` and nothing else.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","scopes"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"example":"Production trading server"},"scopes":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["accounts:read","accounts:write","trading:read","trading:reduce","trading:execute","history:read","webhooks:write"]},"description":"`admin` and `keys:manage` cannot be granted to a key through the API."},"environment":{"type":"string","enum":["live","test"],"default":"live"},"expiresAt":{"type":"string","format":"date-time","description":"Optional. Must be in the future."}}}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"publicId":{"type":"string","description":"The key's public half. Safe to log and to show."},"hint":{"type":"string","description":"The key with its secret elided, e.g. `fx_live_7f3a9c21_…`, for telling keys apart."},"environment":{"type":"string","enum":["live","test"]},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["null","string"],"format":"date-time","description":"To the minute. Null if it has never been used."},"expiresAt":{"type":["null","string"],"format":"date-time"},"revokedAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"key":{"type":"string","description":"The whole key. Shown here and never again — store it now. We cannot show it again after creation."}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/api-keys/{id}":{"get":{"summary":"Read one key","tags":["API keys"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"publicId":{"type":"string","description":"The key's public half. Safe to log and to show."},"hint":{"type":"string","description":"The key with its secret elided, e.g. `fx_live_7f3a9c21_…`, for telling keys apart."},"environment":{"type":"string","enum":["live","test"]},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["null","string"],"format":"date-time","description":"To the minute. Null if it has never been used."},"expiresAt":{"type":["null","string"],"format":"date-time"},"revokedAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}},"patch":{"summary":"Rename or re-scope a key","tags":["API keys"],"description":"New scopes replace the old ones and apply from the key's next request. The key itself does not change, so nothing using it needs redeploying. A revoked key cannot be changed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"name":{"type":"string","minLength":1,"maxLength":80,"example":"Production trading server"},"scopes":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["accounts:read","accounts:write","trading:read","trading:reduce","trading:execute","history:read","webhooks:write"]},"description":"Replaces the key's scopes. Takes effect on its next request."}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"publicId":{"type":"string","description":"The key's public half. Safe to log and to show."},"hint":{"type":"string","description":"The key with its secret elided, e.g. `fx_live_7f3a9c21_…`, for telling keys apart."},"environment":{"type":"string","enum":["live","test"]},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["null","string"],"format":"date-time","description":"To the minute. Null if it has never been used."},"expiresAt":{"type":["null","string"],"format":"date-time"},"revokedAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/api-keys/{id}/revoke":{"post":{"summary":"Revoke a key","tags":["API keys"],"description":"Refused from its very next request — there is no cache to wait for. Revoking an already-revoked key succeeds and changes nothing, so a retried revoke is safe.\n\nA key may revoke itself. Anything it was doing stops with it.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"publicId":{"type":"string","description":"The key's public half. Safe to log and to show."},"hint":{"type":"string","description":"The key with its secret elided, e.g. `fx_live_7f3a9c21_…`, for telling keys apart."},"environment":{"type":"string","enum":["live","test"]},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["null","string"],"format":"date-time","description":"To the minute. Null if it has never been used."},"expiresAt":{"type":["null","string"],"format":"date-time"},"revokedAt":{"type":["null","string"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/workspace":{"get":{"summary":"The workspace this credential belongs to","tags":["Workspace"],"description":"Its plan, and whether trading is switched off. Our staff switch trading off for a whole workspace only during an incident; this is where to see it.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["id","name","plan","planName","planAssigned","tradingDisabled"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"plan":{"type":"string","description":"The plan in force. `none` when the workspace pays for nothing and no free plan is offered: choose a plan to connect accounts and open trades."},"planName":{"type":"string"},"planAssigned":{"type":"boolean","description":"The plan was arranged with us rather than paid for, and is changed by talking to us."},"tradingDisabled":{"type":"boolean","description":"Trading is switched off for the whole workspace. While it is, every order is refused with `409 TRADING_DISABLED` — closes included."}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/members":{"get":{"summary":"List this workspace's members","tags":["Members"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["owner","admin","member"]},"you":{"type":"boolean","description":"True for the member making the request."},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/members/{id}":{"patch":{"summary":"Change a member's role","tags":["Members"],"description":"Only the owner changes roles, and the owner's own role cannot be changed here. Takes effect on the member's next request.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"additionalProperties":false,"properties":{"role":{"type":"string","enum":["admin","member"]}}}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["owner","admin","member"]},"you":{"type":"boolean","description":"True for the member making the request."},"createdAt":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/members/{id}/remove":{"post":{"summary":"Remove a member","tags":["Members"],"description":"Signs them out everywhere at once and deletes their password. What they did stays attributed to them. Keys they created belong to the workspace and are not revoked — revoke them separately if they should stop too.\n\nThe owner cannot be removed. An admin may remove plain members only.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/members/invitations":{"get":{"summary":"List open invitations","tags":["Members"],"description":"Unaccepted, unrevoked and unexpired.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","member"]},"url":{"type":"string","description":"The link to send them. The same link every time it is asked for, until it is accepted, revoked or expires."},"invitedBy":{"type":["null","string"],"description":"Name of the member who sent it."},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}},"post":{"summary":"Invite someone","tags":["Members"],"description":"Returns a link to send them; there is no email yet. An address that already has an open invitation gets that invitation back — the same link, not a new one — with `handedBack: true`. To change the role on it, revoke it and invite again.\n\nThe owner may invite admins and members; an admin may invite members.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","role"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":254},"role":{"type":"string","enum":["admin","member"]}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","member"]},"url":{"type":"string","description":"The link to send them. The same link every time it is asked for, until it is accepted, revoked or expires."},"invitedBy":{"type":["null","string"],"description":"Name of the member who sent it."},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"handedBack":{"type":"boolean","description":"True when this address already had an open invitation and that one was returned instead."}}}}}}}},"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","member"]},"url":{"type":"string","description":"The link to send them. The same link every time it is asked for, until it is accepted, revoked or expires."},"invitedBy":{"type":["null","string"],"description":"Name of the member who sent it."},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"handedBack":{"type":"boolean","description":"True when this address already had an open invitation and that one was returned instead."}}}}}}}},"400":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}},"/v1/members/invitations/{id}/revoke":{"post":{"summary":"Withdraw an invitation","tags":["Members"],"description":"Its link stops working immediately. Safe to repeat.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}}}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable. Match on this, not on the message."},"message":{"type":"string","description":"For a human. May change between releases."},"details":{"type":"array","items":{"type":"object","additionalProperties":true}}},"required":["code","message"]},"requestId":{"type":"string","description":"Also on every response. Quote it in a support request and the whole call can be found."}}}}}}}}}},"servers":[{"url":"https://api.fxapis.com","description":"Production"}],"security":[{"apiKey":[]}],"tags":[{"name":"Accounts","description":"Connecting MT5 accounts and reading their state."},{"name":"Lifecycle","description":"Bringing an account online, taking it offline, and changing how it stays connected."},{"name":"Trading","description":"Placing orders and reading positions. The endpoints that move money."},{"name":"History","description":"The durable record: every order, every deal the broker recorded, and what it returned. Includes deals no order of yours caused."},{"name":"Waves","description":"Multi-account orders: one trade placed on many accounts at once, with a result for each account. For signals that should reach every follower at the same moment."},{"name":"Workspace","description":"The workspace a credential belongs to: its plan, and whether trading is switched off."},{"name":"Billing","description":"Plans, usage, what the month would cost, invoices, and paying for a plan."},{"name":"Members","description":"Who is in this workspace, who has been invited, and what each may do. Managed by a signed-in owner or admin from the dashboard; API keys, `admin` included, are refused."},{"name":"API keys","description":"Issuing, renaming, re-scoping and revoking this workspace's keys. A key's secret is returned once, by the request that creates it, and never again. Needs `keys:manage`."},{"name":"Health","description":"Liveness and readiness. No key required."}]}