Skip to content

Trading

Place, modify, and cancel account-scoped orders with authorization and idempotency.

After a user grants trading access, you can place, modify, and cancel orders through Finatic's account-scoped routes. The route accountId is the financial account ID returned by account APIs, not the tenant companyAccountId.

Requirements

Every trading command requires:

  • An active account grant with canTrade: true.
  • A valid financial accountId.
  • Broker-specific fields from the account's order schema.
  • An idempotency key for the write command.

Important: Read access does not authorize trading. If canTrade is false, send the user back through Connect and request trading access for that account.

Basic workflow

  1. Confirm the selected account grant has canTrade: true.
  2. Fetch GET /api/v1/accounts/{accountId}/order-schemas?action=place.
  3. Submit { order: { ... } } with an idempotency key.
  4. Check accepted and the embedded order snapshot.
  5. Track state with listOrders or webhooks.
  6. Read fills and events from the same account-scoped order route family.
  7. Modify or cancel only while the provider and returned schema permit it.

Request and response shape

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} 12

broker is optional. When present, it must match the account grant's broker.

A successful command returns an 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} 14

executionStrategy describes how Finatic handled the command:

ValueMeaning
CANCEL_REPLACEThe provider contract requires cancel plus replacement, for example Robinhood
NATIVE_MODIFYThe provider supports same-ID modification, for example TradeStation or eToro position SL/TP
ASYNC_COMMANDMT4/MT5 queues the command to the customer's EA and waits for an acknowledgement

For ASYNC_COMMAND, the embedded order can receive the EA ticket as brokerOrderId after acknowledgement. Handle MT_COMMAND_TIMEOUT and MT_COMMAND_REJECTED explicitly; accepted: true by itself is not final execution proof.

Order field schemas

Fetch the provider schema at runtime instead of hard-coding a matrix:

1const schema = await finatic.v1.getAccountOrderSchema({ 2 accountId: 'account_123', 3 action: 'place', 4}); 5

Supported actions are place, modify, and cancel. The response includes supportedOrderTypesByAssetClass and provider-specific field requirements.

1{ 2 "supportedOrderTypesByAssetClass": { 3 "equity": ["market", "limit", "stop", "stop_limit", "trailing_stop"], 4 "option": ["market", "limit"] 5 } 6} 7

Event Contract eligibility

Use the generated Client SDK contract for Event Contracts. Do not reuse an equity enum, infer support from a provider-wide trading flag, or treat catalog visibility as permission to trade.

1import { 2 FDXAssetType, 3 type AccountOrderSchemaResponse, 4 type FinaticAPIErrorResponse, 5} from '@finatic/client'; 6 7const response = await finatic.v1.getAccountOrderSchema<AccountOrderSchemaResponse>({ 8 accountId, 9 action: 'place', 10}); 11const capability = response.success?.data.eventContract; 12const canOpen = 13 capability?.publicEnabled === true && 14 capability.accountEntitled === true && 15 capability.closingOnly !== true; 16 17if (canOpen) { 18 await finatic.v1.createAccountOrder({ 19 accountId, 20 idempotencyKey: crypto.randomUUID(), 21 body: { 22 order: { 23 assetType: FDXAssetType.EventContract, 24 symbol: selectedEventContractSymbol, 25 }, 26 }, 27 }); 28} 29

Treat a missing capability, disabled public access, missing account entitlement, or closingOnly: true as a hard block on opening orders. A closing-only account can close an existing position only when the returned account schema permits that exact action.

For a FinaticAPIErrorResponse, show the attempted operation, stable error code, and safe trace or correlation reference. Never render raw provider bodies, stack traces, tokens, credentials, or account identifiers.

Provider restrictions

BrokerEntry order typesModify behavior
Alpaca, Webull, Tastytrade, NinjaTrader, Trading 212Use supportedOrderTypesByAssetClassUse the returned provider capability and schema; do not infer behavior from transport type
RobinhoodUse supportedOrderTypesByAssetClassCancel-replace (CANCEL_REPLACE)
TradeStationUse supportedOrderTypesByAssetClassNative same-ID modify (NATIVE_MODIFY)
eToromarket and limit by asset classPosition SL/TP uses modifyTarget: position_sl_tp; other modifies use cancel-replace
MT4 / MT5market, limit, and stop onlyAsync EA commands (ASYNC_COMMAND); a registered connector is required

For eToro, fetch the place schema because payloads use instrumentId and amount or units instead of a universal symbol and orderQty pair. MT commands can wait up to 90 seconds for a ticketed acknowledgement.

Place an order

1const result = await finatic.v1.createAccountOrder({ 2 accountId: 'account_123', 3 idempotencyKey: 'partner-order-123', 4 body: { 5 order: { 6 orderType: 'market', 7 assetType: 'equity', 8 action: 'buy', 9 timeInForce: 'day', 10 symbol: 'AAPL', 11 orderQty: 1, 12 }, 13 }, 14}); 15 16if (!result.success?.data?.accepted) { 17 console.error(result.error, result._id); 18}

Select an exact instrument

When a read returns instrument.finaticInstrumentId, send that opaque value as finaticInstrumentId inside the provider order payload. instrumentId remains the provider-native identifier for providers that require it. If both are sent, they must resolve to the same instrument within the authorized account and provider.

Never derive a canonical ID from a product root such as MGC, parse MGCZ6, or substitute another contract month. Fetch the descriptor through the same authorized account/provider path used for placement.

Modify an order

Pass only changed fields inside order and use a new idempotency key for this distinct command.

1const result = await finatic.v1.modifyAccountOrder({ 2 accountId: 'account_123', 3 orderId: 'order_123', 4 idempotencyKey: 'partner-order-123-modify-1', 5 body: { 6 order: { 7 orderQty: 2, 8 limitPrice: 185.25, 9 }, 10 }, 11});

For eToro position stop-loss or take-profit updates, include modifyTarget: position_sl_tp and positionId. Native modifies return NATIVE_MODIFY; cancel-replace can return supersededOrderId.

Cancel an order

1const result = await finatic.v1.cancelAccountOrder({ 2 accountId: 'account_123', 3 orderId: 'order_123', 4 idempotencyKey: 'partner-order-123-cancel-1', 5});

Track the order

1const orders = await finatic.v1.listOrders({ 2 accountId: 'account_123', 3 limit: 50, 4}); 5 6const fills = await finatic.v1.getAccountOrderFills({ 7 accountId: 'account_123', 8 orderId: 'order_123', 9}); 10

Error handling

  • Missing trading permission: The grant does not have canTrade: true.
  • Idempotency conflict: Reuse a key only for the exact same command; preserve it while reconciling a timeout.
  • Broker rejection: Use stable error details without exposing raw provider bodies.
  • Market restriction: The market, instrument, or asset is not currently tradable.

Log traceId with your own order ID and idempotency key. Never blindly retry a write with a new key.

Next steps

  1. Getting Data — read orders, fills, and events.
  2. Webhooks — subscribe to order lifecycle events.
  3. Error Handling — handle terminal and retryable failures.