Skip to content
Fuse
Esc
↑↓navigate↵open⌘Jpreview

Decide intervention

Approves or rejects a pending intervention. Approving an ask needs an answer (one of choices when the ask set them). A reject always carries a reason; one is filled in when omitted. Deciding a resolved intervention, including one whose deadline passed before the decision arrived, is 409.

Requires the hitl:decide scope or the master token. Full-access keys never hold hitl:decide, so an agent driving Fuse with its own key cannot answer its own request; mint a scoped key for the human instead (fuse hitl invite). Every refusal is 403 with code forbidden and a details.reason:

  • missing_scope: the key lacks hitl:decide (details.scope names it).
  • self_approval: the deciding key, or the deciding actor, is the one that raised the intervention. The master token naming no actor is exempt from the actor half of this check.
  • actor_required: the environment’s policy sets require_actor and the decision carries neither a pinned nor an asserted actor.
  • actor_mismatch / actor_not_allowed: the Fuse-Actor header was refused (see the FuseActor parameter).

Signals. A stall is acked with a plain approve or reject; either one lifts the lock on the agent. Approving an expiry keeps the environment away from the reaper that was due for extend_seconds (60 to 86400), or one more full window of that reaper (capped at 24h) when it is omitted; the decision’s answer reads extended until <time> and payload.signal.extended_until holds the time. Rejecting an expiry lets the reap go ahead. extend_seconds on anything but an expiry approval is 400, and so are edited_argv, edited_text and grant on a signal.

Gated actions. edited_argv (a held exec) and edited_text (a held computer type) replace the action before the orchestrator performs it. Either is re-checked against the deny gates, a match being 403 hitl_denied. An edit cannot be combined with an explicit grant (400), and a gate’s default grant drops to once with one. Both are 400 on a reject.

Reviews. A review is approved or rejected with the reviewer’s comments in reason, which the agent reads on both outcomes. It takes no answer, edit or grant.

Secrets. ?action=secret answers a secret request with an AnswerSecretRequest body instead of deciding it. It needs hitl:secret rather than hitl:decide (full-access keys hold neither) and is subject to the same self_approval and actor_required checks. The value is written into the guest at the declared target, then dropped: it is never stored, logged, returned or put on the event stream, and it never appears in an error message. The intervention is approved with decision.answer "done", which is all the agent reads. 400 when the intervention is not a secret request, the value is empty or over 64 KiB, the target is no longer declared, or the orchestrator has no TOKEN_ENCRYPTION_KEY. 409 when it is already resolved, or another answer to it is being written right now. Once the value is in the guest the request counts as answered, even if its deadline passed during the write.

