Sign-offs
Your agent can tighten its own guardrails at once, but a change that loosens them waits for a workspace admin to approve it.
An agent may never loosen its own guardrails. When your agent, your CLI login or an MCP session asks to raise a limit, remove a budget, add an action or relax an approval policy, Anlyon does not apply the change. It records a sign-off request and answers 202 with a link. A workspace admin opens the link, reads what would change, and approves or rejects it. A change that tightens a guardrail applies at once, as before.
Why sign-offs matter
A coding agent that sets Anlyon up holds a credential that can change the rules it runs under. Without sign-offs, one mistaken or manipulated step can raise the refund limit it is held to, or turn off the approval that stops it. With sign-offs:
- The limits you set stay set. An agent can lower a limit or add a policy on its own. It cannot raise or remove one without a person saying yes.
- Setup still runs unattended. Most setup tightens or adds nothing risky, so it goes through. The few changes that loosen wait for one click from an admin instead of blocking the whole run.
- You see exactly what changes. Each request carries a plain diff, field by field, with the reason it counts as loosening.
- Every step is on the record. The request, the approval or rejection, a cancellation and an expiry are each an audit record.
What needs a sign-off
A change needs a sign-off when it loosens a guardrail and it comes from an API key, a CLI login, an MCP session or an end-user session token. A person signed in to the console applies changes directly.
| Change | Tightens, applies at once | Loosens, needs a sign-off |
|---|---|---|
| Impact limit | Create one, lower one, rename one | Raise one, archive one |
| Budget for a key | Add a cap, lower a cap, alert earlier | Raise or remove a cap, alert later, turn the budget off |
| Approval policy | Add or tighten a policy that is at least as strict as what it takes over, delete an auto-approve policy | Add an auto-approve policy, require fewer approvals, change who decides, disable or delete a policy that gated calls, widen what auto-approve matches |
| Policy file apply | Every change in the file tightens | Any one change in the file loosens |
| Kill switch | Halt the environment | Resume it |
| Action | Disable or delete one, require approval, add a read-back check | Create an enabled action, import actions, enable one, stop requiring approval, change where or how it sends |
| Secret | Narrow its hosts or placements, rotate its value | Widen its hosts or placements, point it at a secret store |
| Secret store | Remove one | Connect one, change one, let it reach private addresses |
| Your own OAuth app | Remove it | Set or replace it |
Anything Anlyon cannot show to be tighter counts as loosening. A new setting counts as loosening until it is classified.
What your agent gets back
The route answers 202 Accepted instead of applying the change. Location and data.pollUrl name the request to poll.
{
"success": true,
"signOffRequired": true,
"data": {
"object": "sign_off_request",
"id": "sor_V1StGXR8Z5jdHi6BmyT9q",
"status": "pending",
"operation": "impact_limit.update",
"summary": "Raise impact limit \"Daily refunds\" from 500 to 5000 usd",
"diff": "amount (loosens): Raises the limit.\n- 500\n+ 5000",
"signOffUrl": "https://console.anlyon.com/sign-offs/sor_V1StGXR8Z5jdHi6BmyT9q",
"pollUrl": "/api/v2/sign-offs/sor_V1StGXR8Z5jdHi6BmyT9q",
"pollAfterSeconds": 5,
"expiresAt": "2026-10-29T10:00:00.000Z"
}
}
Give signOffUrl to a person. Then read GET /api/v2/sign-offs/{id} every pollAfterSeconds until status is no longer pending:
| Status | Meaning |
|---|---|
pending |
Waiting for a workspace admin. |
applied |
Approved, and the change is in effect. result holds what the original call would have returned. |
rejected |
An admin said no. Nothing changed. |
expired |
Nobody decided within 24 hours. Nothing changed. |
cancelled |
The credential that asked withdrew it with POST /api/v2/sign-offs/{id}/cancel. Nothing changed. |
failed |
Approved, but it could not be applied. error.code says why, such as SIGN_OFF_STALE when what it changes moved after it was asked for. Nothing changed. |
Asking for the same change again from the same credential returns the same pending request, so a retry never creates a second one.
Approve or reject a request
A workspace admin decides, in one of two places:
- The console. Sign-offs lists every request with its diff. Approve applies the change; Reject leaves everything as it is.
- The email. Each admin is emailed a link of their own. Opening it shows the change and decides nothing, so a mail scanner cannot approve it. Approving or rejecting is a button on that page.
Requests also reach your notification channels, such as Slack, with a link to the console.
No API key, CLI login or MCP session can approve a request, whatever its scopes, including the credential that asked. You can approve your own agent’s request from the console: the rule is about the channel, not the person.
Use it from the CLI, an MCP client or the SDK
anlyon actions declare refund-order --ungoverned --yes --wait 10m
# Needs a human's sign-off. A workspace admin must approve this change before it applies:
# https://console.anlyon.com/sign-offs/sor_...
# Without --wait the command exits 15. With --wait: 0 when applied, 14 when it ends any other way, 9 when the wait runs out.
anlyon sign-offs list --status pending
anlyon sign-offs get sor_V1StGXR8Z5jdHi6BmyT9q --wait 10m
anlyon sign-offs cancel sor_V1StGXR8Z5jdHi6BmyT9qimport { Client, SignOffRequiredError } from '@anlyonhq/sdk';
const anlyon = new Client({ apiKey: process.env.ANLYON_API_KEY! });
try {
await anlyon.impactLimits.update('lim_01JABCDEF', { amount: 5000 });
} catch (error) {
if (!(error instanceof SignOffRequiredError)) throw error;
console.log(`Ask a workspace admin to approve: ${error.signOffUrl}`);
const decided = await anlyon.signOffs.wait(error.signOffId);
console.log(decided.status); // applied, rejected, expired, cancelled or failed
}from anlyon import Client, SignOffRequiredError
anlyon = Client()
try:
anlyon.impact_limits.update("lim_01JABCDEF", amount=5000)
except SignOffRequiredError as error:
print(f"Ask a workspace admin to approve: {error.sign_off_url}")
decided = anlyon.sign_offs.wait(error.sign_off_id)
print(decided["status"])In an MCP client, a tool call whose change is held returns a normal result that starts with “A human must approve this change before it applies. Give them this link:”, followed by the diff. The agent can call wait_for_sign_off or check_sign_off with the request’s id.
Fix the errors you will meet
| Status and code | When | What to do |
|---|---|---|
202 with signOffRequired: true |
The change loosens a guardrail. | Give data.signOffUrl to a workspace admin and poll data.pollUrl. |
409 SIGN_OFF_NOT_PENDING |
Approving, rejecting or cancelling a request that already ended. | Read the request. If it expired or failed, make the change again to ask anew. |
400 SIGN_OFF_CANCEL_BY_REQUESTER |
A console session tried to cancel a request. | Reject it instead. |
403 AUTHORIZATION_ERROR |
A member who is not an admin tried to decide. | Ask a workspace admin. |
Related
- Approvals and policies: the approvals your agent’s calls wait for.
- Workspace defaults: the rules no default can change.
- Audit: the records each request leaves.
- Authentication and API keys: scopes and operator keys.

