Undo a watched session in one step (Pro)
const url = 'http://127.0.0.1:8080/v1/observe/sessions/example/undo';const options = { method: 'POST', headers: { 'Deedglass-Version': '2026-10-01', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, body: '{"plan_digest":"example","reason":"example","allow_triggers":true,"wait":1}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://127.0.0.1:8080/v1/observe/sessions/example/undo \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'Deedglass-Version: 2026-10-01' \ --data '{ "plan_digest": "example", "reason": "example", "allow_triggers": true, "wait": 1 }'Pro (undo_session): 403 pro_required without it, naming the free path. Previews again, refuses undo_conflict (rows changed after the session), undo_unavailable (with error.checks), undo_plan_stale (the plan changed since the preview) or already_undone, then sends the undo through the Gate as an action of the org’s console system agent: tool <connector>.undo_session, policy (the starter rule asks for a countersign), one transaction in Postgres, an L2 witness against the plan’s exact footprint, and its own deed. The answer is the action object (202 pending_approval, or 200 once sealed).
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The session (obs_…).
Header Parameters
Section titled “Header Parameters”The API version (YYYY-MM-DD). Leave it out to use your org’s pinned version (public routes use the latest). An unknown version is 400 api_version_invalid.
Needed when the request uses a Console session (it changes state); without it the answer is 403 csrf_failed. Not sent with an API key.
1 to 255 printable ASCII characters. A retry with the same key and body returns the stored response with Deedglass-Idempotent-Replayed: true for 24 hours.
Request Bodyrequired
Section titled “Request Bodyrequired”object
The preview’s plan_digest.
Seconds to wait for the outcome.
Examplegenerated
{ "plan_digest": "example", "reason": "example", "allow_triggers": true, "wait": 1}Responses
Section titled “Responses”The undo action, sealed.
One agent action. Its sections mirror the deed’s. status is the one field an agent needs; state is the exact lifecycle state (docs/GATE.md). The Gate’s own responses (POST /v1/actions and its result report) add execution.result and witness.reasons; lists leave both out.
object
true for a dg_live_ key, false for a dg_test_ key. Recorded, not enforced: connectors have no test mode in v1.
object
executed: the tool ran and the action is sealed, whatever the verdict. failed: the tool certainly didn’t apply the call. authorized: proxy mode, run the tool at the edge now. processing: still executing or witnessing. pending_approval: waiting for a person. denied: a policy, a budget or Stop refused it, and nothing ran. expired and cancelled: it waited for approval and never ran. A sealed action keeps the status of the state it was sealed from.
The action’s undo object (S23). undoes names the action an undo puts back; undone_by names the undo that put this one back; pending an undo waiting for approval or running. eligible is cheap (L2 evidence from a bound transaction, gateway mode, sealed, not undone, within undo.max_age, the row images kept): it only decides whether the Console shows Preview undo. The full answer is always the preview.
object
S40, additive: the window an approved action runs in, stored once at creation and never changed. Absent when the request carried no schedule.
object
The policy decision as an agent reads it: S11’s decision, its source, its message verbatim, the policy version that made it (pinned: a later activation doesn’t change it), and the shadow decision next to it. null until the action is evaluated.
object
The refusal code (policy_denied, budget_exceeded, agent_stopped, tool_unknown, …), approval_required, or null for an allow.
builtin is the Gate’s own decision, such as tool_unknown.
The sentence the agent sees
The policy id (pol_…).
The policy version id (pv_…).
S27, additive: the decision trace’s line about the anomaly facts the policy saw, like anomaly facts: volume_spike, or anomaly facts: detector_stale (…) when detection was behind and the policy treated the agent as anomalous. Absent when no anomaly fact was active.
What the shadow policy would have decided. Recorded, never enforced.
object
object
The approval the action waits for, or waited for. null when no rule asked for one.
object
The approval id (apr_…).
S22, additive: the reason a person gave when they denied it. Absent otherwise.
object
A short, redacted message from the tool or the Gate (at most 1 KiB).
The tool’s result. Only on the Gate’s own responses (POST /v1/actions and POST /v1/actions/{id}/result); never on lists. GET /v1/actions/{id} has it as the top-level result.
The Witness’s verdict about the action (S19): one level, one verdict and the method of that level. A verdict is never stronger than its evidence: VERIFIED needs a clean level at or above required_level; any contradicting evidence is MISMATCH; missing evidence is UNVERIFIABLE. Lists carry the claim only. The Gate’s own responses and GET /v1/actions/{id} add summary, reasons (violations and gaps; passes and notes are in the detail) and witnessed_at; ?expand[]=witness.detail adds detail. An executed action answers 200 whatever the verdict: the tool ran, and the verdict says what that means.
object
The claimed level’s method: reported, edge_reported or response.schema (L0); readback.<connector type>.<read tool>, readback.postgres.row or postcondition.<connector type> (L1); postgres.pgoutput (L2).
The level the policy’s verify: asks for (null on actions recorded before S19).
One sentence a support engineer can read aloud.
One piece of evidence, or one missing piece. The class and the message come from the reason catalog (docs/witness/REASONS.md). The full WitnessReason schema is in openapi.yaml.
object
sha256: of JCS(witness_detail): the deed’s witness.detail_digest.
Witness_detail v1 (S19): what actions.witness_detail holds and what the deed’s witness.detail_digest is the digest of. Changes are additive only; S21 fills levels.L2. The full WitnessDetail schema is in openapi.yaml.
object
GET /v1/actions/{id} only (S21): every witness section sealed about the action, oldest first: the action deed, then any amendment deeds (a later verdict never edits a deed; at most three amendments). An entry a later amendment replaced names the reason it stopped where it did (l2_timeout, …).
object
object
“sha256:” + lowercase hex of SHA-256.
What the caller does next. await_approval: wait for a person (poll_url long-polls the action). execute_at_edge: proxy mode, run the tool at the edge and report the result to report_url before deadline. scheduled (S40): approved, it runs between not_before and not_after. null otherwise.
object
await_approval only: the approval id.
scheduled only (S40): an approved action runs from this time.
scheduled only (S40): past it the action is cancelled and sealed with schedule_missed; nothing runs.
await_approval only.
await_approval only: /v1/actions/{id}?wait=25.
execute_at_edge only: /v1/actions/{id}/result.
execute_at_edge only: after it, the action is sealed UNVERIFIABLE (edge_lost).
Example
{ "object": "action", "id": "act_01j9zt3a5c7e9g1j3m5p7r9t1v", "created_at": "2026-10-02T10:14:03.401Z", "livemode": true, "agent": { "id": "agt_01j9z9b1d3f5h7k9m1p3r5t7v9", "name": "support-bot" }, "tool": "postgres.execute", "connector": null, "mode": "gateway", "state": "sealed", "status": "executed", "on_behalf_of": "u_4821", "session": "chat_77", "decision": { "outcome": "allow", "code": null, "source": "rule", "rule_id": "allow-order-updates", "reason": "Order updates are allowed.", "message": null, "policy": null, "policy_version": null, "shadow": null }, "policy": { "id": null, "version_id": null, "mode": "enforce", "decision": "allow", "rule_id": "allow-order-updates" }, "approval": null, "execution": { "status": "succeeded", "started_at": "2026-10-02T10:14:03.455Z", "finished_at": "2026-10-02T10:14:03.496Z", "latency_ms": 41, "error_code": null, "error_message": null }, "witness": { "object": "witness", "level": "L2", "verdict": "MISMATCH", "method": "postgres.pgoutput", "required_level": "L0", "detail_digest": "sha256:c3e1a07e5d1c9b20c3e1a07e5d1c9b20c3e1a07e5d1c9b20c3e1a07e5d1c9b20" }, "cost": null, "request_digest": "sha256:9c1e5b7d0a2f4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e", "result_digest": null, "payload_pruned_at": null, "deed": "deed_01j9zt3q5s7v9w1y3a5c7e9g1j", "next_action": null}Headers
Section titled “Headers”This request’s id. Quote it to support; it matches error.request_id.
The API version this response follows.
The undo action, waiting (usually for a countersign).
One agent action. Its sections mirror the deed’s. status is the one field an agent needs; state is the exact lifecycle state (docs/GATE.md). The Gate’s own responses (POST /v1/actions and its result report) add execution.result and witness.reasons; lists leave both out.
object
true for a dg_live_ key, false for a dg_test_ key. Recorded, not enforced: connectors have no test mode in v1.
object
executed: the tool ran and the action is sealed, whatever the verdict. failed: the tool certainly didn’t apply the call. authorized: proxy mode, run the tool at the edge now. processing: still executing or witnessing. pending_approval: waiting for a person. denied: a policy, a budget or Stop refused it, and nothing ran. expired and cancelled: it waited for approval and never ran. A sealed action keeps the status of the state it was sealed from.
The action’s undo object (S23). undoes names the action an undo puts back; undone_by names the undo that put this one back; pending an undo waiting for approval or running. eligible is cheap (L2 evidence from a bound transaction, gateway mode, sealed, not undone, within undo.max_age, the row images kept): it only decides whether the Console shows Preview undo. The full answer is always the preview.
object
S40, additive: the window an approved action runs in, stored once at creation and never changed. Absent when the request carried no schedule.
object
The policy decision as an agent reads it: S11’s decision, its source, its message verbatim, the policy version that made it (pinned: a later activation doesn’t change it), and the shadow decision next to it. null until the action is evaluated.
object
The refusal code (policy_denied, budget_exceeded, agent_stopped, tool_unknown, …), approval_required, or null for an allow.
builtin is the Gate’s own decision, such as tool_unknown.
The sentence the agent sees
The policy id (pol_…).
The policy version id (pv_…).
S27, additive: the decision trace’s line about the anomaly facts the policy saw, like anomaly facts: volume_spike, or anomaly facts: detector_stale (…) when detection was behind and the policy treated the agent as anomalous. Absent when no anomaly fact was active.
What the shadow policy would have decided. Recorded, never enforced.
object
object
The approval the action waits for, or waited for. null when no rule asked for one.
object
The approval id (apr_…).
S22, additive: the reason a person gave when they denied it. Absent otherwise.
object
A short, redacted message from the tool or the Gate (at most 1 KiB).
The tool’s result. Only on the Gate’s own responses (POST /v1/actions and POST /v1/actions/{id}/result); never on lists. GET /v1/actions/{id} has it as the top-level result.
The Witness’s verdict about the action (S19): one level, one verdict and the method of that level. A verdict is never stronger than its evidence: VERIFIED needs a clean level at or above required_level; any contradicting evidence is MISMATCH; missing evidence is UNVERIFIABLE. Lists carry the claim only. The Gate’s own responses and GET /v1/actions/{id} add summary, reasons (violations and gaps; passes and notes are in the detail) and witnessed_at; ?expand[]=witness.detail adds detail. An executed action answers 200 whatever the verdict: the tool ran, and the verdict says what that means.
object
The claimed level’s method: reported, edge_reported or response.schema (L0); readback.<connector type>.<read tool>, readback.postgres.row or postcondition.<connector type> (L1); postgres.pgoutput (L2).
The level the policy’s verify: asks for (null on actions recorded before S19).
One sentence a support engineer can read aloud.
One piece of evidence, or one missing piece. The class and the message come from the reason catalog (docs/witness/REASONS.md). The full WitnessReason schema is in openapi.yaml.
object
sha256: of JCS(witness_detail): the deed’s witness.detail_digest.
Witness_detail v1 (S19): what actions.witness_detail holds and what the deed’s witness.detail_digest is the digest of. Changes are additive only; S21 fills levels.L2. The full WitnessDetail schema is in openapi.yaml.
object
GET /v1/actions/{id} only (S21): every witness section sealed about the action, oldest first: the action deed, then any amendment deeds (a later verdict never edits a deed; at most three amendments). An entry a later amendment replaced names the reason it stopped where it did (l2_timeout, …).
object
object
“sha256:” + lowercase hex of SHA-256.
What the caller does next. await_approval: wait for a person (poll_url long-polls the action). execute_at_edge: proxy mode, run the tool at the edge and report the result to report_url before deadline. scheduled (S40): approved, it runs between not_before and not_after. null otherwise.
object
await_approval only: the approval id.
scheduled only (S40): an approved action runs from this time.
scheduled only (S40): past it the action is cancelled and sealed with schedule_missed; nothing runs.
await_approval only.
await_approval only: /v1/actions/{id}?wait=25.
execute_at_edge only: /v1/actions/{id}/result.
execute_at_edge only: after it, the action is sealed UNVERIFIABLE (edge_lost).
Example
{ "object": "action", "id": "act_01j9zt3a5c7e9g1j3m5p7r9t1v", "created_at": "2026-10-02T10:14:03.401Z", "livemode": true, "agent": { "id": "agt_01j9z9b1d3f5h7k9m1p3r5t7v9", "name": "support-bot" }, "tool": "postgres.execute", "connector": null, "mode": "gateway", "state": "sealed", "status": "executed", "on_behalf_of": "u_4821", "session": "chat_77", "decision": { "outcome": "allow", "code": null, "source": "rule", "rule_id": "allow-order-updates", "reason": "Order updates are allowed.", "message": null, "policy": null, "policy_version": null, "shadow": null }, "policy": { "id": null, "version_id": null, "mode": "enforce", "decision": "allow", "rule_id": "allow-order-updates" }, "approval": null, "execution": { "status": "succeeded", "started_at": "2026-10-02T10:14:03.455Z", "finished_at": "2026-10-02T10:14:03.496Z", "latency_ms": 41, "error_code": null, "error_message": null }, "witness": { "object": "witness", "level": "L2", "verdict": "MISMATCH", "method": "postgres.pgoutput", "required_level": "L0", "detail_digest": "sha256:c3e1a07e5d1c9b20c3e1a07e5d1c9b20c3e1a07e5d1c9b20c3e1a07e5d1c9b20" }, "cost": null, "request_digest": "sha256:9c1e5b7d0a2f4c6e8b0d2f4a6c8e0b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e", "result_digest": null, "payload_pruned_at": null, "deed": "deed_01j9zt3q5s7v9w1y3a5c7e9g1j", "next_action": null}Headers
Section titled “Headers”This request’s id. Quote it to support; it matches error.request_id.
The API version this response follows.
default
Section titled “default”An error. Every error has this envelope; doc_url explains the code.
object
object
One or more calm sentences with the next step.
The request field, query parameter or header the error is about.
On policy_invalid only. Each problem with its line, code and hint.
One finding about a policy file, with its position and the next step. Codes are stable and listed in docs/POLICY_ERRORS.md. In the policy_invalid error envelope each diagnostic also carries object: policy_diagnostic. The full PolicyDiagnostic schema is in openapi.yaml.
object
One agent action. Its sections mirror the deed’s. status is the one field an agent needs; state is the exact lifecycle state (docs/GATE.md). The Gate’s own responses (POST /v1/actions and its result report) add execution.result and witness.reasons; lists leave both out. The full Action schema is in openapi.yaml.
object
On undo_unavailable only. Every check of the undo, each with its fix. Additive (S23).
One of the twelve safety checks (S23 §3.3). Available means no check failed. The full UndoCheck schema is in openapi.yaml.
object
Example
{ "error": { "type": "invalid_request_error", "code": "not_found", "message": "There's no endpoint at GET /v1/helth. Did you mean /v1/health?", "param": null, "request_id": "req_01j9zr3m5k8q2w7x4t6v0b1c9d", "doc_url": "https://docs.deedglass.com/errors/not_found" }}Headers
Section titled “Headers”This request’s id. Quote it to support; it matches error.request_id.
The API version this response follows.