Skip to main content

User API: Approving Requests That Start Trading

Last updated November 5, 2026

Turning a bot on, changing a live bot, resuming trading and, for a connected AI app, pausing trading start, change or stop live trading, so each waits for your approval. This step covers what your program or app sees while a request waits, how you decide, and how the outcome is read.

Each needs the permission the table names; Enable bots and Edit live bots also need Trader Unlimited. Enable bots, Edit live bots and Resume all trading start off. For an API client, Connected Apps has an Ask me to approve each request switch under each, on by default; switched off, that client's requests run straight away, with an API Action Completed notice each time. A connected AI app has no such switch: its requests always wait. See Safeguards.

  A Multi-Entry bot is never turned on, or changed while live, through the API or by a connected app: those requests are refused (422 family_edit_unsupported, 409 family_live_edit_unsupported) and never wait.

The requests that can wait for approval

RequestPermissionAction nameWhen it waits
POST /bots/{botId}/enablebots.enable Enable botsenableAlways, while the client asks for approval. A bot already enabled is answered straight away with alreadyEnabled true.
PATCH /bots/{botId}bots.edit.live Edit live botsedit_liveWhen the bot is live and the edit changes something. A bot is live while enabled, holding or opening a trade, with an exit order working or a launch pending, and for ten minutes after it is disabled. An idle bot's edit needs bots.edit.idle and saves straight away; an edit that changes nothing answers changed false. Neither waits.
POST /trading/resumetrading.resume Resume all tradingresumeAlways, while the client asks for approval. When trading already runs, the answer is immediate with alreadyRunning true. A client limited to some broker accounts can never resume: 403 constraint_violation.
POST /trading/pausetrading.pause Pause all tradingpauseOnly through the connector (MCP), where it always waits. Over REST a pause needs no approval.

No other request waits: creating a bot and disabling it run straight away. Over MCP the four in the table always wait, whatever the token.

What the API answers when a request is held

First everything is checked that can be without a write: the client's permission and limits, that the bot exists, is visible to the client, is not archived and is not a Multi-Entry bot, that the plan has room for it, and for a live edit that the settings would save. A failure is refused with the usual code and nothing is stored. Over REST the request needs an Idempotency-Key; over MCP the key is optional. One that passes is stored and answered 202 Accepted:

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "ok": true,
  "data": {
    "status": "pending_confirmation",
    "actionId": "a_3f9c2b7d1e4a6c8b0d2f4a",
    "expiresAt": "2026-11-03T15:30:00Z"
  },
  "error": null,
  "apiVersion": "rest-v1",
  "requestId": "..."
}
  • pending_confirmation: nothing has changed yet. The bot is still off, the edit unsaved, trading still paused.
  • actionId, starting a_, is what you poll with GET /pending-actions/{actionId}.
  • expiresAt, in UTC, is when it stops waiting: a request expires after 1 hour.

A retry with the same key returns the same 202, as does the same request from the same client with a new key while the first waits. These answers mean nothing was stored:

