Skip to main content
The approval tools family is the human’s workflow, not the agent’s. The agent can request that a backtested strategy be promoted to paper trading, but only a human can approve or deny that request. This is the money-path gate — enforced at the code level, not just by policy.

The approval state machine

Every approval request moves through a simple state machine:
pending → approved
        → denied
StateDescription
pendingThe request has been created (by the agent) and is waiting for a human decision.
approvedA human approved the request. The strategy is now promoted to paper trading.
deniedA human denied the request. The strategy is not promoted. The reason field records why.
approved and denied are terminal. An approval cannot be reversed through the tools API. If a strategy was approved by mistake, it must be retired from paper trading through the web UI — a separate, audited action.

Who can call what

ToolAgentHuman
list_approvalsYes (read-only)Yes
decide_approvalNoYes
The agent can call list_approvals to check the status of a request it submitted. The agent cannot call decide_approval — the MCP server rejects the call with an insufficient_privilege error if the caller is an agent-level API key. Only human-level API keys (issued through the web UI) can decide approvals. This is enforced at the authentication layer, not at the UI layer. There is no way for the agent to promote a strategy to paper trading, period.

list_approvals

List pending approval requests for the tenant. The agent calls this to check whether a promotion request it submitted has been decided. The human calls this (or uses the web UI) to see what’s waiting.

Arguments

ArgumentTypeRequiredDescription
statusstringNoFilter by status: pending (default), approved, denied, all.

Return value

An array of approval records:
FieldTypeDescription
approval_idstring (UUID)The approval identifier.
strategy_idstringThe strategy being promoted.
strategy_versionintegerThe version of the strategy.
run_idstringThe backtest run that supports the promotion.
requested_bystringagent or human.
statusstringpending, approved, or denied.
reasonstring | nullThe reason provided with the request.
decision_reasonstring | nullThe reason provided with the decision (if decided).
decided_bystring | nullThe human who decided (if decided).
created_atstringWhen the request was created.
decided_atstring | nullWhen the request was decided.

Example call

{ "status": "pending" }

Example response

[
  {
    "approval_id": "appr_01HQKX2J3K4M5N6P7R8S9T0V4G",
    "strategy_id": "strat_01HQKX2J3K4M5N6P7R8S9T0V1Y",
    "strategy_version": 1,
    "run_id": "run_01HQKX2J3K4M5N6P7R8S9T0V2A",
    "requested_by": "agent",
    "status": "pending",
    "reason": "NVDA momentum strategy backtested with Sharpe 1.84 and CAGR 46% over 2022-2024. Requesting paper-trading promotion.",
    "decision_reason": null,
    "decided_by": null,
    "created_at": "2025-01-15T14:40:00.000Z",
    "decided_at": null
  }
]

decide_approval

Approve or deny a pending approval request. Human-only. The MCP server rejects this call if the caller is an agent-level API key.

Arguments

ArgumentTypeRequiredDescription
approval_idstringYesThe approval identifier.
decisionstringYesapproved or denied.
reasonstringNoA human-readable reason for the decision. Stored on the approval record.

Return value

FieldTypeDescription
approval_idstringThe approval identifier.
statusstringThe new status (approved or denied).
decided_bystringThe human who decided.
decided_atstring (ISO 8601)When the decision was made.
reasonstringThe reason provided.
paper_strategy_idstring | nullIf approved, the ID of the new paper-trading strategy instance.

Example call

{
  "approval_id": "appr_01HQKX2J3K4M5N6P7R8S9T0V4G",
  "decision": "approved",
  "reason": "Metrics look solid. Sharpe 1.84, max DD 28% is within tolerance. Approving for paper trading with a 90-day review window."
}

Example response

{
  "approval_id": "appr_01HQKX2J3K4M5N6P7R8S9T0V4G",
  "status": "approved",
  "decided_by": "usr_01HQKX2J3K4M5N6P7R8S9T0V4H",
  "decided_at": "2025-01-15T15:02:33.111Z",
  "reason": "Metrics look solid. Sharpe 1.84, max DD 28% is within tolerance. Approving for paper trading with a 90-day review window.",
  "paper_strategy_id": "paper_01HQKX2J3K4M5N6P7R8S9T0V4I"
}

The promotion workflow

1

Agent runs a backtest

The agent calls backtest_run and reviews the stats. If the results look good, it proceeds.
2

Agent requests promotion

The agent submits a promotion request (via the strategy API or by flagging the run). This creates a pending approval record.
3

Human reviews

The human opens the web UI, reviews the strategy spec, backtest results, risk attestation, and the agent’s reasoning. The human can also re-run the backtest with different parameters to sanity-check.
4

Human decides

The human calls decide_approval (or clicks Approve / Deny in the web UI). If approved, the strategy is promoted to paper trading and begins generating virtual positions against live market data.
5

Agent checks status

The agent calls list_approvals to see whether its request was approved or denied, and reports the outcome to the human.
Paper trading is the last step before live capital. A paper strategy trades on live market data with virtual money. Promoting from paper to live capital is a separate, even more tightly gated workflow that is not exposed through the tools API at all.
The human should always review the risk attestation on the backtest run before approving. A high Sharpe with a breached max_weight constraint is a red flag, not a green light.