Features marked Planned are not shipped. Everything else on this page is backed by code in the current SDK.
Who is accountable when an AI agent acts?
The human or team that owns the agent. In theAuth every agent is created with anownerId that must match a real user row, and every audit row stores the agent that acted. Delegation records who gave what to whom, so you can walk back from a sub-agent to the agent that delegated to it and to that agent’s owner.
ownerId, but the accountable human is the owner of the root agent in its delegation chain. delegate() returns that owner as rootOwnerId and the agent ids from the root down as path, and policy.evaluate() returns the same lineage on decision.principal.
How do you give an AI agent an identity?
Create it as its own principal, not as a copy of a user session. An agent has a stable id, a hashed bearer token, a type, an expiry and a status. It has no password and no browser session.theauth.agent.rotate(agentId), and the old token stops working at once. See Agent identity. For agents that call your MCP servers, see MCP OAuth 2.1.
How do you limit what an AI agent can do?
Check every action before it runs.authorize() evaluates the agent’s permissions and any it received through delegation, then returns allowed or not. policy.evaluate() does the same and returns a three-way decision: allow, deny or escalate. Constraints narrow a permission: a call rate, argument patterns, a time window, an IP allowlist, and human approval.
requireApproval permission is always denied by authorize(). Your code reads the reason, opens an approval request and runs the action itself after a human approves. policy.evaluate() returns escalate for the same permission instead, which is easier to branch on. See Approval flows and Permissions.
To let a sub-agent act within a narrower scope, delegate a subset with a short expiry. Constraints on the delegated permissions are stored and enforced, and a child can only tighten what its parent holds: requireApproval is OR-ed, maxCallsPerHour takes the lower value, and lists are intersected.
Escalate above an amount
The amount threshold is a risk-tier rule, not a numeric permission constraint. You pass the amount incontext.risk, and a tier in the policy config decides what an action of that size requires. A permitted action that lands in an approval or step_up tier becomes escalate.
amount, currency, dataClass, irreversible and extras. A rule can match on minAmount, currency, dataClasses and irreversible, and the strongest matching requirement wins. Risk only escalates an action that would otherwise be allowed. Cumulative spend is not tracked. See Policy engine.
Act only for the owner
Pass the owner the request claims to act for assubject.ownerId. If it differs from the agent’s ownerId, evaluate() denies with POLICY_OWNER_MISMATCH. A revoked, suspended or expired agent is denied with POLICY_AGENT_INACTIVE. Both checks run before the decision cache, so they take effect at once.
How do you ask a human before the agent acts?
UserequireApproval or a risk tier, then the approval module. The request stores the agent, the owner, the action, the resource and the arguments. The human’s answer and their identifier are stored on the request. Calling policy.evaluate(input, { openApproval: true }) opens the request for you on escalate and returns its id as decision.escalation.approvalRequestId. Otherwise open it yourself:
webhookUrl or onApprovalNeeded. See Approval flows.
How do you audit AI agent actions?
Every decision about a known agent writes an audit row, with the action, resource, arguments and result (allowed, denied or escalated). Rows also carry subject_id (the accountable human), actor_id (the agent), policy_version (from policy.policyVersion), approver_id, delegation_chain, input_provenance and hash_version. Turn on the hash chain to make edits and deletions detectable, then verify, replay and export.
verifyAuditExport checks it later without a database. See Tamper-evident audit trail and Audit log.
The decision row and the approval answer are separate rows. The decision row holds the inputs, the policy version and the lineage. The approval row holds approver_id. Replay merges them in time order. Three limits apply:
input_provenanceis a column the caller fills in throughrecordAuditEvent. Nothing in the SDK populates it automatically.- Audit writes from
recordAuditEvent(policy decisions, delegation, approval and SCIM events) are best effort. They never throw, failures are counted bygetAuditWriteFailures, and the next successful write is preceded by anaudit.write_failedrow. - Chained rows hash keyed pseudonyms of the ids, so GDPR erasure can blank raw ids without breaking verification. Whoever holds the key can still link a pseudonym to an id.
How do you revoke an AI agent?
Calltheauth.agent.revoke(agentId). The agent’s token is rejected on its next call and every later authorize() attempt is written as a denied row with the reason agent_revoked. policy.evaluate() denies a revoked or expired agent with POLICY_AGENT_INACTIVE. Revocation is permanent. To cut off a sub-agent without touching its parent, revoke the delegation:
What is not shipped yet?
- Planned: revoking an agent that cascades to everything it delegated. In the TypeScript package,
agent.revoke()only changes the agent’s status, so revoke the delegation chains explicitly, as above. The chain rows stay active until you do. - Planned: the approver on the same row as the decision. Today the approver is on the approval’s own audit row.
- Planned: automatic
input_provenance. The column exists and you can write it, but the SDK does not fill it.
FAQ
Is an AI agent a user?
No. An agent has no email, password or session. It has a token and a permission set, and it belongs to a user throughownerId.
Does a RACI matrix replace runtime enforcement?
No. The matrix records intent. The checks above are what make the intent hold when an agent runs unattended. Use the RACI template to write the matrix, then enforce each cell.Can an agent approve its own action?
The approval module records who answered through the identifier your application passes toapprove(). theAuth does not stop your code from passing an agent identifier there, so authenticate the approver as a human in your handler.
Are denied attempts recorded?
Yes, for known agents, including requests from revoked or expired agents. Unknown agent ids and unknown tokens write no row, because the audit table requires a real agent. See what gets recorded.Can I prove the audit log was not edited?
Withaudit.tamperEvident on, verifyAuditChain reports edits, gaps and reordering. Deleting the newest rows is only detectable against a saved chain head. Policy decisions, delegations and approvals are all chained. See the limits.
Does it work with MCP?
Yes. Agents that call MCP servers authenticate through MCP OAuth 2.1 and are checked with the same permissions and audit trail.Next steps
RACI template
A RACI table for agent action classes, with the enforcement for each cell.
Policy engine
How decisions are combined and cached.
Trust scoring
A score you can use in your own approval rules.
Budget policies
Spend and call caps per agent.