Orders & positions
How to open, change and close trades, what each answer means, and how to read the account back.
The calls
| What | Call |
|---|---|
| Place a market or pending order | POST /accounts/{account}/orders |
| Change the stop loss or take profit of a position | PATCH /accounts/{account}/positions/{ticket} |
| Close a position, wholly or partly | POST /accounts/{account}/positions/{ticket}/close |
| Change a pending order | PATCH /accounts/{account}/orders/{ticket} |
| Cancel a pending order | DELETE /accounts/{account}/orders/{ticket} |
| See where an order request stands | GET /accounts/{account}/requests/{request_id} |
| Account summary, positions, pending orders | GET /accounts/{account}, …/positions, …/orders |
| Closed trades and balance operations | GET /accounts/{account}/trades, …/transactions |
Every write needs an Idempotency-Key header (see Idempotency), and a key with the trade or trade:propose scope.
Placing an order
| Field | Required | Meaning |
|---|---|---|
symbol | yes | Instrument, for example EURUSD. |
side | yes | buy or sell. |
volume | yes | Lots, as a decimal string: "0.10". |
type | no | market (default), limit or stop. |
price | limit, stop | Trigger price of a pending order. |
stop_loss, take_profit | no | Prices. Must sit on the correct side of the market, at least the broker's minimum distance away. |
expires_at | no | Pending orders only: a future time (ISO-8601 or epoch ms) after which the order is removed. |
comment | no | Up to 200 characters, stored with the trade. |
client_tag | no | Your label for the trade (see Tags). |
A limit order buys below or sells above the current price; a stop order buys above or sells below it. Pending orders come back typed as buy_limit, sell_limit, buy_stop or sell_stop.
curl -X POST https://terminal.jafx.com/trading-api/v1/accounts/$ACCOUNT/orders \
-H "Authorization: Bearer $JAFX_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"symbol":"EURUSD","side":"buy","type":"market","volume":"0.02","stop_loss":1.1100,"client_tag":"breakout-v2"}'order = {"symbol": "EURUSD", "side": "buy", "type": "market", "volume": "0.02",
"stop_loss": 1.1100, "client_tag": "breakout-v2"}
key = str(uuid.uuid4()) # keep it: retries of THIS order reuse it
r = api.post(f"/accounts/{account}/orders", json=order, headers={"Idempotency-Key": key})const order = { symbol: 'EURUSD', side: 'buy', type: 'market', volume: '0.02', stop_loss: 1.11, client_tag: 'breakout-v2' }
const key = randomUUID() // keep it: retries of THIS order reuse it
const r = await fetch(`${BASE}/accounts/${account}/orders`, {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': key },
body: JSON.stringify(order),
})A buy limit order with a stop, a target and an expiry:
{"symbol":"EURUSD","side":"buy","type":"limit","volume":"0.10","price":1.1080,
"stop_loss":1.1040,"take_profit":1.1180,"expires_at":"2026-10-20T00:00:00Z","client_tag":"breakout-v2"}What each answer means
| HTTP | status | Meaning | What to do |
|---|---|---|---|
| 201 | filled | Market order executed. ticket and position describe the new position. | Done. |
| 201 | placed | Pending order placed. order describes it. | Done. |
| 200 | filled / placed | A close executed, or a change or cancel was applied. | Done. |
| 202 | pending | The broker is still deciding, for example while covering the trade with a liquidity provider. | Watch request.decided on the orders stream, or poll the request. Do not resend. |
| 202 | outcome_unknown | The broker connection failed while the request was in flight. It may or may not have executed. | Poll the request until it resolves. Never resend it under a new key. |
| 202 | awaiting_confirmation | A proposal was created. Nothing was sent to the broker. | The account owner confirms it in the web terminal. |
| 422 | problem | The broker refused the order. code says why: rejected_by_broker, rejected, invalid_volume, invalid_stops… | Fix the order, then send it with a new key. |
| 4xx | problem | JAFX's checks refused it before it reached the broker (see below). | See the code. |
The result object:
{
"request_id": "01JQ8Z6M3S4X7T2N9CKV5W0HRB",
"kind": "market",
"status": "filled",
"ticket": 5001,
"broker_status": "dc-answered",
"failure": null,
"client_tag": "breakout-v2",
"position": {
"ticket": 5001, "symbol": "EURUSD", "side": "buy", "volume": "0.02",
"open_price": 1.11555, "open_time": "2026-10-13T09:29:47.512Z",
"stop_loss": 1.11, "take_profit": null, "profit": "-0.02",
"origin": "api", "client_tag": "breakout-v2", "from_ticket": null
}
}request_ididentifies this order request; use it withGET /accounts/{account}/requests/{request_id}.kind:market,pending,modify_position,modify_order,closeorcancel.broker_statusis the broker's own decision code, useful when you contact support. Branch onstatus, not on it.failureexplains a refusal in the broker's words.
Following a request
GET /accounts/{account}/requests/{request_id} shows where any order request stands. When the request is still open it checks with the broker before answering.
status | Meaning |
|---|---|
received, sent | Recorded, then sent to the broker; the decision is on its way. |
pending | The broker is holding it. |
filled | A market open or a close executed. |
placed | A pending order was placed, or a change or cancel was applied. |
rejected | The broker decided against it. |
refused_by_broker | The broker refused the request itself, for example because of invalid stops. |
refused | JAFX's checks refused it; it never reached the broker. |
not_sent | The broker connection was unavailable; nothing happened. |
outcome_unknown | The connection failed mid-flight; JAFX keeps checking until it knows. |
proposed | It became a proposal (confirm mode). |
Stops and targets
Send only what changes. A field you omit is kept; null removes it.
# Move the stop loss; the take profit stays as it is
curl -X PATCH https://terminal.jafx.com/trading-api/v1/accounts/$ACCOUNT/positions/5001 \
-H "Authorization: Bearer $JAFX_API_KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" -d '{"stop_loss":1.1105}'
# Remove the take profit
curl -X PATCH https://terminal.jafx.com/trading-api/v1/accounts/$ACCOUNT/positions/5001 \
-H "Authorization: Bearer $JAFX_API_KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" -d '{"take_profit":null}'Stop and target changes count against the key's modifications_per_minute, not its order budget. A trailing stop should move the stop only when it has moved meaningfully, rather than on every quote.
Pending orders
PATCH /accounts/{account}/orders/{ticket} changes price, stop_loss, take_profit or expires_at; omitted fields are kept and null removes a stop or the expiry. DELETE /accounts/{account}/orders/{ticket} cancels the order. When a pending order fills, the orders stream sends order.triggered followed by position.opened.
Closing
An empty body closes the whole position at market. {"volume": "0.01"} closes part of it.
After a partial close, the part that stays open is a new position with a new ticket: the answer's ticket is that new ticket, and its position.from_ticket names the original. The remainder keeps the original's client_tag. In the history, the closed part appears with "state": "closed_partial".
{
"request_id": "01JQ8ZD9R2K6W4T8NB1XQ5VH3M",
"kind": "close",
"status": "filled",
"ticket": 5002,
"position": { "ticket": 5002, "volume": "0.01", "client_tag": "breakout-v2", "from_ticket": 5001 }
}Tags (client_tag)
- Optional, up to 32 characters from
A–Z a–z 0–9 . _ : -, set when you place the order. - Returned on positions, pending orders, closed trades and stream events, and filterable:
GET …/positions?client_tag=breakout-v2. - Kept by the remainder of a partial close.
- Use it to tell strategies apart, like the magic number some platforms give each strategy:
"magic:20231105".
Trades placed outside the API have no tag. Their origin says where they came from: api, web, mobile, dealer, expert, signal, client or other.
Reading the account
- Summary (
GET /accounts/{account}):balance,credit,equity,margin,free_margin,floating_profitin the account currency, andmargin_levelin percent (nullwith no margin in use). - Positions include the floating
profit,swap,commissionandtaxes. - Closed trades (
GET …/trades) are ordered by close time, newest first, in windows of up to 90 days, withnext_cursorfor more.stateisclosed,closed_partial,closed_by,cancelledorexpired; cancelled and expired pending orders appear here too. - Balance operations (
GET …/transactions): deposits, withdrawals and credit, asbalanceorcreditrows.
For live updates, subscribe to the account and orders channels on the stream instead of polling.
What can refuse an order
JAFX checks these before anything reaches the broker, in this order. A refusal never costs you a trade, and the idempotency key is released so you can retry once the cause is fixed.
| Check | Refusal |
|---|---|
The key has trade or trade:propose | 403 insufficient_scope |
| Trading is not halted for the key, the account or everyone | 423 trading_halted |
| The key matches the account: a demo key cannot trade a live account (and live trading is switched on) | 403 live_trading_disabled |
| The ticket exists and is a position (or a pending order) of this account | 404 order_not_found |
| The instrument is on the key's allow-list and not excluded from API trading | 403 symbol_not_allowed |
The volume is within max_volume_per_order | 422 volume_limit_exceeded |
Open positions and orders stay within max_open_positions | 422 position_limit_exceeded |
| The per-minute budgets of the key and the account | 429 order_rate_limited, 429 modification_rate_limited |
Today's loss is below daily_loss_limit (new positions only) | 403 daily_loss_limit_reached |
| The account has an active trading session: a sign-in to JAFX Terminal in the last 24 hours, or around-the-clock trading on | 409 sign_in_required |
Then the order goes to the broker through your account's own session, and the broker validates it against your account: the instrument (404 symbol_not_available), volume steps, stop distances, margin and market hours.
Not available yet
Maximum slippage on market orders, stop-limit orders, OCO pairs and server-side trailing stops. Tell us which you need most.
Porting a bot from another platform
| In your bot | With the JAFX API |
|---|---|
| A strategy's magic number | client_tag, for example "magic:20231105", and ?client_tag= filters. |
| Retrying an order that may have gone through | One Idempotency-Key per intended order, reused for every retry. A retry never opens a second position. |
| Looping over the open orders | GET …/positions?client_tag=…, or keep state from the orders stream. |
| Reading the account's equity | GET /accounts/{account} or the account stream channel. |
| Trailing stop on every tick | Move the stop only when it has moved by a meaningful step; changes use modifications_per_minute. |
| Server time | UTC everywhere; the broker day starts at midnight Europe/Athens. |
Most orders are acknowledged well within a second. When the broker covers a trade with a liquidity provider it can take a few seconds: that is the 202 pending case.