Trading
Learn how to place, modify, and cancel account-scoped orders with idempotency.
Once the user grants trading access for a financial account, you can place, modify, and cancel orders through Finatic account-scoped routes. The accountId path parameter is the financial account ID returned by account APIs.
Requirements
All trading operations require:
- An active account grant with
canTrade: true. - A valid financial
accountId. - Broker-specific order fields (see order schemas).
- An idempotency key for write commands.
Important: Trading operations fail with an access error when the user only granted read permissions. Send the user back through Connect and request trading access for that account.
Typical workflow
- Confirm the selected account grant has
canTrade: true. - Fetch broker order field schemas for the account (
GET /api/v1/accounts/{accountId}/order-schemas?action=place). - Submit an order with an idempotency key and
{ order: { ... } }body envelope. - Read the command result (
accepted, embeddedordersnapshot). - Track order status with
finatic.v1.listOrders({ accountId })or webhooks. - Read fills and events from
/api/v1/accounts/{accountId}/orders/{orderId}/fillsand/events. - Modify or cancel active orders with the same account-scoped route family.
Request and response shape
Write commands use an envelope body:
1{
2"broker": "robinhood",
3"order": {
4"orderType": "market",
5"assetType": "equity",
6"action": "buy",
7"timeInForce": "day",
8"symbol": "AAPL",
9"orderQty": 1
10}
11}
12broker is optional. When provided, it must match the account grant's broker.
Responses return FDXBrokerOrderCommandResult:
1{
2"action": "PLACE",
3"accepted": true,
4"executionStrategy": "CANCEL_REPLACE",
5"order": {
6"orderId": "ord_123",
7"status": "PENDING",
8"legs": []
9},
10"clientOrderId": "partner-order-123",
11"supersededOrderId": null,
12"message": null
13}
14executionStrategy tells you how Finatic executed the command:
| Value | When |
|---|---|
CANCEL_REPLACE | Default for REST brokers — modify is implemented as cancel + replace when needed |
NATIVE_MODIFY | Broker-native modify (e.g. eToro position SL/TP via modifyTarget=position_sl_tp) |
ASYNC_COMMAND | MT4/MT5 — command queued to the customer EA; Finatic waits for an acknowledgement window |
For ASYNC_COMMAND, the embedded order may include the EA ticket as brokerOrderId once acknowledged. On timeout or rejection, the API returns MT_COMMAND_TIMEOUT or MT_COMMAND_REJECTED.
The embedded order uses the same shape as GET /api/v1/accounts/{accountId}/orders. Order lifecycle webhooks fire from persisted integration.orders updates — you do not need to poll after a successful write.
Order field schemas
Discover broker-specific fields before building your UI or validation:
1const schema = await finatic.v1.getAccountOrderSchema({
2accountId: 'account_123',
3action: 'place',
4});
5Supported action values: place, modify, cancel.
The schema response includes supportedOrderTypesByAssetClass — a map of asset class to allowed orderType values for that broker account. Use this at runtime instead of hard-coding broker matrices.
1{
2"supportedOrderTypesByAssetClass": {
3"equity": ["market", "limit", "stop", "stop_limit", "trailing_stop"],
4"option": ["market", "limit"]
5}
6}
7Per-broker trading notes
| Broker | Entry order types | Modify behavior |
|---|---|---|
| Alpaca, Robinhood, Webull, TradeStation, Tastytrade, NinjaTrader, Trading 212 | See supportedOrderTypesByAssetClass | |
Cancel-replace (CANCEL_REPLACE) | ||
| eToro | market, limit per asset class | Position SL/TP: set modifyTarget to position_sl_tp with positionId |
(NATIVE_MODIFY). Other modifies use cancel-replace. | ||
| MT4 / MT5 | market, limit, stop only | Async EA commands (ASYNC_COMMAND); requires a registered connector |
eToro place payloads use instrumentId and amount/units rather than symbol/orderQty. Fetch the place schema for field names before building your form.
MT brokers require FinaticConnect EA onboarding. Commands are not synchronous — plan for the acknowledgement window and handle MT_COMMAND_TIMEOUT / MT_COMMAND_REJECTED explicitly.
Place order
Modify order
Pass the financial accountId, the order ID, an idempotency key, and only the fields you want to change inside order:
For eToro position stop-loss / take-profit updates, include broker-specific modify fields inside order:
1await finatic.v1.modifyAccountOrder({
2accountId: 'account_123',
3orderId: 'position_456',
4idempotencyKey: 'etoro-sl-tp-1',
5body: {
6order: {
7modifyTarget: 'position_sl_tp',
8positionId: 'position_456',
9stopLossRate: 180.5,
10takeProfitRate: 195.0,
11},
12},
13});
14When Finatic uses native modify, the command result includes executionStrategy: "NATIVE_MODIFY". Cancel-replace modifies return supersededOrderId when the prior order row was replaced.
Cancel order
Order tracking
Use account-scoped reads after write commands:
1const orders = await finatic.v1.listOrders({
2accountId: 'account_123',
3limit: 50,
4});
5
6const fills = await finatic.v1.getAccountOrderFills({
7accountId: 'account_123',
8orderId: 'order_123',
9});
10Error handling
Common trading failures:
- Missing trading permission: The account grant does not have
canTrade: true. - Invalid idempotency key: Reuse only when retrying the exact same command.
- Broker rejection: Broker-specific rejection context is returned in error details.
- Market restrictions: The broker rejected the order because the market or asset is not currently tradable.
Always log traceId with your own order ID and idempotency key.
Next steps
- Getting Data - Read account orders, fills, and events.
- Webhooks - Subscribe to order lifecycle events.
- Error Handling - Handle domain errors and retries.
