Skip to content
Hexel StudioDocs

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

  1. A rule in your deployment's policy has the effect require_approval for the action, for example posting a message to a Slack channel.
  2. The agent calls the capability. Titan does not run it: it records an approval request and answers the agent with 202 and the request's id (apr_...).
  3. Approvers see the request in the console, the CLI or the API, and approve or deny it.
  4. 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.
  5. 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:

FieldMeaning
role_idsApproval roles whose members may approve. A role (approval_roles) lists people by their user id (human_principal_ids).
quorumHow many approvals complete the request.
requester_separation_requiredThe person who made the request can't approve it.
maximum_age_secondsHow 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

Bash
hexel titan approval list            # pending requests; --state all for every state
hexel titan approval approve apr_0123456789abcdef0123456789abcdef
hexel titan approval deny apr_0123456789abcdef0123456789abcdef

See hexel titan approval in the command reference.

Decide with the API

List pending requests:

Bash
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:

Bash
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:

stateMeaning
pendingYour vote is recorded; more approvals are needed.
approvedThe quorum was reached and Titan ran the action. execution_id names the execution in the Agent Ledger.
deniedYou denied it, or the action no longer passed the policy check.
expiredThe 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:

JSON
{
  "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.