AAuth
AAuth support is experimental: the API, SDK and CLI shapes may change between minor releases.
AAuth is an open protocol for agents acting on someone’s behalf. The agent signs every request; the AAuth resource it calls asks the person’s Person Server for an authorization decision; and the Person Server answers with a token the AAuth resource trusts. Introspection supports the native flow AAuth calls Person Server Managed: the AAuth resource issues a resource token bound to the person, and the Person Server returns an auth token for it.
Your recipe holds no credential for any of this. Introspection is the agent’s Agent Provider, and the platform’s egress signs each request for the session.
| Party | Who runs it |
|---|---|
| Agent | Your recipe, in its sandbox |
| Agent Provider | Introspection: it issues the agent’s token, bound to a key per session |
| AAuth resource | The API your agent calls, such as a booking provider. Build one with @introspection-sdk/aauth-resource or the Rust aauth-protocol crate |
| Person Server | The person’s organisation: its own, or the one Introspection hosts |
Missions and approvals
A mission is one approval for a piece of work, such as “Flights and a hotel for Sam, Monday to Friday next week, up to $3,000.” In the travel example below, Sam asks the agent and Dana, Sam’s manager at Acme, approves:
- The agent proposes the mission. The person’s approver approves it once and can set its budget.
- Requests within the mission’s budget go through at once and are spent against it. One over what is left goes back to the approver.
- When the work is done, the agent asks the person to accept the mission as complete. A completed mission issues nothing more.
While a request waits for someone, the agent’s call answers with HTTP status
428 Precondition Required: the request is valid, but it needs a person’s
approval first. The response says what is waiting, and the agent tells the
person instead of retrying.
When the approver decides, Introspection resumes the conversation with the outcome: approved, declined, or lapsed with no decision. A deferred approval stays open for 48 hours unless your plane is configured otherwise.
Connect an AAuth API
You need: an owner or admin, and the API’s AAuth resource identifier.
- Create the application your agent acts as. Its client id is the agent’s
identity at the Person Server:
introspection applications create --type service-account --name "Travel agent" - Create the connector.
--api-hostis the host your recipe calls, and the egress signs every request to it:introspection connectors create --name "Booking" --provider booking --auth-mode aauth \ --api-host api.booking.example \ --person-server-url https://ps.acme.example \ --application-id <application-id> \ --metadata aauth_resource=https://api.booking.example - In your recipe, call the API with plain
fetch. To propose a mission, post{description}tohttps://person-server.aauth/mission: the egress fills in who the agent acts for and which resources it covers, signs it, and sends it to the connector’s Person Server.
A recipe that routes connectors in policies/routes.yaml gets only the AAuth
connectors it routes. Its policy rails still run before any
request is signed.
Choose a Person Server
- The customer’s own. Point
--person-server-urlat it. The customer keeps approvals and missions; Introspection only signs and polls. Build one with@introspection-sdk/person-serveror the Rustaauth-person-servercrate. - The one Introspection hosts. A data plane can host a Person Server, with
each project as its own tenant. Point
--person-server-urlat the plane’s Person Server, add--metadata aauth_tenant=<org-id>:<project-id>, and decide approvals from your own app as below. Ask us to turn it on for your plane.
Decide approvals from your app
On the hosted Person Server, your app owns the human side: it finds what is
waiting, reaches the approver however it likes, and records the answer. Use an
application with approvals:read approvals:write; agent sessions never hold
these scopes, so an agent cannot approve its own mission.
- List what is waiting:
Each approval names the person it is for (
introspection aauth approvals list --status pendingmember_id), its description, and the AAuth resource’s own summary of the request. - Record the decision, naming who decided. A mission approval can set its
budget, in minor units:
Name the approver with
introspection aauth approvals decide <approval-id> --approve \ --decided-by user:dana@acme.example --budget-amount 300000 --budget-unit USD--decided-by user:<id>,anon:<id>ormember:<uuid>; it is recorded as their member id. A completion is the person’s own to accept. - See approved missions and what each has spent:
introspection aauth missions list.
From Rust, the same calls are on client.aauth():
let decision = ApprovalDecision::approve(DecidedBy::user("dana@acme.example"))
.with_budget(Budget { amount: 300_000, unit: "USD".into(), decimals: 2 });
client.aauth().approvals().decide(approval_id, &decision).await?;The endpoints are in the API reference. The mission text and approval descriptions are encrypted at rest.
Example
The AAuth travel demo
books flights and a hotel for Sam, an Acme employee. Dana, Sam’s manager at
Acme, approves the trip as a mission with a budget; Flight Sector’s booking
API is the AAuth resource. It runs against Acme’s own Person Server or the hosted
one, and pnpm dev sets up the connector, the applications and the recipe for
you.