POST/v1/interventions/{interventionId}
Authorization
AuthorizationBearer token · headerrequired
`Authorization: Bearer <token>` — either the static master token (`ORCH_AUTH_TOKEN`) or a revocable API key issued via /v1/api-keys (API keys require Postgres; without a database only the master token is accepted). Master-only surfaces (exec, attach, API key management) refuse API keys with 403. An expired key is refused with 401 like an unknown one. A key minted with `scopes` reaches only the hitl routes (`/v1/interventions`, `/v1/hitl/*`, and POST /v1/api-keys to mint a narrower key). Any other route is 403 code `forbidden`, reason `env_scope`, except an `/v1/environments/{vmId}` route for an environment the key does not cover, which is 404 so the key cannot probe which environments exist. **Authentication can be disabled entirely.** When neither `ORCH_AUTH_TOKEN` nor an API key store (`DATABASE_URL`) is configured — the default local-development posture — the bearer middleware becomes a pass-through: every operation this document marks as secured is reachable with no credentials, requests carry no principal, and master-only surfaces therefore admit everyone. Never run that configuration on a reachable network; set `ORCH_REQUIRE_AUTH=true` to make the orchestrator refuse to start without a master token.
or
fuse_sessionAPI key · cookierequired
HttpOnly session cookie set by POST /login for browser callers. Consulted only when no Authorization header is present.
Path parameters
interventionIdstringrequired
Query parameters
actionstringrequired
Allowed:approverejectsecret
Header parameters
Fuse-Actorstring
The person behind this request, as an opaque id (`user_42`, `alice@corp.com`, `slack:U0123`). Fuse records it on hitl interventions and the audit log and never checks it. Accepted on every authenticated route; it matters wherever an actor is recorded (raising, deciding, minting). At most 128 bytes, no control characters; a malformed value is 400. How the recorded actor is resolved: - key pinned to an actor: that actor (`source: pinned`). A different `Fuse-Actor` is 403 `forbidden`, reason `actor_mismatch`. - key with `hitl:act-as`: the header value (`source: asserted`, with the asserting key's id), else `key:<key_id>`. - any other key: `key:<key_id>` named after the key's label (`source: key`). Sending the header is 403 `forbidden`, reason `actor_not_allowed`, rather than silently dropped. - master token: the header value (`source: asserted`), else `master` (`source: key`). An asserted actor is only as true as the key holder's own auth.
max length 128
Fuse-Actor-Namestring
Optional display name for `Fuse-Actor`, at most 256 bytes. Sending it without `Fuse-Actor` is 400.
max length 256
Request body
application/json
DecideInterventionRequest for `approve` and `reject`; AnswerSecretRequest (required) for `secret`.
One of:
DecideInterventionRequest
answerstring
Required to approve an ask.
reasonstring
Text fed back to the agent. For a review, the reviewer's comments.
edited_argvstring[]
Replaces a held exec's argv on approval. It is re-checked against the deny gates.
edited_textstring
Replaces a held computer `type` action's text on approval. 400 on anything but a held `type`. It is re-checked against the deny gates.
min length 1 · max length 65536
grantstring
How far an approval of a gated action reaches: `once`, `env` or `ttl:<duration>`. Empty takes the gate's default. Cannot be combined with `edited_argv` or `edited_text`.
extend_secondsinteger<int64>
Approving an `expiry` only: how long to keep the environment away from the reaper, 60 to 86400. Omitted or 0 is one more full window of the reaper that was due, capped at 24h.
AnswerSecretRequest
valuestringrequired
Written into the guest at the declared target and dropped. Never stored, logged or returned.
max length 65536
Responses
200The resolved intervention.
idstringrequired
env_idstringrequired
kindstringrequired
Allowed:asksecrettakeoverreviewexeclifecyclecomputerstallexpiry
gatestring
The gate that raised it. Empty for a request.
modestring
The gate's mode. Notify and deny records are born terminal.
Allowed:asknotifydeny
statestringrequired
Allowed:pendingapprovedrejectedexpiredcancelled
payloadobjectrequired
What was asked or attempted: `question` for an ask, `secret`, `takeover` or `review` for those requests, `exec`, `lifecycle` or `computer` for a gate, `signal` for a stall or expiry. `decider` is set when a gate's decider sent the action on to a human.
Show properties
questionstring
choicesstring[]
execExecPayload
A held exec.
Show properties
argvstring[]required
shellstring
The script when the caller sent `shell`; argv is then the sh -lc that runs it.
timeout_msinteger<int64>
lifecycleLifecyclePayload
A held environment action.
Show properties
actionstringrequired
Allowed:destroyforkmigratedrainsnapshotrotate-token
secretInterventionSecret
A secret request. It never holds the value.
Show properties
targetstringrequired
The declared target's `env` name or `file` path.
labelstringrequired
The target's label from the policy. Show it first: the agent writes `message`, so `message` alone could claim to be anything.
messagestring
The agent's own text.
takeoverInterventionTakeover
A takeover. `holder` and `started_at` are set while a human holds the environment, which is also when the agent's exec and computer calls get 423. The intervention stays `pending` until release.
Show properties
viastringrequired
Allowed:terminaldesktop
reasonstring
holderActor
Show properties
idstringrequired
A pinned or asserted actor id, `key:<key_id>`, `master`, `system` for fuse itself, or `decider:<gate>`.
namestring
sourcestringrequired
`pinned`: fixed on the key at mint time. `asserted`: named in `Fuse-Actor` by a `hitl:act-as` key or the master token, and only as true as that caller's own auth. `key`: derived from the credential. `system`: expiry and cancellation. `decider`: a gate's decider allowed or denied the action; the id is `decider:<gate>`.
Allowed:pinnedassertedkeysystemdecider
key_idstring
The key that authenticated the request, including the key that asserted an actor. Omitted for the master token.
started_atstring<date-time>
signalInterventionSignal
What fuse saw when it raised a stall or expiry. A stall carries `failures` and `last_exit_code`, or `quiet_seconds`. An expiry carries `reaper` and `reap_at`, and once approved `extended_until`.
Show properties
failuresinteger
The run of consecutive non-zero exec exits.
last_exit_codeinteger
The exit code that completed the run.
quiet_secondsinteger<int64>
How long the agent had made no exec or computer call.
reaperstring
What will tear the environment down.
Allowed:idle_timeoutmax_runtime
reap_atstring<date-time>
When. The expiry's own deadline is capped at this, with a 10s floor.
extended_untilstring<date-time>
Set on approval. The reapers leave the environment alone until then.
computerComputerPayload
A held computer action. On approval the orchestrator replays `input` to the guest, with the approver's `edited_text` in place of `text` when there is one.
Show properties
actionstringrequired
The computer action name.
textstring
The text of a `type` (or the keys of a `key`), as sent.
inputobjectrequired
The whole action body as the caller sent it (a ComputerActionRequest).
reviewInterventionReview
A review request. When `path` is set, `content` is that file as fuse read it at request time, so the human reviews what the agent submitted even if the file changed afterwards.
Show properties
pathstring
contentstringrequired
formatstringrequired
Allowed:textdiff
messagestring
The agent's note to the reviewer.
truncatedboolean
Set when the file was longer than 1 MiB and was cut off.
deciderInterventionDecider
What a gate's decider said about an action it sent on to a human: `verdict` `ask` with its `reason`, or the `error` that made fuse fall through to a human.
Show properties
verdictstringrequired
Allowed:ask
reasonstring
errorstring
Set when the decider call failed.
triggered_byActorrequired
Show properties
idstringrequired
A pinned or asserted actor id, `key:<key_id>`, `master`, `system` for fuse itself, or `decider:<gate>`.
namestring
sourcestringrequired
`pinned`: fixed on the key at mint time. `asserted`: named in `Fuse-Actor` by a `hitl:act-as` key or the master token, and only as true as that caller's own auth. `key`: derived from the credential. `system`: expiry and cancellation. `decider`: a gate's decider allowed or denied the action; the id is `decider:<gate>`.
Allowed:pinnedassertedkeysystemdecider
key_idstring
The key that authenticated the request, including the key that asserted an actor. Omitted for the master token.
deadlinestring<date-time>required
on_timeoutstringrequired
Allowed:rejectallow
decisionobject
Present once terminal. An expired intervention carries the on_timeout outcome and a cancelled one a reject, both decided by "system".
Show properties
outcomestringrequired
Allowed:approvereject
answerstring
reasonstring
decided_byActorrequired
Show properties
idstringrequired
A pinned or asserted actor id, `key:<key_id>`, `master`, `system` for fuse itself, or `decider:<gate>`.
namestring
sourcestringrequired
`pinned`: fixed on the key at mint time. `asserted`: named in `Fuse-Actor` by a `hitl:act-as` key or the master token, and only as true as that caller's own auth. `key`: derived from the credential. `system`: expiry and cancellation. `decider`: a gate's decider allowed or denied the action; the id is `decider:<gate>`.
Allowed:pinnedassertedkeysystemdecider
key_idstring
The key that authenticated the request, including the key that asserted an actor. Omitted for the master token.
decided_atstring<date-time>required
edited_argvstring[]
The argv the approver replaced a held exec's with.
edited_textstring
The text the approver replaced a held `type` action's with.
grantstring
How far an approval of a gated action reaches.
resultInterventionResult
What performing an approved gated action produced.
Show properties
execExecEnvironmentResponse
The outcome of a guest command. A non-zero `exit_code` arrives with **HTTP 200**: the command ran and failed, which is a successful answer to the question the caller asked. It is not an error and must not be raised by an SDK. HTTP errors mean the command could not be run at all. `stdout` and `stderr` are kept apart, and are plain strings rather than base64 — exec output is overwhelmingly text, and encoding it would make every caller pay a decoding tax for the rare binary case.
Show properties
exit_codeintegerrequired
The guest command's own exit status. Non-zero is a normal result, delivered with HTTP 200.
stdoutstringrequired
stderrstringrequired
computerComputerActionResponse
The guest's answer to one action.
Show properties
outputstring
Textual output, e.g. the pointer location for `cursor_position`.
screenshotstring
Base64 PNG of the display after the action settled. Present on every action that implies one; for `zoom` it is the cropped region at full resolution.
env_idstring
The environment a fork or migrate produced.
snapshot_idstring
The snapshot a snapshot action produced.
errorstring
Set when the approved action could not be performed. A non-zero exec exit code is a result in `exec`, not an error.
finished_atstring<date-time>required
created_atstring<date-time>required
updated_atstring<date-time>required
400Malformed request.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
403The caller authenticated, but may not make this request. Master-only surfaces (exec, attach, listing and revoking API keys) refuse API keys; those 403 bodies carry code `unauthorized`. CIDR allowlist rejections return 403 with code `forbidden` and no reason. Access checks on the hitl routes and scoped keys return 403 with code `forbidden` and `details.reason` set to one of: - `missing_scope`: the key lacks the scope the route needs (`details.scope` names it). Full-access keys never hold `hitl:decide`, `hitl:secret`, `hitl:takeover` or `hitl:act-as`. - `env_scope`: a scoped key called a route outside the hitl routes. - `self_approval`: the key or actor that raised an intervention tried to decide it. - `actor_required`: the environment sets `require_actor` and the decision names no pinned or asserted actor. - `actor_mismatch`: `Fuse-Actor` differs from the key's pinned actor. - `actor_not_allowed`: `Fuse-Actor` sent by a key without `hitl:act-as`. - `hitl_denied`: a hitl gate in `deny` mode, or a gate's decider, refused an exec, lifecycle or computer action, or an edit matched a `deny` gate. `details.gate` names the gate and `details.intervention_id` the terminal record; the message is the gate's `reason`. - `mint_escalation`: a scoped key tried to mint a key wider than itself.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
404Resource does not exist. Note the distinct code `route_not_found`, returned when the URL matches no registered route at all (wrong host, port, or path prefix) rather than a known route with a missing resource.
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
409Conflicts with current state (e.g. duplicate task assignment).
errorobjectrequired
Show properties
codestringrequired
Stable machine-readable code. `route_not_found` (404) means the URL matches no route this server exposes — usually a wrong host, port, or path prefix — as opposed to `not_found`, where the route exists but the resource does not. `forbidden` is emitted by CIDR-allowlist rejections and by hitl access checks, which set `details.reason` (see the Forbidden response); master-only refusals (exec, attach, listing and revoking API keys) return 403 with code `unauthorized`.
Allowed:not_foundroute_not_foundconflictinvalid_argumentunauthorizedforbiddenunavailableinternalunimplementedlocked
messagestringrequired
Human-readable description.
detailsobject
Optional stable, non-sensitive metadata (e.g. ids, counts). Omitted when empty. A 403 `forbidden` from a hitl access check carries `reason` (`missing_scope`, `env_scope`, `self_approval`, `actor_required`, `actor_mismatch`, `actor_not_allowed`, `mint_escalation`, `hitl_denied`), plus `scope` for `missing_scope`, and `gate` and `intervention_id` for `hitl_denied`. A 423 `locked` carries `intervention_id` and `kind` (`takeover` or `stall`).
Request
curl -X POST "http://localhost:8080/v1/interventions/string?action=approve" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "answer": "string",
  "reason": "string",
  "edited_argv": [
    "string"
  ],
  "edited_text": "string",
  "grant": "string",
  "extend_seconds": 0
}'
Response
{
  "id": "string",
  "env_id": "string",
  "kind": "ask",
  "gate": "string",
  "mode": "ask",
  "state": "pending",
  "payload": {
    "question": "string",
    "choices": [
      "string"
    ],
    "exec": {
      "argv": [
        "string"
      ],
      "shell": "string",
      "timeout_ms": 0
    },
    "lifecycle": {
      "action": "destroy"
    },
    "secret": {
      "target": "string",
      "label": "string",
      "message": "string"
    },
    "takeover": {
      "via": "terminal",
      "reason": "string",
      "holder": {
        "id": "string",
        "name": "string",
        "source": "pinned",
        "key_id": "string"
      },
      "started_at": "2024-01-01T00:00:00Z"
    },
    "signal": {
      "failures": 0,
      "last_exit_code": 0,
      "quiet_seconds": 0,
      "reaper": "idle_timeout",
      "reap_at": "2024-01-01T00:00:00Z",
      "extended_until": "2024-01-01T00:00:00Z"
    },
    "computer": {
      "action": "string",
      "text": "string",
      "input": {}
    },
    "review": {
      "path": "string",
      "content": "string",
      "format": "text",
      "message": "string",
      "truncated": true
    },
    "decider": {
      "verdict": "ask",
      "reason": "string",
      "error": "string"
    }
  },
  "triggered_by": {
    "id": "string",
    "name": "string",
    "source": "pinned",
    "key_id": "string"
  },
  "deadline": "2024-01-01T00:00:00Z",
  "on_timeout": "reject",
  "decision": {
    "outcome": "approve",
    "answer": "string",
    "reason": "string",
    "decided_by": {
      "id": "string",
      "name": "string",
      "source": "pinned",
      "key_id": "string"
    },
    "decided_at": "2024-01-01T00:00:00Z",
    "edited_argv": [
      "string"
    ],
    "edited_text": "string",
    "grant": "string"
  },
  "result": {
    "exec": {
      "exit_code": 0,
      "stdout": "hi\n",
      "stderr": ""
    },
    "computer": {
      "output": "string",
      "screenshot": "string"
    },
    "env_id": "string",
    "snapshot_id": "string",
    "error": "string",
    "finished_at": "2024-01-01T00:00:00Z"
  },
  "created_at": "2024-01-01T00:00:00Z",
  "updated_at": "2024-01-01T00:00:00Z"
}