AnswerMeaning
409 pending_action_existsAnother request for this bot (or the same request to resume or pause trading), from any client, waits or is being carried out; error.data.actionId names it. Wait for its decision, or ask the account owner to reject it. Any edit of the bot meets this meanwhile, as does an enable or resume from a client that does not ask for approval.
429 rate_limitederror.data.reason says why: too_many_for_connection (this client has the most requests waiting it may have), too_many_for_account (so has the account, across all clients) or recently_rejected (you rejected this client's request about the same bot, or the same request to resume or pause trading, a short time ago). Retry-After and error.data.retryAfterSeconds say when the first waiting request expires, or how long the rejection has left.

Turning a bot on and resuming trading count toward the limits on changes, whether they wait or not.

How you approve or reject

You decide on the website, or in the tradeSteward app where app approval is available, one request at a time. On the website:

  1. An API Approval Needed notice arrives (delivery choices on Notifications settings page), and the request is listed on Connected Apps under Actions awaiting approval, grouped by client and by when it was made. Each row shows the bot (or your account, for a resume or pause), the kind of request and the settings it changes. The newest 50 are listed, and a line says how many more wait.
  2. Select Show all on a group, or Show review on one row, for the full review: the client's name, when the request was made and expires (US Eastern), the bot's key settings with what the API changed since it last ran, and for a live edit each setting the save would change, current and requested, and anything else the save does. A resume shows your trading status. A pause says that trading stops for every bot until you resume it, waiting launches and a timed resume are cleared, and open trades keep being managed.
  3. Tick the requests you want and select Approve. Approve stays off until the review of every ticked request is shown. One check covers every ticked request: your passkey, your authenticator code or, if you have neither, your password. Reject asks you to confirm but needs no check.

An approval is bound to what was shown: if the bot changed after you saw a request, it is not carried out and its row says so; select Read again to review it. A request whose review cannot show everything it holds offers Reject only. The single-request page that a notification links to still works; when the bot changed, it says the request must be sent again.

What happens on a decision

On approval

Everything is checked again: not expired, the permission still held (the client's grant, your account limits and plan read fresh), the target within the client's limits, and the bot exactly as the page showed it. Then the action runs, you receive an API Action Completed notice, and it becomes executed. A failed check ends it failed (or expired) and nothing changes. An approval that finds the target already as requested (the bot on, trading running or already paused) ends executed with no notice.

While changes through the API are switched off, an approval does not go through: the page says changes are paused, and the request stays pending until you approve it or it expires. A pause is the exception: it can still be approved.

Requests approved together are each checked and carried out on their own, in the order listed, so a refusal never stops the others. Each row then says whether it ran, was refused (with the reason), changed since you saw it or was not reached in time. The last two keep waiting. A request that runs reaches your program or app as if you had approved it alone.

On rejection

Nothing changes. The action becomes rejected, and for 15 minutes the same client cannot ask again about the same bot, or send the same request to resume or pause trading (recently_rejected above).

Expiry

An undecided request expires 1 hour after it was made, and nothing changes. It ends early as expired when the bot changes under it: you save it on the website, or a new request for it arrives at a newer version from any client, which then goes ahead or waits in its place.

States, result codes and decidedVia

statusMeaningFinal?
pendingWaiting for your decision.No
executingApproved and being carried out right now.No
executedApproved and carried out. resultCode is ok.Yes
rejectedYou rejected it. Nothing changed. resultCode is rejected.Yes
expiredIt ran out of time (action_expired) or the bot changed under it (bot_changed_since_request). Nothing changed.Yes
failedApproved, but a check failed before anything was written. Nothing changed. resultCode says why (below).Yes
interruptedApproved and started, outcome unknown: the save did not answer, or the process ended during the action. Read the bot (GET /bots/{botId}) or GET /trading/status before sending it again.Yes

A failed action's resultCode is one of these error codes: bot_changed_since_request (the bot changed between the page and the approval), not_found (the bot is gone, or the client can no longer see it), permission_denied, entitlement_required, constraint_violation, family_edit_unsupported, bot_archived, account_disabled, subscription_limit, validation_failed, lock_busy, pause_locked or service_unavailable. Send a new request once the cause is fixed.

resultCode is null while a request waits; an interrupted action reads interrupted. decidedVia is web for the tradeSteward website, app for the tradeSteward app, and null while the request waits or when it expired undecided.

How to poll

  • Read GET /pending-actions/{actionId} at most every 15 seconds until status is final: a person has 1 hour, and each read counts toward the reads limit. With several, read GET /pending-actions instead. Treat executing like pending.
  • After executed, read the target: GET /bots/{botId} shows enabled true and the new version; GET /trading/status shows paused false after a resume and true after a pause.
  • After expired or failed, if the request is still wanted, send it again with a new Idempotency-Key: the original key holds the 202 answer.
  • GET /pending-actions lists everything this client has asked for, for example after a restart.

The endpoints

Both reads need only a working token: any API client may read the requests it sent itself. Another client's requests, or another account's, answer 404 not_found, exactly like an id that does not exist.

GET /pending-actions List this client's requests

The requests this client sent that needed approval, in any state, newest first, 50 at most. Operation id: listPendingActions. No parameters. Returns pendingActions, a list of pending action objects (see the single read), and moreCount, how many older requests it leaves out (0 when none). It is not paged: decided requests fall off the end as newer ones arrive. Refusals: 401 invalid_token; 429 rate_limited when the reads limit is used up.

GET /pending-actions/{actionId} Read one request

One request this client sent: poll this after a 202. Operation id: getPendingAction. The path parameter actionId is the id from the 202, a_ and 22 hex characters. It returns one pending action object: action (enable, edit_live, resume or pause), botId (null for a resume or pause), status (one of the states), createdAt, expiresAt (1 hour later) and decidedAt (null while it waits), all UTC, and decidedVia and resultCode as described under States.

{
  "ok": true,
  "data": {
    "actionId": "a_3f9c2b7d1e4a6c8b0d2f4a",
    "action": "enable",
    "botId": "aB3xK9mN2q",
    "status": "executed",
    "createdAt": "2026-11-03T14:30:00Z",
    "expiresAt": "2026-11-03T15:30:00Z",
    "decidedAt": "2026-11-03T14:33:21Z",
    "decidedVia": "web",
    "resultCode": "ok"
  },
  "error": null,
  "apiVersion": "rest-v1",
  "requestId": "..."
}

Refusals: 401 invalid_token; 404 not_found, the same answer for an id this client did not create, a malformed id and one that does not exist; 429 rate_limited as above.

A worked example: turning a bot on

The client "Portfolio runner" holds Enable bots, set to ask for approval. The bot "SPX 10 Delta Put Spread 9:31a" was created through the API, so it is off.

  1. The program sends POST /bots/{botId}/enable with an Idempotency-Key and gets the 202 shown above.
  2. You receive an API Approval Needed notice: the client "Portfolio runner" asks to enable your bot "SPX 10 Delta Put Spread 9:31a". The program polls and reads pending.
  3. On Connected Apps, under Actions awaiting approval, you select Show all on the "Portfolio runner" group, read the bot's settings as it will start, tick the request, select Approve and confirm it is you. The row is marked Approved, and you receive an API Action Completed notice.
  4. The program's next read is the executed object shown under Read one request, and the bot now reads enabled true, live true and a new version.

Had you rejected instead, the action would read rejected, the bot would stay off, and the same client could not ask for this bot again for 15 minutes.