Approvals
Make an agent's action wait for people to approve it, decide approvals in the console, the CLI or the API, and gate actions such as posting to Slack.
On this page
An approval holds one agent action until enough of the right people approve it. Titan records the request before it answers the agent, so nothing runs until the approval is complete, and Titan runs the action itself once it is.
How an approval works
- A rule in your deployment's policy has the effect
require_approvalfor the action, for example posting a message to a Slack channel. - The agent calls the capability. Titan does not run it: it records an approval request and answers the agent with
202and the request's id (apr_...). - Approvers see the request in the console, the CLI or the API, and approve or deny it.
- When enough approvers have approved, Titan checks the action again against the policy in force now, then runs it and records the execution. If that check fails, or the request has expired, the request is denied and nothing runs.
- The agent reads the outcome with
GET /v1/titan/agent/approvals/{id}. Only the requesting agent, on the same deployment and runtime principal, can read it.
A single deny ends the request at once.
Who can approve
The rule names one or more approval profiles (approval_profile_ids), and each profile is defined in the same policy:
| Field | Meaning |
|---|---|
role_ids | Approval roles whose members may approve. A role (approval_roles) lists people by their user id (human_principal_ids). |
quorum | How many approvals complete the request. |
requester_separation_required | The person who made the request can't approve it. |
maximum_age_seconds | How long the request stays open. |
To decide, a person also needs the titan:approval:decide permission in your organization, and titan:approval:read to list requests. Someone who isn't a member of an applicable role is refused with 403 NOT_AN_ELIGIBLE_APPROVER.
How long a request stays open
A request expires at the earliest of:
- the profile's
maximum_age_seconds; - 24 hours;
- the end of the agent's current runtime token, which lasts 5 minutes.
On Titan Cloud an agent's approval therefore has to be decided within a few minutes. An expired request reads as expired and can't be approved.
Decide in the console
Open Titan → Approvals in the console, find the pending request, and approve or deny it.
Decide with the CLI
hexel titan approval list # pending requests; --state all for every state
hexel titan approval approve apr_0123456789abcdef0123456789abcdef
hexel titan approval deny apr_0123456789abcdef0123456789abcdefSee hexel titan approval in the command reference.
Decide with the API
List pending requests:
curl "https://api.hexelstudio.com/v1/titan/admin/approvals?state=pending" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Titan-Enforcement-Deployment: $TITAN_ENFORCEMENT_ID"state is one of pending, approved, denied, revoked, expired or invalidated. Each item has the approval_request_id, the capability, operation, resource_type and resource_id, the requesting agent, recorded_approvals and expires_at.
Approve or deny one:
curl -X POST "https://api.hexelstudio.com/v1/titan/admin/approvals/apr_0123456789abcdef0123456789abcdef:decide" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Titan-Enforcement-Deployment: $TITAN_ENFORCEMENT_ID" \
-H "Content-Type: application/json" \
-d '{"decision": "approve"}'decision is approve or deny. The response is the request's new state:
state | Meaning |
|---|---|
pending | Your vote is recorded; more approvals are needed. |
approved | The quorum was reached and Titan ran the action. execution_id names the execution in the Agent Ledger. |
denied | You denied it, or the action no longer passed the policy check. |
expired | The request expired before the quorum was reached. |
Deciding a request that is no longer pending returns 409 APPROVAL_NOT_PENDING.
Example: approve before an agent posts to Slack
On Titan Cloud, approvals are decided in the console, the CLI or the API. Titan doesn't send approval requests into Slack. What you can do is make an agent's Slack actions wait for approval.
With a Slack connection on your deployment, a policy can allow the agent to read channels and require approval for every post to one channel:
{
"approval_roles": [
{ "role_id": "release-approvers", "human_principal_ids": ["<user id>"] }
],
"approval_profiles": [
{
"approval_profile_id": "post-approval",
"role_ids": ["release-approvers"],
"quorum": 1,
"requester_separation_required": false,
"maximum_age_seconds": 300
}
],
"rules": [
{
"rule_id": "approve-slack-post",
"effect": "require_approval",
"match": { "capability_ids": ["slack.message.post"] },
"approval_profile_ids": ["post-approval"]
}
]
}This is an excerpt of a policy, not a complete one; your deployment's policy also needs its other required fields and its allow rules. When the agent posts, it gets 202 with the request id. An approver approves it in Titan → Approvals within the five minutes, and Titan then posts the message and records the execution.