API keys & security
Every request carries an API key. A key belongs to one trading account, allows only the scopes you chose, and carries limits the server enforces on every order.
How keys work
- The account owner creates keys in the web terminal under Developers. Creating or rolling a key needs a fresh login with the account's master password; the investor password cannot create keys. That login also starts the keys' trading session.
- A key belongs to one account on one server: the Live Server or the Demo Server, the two choices on the terminal's sign-in. Account numbers are per server, so live account 210 and demo account 210 are two different accounts, each with its own keys.
GET /meshows the key'sserver(liveordemo) andmode. - A key is shown once, when it is created. JAFX stores only a one-way hash, so nobody can show it to you again. If you lose it, roll or revoke it and create another.
- Keys look like
jafx_demo_k7mx2v9qr4tz8w1b6nfa_…: the account mode (demofor a Demo Server account,livefor a Live Server account), a public key ID (shown in the terminal and inGET /me) and the secret. - Send the key on every request as
Authorization: Bearer <key>. Never put it in a URL. - An account can hold up to 10 active keys. Keys expire after 1 to 365 days (default 90).
Trading sessions: around the clock, or 24 hours after each sign-in
Your keys place orders through your account's own trading session, the way JAFX Terminal places a manual trade, so the broker treats them as your own requests. For a bot or AI assistant that runs for days, switch on Keep my bots trading around the clock: JAFX then renews the session by itself, and your keys keep trading without you signing in.
With it off, the session comes from your latest sign-in: a password sign-in to JAFX Terminal, creating or rolling a key, or Renew on the Developers page. It lasts 24 hours from that sign-in, whether or not the terminal stays open, and the next sign-in renews it.
- Without an active session, orders answer
409 sign_in_requiredand nothing reaches the broker. Retry with the sameIdempotency-Keyonce you have signed in again or pressed Renew. On the Live Server, account data (the account, positions, orders and history) needs the session too. GET /meshows it undersession: whether it isactive, and until when (expires_at). The MCP toolget_limitsshows the same.- Stop API trading now on the Developers page ends it at once. So does a password change, or signing out of that session elsewhere. Signing out of JAFX Terminal itself does not stop your keys: the terminal's own session is a separate one.
- An AI assistant connected over MCP can press the same Stop when you ask it to, with
stop_api_trading, whatever its key. Nothing over MCP starts trading again: that is a sign-in to JAFX Terminal, or Renew. - Two hours before the session ends, and when it has ended (with why), JAFX leaves a notification on the Developers page and for your AI assistants, unless around-the-clock trading is on.
{ "session": { "active": true, "renews_automatically": false, "signed_in_at": "2026-11-04T18:20:00.000Z", "expires_at": "2026-11-05T18:20:00.000Z",
"note": "Orders go through account 123456's own session until expires_at. Signing in to JAFX Terminal again renews it for 24 hours." } }Around the clock
For bots and AI assistants that run for days, switch on Keep my bots trading around the clock on the Developers page, or tick it when you create a trading or proposals key. JAFX then keeps your trading password encrypted and signs your keys' session in again by itself, so they keep trading, reading and streaming without you signing in to JAFX Terminal.
- To switch it on you enter your master password once. JAFX signs in with it straight away to check it, and keeps it only if the sign-in works. The investor password can't be used.
- JAFX renews the session ahead of its 24 hours while your program is connected to the stream or has used its key in the last day, so a stream sees no gap. Otherwise it signs in again at your next request, which then takes a moment longer.
- It is off by default. JAFX deletes the password at once when you switch it off, press Stop API trading now, or revoke the account's last key that can trade or propose.
- If your trading account refuses the saved password, for example after you changed it, JAFX deletes it and never tries it again. Orders then answer
409 sign_in_required,GET /meshowssession.ended_reason: password_refused, and the Developers page says around-the-clock trading stopped. Switch it on again with your current password. - If the trading server can't be reached, JAFX keeps the password and tries again shortly: meanwhile account data and trading answer
503 broker_unavailablewithRetry-After.
{ "session": { "active": true, "renews_automatically": true, "signed_in_at": "2026-11-04T18:20:00.000Z", "expires_at": "2026-11-05T18:20:00.000Z",
"note": "Orders go through account 123456's own session. Around-the-clock trading is on: JAFX signs the account in again by itself before expires_at, so your keys keep trading." } }A key's mode follows its account: Demo Server accounts get demo keys, Live Server accounts get live keys. Start with a demo account: if you have none, open one in the JAFX client area, under Accounts. A live key starts with tighter limits (0.10 lot per order, 10 open positions, 10 orders a minute, a 2% daily loss stop) and confirm mode on, so every order waits for your confirmation until you choose otherwise. Creating a trading key for a live account asks you to confirm that it can trade real money.
Presets and scopes
The web terminal offers three presets. Each is a set of scopes:
| Preset | Scopes | Use it for |
|---|---|---|
| Read only | market:read account:read | Dashboards, journals, research agents. Cannot trade. |
| Trading | market:read account:read trade | Bots that place and manage orders themselves. |
| Proposals | market:read account:read trade:propose | AI assistants. Orders become proposals that you confirm in the terminal. |
| Scope | Allows |
|---|---|
market:read | Instruments, quotes and candles; the quotes and candles stream channels. |
account:read | Account summary, positions, pending orders, closed trades, balance operations and request status; the account and orders stream channels. |
trade | Place, change, close and cancel orders. They go to the broker straight away, after the checks below. |
trade:propose | The same calls, but each order becomes a proposal; nothing reaches the broker until you confirm it. |
A call outside the key's scopes answers 403 insufficient_scope. MCP tools follow the same scopes: an agent with a read-only key does not even see the trading tools.
Limits
Every key carries limits. The server checks them before anything reaches the broker, whatever your program or an AI agent sends. You set them when you create the key; GET /me shows them.
| Limit | Demo default | Live default | You can set | Refusal |
|---|---|---|---|---|
max_volume_per_order | 1.00 lot | 0.10 lot | Up to 100 lots | 422 volume_limit_exceeded |
max_open_positions | 20 | 10 | 1–200 (positions and pending orders together) | 422 position_limit_exceeded |
orders_per_minute | 30 | 10 | 1–120 (opens, closes, cancels) | 429 order_rate_limited |
modifications_per_minute | 60 | 30 | 1–240 (stop, target, price and expiry changes) | 429 modification_rate_limited |
daily_loss_limit | 10% of the day's starting equity | 2% | A percentage, an amount in the account currency, or none | 403 daily_loss_limit_reached |
symbols | All instruments | All instruments | An allow-list of up to 100 | 403 symbol_not_allowed |
ip_allowlist | None (recommended: set it) | None (recommended: set it) | Up to 20 IP addresses or CIDR ranges; see Keeping your key safe | 403 ip_not_allowed |
confirm_mode | Off | On | On: every order becomes a proposal | — |
mcp_enabled | On | On | Off: the key cannot be used by MCP clients | 403 insufficient_scope |
Daily loss stop
The baseline is the account's equity at the first reading of each broker day with money in it; the day starts at midnight Europe/Athens. While the account is empty there is no baseline, so a percentage stop starts once money is in. Once the day's loss reaches the limit, new positions are refused until the next broker day. Closing and reducing positions always works, and nothing is closed for you.
The stop reads the account's equity, so money moved out of the account during the day counts as a loss, and money moved in offsets one.
Trading halts
JAFX can pause API trading for everyone, for one account or for one key, either fully or for new risk only (closing still allowed). While a halt applies, refused calls answer 423 trading_halted, and GET /me shows it under trading:
{ "trading": { "mode": "close_only", "level": "global", "reason": "Market event" } }mode is on, close_only or off. level says where the setting comes from: env (the service default), global, account or key.
Proposals (confirm mode)
With a Proposals key, or any key with confirm_mode on, order calls do not reach the broker. They create a proposal and answer 202:
{
"request_id": "01JQ8ZB0Y4D2PQ7RS1VW3XHN6E",
"kind": "market",
"status": "awaiting_confirmation",
"proposal_id": "01JQ8ZB0Y4D2PQ7RS1VW3XHN6E",
"summary": "BUY 0.01 EURUSD market",
"expires_at": "2026-10-13T09:50:00.000Z",
"note": "Nothing was sent to the broker. The account owner must confirm this proposal in the JAFX web terminal (Developers → Proposals)."
}- You confirm or reject it in the web terminal under Developers → Proposals. Unconfirmed proposals expire after 10 minutes.
- Proposals pass every check (limits, daily loss, halts) when they are created, and again at the moment a confirmed one executes. From there it follows the normal order path.
- Your program can follow it with
GET /accounts/{account}/proposalsorGET /accounts/{account}/requests/{request_id}, and an AI assistant with the MCP toollist_proposals. How each one ended (confirmed and what its order did, rejected, or expired) is also a notification, so the assistant hears it the next time it connects.
Proposals are the recommended setup for AI agents that suggest trades: the agent does the analysis, you keep the last word.
Rolling, revoking and expiry
- Roll a key in the terminal to get a new secret with the same settings. The old secret keeps working for 24 hours so you can update your program without downtime.
- Revoke a key and it stops working within 5 seconds. Its open streams close with code
4401. - Expiry:
GET /meshowsexpires_at. After that, requests answer401 api_key_expired.
Activity: what your keys did
JAFX keeps a log of every call each of your keys makes, so you can see what your bots and AI assistants did on your account and find what went wrong. It is under Activity on the Developers page: the latest 100 calls, or one key's, each with its time, key, what it was and how it ended. An AI assistant connected over MCP reads the same list with get_activity.
- What is logged: every REST call, every stream connection and every MCP tool call (and each time an MCP client connects), whether it worked or was refused: what was called, how it came (REST, the stream, or MCP with the client's name for itself), when, how long it took, and how it ended (OK, an order's status such as filled or waiting for your confirmation, or why it was refused). Calls refused because of the key itself count too: a revoked, expired or suspended key, or one used from an IP address it isn't locked to.
- What it never holds: prices and stop levels, an order's comment and
client_tag, search words, request bodies, IP addresses, and the key itself. A row's summary holds only the instrument, side, volume, ticket and which fields a change sets, such asBUY 0.01 EURUSD market, SL and TP. Calls with a wrong or unknown key aren't in it: they belong to no account. - Repeats share a row: the same read, or the same refusal, by one key within a quarter of an hour is one row with how many times it happened, from the first to the last. Each order request, and each stop of API trading, has a row of its own, with its request id and ticket.
- How long: each row is kept for 180 days after its last call, then deleted.
Keeping your key safe
Anyone who gets a key can use it as you, within its limits, at any hour: a person, a program, or an AI assistant you paste it into, which a web page or document it reads can talk into placing trades. The Developers page says so when you create a key that can trade or propose.
- Lock it to your bot's IP addresses. Under Only allow these IP addresses, enter your bot's server address (up to 20 addresses or CIDR ranges). A locked key answers
403 ip_not_allowedfrom anywhere else, even if it leaks. Leaving a trading or proposals key open to any IP address takes a tick, Let this key be used from any IP address. The lock is set when the key is created; the key list shows each key's lock. - Proposals keys for AI assistants in the cloud. Hosted assistants and their connectors (ChatGPT, Claude and the like) call from their provider's servers, whose addresses change, so an IP lock can't hold them. Give them a Proposals key: every order waits for your OK in the terminal.
- Never share a key. Not in a chat, a website, a screenshot or code you share. Read it from an environment variable or a secret manager, never commit it, and keep it out of logs.
- Server-side only. Never ship a key in a web page, a browser extension or a mobile app. The API does not accept cross-origin browser requests.
- One key per program. Then you can see which program did what under Activity, and revoke one without stopping the others.
- Least privilege. A dashboard needs a read-only key. An AI agent that suggests trades needs a proposals key.
- Revoke at once if a key may have leaked, then create a new one.
- JAFX never asks for your password or your API key by email, chat or phone.
Repeated invalid keys from one IP address are blocked for 10 minutes after 10 failures in a minute.