Skip to main content
Developers

Orders & positions

How to open, change and close trades, what each answer means, and how to read the account back.

The calls

WhatCall
Place a market or pending orderPOST /accounts/{account}/orders
Change the stop loss or take profit of a positionPATCH /accounts/{account}/positions/{ticket}
Close a position, wholly or partlyPOST /accounts/{account}/positions/{ticket}/close
Change a pending orderPATCH /accounts/{account}/orders/{ticket}
Cancel a pending orderDELETE /accounts/{account}/orders/{ticket}
See where an order request standsGET /accounts/{account}/requests/{request_id}
Account summary, positions, pending ordersGET /accounts/{account}, …/positions, …/orders
Closed trades and balance operationsGET /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

FieldRequiredMeaning
symbolyesInstrument, for example EURUSD.
sideyesbuy or sell.
volumeyesLots, as a decimal string: "0.10".
typenomarket (default), limit or stop.
pricelimit, stopTrigger price of a pending order.
stop_loss, take_profitnoPrices. Must sit on the correct side of the market, at least the broker's minimum distance away.
expires_atnoPending orders only: a future time (ISO-8601 or epoch ms) after which the order is removed.
commentnoUp to 200 characters, stored with the trade.
client_tagnoYour 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
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"}'

A buy limit order with a stop, a target and an expiry:

JSON
{"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

HTTPstatusMeaningWhat to do
201filledMarket order executed. ticket and position describe the new position.Done.
201placedPending order placed. order describes it.Done.
200filled / placedA close executed, or a change or cancel was applied.Done.
202pendingThe 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.
202outcome_unknownThe 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.
202awaiting_confirmationA proposal was created. Nothing was sent to the broker.The account owner confirms it in the web terminal.
422problemThe 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.
4xxproblemJAFX's checks refused it before it reached the broker (see below).See the code.

The result object:

Response 201
{
  "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_id identifies this order request; use it with GET /accounts/{account}/requests/{request_id}.
  • kind: market, pending, modify_position, modify_order, close or cancel.
  • broker_status is the broker's own decision code, useful when you contact support. Branch on status, not on it.
  • failure explains 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.

statusMeaning
received, sentRecorded, then sent to the broker; the decision is on its way.
pendingThe broker is holding it.
filledA market open or a close executed.
placedA pending order was placed, or a change or cancel was applied.
rejectedThe broker decided against it.
refused_by_brokerThe broker refused the request itself, for example because of invalid stops.
refusedJAFX's checks refused it; it never reached the broker.
not_sentThe broker connection was unavailable; nothing happened.
outcome_unknownThe connection failed mid-flight; JAFX keeps checking until it knows.
proposedIt became a proposal (confirm mode).

Stops and targets

Send only what changes. A field you omit is kept; null removes it.

Shell
# 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".

Partial close, response 200
{
  "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_profit in the account currency, and margin_level in percent (null with no margin in use).
  • Positions include the floating profit, swap, commission and taxes.
  • Closed trades (GET …/trades) are ordered by close time, newest first, in windows of up to 90 days, with next_cursor for more. state is closed, closed_partial, closed_by, cancelled or expired; cancelled and expired pending orders appear here too.
  • Balance operations (GET …/transactions): deposits, withdrawals and credit, as balance or credit rows.

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.

CheckRefusal
The key has trade or trade:propose403 insufficient_scope
Trading is not halted for the key, the account or everyone423 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 account404 order_not_found
The instrument is on the key's allow-list and not excluded from API trading403 symbol_not_allowed
The volume is within max_volume_per_order422 volume_limit_exceeded
Open positions and orders stay within max_open_positions422 position_limit_exceeded
The per-minute budgets of the key and the account429 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 on409 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 botWith the JAFX API
A strategy's magic numberclient_tag, for example "magic:20231105", and ?client_tag= filters.
Retrying an order that may have gone throughOne Idempotency-Key per intended order, reused for every retry. A retry never opens a second position.
Looping over the open ordersGET …/positions?client_tag=…, or keep state from the orders stream.
Reading the account's equityGET /accounts/{account} or the account stream channel.
Trailing stop on every tickMove the stop only when it has moved by a meaningful step; changes use modifications_per_minute.
Server timeUTC 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.

Automated and AI-driven trading can lose money quickly; nothing in these docs is investment advice.

JAFX (PTY) LTD · FSCA FSP No. 52058 · Trading leveraged derivatives with JAFX carries a high risk of losing money rapidly. It is not suitable for everyone.