ActionBoxDOCS

Action Controls

Let a reviewer request a bounded secondary operation without resolving the Action.

Action Controls sit beside an Action's main response. They let a reviewer request an operation such as retrying a job or rolling back a deployment while the Action stays open. A link control opens supporting material without creating a Control Request.

Controls are available on paid plans when the feature is enabled for the workspace. A plan without the feature returns 403 ACTION_CONTROLS_PLAN_REQUIRED. A workspace where the staged feature is not enabled returns CONTROLS_DISABLED.

Control schema

An Action accepts up to 12 controls, with no more than 3 mutating controls. Keys must be unique.

FieldTypeRequiredConstraint
keystringYes1 to 64 characters. Start with a lowercase letter, then use lowercase letters, numbers, _, or -.
labelstringYes1 to 80 characters.
kindstringYescallback, link, or actionbox.
urlstringFor linkHTTP or HTTPS URL, up to 2,048 characters. Rejected for other kinds.
destructivebooleanNoDefaults to false. Link controls cannot be destructive.
expires_atstringNoA future timestamp after which the control is unavailable.

callback controls use the Action's callback_url, so the Action must define one. actionbox controls are reserved for Watch operations and accept only pause_watch, resume_watch, or skip_current_occurrence as the key.

{
  "title": "Deployment failed",
  "description": "The health check timed out.",
  "callback_url": "https://ci.example.com/actionbox",
  "controls": [
    { "key": "retry", "label": "Retry deployment", "kind": "callback" },
    {
      "key": "rollback",
      "label": "Roll back",
      "kind": "callback",
      "destructive": true
    },
    {
      "key": "logs",
      "label": "Open logs",
      "kind": "link",
      "url": "https://ci.example.com/runs/4821"
    }
  ]
}

ActionBox records and delivers the request. It does not run your infrastructure command. The Source integration must verify the signed callback, allowlist the control key, perform the operation, and report the result.

Invoke a mutating control

The web and mobile clients call:

POST /v1/actions/{action_id}/controls/{control_key}/invoke
Idempotency-Key: retry-deploy-4821
Content-Type: application/json

{
  "action_version": 3,
  "fingerprint": "sha256:<64 lowercase hex characters>"
}

The version and fingerprint bind the request to what the reviewer saw. The API rejects stale bindings and permits one active mutating request per Action. Reusing the same idempotency key for the same request returns the original Control Request.

Destructive controls require a press and hold in the supported review clients. Treat that gesture as confirmation, not authorization. The server still checks the signed-in user's workspace role and current Action binding.

Receive and report the operation

A callback control produces a signed action.control_requested webhook. Verify it with the Source webhook secret used for other Action callbacks. The payload identifies the Control Request, Action, exact Action version and fingerprint, control key, requesting user, and Source.

The owning Source reports progress to:

POST /v1/source/control-requests/{control_request_id}/result
Authorization: Bearer $ACTIONBOX_SOURCE_KEY
Content-Type: application/json

{
  "status": "succeeded",
  "message": "Deployment recovered"
}
FieldTypeRequiredConstraint
statusstringYesrunning, succeeded, or failed.
messagestringNoUp to 500 characters.
reason_codestringNo1 to 80 uppercase letters, numbers, or underscores; must start with a letter.

Reporting a control result does not resolve the parent Action. Delivery state and operation state remain separate in the audit trail. GET /v1/actions/{action_id}/control-requests returns the latest 100 requests visible to the signed-in workspace.

Allowlist every operation

Never execute a command assembled from a label, URL, or untrusted payload field. Map each known control key to a bounded operation in your Source integration and reject unknown keys.

The CLI accepts controls through --controls-json JSON|@FILE. The Python and TypeScript SDKs accept the same wire objects. MCP ask_human accepts controls and callback_url; MCP update_action can replace controls on an open Action.

Next: Webhooks and callbacks, Webhook operations, or Decision context.

On this page