The Agent Authentication Protocol (AAP) defines a lightweight, decentralized mechanism for AI agents to authenticate to third-party services on behalf of users. It separates three distinct concerns — operator identity, user delegation, and per-task consent — and resolves each with the most appropriate existing mechanism rather than introducing new infrastructure.
Operator identity is anchored to DNS and domain control, following the same trust model as TLS certificates. User delegation builds on OAuth 2.0. Per-task consent is captured as a signed, auditable receipt. No central authority is required. No model provider infrastructure is assumed.
The protocol defines two first-class identity modes: User-Delegated (the agent acts as a specific user, seeing only what they can see) and Service Account (fixed non-human credentials with explicitly declared scope — narrow or wide — authorized by an admin). Service account scope is not assumed to be org-wide; it is whatever was granted at bootstrap and nothing more.
This version adds: auto-revoke scope policies with five trigger types (time, task completion, inactivity, lifecycle events, usage budget) computed into a layered effective policy at grant time; token binding via DPoP to eliminate bearer token replay attacks; irreversible action safeguards including dry-run mode, undo windows, and pre-action confirmation for high-risk scopes; usage limits on consent receipts; cumulative consent visibility via a mandatory access-summary endpoint; multi-tenancy isolation requirements; protocol version negotiation; and a mandatory liability disclosure at bootstrap. All open questions from v1.0 are resolved.
Motivation & Problem Statement
AI agents are acting on behalf of users at services they've never visited. Existing authentication protocols assume a human is present. They aren't.
The agentic era introduces a category of principal the web was not designed for: an automated system acting with delegated human authority, making decisions at machine speed, across services that have no prior relationship with the agent or its operator.
Three questions must be answered for every agentic interaction:
Which company or developer deployed this agent? Are they legitimate? Can we hold them accountable?
→ DNS-anchored operator manifestDid a real user — one known to this service — actually authorize this agent to act for them? When and under what conditions?
→ Service-issued delegation tokenWhat specifically did the user authorize? Is this particular action within those declared bounds? Can the user see what was done?
→ Signed consent receipt + intent headersExisting approaches — hardcoded API keys, standard OAuth, session cookies — fail at least one of these questions when applied to agents. Section 8 (Alternatives) documents each approach and why it was not chosen.
Trust Model
Trust is not binary. AAP defines four trust tiers that services can accept based on their risk tolerance.
Identity and trust are different problems. AAP solves identity — proving who an agent is and who authorized it. Trust (is this agent well-behaved?) is earned through reputation over time and is deliberately out of scope for this protocol.
2.1 The Three Trust Anchors
2.2 Tier Definitions
| Tier | What's Verified | Required For | Infrastructure Needed |
|---|---|---|---|
| T0 — Anonymous | Nothing. Rejected by AAP. | — | — |
| T1 — Operator | DNS domain control only | Low-stakes APIs | Static JSON file on operator domain |
| T2 — Delegated | Operator + user delegation token | User-data access | T1 + one-time OAuth flow |
| T3 — Consented | Operator + delegation + consent receipt | Write/mutate operations | T2 + consent UI in operator app |
| T4 — Provider-backed | T3 + model provider operator registry | High-stakes (financial, medical) | T3 + provider participation |
Services declare in their discovery manifest which tier they require. Agents that cannot meet the required tier must not proceed and should inform the user that manual authorization is needed.
Service Discovery
Every service participating in AAP MUST host a machine-readable manifest at a predictable path. An optional human-readable companion document MAY be co-located.
Required path
GET /.well-known/agent-auth.json
Manifest schema
JSON{ "spec": "aap/1.0", "service": "https://api.example.com", "display_name": "Example API", "minimum_tier": 2, // T1–T4, service decides "scopes": [ { "id": "data.read", "description": "Read user data" }, { "id": "data.write", "description": "Modify user data" } ], "intent_required": true, // agent must declare X-Agent-Intent "credential_ttl": 3600, // max seconds before re-assertion "scope_expansion": "notify", // "reject" | "notify" | "silent" "endpoints": { "register": "/agent/register", "delegate": "/agent/delegate", "revoke": "/agent/revoke", "audit": "/agent/audit" // MANDATORY — see §7 } }
The agent discovers this document on first encountering a 401 response that includes the header WWW-Authenticate: Bearer agent_auth_required="/.well-known/agent-auth.json". Agents SHOULD proactively fetch this document before making any API call if they have reason to believe a service participates in AAP.
3.1 Multi-Tenant Discovery
The base spec assumes a single-tenant deployment: one domain, one discovery manifest, one set of scopes and policies. In practice, many AAP-implementing services are multi-tenant platforms — a single deployment serves many isolated projects or tenants, each with its own scopes, minimum trust tier, and auto-revoke policies. Firebase, Auth0, WorkOS, and Clerk all follow this model. This section defines how AAP discovery works in that context.
The problem
A multi-tenant auth service cannot serve a single global agent-auth.json that accurately describes every tenant. Project A may require T3 with DPoP; Project B may accept T1 without it. The endpoint URLs, available scopes, and safety configuration are all project-specific. Without guidance, implementers will invent incompatible solutions — custom headers, non-standard paths, or out-of-band configuration.
Two approaches
Multi-tenant services SHOULD support one of the following patterns for project-scoped discovery. Both keep the /.well-known/agent-auth.json path intact (preserving spec compliance and agent expectations) while allowing per-project configuration.
Pattern A: Query parameter scoping (RECOMMENDED)
The discovery endpoint accepts an optional projectId query parameter. When absent, it returns the service-level defaults — the platform's baseline capabilities, supported algorithms, and endpoint URL templates. When present, it returns the project-specific configuration with resolved endpoint URLs.
Service-level (no project)GET /.well-known/agent-auth.json { "spec": "aap/2.0-draft", "service": "https://auth.example.com", "display_name": "Example Auth Platform", "minimum_tier": 1, "endpoints": { "register": "/internal/projects/{projectId}/agent/register", "delegate": "/internal/projects/{projectId}/agent/delegate", "revoke": "/internal/projects/{projectId}/agent/revoke", "audit": "/internal/projects/{projectId}/agent/audit" } }
Project-scopedGET /.well-known/agent-auth.json?projectId=acme-prod { "spec": "aap/2.0-draft", "service": "https://auth.example.com", "display_name": "Example Auth Platform", "project_id": "acme-prod", "minimum_tier": 3, "endpoints": { "register": "/internal/projects/acme-prod/agent/register", "delegate": "/internal/projects/acme-prod/agent/delegate", "revoke": "/internal/projects/acme-prod/agent/revoke", "audit": "/internal/projects/acme-prod/agent/audit" }, "dpop_required": true }
The service-level response uses URI templates (RFC 6570) with {projectId} placeholders in endpoint URLs. This tells the agent "you need a project ID to proceed" without requiring out-of-band documentation. Agents that already know their target project ID can skip the service-level call entirely and go straight to ?projectId=X.
This pattern is the simplest to implement — no infrastructure changes, no subdomain routing, no wildcard DNS. It mirrors how platforms like Firebase resolve project context via an API key in the query string.
Pattern B: Subdomain routing
Each project is assigned a subdomain. The agent queries /.well-known/agent-auth.json on the project's subdomain, and the response is inherently project-scoped because the domain itself identifies the tenant.
Subdomain-scopedGET https://acme-prod.auth.example.com/.well-known/agent-auth.json { "spec": "aap/2.0-draft", "service": "https://acme-prod.auth.example.com", "project_id": "acme-prod", "minimum_tier": 3, "endpoints": { "register": "/agent/register", "delegate": "/agent/delegate", "revoke": "/agent/revoke", "audit": "/agent/audit" }, "dpop_required": true }
This is the cleanest from the agent's perspective — each project looks like a standalone AAP-compliant service with unmodified endpoint paths. However, it requires wildcard DNS (*.auth.example.com), wildcard TLS certificates, and subdomain-to-project routing middleware. This overhead is justified for platforms that already use subdomain isolation (e.g., Shopify, Heroku) but is excessive for most deployments.
What stays service-level
Regardless of the multi-tenant pattern chosen, the following endpoints MUST remain service-level (not project-scoped), because they describe the operator's platform identity and signing keys which are shared across all projects:
/.well-known/agent-identity.json— operator identity manifest (DNS-anchored to the platform domain)/.well-known/agent-jwks.json— operator signing keys (used to verify all operator JWTs regardless of project)
Signing keys are infrastructure — they belong to the platform, not to any individual tenant. Per-project signing keys would multiply key management complexity without meaningful security benefit, since the trust anchor is DNS control of the platform domain, not the project identifier.
Implementation requirements
Multi-tenant services implementing AAP MUST:
- Ensure complete data isolation between projects. A delegation created in Project A MUST NOT be usable in Project B. Access tokens, audit events, and usage counters are all project-scoped.
- Include
project_idin the project-scoped discovery response so agents can confirm they received the configuration for the intended project. - Return
404(not200with defaults) when?projectId=Xreferences a project that does not exist, to prevent agents from silently operating against a nonexistent tenant. - Validate the project context on every operational endpoint — registration, delegation, revocation, and audit — not just at discovery time.
Operator Identity via DNS
The operator identity problem is the same problem TLS solved for websites: how do you know you're talking to who you think you're talking to? The answer is the same — DNS and public-key cryptography.
Any entity deploying an AAP-compliant agent MUST host an identity manifest on their own domain. This file is the operator's identity card. It can be fetched and verified by any service without calling any third party.
Required path (on operator domain)
GET https://acme.com/.well-known/agent-identity.json
Identity manifest schema
JSON{ "operator": "Acme Corp", "domain": "acme.com", "contact": "security@acme.com", "since": "2025-01-01", "signing_keys": "https://acme.com/.well-known/agent-jwks.json", "model_providers": [ "anthropic.com", "openai.com" ], // Optional: link to provider-published operator registries "provider_registry_urls": [ "https://anthropic.com/.well-known/agent-operators.json" ] }
4.1 How Verification Works
When an agent presents a JWT claiming to be from acme.com, the receiving service:
Services SHOULD cache the identity manifest and JWKS with a TTL of no more than 1 hour. Key rotation is handled by the operator updating their JWKS endpoint. Short cache TTLs ensure revoked keys propagate within an acceptable window.
4.2 Model Provider Registries (Optional, T4)
Model providers MAY publish a list of verified operators at a well-known path. This is purely additive — services that want T4 guarantees can cross-reference this list. Providers are not required to participate for the base protocol to function.
GET https://anthropic.com/.well-known/agent-operators.json JSON{ "published": "2026-06-01T00:00:00Z", "operators": [ { "domain": "acme.com", "verified": "2025-03-15", "standing": "good" // "good" | "suspended" | "revoked" } ] }
This mirrors certificate transparency logs — Anthropic doesn't sign individual tokens, they simply maintain a public, inspectable list. Far lower operational burden than per-request signing.
User Delegation
Operator identity answers "who built this agent." User delegation answers "did a real user at this service authorize it." These are separate claims requiring separate mechanisms.
The very first interaction between a user and a service must be human-in-the-loop. There is no cryptographic shortcut around this, and there should not be. The entire delegation chain is rooted in this one moment of explicit human action. AAP embraces this rather than engineering around it.
5.1 Bootstrap Flow
When an agent discovers it lacks a delegation token for a given service, it MUST surface this to the user rather than proceeding anonymously or fabricating credentials.
5.2 Delegation Token Structure
The delegation token is a JWT signed by the service's own keys. It proves the service recognized this user and approved this specific operator's agent to act for them.
JWT PAYLOAD{ "iss": "api.example.com", // service-signed "sub": "john@example.com", "delegated_to": "acme.com", // operator domain "delegation_id":"del_k9x2mq", "scopes": ["data.read", "data.write"], "iat": 1748822400, "exp": 1780358400, // long-lived; revocable via /agent/revoke "max_agent_ttl":3600 // max TTL for derived access tokens }
This token is never sufficient on its own to make API calls — it must be combined with a valid operator JWT. Possession of the delegation token without the matching operator private key is useless.
Per-Task Consent & Scope
Having authorization to access a service is not the same as having authorization for a specific action. The consent receipt closes this gap.
6.1 Consent Receipt
Before an agent begins a task that requires acting on a service, the operator's platform MUST present the user with a clear description of what the agent intends to do, and record their approval as a signed consent receipt.
JWT PAYLOAD{ "iss": "acme.com", // operator-signed "delegation_id": "del_k9x2mq", "user": "john@example.com", "consented_at": 1748822400, "intent": "Schedule a meeting with Sarah next week", "scopes": ["calendar.read", "calendar.write"], "consent_method":"explicit_ui", "session_id": "sess_p7q3wr", "exp": 1749427200 // short-lived: hours to days }
6.2 Per-Request Intent Headers
Every API call made by an agent under an AAP credential MUST include an intent header. Services log this. They do not verify truthfulness — but the header creates an auditable trail the user can review.
HTTP REQUESTPOST /calendar/events HTTP/1.1 Authorization: Bearer <access_token> X-Agent-Delegation: del_k9x2mq X-Agent-Consent: <consent_receipt_jwt> X-Agent-Intent: "Booking team standup for Monday 9am" X-Agent-Session: sess_p7q3wr
6.3 Scope Expansion
If an agent determines mid-task that it needs a scope beyond what the consent receipt granted, the behavior is governed by the service's declared scope_expansion policy:
| Policy | Behavior | Use Case |
|---|---|---|
| reject | Service returns 403. Agent must surface to user. | High-security services |
| notify | Service sends push notification to user. Agent polls for approval. | Most consumer APIs |
| silent | Service grants if within delegation token scopes. Logged only. | Developer/low-stakes APIs |
Services should default to notify. Silent expansion trades user control for convenience, which is the right tradeoff for some contexts but should be an explicit opt-in by the service, not a default.
Complete Request Flow
Combining all three layers, a fully verified AAP request works as follows. This example shows a T3 (Consented) interaction after bootstrap has already occurred.
7.1 Mandatory Audit Endpoint
AAP requires all participating services to expose /agent/audit. This is non-negotiable. Agents are only trustworthy if users can inspect what they did.
HTTPGET /agent/audit?delegation_id=del_k9x2mq [ { "timestamp": "2026-06-02T10:14:22Z", "method": "POST", "path": "/calendar/events", "intent": "Booking standup Monday 9am", "session_id": "sess_p7q3wr", "outcome": "201 Created" } ]
Revocation
Revocation is a first-class concern, not an afterthought. The protocol is designed so revocation propagates quickly with minimal infrastructure. AAP defines three revocation modes: short TTLs as a structural default, explicit manual revocation, and declarative auto-revoke policies that fire without human intervention.
8.1 Short TTLs as Primary Mechanism
Access tokens MUST expire within the credential_ttl declared by the service (default 3600 seconds). Refresh requires re-presenting the operator JWT, delegation token, and a valid consent receipt. If any of these have been revoked, refresh silently fails and the agent loses access — no push infrastructure required.
8.2 Explicit Revocation
| What to Revoke | Who Can Revoke | Mechanism | Effect |
|---|---|---|---|
| delegation_token | User or Service | POST /agent/revoke | Agent loses access to this service entirely |
| consent_receipt | User via operator app | Operator invalidates receipt | Access token refresh fails after TTL |
| Operator signing key | Operator | Rotate JWKS on operator domain | All tokens signed with old key rejected on next verification |
| Operator from provider list | Model provider | Update operator registry | T4-requiring services reject operator |
8.3 Auto-Revoke Policies
Manual revocation assumes someone remembers to act. In practice, consents accumulate, tasks complete without cleanup, users leave organisations, and nobody audits the connected-agents screen unless something breaks. Auto-revoke policies close the gap between what the user intended — temporary, purposeful access — and what actually happens without them.
Auto-revoke policies are declarative and forward-looking. They are defined at grant time and enforced automatically by the service without requiring any subsequent human action.
Policy Triggers
| Trigger | Field | Description |
|---|---|---|
| Time-based | after_duration |
Revoke after a fixed duration regardless of activity. Applies from grant time. |
| Task completion | on_task_completion |
Agent calls POST /agent/task-complete to trigger. Service revokes on receipt. |
| Inactivity | after_inactivity |
No requests in N days. Catches abandoned sessions and completed-but-unclosed tasks. |
| Event-based | on_events |
Service-defined lifecycle events: user.offboarded, org.subscription_cancelled, resource.deleted, user.role_changed. Service fires revocation when it emits the event. |
| Usage budget | after_use |
Revoke after N requests or N writes. User must re-authorize to continue. |
The user.offboarded event is particularly critical. Without it, a former employee's delegation tokens remain valid until natural expiry — potentially months. Services MUST fire this event when a user account is deactivated, and it MUST trigger immediate revocation of all associated delegation tokens regardless of their exp value.
8.4 Layered Policy Minimums
Three parties may declare auto-revoke policies: the service, the operator, and the user. Policies are not negotiated — the most restrictive value across all three always wins. This is computed at grant time into an effective_policy stored in the delegation token.
Auto-Revoke Policy — Layered Resolution{ "auto_revoke_policy": { // Service declares minimums — operator and user cannot override upward "service_minimum": { "max_duration": "90d", "after_inactivity": "30d", "on_events": ["user.offboarded", "org.subscription_cancelled"] }, // Operator declares defaults for their agents "operator_default": { "after_inactivity": "14d", "on_task_completion": true }, // User may tighten further — never loosen "user_override": { "after_duration": "7d" }, // Computed at grant time. Stored in token. Authoritative. "effective_policy": { "expires_at": "2026-06-09T10:00:00Z", // min(90d, 7d) = 7d from grant "after_inactivity": "14d", // min(30d, 14d) "on_task_completion": true, "on_events": ["user.offboarded", "org.subscription_cancelled"] } } }
8.5 Revocation Notifications
Auto-revoke without notification creates silent failures — the agent stops working and nobody knows why. The spec requires a notification chain on every auto-revoke event:
Services MUST include an X-Agent-Revoke-Reason header on 401 responses caused by revocation, distinguishing them from ordinary auth failures. Valid values: policy_expired, inactivity, task_complete, event_triggered, manual_revoke, key_rotation.
Security Considerations
Good token design is necessary but not sufficient. Several structural vulnerabilities exist in any bearer-token protocol that AAP must explicitly address.
13.1 Token Binding — The Stolen Token Problem
Every token in AAP is currently a bearer token: whoever holds it can use it. If a delegation token or consent receipt is intercepted — via logging pipeline leakage, compromised cloud storage, or a man-in-the-middle — it can be replayed by the attacker with no way for the service to distinguish the attacker from the legitimate agent.
This matters more for agents than browsers because agents run in cloud infrastructure with shared environments, multiple processes, and verbose logging — all surfaces where tokens can leak in ways a browser session wouldn't.
AAP RECOMMENDS implementing DPoP (Demonstrating Proof of Possession, RFC 9449) for access tokens. The agent generates an ephemeral keypair per session. The public key is bound into the access token at issuance. On every request, the agent includes a signed DPoP proof header. A stolen token without the matching private key is useless.
DPoP Flow1. Agent → generates ephemeral keypair (per session) 2. Agent → Service includes public key in registration request 3. Service → issues access_token bound to that public key 4. Agent → Service every request carries DPoP proof header (signed by private key) 5. Service ⟳ verifies proof signature — stolen token without key = rejected
Services that require T3 or T4 trust SHOULD require DPoP. Services at T1 or T2 MAY accept plain bearer tokens where DPoP implementation is impractical.
13.2 Agent Impersonation
The spec verifies operator identity to the service — but the user never independently sees this verification. A malicious operator could build an agent that mimics the UI and name of a trusted operator. The service correctly identifies the request as coming from the malicious operator's domain, but the user believes they are using the trusted one. This is phishing at the agent layer.
Mitigations AAP requires:
Operator identity surfaced to users. The operator's verified domain MUST be displayed in the agent's UI at bootstrap and accessible at any time thereafter. Users must be able to see which domain's agent they are using, not just the display name the operator chose for themselves.
Display name ≠ identity. Services MUST NOT accept display names as identity claims. Only the cryptographically verified operator domain is identity. The discovery manifest may include a display name, but relying parties must treat it as decorative.
Phishing-resistant bootstrap. The bootstrap OAuth flow (§5.1) MUST open in the service's own browser context, not an in-app webview controlled by the operator. An operator controlling the webview could intercept credentials. This is the same requirement as OAuth 2.0 best practices (RFC 8252).
13.3 Multi-Tenancy and Session Isolation
Most agent platforms are multi-tenant: one deployment serves thousands of users. The same operator JWT covers all of them. A bug in the platform could cause user A's delegation token to appear in user B's session. For how multi-tenant services handle AAP discovery and endpoint scoping, see §3.1.
AAP requires the following isolation properties:
Session IDs must be cryptographically unguessable and generated fresh per task, not derived from user identifiers or timestamps. UUID v4 minimum; CSPRNG-derived preferred.
Delegation tokens are non-transferable. A token issued for acting_for: john@example.com MUST be rejected if presented in a request where the session context indicates a different user. Services MUST validate that the acting_for claim matches the authenticated session context on every request, not just at registration.
Concurrent session detection. Services SHOULD flag when the same delegation_id is used simultaneously from two different IP addresses or geographic regions. This is a signal of either token theft or a platform isolation failure. The appropriate response is to suspend the delegation pending operator notification rather than silently accept both sessions.
13.4 Protocol Version Negotiation
The spec will evolve. Without version negotiation, a service built on AAP/1.0 cannot safely interact with an agent implementing AAP/2.0 — neither knows what the other expects, and silent incompatibility produces unpredictable behaviour.
The discovery manifest MUST declare supported spec versions from day one:
Discovery Manifest — Version Field{ "spec_versions_supported": ["aap/1.2", "aap/1.1"], "spec_version_preferred": "aap/1.2" }
Every agent registration request MUST declare its implemented spec version. If the service does not support the agent's version, it returns a 400 with X-AAP-Version-Required listing its supported versions. Agents MUST NOT proceed with an unsupported version — silent degradation is not permitted.
Safety & Trust
Identity verification tells you who is acting. It does not tell you whether what they are about to do is safe, reversible, or within what the user actually meant. This section addresses the gap.
14.1 Irreversible Action Safeguards
A correctly authenticated agent with valid scopes can still take actions the user never intended — because intent declarations are not verified, and some actions cannot be undone. Deleting data, sending communications, making payments, and publishing content are categorically different from read operations: they have real-world consequences that persist beyond any revocation.
AAP defines three safeguards for irreversible operations:
Dry-run mode. Services exposing mutating operations MUST implement a dry-run endpoint that returns what an action would do without committing it. Agents SHOULD call dry-run before any destructive operation and surface the result to the user when the task involves ambiguity.
HTTPPOST /calendar/events?dry_run=true → returns { "would_create": {...}, "conflicts": [...] } without committing
Undo window. For destructive operations (delete, send, publish, pay), services SHOULD implement a short reversibility window — a grace period during which the action can be cancelled. The window duration is declared in the scope schema. Agents MUST expose an abort mechanism to the user during this window.
Scope Schema — Undo Window{ "id": "mail.send", "irreversible": true, "undo_window_s": 30, // 0 means no undo supported "undo_endpoint": "/mail/cancel-send" }
Pre-action confirmation for high-risk scopes. Certain scope actions MUST require an explicit confirmation signal from the agent before execution, regardless of what the consent receipt granted. Services declare these in the scope schema as "requires_confirmation": true. The agent sends a confirmation token in the request header; without it the service returns 428 Precondition Required.
14.2 Usage Limits
A consent receipt grants what an agent can do. It says nothing about how much. An agent with calendar.write could create one event or ten thousand. A compromised or buggy agent can cause significant damage within its authorized scope with no rate signal in the current protocol.
The consent receipt and service account delegation token SHOULD include a usage_limits block. Services enforce these as hard limits; exceeding them triggers a 429 with X-Agent-Limit-Reason and requires re-consent to continue rather than a silent failure or abuse flag.
Consent Receipt — Usage Limits{ "intent": "Schedule team standups for this week", "scopes": ["calendar.write"], "usage_limits": { "requests_per_hour": 60, "writes_per_session": 10, "max_data_egress_kb": 500 } }
14.3 Cumulative Consent Visibility
A user approves an agent for calendar.read in January, calendar.write in February, contacts.read in March. By April the agent has extensive access, but no single consent review made this visible. The user never saw "this agent has access to your calendar and your contacts" — only three isolated prompts.
The spec requires services to expose a cumulative consent view — a dedicated endpoint showing the agent's total current access, not a log of historical approvals:
HTTPGET /agent/access-summary?delegation_id=del_k9x2mq { "agent": "acme.com", "acting_for": "john@example.com", "active_since": "2026-01-15", "current_scopes": ["calendar.read", "calendar.write", "contacts.read"], "effective_policy": { "expires_at": "2026-07-15", "after_inactivity": "14d" }, "last_active": "2026-06-02T09:14:00Z", "revoke_url": "/agent/revoke?delegation_id=del_k9x2mq" }
Operators MUST surface this view in their product UI. The unit of display is total current access — not historical approvals. A one-click revoke from this view MUST be supported.
14.4 Liability Acknowledgement
When an agent causes harm — deletes data, sends an unintended communication, makes an unauthorized purchase — the protocol creates a clear audit trail but does not resolve who bears liability. This gap will not stop developers from building, but it will stop enterprises and regulated industries from deploying at scale.
AAP cannot resolve liability — it is a legal and commercial question beyond the scope of a technical protocol. However, the spec requires that operators surface a liability disclosure to users at bootstrap time, before first agent action:
Bootstrap — Liability Disclosure (required)// Displayed to user before they approve delegation "If this agent takes an action that causes unintended harm or data loss, [Operator Name] is responsible for investigating and remediating the issue. You can revoke this agent's access at any time from your connected agents dashboard. Actions taken before revocation may not be reversible."
The disclosure content is the operator's responsibility. The requirement that it be displayed — in plain language, before first action, not buried in terms of service — is the protocol's. Making the liability gap visible and attributable is better than ignoring it.
Approaches Considered & Rejected
Every design decision is a rejection of alternatives. What follows is an honest accounting of what was evaluated and why each path was not taken.
Hardcoded API Keys
RejectedThe status quo. Developer generates an API key, pastes it into the agent's config. Simple, widely understood, works today.
Fails because: keys are long-lived and broad in scope, tied to developers not users, not revocable per-agent, and provide no audit trail or consent record.
Standard OAuth 2.0
PartialOAuth is the right foundation for user delegation. AAP's delegation token is explicitly OAuth-derived. The protocol is not rejected — it's the wrong fit as a complete solution.
Fails as a standalone solution because OAuth assumes a human clicks an authorization screen for every new service. Agents encounter new services autonomously at runtime.
Model Provider Signing
RejectedAnthropic (or OpenAI) signs every agent request, similar to how email providers sign with DKIM. Strong guarantees — the model provider vouches for every invocation.
Requires all major providers to build and operate signing infrastructure. Requires agreement on token format. Creates a central dependency. No provider has committed to this. Makes the protocol dead on arrival without industry coordination.
Central Agent Registry
RejectedA single registry (like npm or a CA) where operators register their agents and receive verified identities. Services check the registry to verify operator legitimacy.
Creates a central point of failure and control. Registry operator becomes a gatekeeper. Doesn't exist yet and bootstrapping one is a governance problem, not a technical one. Adds latency to every verification. History of such registries becoming either captured by incumbents or abandoned.
auth.md (WorkOS approach)
PartialHosts a Markdown file at the service domain describing how agents should authenticate. Three flows: ID-JAG assertion, verified email, anonymous with OTP claim.
Direction is right but: Markdown as machine-readable format is ambiguous; anonymous agent flow has no operator accountability; OTP flows require human presence for every new service; ID-JAG is a new token type requiring provider adoption; revocation is bolted on rather than structural.
mTLS Client Certificates
RejectedAgents present a client TLS certificate during the handshake. Standard PKI handles identity. Well-understood, hardware-backed, very strong guarantees.
Certificate provisioning per-agent is operationally heavy. Most HTTP clients and APIs don't support mTLS easily. Certificate lifecycle management (rotation, revocation via CRL/OCSP) is complex. Over-engineered for most agent use cases. Better fit for machine-to-machine in controlled infrastructure, not open web APIs.
Session Cookie Delegation
RejectedUser logs in normally; agent receives the session cookie and uses it to make requests as if it were the user.
Gives agent full account access with no scoping. Cookies are tied to browsers, not agents. No audit trail distinguishes agent actions from user actions. No revocation path that doesn't log the user out entirely. Fundamentally bypasses the consent model.
Verifiable Credentials (W3C VC)
RejectedW3C Verifiable Credentials provide a rich, standardized framework for expressing and verifying claims about any subject — including agents. Cryptographically strong, privacy-preserving, standards-backed.
Ecosystem maturity is low. Tooling is immature. Complexity is high relative to the problem. Most developers don't know it. A better protocol for a more sophisticated future ecosystem — but adopting it now means building on sand. AAP's JWT profile achieves 90% of the benefit with a fraction of the implementation complexity.
Comparative Analysis
| Criterion | API Key | OAuth Only | auth.md | AAP (This Spec) |
|---|---|---|---|---|
| agt_operator_verified | ✗ | ✗ | ~ | ✓ |
| agt_user_delegation | ✗ | ✓ | ~ | ✓ |
| agt_consent_receipt | ✗ | ✗ | ✗ | ✓ |
| agt_audit_trail | ✗ | ✗ | ✗ | ✓ (mandatory) |
| agt_no_central_authority | ✓ | ✓ | ~ | ✓ |
| agt_works_today | ✓ | ~ | ~ | ✓ (T1–T3) |
| agt_provider_optional | ✓ | ✓ | ✗ | ✓ |
| agt_granular_revocation | ✗ | ~ | ~ | ✓ |
| agt_scope_narrowing | ✗ | ~ | ✗ | ✓ |
| agt_human_loop_minimal | ✓ | ✗ | ✗ | ✓ (once per service) |
✓ fully satisfies · ~ partially satisfies · ✗ does not satisfy
Identity Modes
Inspired by Harrison Chase's framing at LangChain Interrupt: agents either act as a specific user or as themselves on behalf of an organisation. Both are legitimate. Both require different trust chains. Both must be declared and disclosed.
AAP defines two first-class identity modes. The mode governs which bootstrap flow is used, what the delegation token looks like, what audit granularity is required, and — critically — what the end user must be told before the agent acts.
The agent uses the individual user's own credentials. It sees exactly what that user can see — no more. Different users get different answers. Personalized, contextual, bounded.
Trust chain: §4 operator identity + §5 per-user delegation + §6 consent receiptThe agent uses a fixed, non-human identity with credentials defined at setup time. All users interacting with it get consistent responses because the agent always sees the same slice of the world. That slice may be narrow or wide — the defining property is that it is fixed and not tied to any individual user, not that it is broad.
Trust chain: §4 operator identity + scoped delegation + admin consent + elevated audit11.1 Mode Declaration
Every AAP registration request MUST declare its mode explicitly. There is no default. Omitting mode is a protocol error.
Mode A — User-Delegated{ "mode": "user_delegated", "acting_for": "john@example.com", "delegation_id": "del_k9x2mq", "operator_jwt": "<signed by acme.com>", "consent_receipt":"<task-scoped consent JWT>" }
Mode B — Service Account{ "mode": "service_account", "operator": "acme.com", "account_id": "svc_acme_prod", "authorized_by": "admin@acme.com", "org_id": "org_acme_7x2", "granted_scope": "engineering-team", // declared at setup — could be narrow or wide "operator_jwt": "<signed by acme.com>", "svc_delegation": "<service-account delegation JWT>" }
11.2 Service Account Bootstrap
Mode B has no user to bootstrap with. The bootstrap actor is an admin who explicitly reviews and approves the scope being granted — which may be narrow (a single shared inbox) or wide (all documents in an org). The defining requirement is not breadth but explicitness: the admin must see exactly what they are granting before confirming, and that grant is recorded precisely in the delegation token.
11.3 Service Account Delegation Token
JWT PAYLOAD — Service Account Delegation{ "iss": "api.example.com", // service-signed "sub": "svc_acme_prod", // service account id, not a user "delegated_to": "acme.com", "authorized_by": "admin@acme.com", "org_id": "org_acme_7x2", // Exact scope granted at bootstrap. Encodes what the admin approved. // May be narrow ("engineering-team inbox") or wide ("all-org documents"). // No default. No implicit expansion. This field is authoritative. "granted_scope": { "description": "Engineering team shared inbox", "resources": ["inbox:eng-team@acme.com"], "scopes": ["mail.read", "mail.label"], "crosses_users": true // true if any resource spans >1 user; false otherwise }, "iat": 1748822400, "exp": 1780358400 }
The granted_scope object replaces the earlier data_boundary: "org_only" field, which incorrectly implied that service accounts are inherently org-wide. Scope breadth is a configuration decision made at bootstrap, not a property of the mode itself. A service account for a single shared inbox is just as valid as one for an entire org. The crosses_users flag exists only to trigger the appropriate audit rules — it is not a measure of risk level.
11.4 Elevated Audit Requirements for Mode B
The audit requirement for Mode B tracks what the agent actually touched within its declared scope — regardless of how narrow or wide that scope is. When crosses_users: true in the delegation token, services MUST additionally log data_subjects on every operation, so it is always possible to determine whose data was accessed even when the agent isn't acting for a named user.
Mode B Audit Entry (crosses_users: true){ "timestamp": "2026-06-02T10:14:22Z", "mode": "service_account", "account_id": "svc_acme_prod", "method": "POST", "path": "/messages/label", "intent_type": "LABEL_MESSAGE", "data_subjects": ["user_a", "user_b"], // required when crosses_users: true "outcome": "200 OK" } Mode B Audit Entry (crosses_users: false — single resource){ "timestamp": "2026-06-02T10:14:22Z", "mode": "service_account", "account_id": "svc_acme_bot", "method": "GET", "path": "/documents/shared-roadmap", "intent_type": "READ_DOCUMENT", // data_subjects omitted — single shared resource, no per-user data touched "outcome": "200 OK" }
11.5 Mandatory User Disclosure
Harrison Chase's key practical point: users must know which mode they're dealing with, because their expectations around privacy and data access are completely different. AAP makes this mandatory.
The service discovery manifest MUST declare which modes it supports and MUST include a human-readable disclosure string for each. Operators MUST surface the relevant disclosure to end users before first interaction — not in terms of service, not on a settings page, but in the agent's UI at the point of first use.
Discovery Manifest — Mode Disclosures{ "identity_modes_supported": ["user_delegated", "service_account"], "disclosures": { "user_delegated": "This agent uses your personal credentials. It can only see data you have access to. Different users will receive different responses.", "service_account": "This agent uses fixed credentials authorised by your admin. It does not act as you personally. Its access is limited to: {granted_scope.description}." // {granted_scope.description} is populated from the delegation token at disclosure time. // e.g. "Engineering team shared inbox" or "All documents in your organisation" // Operators must not substitute a generic string — it must reflect actual granted scope. } }
11.6 Mixed-Mode Sessions
A single agent task may legitimately invoke both modes — for example, reading shared org documents via service account and sending an email via user-delegated credentials. This is expected and supported, but each action must carry its mode explicitly.
The per-request headers are extended to carry mode context:
HTTP REQUEST — Mixed Mode// Service account call GET /org/documents/q3-report Authorization: Bearer <svc_access_token> X-Agent-Mode: service_account X-Agent-Account: svc_acme_prod X-Agent-Intent-Type: READ_DOCUMENT X-Agent-Session: sess_p7q3wr // User-delegated call in same session POST /mail/send Authorization: Bearer <user_access_token> X-Agent-Mode: user_delegated X-Agent-Delegation: del_k9x2mq X-Agent-Intent-Type: SEND_MESSAGE X-Agent-Session: sess_p7q3wr
The session ID ties both calls to the same task, while the mode header ensures each is audited under the correct rules. Services must not infer mode from context — it must be declared per request.
11.7 Mode Comparison
| Property | Mode A — User-Delegated | Mode B — Service Account |
|---|---|---|
| Bootstrap actor | Individual user | Admin or operator |
| Data visibility | What that user can see | Whatever scope was granted at setup — may be narrow or wide |
| Response consistency | Varies per user | Same for all callers |
| Consent granularity | Per-user, per-task | Fixed at setup; explicit scope review by admin |
| Privacy risk | Bounded to one user | Depends on granted scope; not inherently higher than Mode A |
| Revocation actor | User or service | Admin only |
| Audit requirement | Action + intent | Action + intent + data_subjects (when crosses_users: true) |
| Intent format | Free text (user-visible) | Structured type (service-logged) |
| Use case fit | Personal assistants, user-specific tasks | Company bots, shared resource automation, background jobs |
Both modes will coexist within the same products. This is expected and healthy. The same agent platform might use Mode B to read shared company documents and Mode A to send emails on a specific user's behalf — in the same session. The spec makes this explicit so developers design for it intentionally rather than discovering it as an edge case.
Resolved Questions
Questions raised in v1.0 as open. Each is now resolved with a defined position. Rationale is included so the reasoning is preserved, not just the decision.
12.1 Cross-Operator Delegation (Agent-to-Agent)
Cross-operator delegation is prohibited in v1.1. It will be introduced in a future version only after a clear accountability model is established.
When Agent A delegates to Agent B — particularly when they belong to different operators — accountability diffuses. The user authorized A, not B. Under current legal frameworks, liability would chase the chain back to A's operator, but this becomes untenable as chains lengthen and cross organizational boundaries.
The temptation to support this early is real: multi-agent workflows are genuinely useful. But getting the accountability model wrong and trying to fix it later is harder than deferring. When cross-operator delegation is eventually specified, the consent receipt will be required to explicitly name every agent in the chain at consent time. Dynamic agent discovery that adds agents mid-task without user awareness will require fresh consent. Ambient re-delegation is never permitted.
Same-operator delegation (Agent A spawning Agent B within the same operator) is permitted under the existing consent receipt model, provided the session ID links all actions to the same user-approved task.
12.2 Offline Agents
Consent is a window, not a moment. The user sets the window explicitly. Background jobs running within the window are permitted. Long-running jobs must implement a heartbeat. Mid-execution revocation must trigger a clean abort.
The consent receipt's exp field is interpreted as an execution window, not merely a token expiry. When the user approves a task, the operator's UI must surface the expected duration: "this will run tonight" implies a longer window than "do this now." The user sets the window; the operator records it in the receipt.
For background jobs expected to run longer than 30 minutes, the agent MUST implement a heartbeat — a periodic signal to the user that the task is still running and still within declared scope. The operator's platform handles delivery. The user must be able to abort from the heartbeat notification without being present at a computer.
If the user revokes the consent receipt while the agent is mid-execution, the agent MUST call /agent/cancel to initiate a clean abort. Services exposing mutating APIs are REQUIRED to implement this endpoint. Partial state should be rolled back where the service supports transactions; where it does not, the audit log must record the partial execution clearly.
Consent Receipt — Extended for Offline{ "intent": "Process overnight batch report", "execution_window": { "earliest": "2026-06-02T22:00:00Z", "latest": "2026-06-03T06:00:00Z" }, "heartbeat_interval": 1800, // seconds between status pings "abort_on_revoke": true // always true; field is informational }
12.3 Competing Scope Definitions
Service scope definitions are authoritative. Operators may constrain scopes further on their side but may not redefine them. Services are required to publish explicit action enumerations, not just human-readable descriptions.
This follows the same model as OAuth: the authorization server defines scope semantics; clients request them. Operators who integrate with a service agree to those definitions. The spec additionally requires services to publish machine-readable scope schemas, eliminating the ambiguity that causes most real-world disputes:
Scope Schema (in discovery manifest){ "id": "calendar.write", "description": "Create and update calendar events", "allows": ["events.create", "events.update"], "explicitly_excludes": ["events.delete", "calendar.delete"] }
Any action not listed in allows is implicitly excluded. Any action in explicitly_excludes is a protocol violation regardless of what the operator's consent receipt declares.
12.4 Privacy of Intent Headers
Dual-layer intent model. Services receive a structured intent type (machine-logged, no sensitive content). Users see a free-text description (operator-held, never sent to the service).
The tension is real: free-text intent enables useful audit logs but leaks sensitive context to services. "Booking a meeting with my therapist" or "paying overdue rent" reveals information the service has no legitimate need to know.
The resolution separates the two audiences. The service receives only a structured type from a controlled vocabulary — enough to detect anomalous agent behaviour, not enough to infer personal context. The user-facing audit log displays the full free-text intent string, but this is fetched from the operator's own audit store, not from the service.
| Field | Value | Sent To | Visible To User |
|---|---|---|---|
| X-Agent-Intent-Type | SCHEDULE_EVENT | Service (logged) | No |
| operator audit store | "Booking with my therapist next Tuesday" | Never leaves operator | Yes, via operator UI |
The controlled intent type vocabulary is defined per-scope in the discovery manifest. Services MAY reject requests with unrecognized intent types, providing a lightweight signal that an agent is operating outside expected patterns.
12.5 Standards Body Home
Start outside a formal body; move in once the core design is proven. Cross-operator delegation, when specified, should go through a formal process due to its liability implications.
Standards bodies produce more rigorous, more interoperable, more durable specifications. They also move slowly and can water down core designs through forced consensus. OAuth 2.0 took years and still required a separate security BCP to be safely implementable.
The pragmatic path: ship as an open community spec, build real implementations, prove the design, then bring it to IETF OAuth WG or OpenID Foundation. This is how many durable web standards actually emerged — practitioners first, formalization second.
The exception is cross-operator delegation. Because it involves liability and accountability questions that transcend engineering, that chapter should go through a formal body from the start rather than arriving as a community proposal for ratification after the fact.
Conformance Levels
Three tiers define what an implementation must support to claim AAP compliance. Each tier is independently testable. Higher tiers are strict supersets.
DNS-anchored operator identity (§4). User-delegated bootstrap and delegation token (§5). Consent receipt with scopes (§6). /agent/register, /agent/revoke, /agent/audit endpoints. Standard error envelope (§16.1). Version negotiation header. T1–T2 trust tiers.
Everything in Core, plus: DPoP token binding (§13.1). Auto-revoke policies (§8.3–8.5). Service Account mode (§11). Dry-run mode and undo window (§14.1). Usage limits enforcement (§14.2). Cumulative access-summary endpoint (§14.3). T3 trust tier.
Required for production deployments handling user dataEverything in Extended, plus: T4 provider registry cross-reference. Liability disclosure at bootstrap (§14.4). Webhook-based auto-revoke event delivery (§17.5). Mixed-mode session audit correlation (§18.3). Pre-action confirmation for high-risk scopes (§14.1).
Required for financial, medical, or regulated deploymentsServices declare their conformance tier in the discovery manifest:
JSON"conformance": { "tier": "extended", // "core" | "extended" | "full" "spec": "aap/2.0", "tested": "2026-06-01" // date of last conformance test run }
Error Taxonomy
Every error across every endpoint uses the same envelope. Machine-readable codes let agents handle failures programmatically rather than parsing human text.
16.1 Standard Error Envelope
JSON — Error Response{ "error": "delegation_expired", // machine-readable code (see §16.2) "error_description":"Delegation token del_k9x2mq expired 2026-05-01", "hint": "Re-bootstrap with the user at /agent/delegate", "request_id": "req_8x2kp9", // for support/correlation "docs_url": "https://aap.dev/errors/delegation_expired" }
The hint field is OPTIONAL but RECOMMENDED — it tells the agent what to do next, not just what went wrong. The docs_url is OPTIONAL.
16.2 Error Code Catalogue
| Error Code | HTTP Status | Meaning | Agent Action |
|---|---|---|---|
| operator_jwt_invalid | 401 | Operator JWT signature failed verification | Check operator signing key; re-sign |
| operator_jwt_expired | 401 | Operator JWT exp claim is in the past | Issue new operator JWT |
| operator_not_found | 401 | operator domain has no agent-identity.json | Operator must publish identity manifest |
| operator_suspended | 403 | Operator appears in a provider revocation list | Contact model provider; do not retry |
| delegation_not_found | 401 | delegation_id does not exist at this service | Re-bootstrap with user |
| delegation_expired | 401 | Delegation token natural expiry reached | Re-bootstrap with user |
| delegation_revoked | 401 | Delegation explicitly revoked (by user or admin) | Inform user; do not retry silently |
| delegation_mismatch | 401 | acting_for in request does not match delegation sub | Check token handling; likely a bug |
| consent_expired | 401 | Consent receipt exp reached | Request fresh consent from user |
| consent_scope_exceeded | 403 | Requested action not in consent receipt scopes | Request expanded consent or abort task |
| consent_service_mismatch | 403 | Consent receipt aud does not match this service | Obtain consent receipt for this service |
| scope_not_granted | 403 | Requested scope not in delegation token | Request scope expansion from user/admin |
| dpop_missing | 401 | DPoP proof header absent where required | Attach DPoP proof; see §17.1 |
| dpop_invalid | 401 | DPoP proof signature or claims invalid | Regenerate DPoP proof |
| dpop_replayed | 401 | DPoP jti already seen (replay detected) | Generate new DPoP proof with fresh jti |
| mode_not_supported | 400 | Service does not support declared identity mode | Check service discovery manifest |
| mode_missing | 400 | mode field absent from registration request | Declare mode explicitly; no default exists |
| boundary_violation | 403 | Mode B request accessed resource outside granted_scope | Check scope; contact admin if scope needs expanding |
| usage_limit_reached | 429 | requests_per_hour or max_writes budget exhausted | Wait for window reset or request re-consent |
| auto_revoked | 401 | Delegation revoked by auto-revoke policy | Check revoke reason; inform user |
| spec_version_unsupported | 400 | Client AAP version not accepted by this service | Check Aap-Version-Accepted header; upgrade or downgrade |
| dry_run_unsupported | 400 | Service does not support dry-run mode | Proceed without dry-run or abort |
| irreversible_requires_confirm | 403 | Operation requires X-Agent-Confirm header | Add X-Agent-Confirm: true after user confirmation |
401 means the identity or token is invalid or absent — the agent cannot be who it claims to be. 403 means identity is verified but the action is not permitted under the granted scope or policy. Agents must not treat these interchangeably.
Endpoint Contracts
Full request and response specifications for every AAP endpoint. Services MUST implement all Core endpoints. Extended and Full endpoints are marked accordingly.
17.1 POST /agent/register
Exchange operator credentials, delegation token, and consent receipt for a short-lived access token. This is the primary entry point for every agent session.
RequestPOST /agent/register Content-Type: application/json DPoP: <dpop_proof_jwt> // Required for Tier E+ Aap-Version: 2.0 { "mode": "user_delegated", // REQUIRED — no default "operator_jwt": "<jwt>", // REQUIRED "delegation_token":"<jwt>", // REQUIRED "consent_receipt": "<jwt>" // REQUIRED for T3+; OPTIONAL for T1–T2 }
Response — 200 OK{ "access_token": "<opaque token>", "token_type": "DPoP", // "DPoP" for Tier E+; "Bearer" for Tier C "expires_in": 3600, "session_id": "sess_p7q3wr", "effective_policy": { // resolved auto-revoke policy for this delegation "expires_at": "2026-09-01T00:00:00Z", "inactivity_timeout": 1209600, // seconds "on_task_completion": true } }
Idempotency: Calling /agent/register again with the same delegation_token and consent_receipt within the access token's validity window MUST return a new access token (not reuse the existing one) but MUST NOT create a duplicate session entry in the audit log. The session_id is stable for the lifetime of the consent receipt.
17.2 POST /agent/delegate (Bootstrap)
Called during the human-in-the-loop bootstrap. The user authenticates normally and this endpoint issues the delegation token. This endpoint is called by the service's own UI, not by the agent directly.
Response — 201 Created{ "delegation_token": "<jwt signed by this service>", "delegation_id": "del_k9x2mq", "liability_disclosure": "<plain text — shown to user before token is issued>" }
17.3 POST /agent/revoke
Request{ "delegation_id": "del_k9x2mq", // REQUIRED "reason": "user_requested" // OPTIONAL — logged in audit // "user_requested" | "admin_requested" | "task_complete" | "policy_triggered" } Response — 200 OK{ "revoked_at": "2026-06-02T12:00:00Z", "delegation_id": "del_k9x2mq" }
Idempotency: Revoking an already-revoked delegation MUST return 200 (not 404 or 409). The operation is idempotent.
17.4 GET /agent/audit
RequestGET /agent/audit ?delegation_id=del_k9x2mq // REQUIRED &from=2026-06-01T00:00:00Z // OPTIONAL — ISO 8601 &to=2026-06-02T00:00:00Z // OPTIONAL &limit=50 // OPTIONAL — default 50, max 200 &cursor=opaque_cursor_val // OPTIONAL — for pagination Response — 200 OK{ "entries": [ /* audit entry objects */ ], "next_cursor": "opaque_cursor_val", // null if no more results "total_count": 142 }
Callers: the user (via delegation_id), the operator (via operator JWT), or an admin (for Mode B). The service MUST verify the caller is authorized for the delegation_id being queried before returning entries.
17.5 GET /agent/access-summary (Tier E+)
Returns the full picture of an agent's current access — all active delegations, their scopes, and effective auto-revoke policies. This is what the "connected agents" UI is built on.
Response — 200 OK{ "delegations": [ { "delegation_id": "del_k9x2mq", "operator": "acme.com", "mode": "user_delegated", "scopes": ["calendar.read", "calendar.write"], "granted_at": "2026-03-01T10:00:00Z", "last_used": "2026-06-01T14:22:00Z", "effective_policy": { /* ... */ } } ] }
17.6 POST /agent/task-complete (Tier E+)
Agent signals task completion. Triggers on_task_completion auto-revoke if set in effective policy. Idempotent.
Request{ "session_id": "sess_p7q3wr", "summary": "Scheduled 3 meetings, sent 1 invite" // OPTIONAL, shown in audit }
17.7 POST /agent/cancel (Tier E+)
Initiates clean abort of an in-progress session. Services MUST attempt rollback where the underlying resource supports transactions. Audit log MUST record partial execution state.
Request{ "session_id": "sess_p7q3wr", "reason": "user_revoked" } Response — 200 OK{ "cancelled_at": "2026-06-02T11:45:00Z", "rollback_status": "completed" // "completed" | "partial" | "not_supported" }
17.8 Webhook — Auto-Revoke Events (Tier F)
Services deliver auto-revoke notifications to operators via webhook. Operators declare their webhook URL in the operator identity manifest. Services MUST retry with exponential backoff on failure. Operators MUST respond 200 within 5 seconds.
Webhook Payload — POST to operator webhook_url{ "event": "delegation.auto_revoked", "delegation_id": "del_k9x2mq", "trigger": "user.offboarded", "revoked_at": "2026-06-02T11:45:00Z", "service": "api.example.com", "signature": "<HMAC-SHA256 of payload body, key = shared webhook secret>" }
For Tier C and E, polling /agent/audit filtered by event_type=auto_revoked is the fallback mechanism.
DPoP Profile for AAP
A precise profile of RFC 9449 for the AAP context. Implementers must follow both RFC 9449 and the additional constraints here.
18.1 Keypair Ownership
The DPoP keypair belongs to the operator's agent instance — specifically the process or container handling a given session. It is generated fresh per session (not per request) and MUST NOT be shared across sessions or users. The public key appears in the DPoP proof JWT header as a JWK.
18.2 Required Endpoints
DPoP is required on: /agent/register, /agent/revoke, and all resource API calls made with the resulting access token. It is NOT required on: /agent/audit, /agent/access-summary (read-only, user-callable endpoints).
18.3 The ath Claim
The ath claim in each DPoP proof MUST be the base64url-encoded SHA-256 hash of the ASCII representation of the access token. This binds the DPoP proof to the specific access token being used.
DPoP Proof JWT Header + Payload// Header { "typ": "dpop+jwt", "alg": "ES256", "jwk": { /* ephemeral public key */ } } // Payload { "jti": "<unique per request — UUID v4>", // prevents replay "htm": "POST", // HTTP method "htu": "https://api.example.com/agent/register", "iat": 1748822400, "ath": "<base64url(SHA-256(access_token))>" // omit on /agent/register (no token yet) }
18.4 Challenge Response
When DPoP is required but absent or invalid, services MUST return:
HTTP 401WWW-Authenticate: DPoP error="use_dpop_nonce", error_description="DPoP proof required", algs="ES256 PS256" DPoP-Nonce: <server-generated nonce — include in next DPoP proof as nonce claim>
Services MAY issue nonces to prevent pre-computation. If a nonce is issued, the agent MUST include it in the DPoP proof nonce claim on retry.
Normative JSON Schemas
These schemas are normative. Implementations MUST validate incoming documents against them. Examples elsewhere in the spec are instances of these schemas, not definitions.
19.1 Service Discovery Manifest
JSON Schema{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://aap.dev/schemas/v2/service-manifest.json", "type": "object", "required": ["spec", "service", "minimum_tier", "endpoints", "conformance"], "properties": { "spec": { "type": "string", "pattern": "^aap/[0-9]+\\.[0-9]+$" }, "service": { "type": "string", "format": "uri" }, "display_name": { "type": "string", "maxLength": 80 }, "minimum_tier": { "type": "integer", "minimum": 1, "maximum": 4 }, "intent_required":{ "type": "boolean", "default": true }, "credential_ttl": { "type": "integer", "minimum": 300, "maximum": 86400 }, "scope_expansion":{ "enum": ["reject", "notify", "silent"], "default": "notify" }, "identity_modes_supported": { "type": "array", "items": { "enum": ["user_delegated", "service_account"] }, "minItems": 1 }, "scopes": { "type": "array", "items": { "type": "object", "required": ["id", "description", "allows"], "properties": { "id": { "type": "string", "pattern": "^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$" }, "description": { "type": "string", "maxLength": 200 }, "allows": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, "explicitly_excludes": { "type": "array", "items": { "type": "string" } } } } }, "endpoints": { "type": "object", "required": ["register", "delegate", "revoke", "audit"], "additionalProperties": { "type": "string", "format": "uri-reference" } }, "conformance": { "type": "object", "required": ["tier", "spec"], "properties": { "tier": { "enum": ["core", "extended", "full"] }, "spec": { "type": "string" } } } }, "additionalProperties": false }
19.2 Operator Identity Manifest
JSON Schema{ "$id": "https://aap.dev/schemas/v2/operator-identity.json", "type": "object", "required": ["operator", "domain", "signing_keys", "contact"], "properties": { "operator": { "type": "string", "maxLength": 120 }, "domain": { "type": "string", "format": "hostname" }, "contact": { "type": "string", "format": "email" }, "signing_keys":{ "type": "string", "format": "uri" }, // JWKS endpoint "webhook_url": { "type": "string", "format": "uri" }, // Required for Tier F "since": { "type": "string", "format": "date" }, "model_providers":{ "type": "array", "items": { "type": "string", "format": "hostname" } } } }
19.3 Consent Receipt JWT Claims
JSON Schema — JWT Payload{ "$id": "https://aap.dev/schemas/v2/consent-receipt.json", "type": "object", "required": ["iss", "aud", "sub", "delegation_id", "intent", "scopes", "consent_method", "session_id", "iat", "exp"], "properties": { "iss": { "type": "string", "format": "hostname" }, // operator domain "aud": { "type": "string", "format": "uri" }, // target service — REQUIRED, binds receipt to one service "sub": { "type": "string" }, // user identifier "delegation_id": { "type": "string" }, "session_id": { "type": "string" }, "intent": { "type": "string", "maxLength": 500 }, // operator-held; never sent to service "intent_type": { "type": "string" }, // structured type — sent to service "scopes": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, "consent_method":{ "enum": ["explicit_ui", "admin_bootstrap"] }, "usage_limits": { "type": "object", "properties": { "requests_per_hour": { "type": "integer", "minimum": 1 }, "writes_per_session": { "type": "integer", "minimum": 1 }, "max_data_egress_kb": { "type": "integer", "minimum": 1 } } }, "execution_window":{ "type": "object", "required": ["earliest", "latest"], "properties": { "earliest": { "type": "string", "format": "date-time" }, "latest": { "type": "string", "format": "date-time" } } }, "iat": { "type": "integer" }, "exp": { "type": "integer" } } }
The aud claim MUST be the service's base URI as declared in its discovery manifest. A consent receipt with aud: "https://api.example.com" MUST be rejected by any other service. This prevents a stolen receipt from being replayed at a different service.
JWKS Rotation Rules
20.1 Key ID Convention
Every key in a JWKS MUST carry a kid (Key ID) claim. JWTs MUST include the kid of the signing key in the JWT header. Services MUST use the kid to look up the matching key in the JWKS rather than trying all keys. If no key matches the kid, the service MUST re-fetch the JWKS once before rejecting the token — this handles the propagation delay after a key rotation.
20.2 Rotation Overlap Period
When rotating keys, operators MUST publish both the old and new key in the JWKS simultaneously for a minimum overlap period of 2× the service's JWKS cache TTL (default: 2 hours). This ensures tokens signed with the old key remain verifiable during the cache window. After the overlap period, the old key MAY be removed.
JWKS During Rotation{ "keys": [ { "kid": "key-2025-03", "use": "sig", /* retiring — keep for overlap period */ }, { "kid": "key-2026-06", "use": "sig", /* current signing key */ } ] }
20.3 Unreachable JWKS Endpoint
If a JWKS endpoint is unreachable during verification, services MUST serve from cache (if within TTL) and MUST NOT fail open. If the cache is expired and the JWKS is unreachable, the verification MUST fail with operator_not_found. Services SHOULD alert on repeated JWKS fetch failures — this may indicate an operator going dark or a network issue requiring investigation.
20.4 Minimum Key Requirements
JWKS keys MUST use one of: RS256, RS384, RS512, ES256, ES384, PS256. Symmetric algorithms (HS256 etc.) are not permitted — they cannot be published in a JWKS for asymmetric verification. ES256 is RECOMMENDED for performance.
Test Vectors
Known-good inputs and expected outputs for implementers to verify their JWT handling, signature verification, and DPoP proof generation. These vectors are normative for conformance testing.
All keys below are test-only and MUST NOT be used in production. The private key material is published here solely to enable implementers to reproduce these vectors. A production JWKS endpoint must never expose private keys.
21.1 Operator Test Key Pair (ES256)
Test Private Key — JWK{ "kty": "EC", "crv": "P-256", "kid": "aap-test-op-1", "use": "sig", "d": "TEST_ONLY_9vApwBBB3iCMbDO0bRqFV7l4sLvBjPe8Z7k2Xl9m", "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" }
21.2 Valid Operator JWT — Expected to Pass
Input Claims{ "iss": "test-operator.aap.dev", "aud": "https://test-service.aap.dev", "iat": 1748822400, "exp": 1748826000, "kid": "aap-test-op-1" } // Signed with test private key above using ES256 // Expected verification result: PASS // Expected kid lookup: matches "aap-test-op-1" in test JWKS
21.3 Failure Vectors
| Vector | Modification | Expected Error |
|---|---|---|
| TV-F-01 | exp set to 1748822399 (1 second before iat) | operator_jwt_expired |
| TV-F-02 | Signature corrupted (last byte flipped) | operator_jwt_invalid |
| TV-F-03 | kid changed to "nonexistent-key" | operator_not_found (after JWKS re-fetch) |
| TV-F-04 | iss domain has no agent-identity.json | operator_not_found |
| TV-F-05 | aud does not match receiving service URI | operator_jwt_invalid |
| TV-F-06 | DPoP jti reused within 5-minute window | dpop_replayed |
| TV-F-07 | Consent receipt aud is different service | consent_service_mismatch |
| TV-F-08 | mode field absent from register request | mode_missing |
19.4 Delegation Token JWT Claims
JSON Schema — JWT Payload{ "$id": "https://aap.dev/schemas/v2/delegation-token.json", "type": "object", "required": ["iss", "sub", "delegated_to", "delegation_id", "scopes", "iat", "exp", "max_agent_ttl"], "properties": { "iss": { "type": "string", "format": "uri" }, // service base URI — self-signed "sub": { "type": "string" }, // user identifier "delegated_to": { "type": "string", "format": "hostname" }, // operator domain "delegation_id": { "type": "string", "pattern": "^del_[a-z0-9]{6,32}$" }, "scopes": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, "max_agent_ttl": { "type": "integer", "minimum": 300, "maximum": 86400 }, "iat": { "type": "integer" }, "exp": { "type": "integer" } }, "additionalProperties": false }
19.5 Service Account Delegation Token JWT Claims
JSON Schema — JWT Payload{ "$id": "https://aap.dev/schemas/v2/svc-delegation-token.json", "type": "object", "required": ["iss", "sub", "delegated_to", "authorized_by", "org_id", "granted_scope", "iat", "exp"], "properties": { "iss": { "type": "string", "format": "uri" }, "sub": { "type": "string", "pattern": "^svc_[a-z0-9_]{3,64}$" }, // service account id "delegated_to": { "type": "string", "format": "hostname" }, "authorized_by": { "type": "string", "format": "email" }, "org_id": { "type": "string" }, "granted_scope": { "type": "object", "required": ["description", "scopes", "crosses_users"], "properties": { "description": { "type": "string", "maxLength": 200 }, "resources": { "type": "array", "items": { "type": "string" } }, "scopes": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, "crosses_users": { "type": "boolean" } }, "additionalProperties": false }, "iat": { "type": "integer" }, "exp": { "type": "integer" } }, "additionalProperties": false }
19.6 Auto-Revoke Policy
JSON Schema — effective_policy object{ "$id": "https://aap.dev/schemas/v2/auto-revoke-policy.json", "type": "object", "properties": { "expires_at": { "type": "string", "format": "date-time" }, "inactivity_timeout": { "type": "integer", "minimum": 3600, /* seconds */ "description": "seconds" }, "on_task_completion": { "type": "boolean" }, "on_events": { "type": "array", "items": { "enum": [ "user.offboarded", "org.subscription_cancelled", "resource.deleted", "user.role_changed" ] } }, "after_use": { "type": "object", "properties": { "max_requests": { "type": "integer", "minimum": 1 }, "max_writes": { "type": "integer", "minimum": 1 } } } } }
Mixed-Mode Session Contract
A single agent task may invoke both identity modes within one session. This is expected and supported. The contract governing how modes interact must be precise.
21.1 Upfront Mode Declaration
Agents MUST declare all modes they intend to use at session start by calling /agent/register once per mode, both using the same session_id. A session that attempts to use a mode not registered at start MUST be rejected with mode_not_supported.
Session Start — Register Both Modes// Call 1 — register user_delegated for this session POST /agent/register { "mode": "user_delegated", "session_id": "sess_p7q3wr", /* ... */ } → returns user_access_token // Call 2 — register service_account for same session POST /agent/register { "mode": "service_account", "session_id": "sess_p7q3wr", /* ... */ } → returns svc_access_token // Both tokens are now valid for session sess_p7q3wr // Each carries X-Agent-Mode on every subsequent request
21.2 Partial Revocation Behaviour
When one mode's credentials are revoked within a mixed-mode session, the session does not terminate entirely. The revoked mode becomes unavailable and the agent MUST NOT retry it. The other mode continues operating normally. The agent MUST surface to the user that part of its capability has been lost.
| Revocation Event | Effect on Session | Agent Required Action |
|---|---|---|
| user_delegated delegation revoked | Mode A calls return 401 delegation_revoked | Inform user; continue Mode B if applicable |
| service_account delegation revoked | Mode B calls return 401 delegation_revoked | Inform admin; continue Mode A if applicable |
| Both revoked | All calls fail | Abort session; notify user and admin |
| Consent receipt expired (Mode A) | Mode A calls return 401 consent_expired | Request fresh consent; session can continue with Mode B |
21.3 Cross-Mode Audit Correlation
Both modes use the same session_id. The audit endpoint MUST be queryable by session_id to return a unified chronological log of all actions taken under that session, clearly labelled with their mode. This allows a user or admin to reconstruct exactly what happened during a mixed-mode task.
Unified Session Audit Entry{ "session_id": "sess_p7q3wr", "timestamp": "2026-06-02T10:14:22Z", "mode": "service_account", // always present — never inferred "method": "GET", "path": "/documents/q3-report", "intent_type": "READ_DOCUMENT", "outcome": "200 OK" }
Protocol Version Negotiation
AAP versions are additive. Agents and services negotiate a compatible version on every request. Neither side should fail silently on a version mismatch.
22.1 Version Headers
Agents MUST include Aap-Version on every request. Services MUST respond with Aap-Version-Served confirming the version they applied. Services MAY include Aap-Version-Accepted listing all versions they support.
Request HeaderAap-Version: 2.0 Response HeadersAap-Version-Served: 2.0 Aap-Version-Accepted: 1.0, 1.1, 1.2, 1.3, 2.0
22.2 Negotiation Rules
If a service receives a request with a version it does not support, it MUST return 400 spec_version_unsupported with the Aap-Version-Accepted header. It MUST NOT attempt to process the request under a different version silently.
If a service supports both the requested version and a newer one, it MUST serve the requested version — downgrade is always the service's responsibility, never assumed. Agents that receive a Aap-Version-Served higher than requested MUST treat it as a protocol error and retry at a mutually supported version.
22.3 Discovery Manifest Versioning
The service discovery manifest MUST declare spec_versions_accepted as an array. This allows agents to select a compatible version before making their first registration request, avoiding a round-trip failure.
Discovery Manifest — Version Field"spec_versions_accepted": ["1.3", "2.0"]
22.4 Forward Compatibility Guarantee
Minor versions (2.0 → 2.1) are guaranteed to be additive — new optional fields only. Implementations MUST ignore unknown fields rather than rejecting them. Major versions (2.x → 3.0) may introduce breaking changes and require explicit negotiation.
Test Vectors
Known-good inputs and expected outputs for implementers to verify their JWT handling, signature verification, and DPoP proof generation. These vectors are normative for conformance testing.
All keys below are test-only and MUST NOT be used in production. The private key material is published here solely to enable implementers to reproduce these vectors. A production JWKS endpoint must never expose private keys.
23.1 Operator Test Key Pair (ES256)
Test Private Key — JWK{ "kty": "EC", "crv": "P-256", "kid": "aap-test-op-1", "use": "sig", "d": "TEST_ONLY_9vApwBBB3iCMbDO0bRqFV7l4sLvBjPe8Z7k2Xl9m", "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" }
23.2 Valid Operator JWT — Expected to Pass
Input Claims{ "iss": "test-operator.aap.dev", "aud": "https://test-service.aap.dev", "iat": 1748822400, "exp": 1748826000, "kid": "aap-test-op-1" } // Signed with test private key above using ES256 // Expected verification result: PASS // Expected kid lookup: matches "aap-test-op-1" in test JWKS
23.3 Valid Consent Receipt — Expected to Pass
Input Claims{ "iss": "test-operator.aap.dev", "aud": "https://test-service.aap.dev", "sub": "user_test_001", "delegation_id": "del_testk9x2", "session_id": "sess_testabc", "intent": "Schedule a meeting for next Monday", "intent_type": "SCHEDULE_EVENT", "scopes": ["calendar.read", "calendar.write"], "consent_method": "explicit_ui", "iat": 1748822400, "exp": 1748908800 // 24h window } // Signed with same test key; aud must match receiving service exactly
23.4 Failure Vectors
| Vector | Modification | Expected Error |
|---|---|---|
| TV-F-01 | exp set to 1748822399 (1 second before iat) | operator_jwt_expired |
| TV-F-02 | Signature corrupted (last byte flipped) | operator_jwt_invalid |
| TV-F-03 | kid changed to "nonexistent-key" | operator_not_found (after JWKS re-fetch) |
| TV-F-04 | iss domain has no agent-identity.json | operator_not_found |
| TV-F-05 | aud does not match receiving service URI | operator_jwt_invalid |
| TV-F-06 | DPoP jti reused within 5-minute window | dpop_replayed |
| TV-F-07 | Consent receipt aud is different service | consent_service_mismatch |
| TV-F-08 | mode field absent from register request | mode_missing |
| TV-F-09 | Mixed-mode session uses mode not declared at start | mode_not_supported |
| TV-F-10 | Service account request touches resource outside granted_scope.resources | boundary_violation |
| TV-F-11 | Aap-Version header value not in service's spec_versions_accepted | spec_version_unsupported |
| TV-F-12 | Destructive operation without X-Agent-Confirm on Tier F service | irreversible_requires_confirm |
A conformance test suite MUST verify all TV-F-01 through TV-F-12 failure cases in addition to the happy-path flow from §7. The full test suite with executable fixtures is maintained at https://aap.dev/conformance.
Terms & Definitions
Every functional, technical, and protocol-specific term used in this specification. Written for three audiences: implementers building systems, product owners designing features, and project managers overseeing integrations.
A
This specification. Defines how AI agents authenticate to third-party services, establish user authority, and maintain auditable records of their actions.
A short-lived opaque credential issued by a service after successful registration. Presented on every API call as a Bearer or DPoP-bound token. Expires after credential_ttl seconds. Distinct from the delegation token — it is session-scoped, not long-lived.
POST /agent/register. Include in Authorization: Bearer or Authorization: DPoP header on resource calls.An AI-powered automated system that takes actions in the real world on behalf of a user or organisation. Agents may call APIs, send messages, read and write data, or orchestrate other agents. In AAP, an agent is always operated by an identified operator and always acts under explicit delegated authority.
The question of on whose behalf an agent acts and whose credentials it uses. AAP defines two distinct identity modes: user-delegated (acting as a specific person) and service account (acting as a fixed non-human identity). Not to be confused with the operator's identity, which is separate.
mode field in registration. Governs which token chain is required and which audit rules apply.The one-time human-in-the-loop setup process for service account (Mode B) agents. An administrator authenticates to the service, reviews the exact scope being granted, and explicitly approves. The service issues an org-scoped delegation token. No per-user consent is needed after this point.
POST /agent/delegate in an authenticated admin session. Token issued includes granted_scope and authorized_by fields.A claim inside a DPoP proof JWT. Contains the base64url-encoded SHA-256 hash of the access token being used. Binds the proof to a specific token, preventing an attacker from reusing a valid DPoP proof with a different stolen token.
base64url(SHA-256(access_token_ascii)). Required on all resource calls where DPoP is used. Omit only on /agent/register where no access token yet exists.A chronological log of every action taken by an agent under a given delegation. Mandatory for all AAP-conformant services. Queryable by the user, operator, or admin. Includes timestamp, method, path, intent type, and (for Mode B with crosses_users: true) data subjects affected.
GET /agent/audit. Must support filtering by delegation_id and date range. Must be paginated. Must persist for the lifetime of the delegation.A declarative set of conditions under which a delegation is automatically revoked without requiring explicit human action. Five trigger types: time-based expiry, task completion, inactivity timeout, lifecycle events (e.g. user offboarding), and usage budget exhaustion. The effective policy is the most restrictive combination of service, operator, and user policies computed at grant time.
effective_policy in the /agent/register response. Service must enforce all conditions. See §19.6 schema.B
The one-time human-in-the-loop step that roots the agent's authority chain. A human authenticates to the target service, reviews what they are granting, and approves. The service issues a delegation token. All subsequent agent activity derives authority from this moment. Cannot be bypassed.
401 + WWW-Authenticate: Bearer agent_auth_required. Must surface to user rather than failing silently.An error condition (HTTP 403, error code boundary_violation) returned when a Mode B service account request attempts to access a resource outside the granted_scope.resources declared in its delegation token. The agent is verified and authorised in general — but this specific resource is outside its declared scope.
C
The difference in system time between the token issuer and the verifying service. AAP requires implementations to tolerate up to 300 seconds (5 minutes) of clock skew when validating iat and exp claims, to prevent valid tokens from being rejected due to minor time differences between systems.
iat is up to 300s in the future or whose exp is up to 300s in the past.One of three levels of AAP compliance: Core (minimum viable), Extended (production-grade), Full (high-assurance). Each tier is a strict superset of the previous. Services declare their tier in the discovery manifest. Tiers map to use case risk levels — Core for low-stakes APIs, Full for financial or regulated deployments.
A signed JWT created by the operator's platform at the moment a user explicitly approves a specific task. Contains the intent, approved scopes, session ID, and expiry window. Bound to a specific service via the aud claim — cannot be replayed at a different service. Short-lived (hours to days). Required for T3+ trust tier.
POST /agent/register. Service verifies signature, aud, and exp. See §19.3 for full schema.A boolean field in the granted_scope object of a service account delegation token. true when the granted resources span more than one user's data. When true, services must log data_subjects in audit entries — identifying whose data was touched on each operation. Not a risk level indicator; purely a flag that triggers elevated audit rules.
true in the delegation token, data_subjects array is REQUIRED in all audit log entries. If false, it may be omitted.D
An array of user identifiers in Mode B audit log entries, recording whose data was accessed or modified by a specific agent action. Required when crosses_users: true in the delegation token. Enables users and admins to answer "did this agent touch my data?" even when the agent isn't acting for any named user.
See Delegation Token. The term "delegation receipt" is used informally to emphasise the document's role as evidence of a user's approval act, rather than as a credential. The delegation token serves both functions.
A long-lived JWT signed by the service (not the operator) after a successful bootstrap. Proves that a specific user (Mode A) or organisation (Mode B) authorised a specific operator's agent to access this service. Presented at every registration call. Cannot be used alone — must be accompanied by a valid operator JWT.
sub (user or svc account), delegated_to (operator domain), scopes, and max_agent_ttl. See §19.4 schema.A JSON document hosted at /.well-known/agent-auth.json on a service's domain. Machine-readable description of how to authenticate agents to that service: supported modes, required trust tier, available scopes, endpoint URLs, conformance level, and disclosure strings. The first thing an agent fetches when encountering a new service.
spec field changes.The trust model AAP uses for operator identity. An operator proves their identity by controlling a domain name — specifically by hosting a verifiable identity manifest and JWKS at well-known paths on that domain. The same trust model as TLS certificates. No central registry required.
https://operator.domain/.well-known/agent-identity.json, fetch JWKS from signing_keys URL, verify JWT signature. If domain is unreachable, reject.An extension to OAuth (RFC 9449) that binds access tokens to a specific keypair. The agent generates an ephemeral keypair per session and proves it holds the private key on every request. Stolen access tokens become useless without the matching private key. Required for Tier E and above in AAP.
DPoP: <proof_jwt> header with every request. Proof must include jti, htm, htu, iat, and ath claims. See §18.A mode in which an agent asks a service "would this action succeed and what would it do?" without actually committing the operation. Allows agents to verify intent and surface expected outcomes to users before taking irreversible actions. Services supporting mutating operations are recommended to implement this endpoint.
X-Agent-Dry-Run: true header. Service processes the request logic but does not commit. Returns 200 with a preview of what would have changed.E
The computed auto-revoke policy that applies to a specific delegation, resolved at grant time as the most restrictive combination of service, operator, and user policies. Stored in the /agent/register response and in the delegation token so all parties can see what will actually happen without needing to recompute it.
min(service.max_duration, operator.max_duration, user.max_duration) for each policy dimension. Store it; do not recompute on each request.The standard JSON shape for all error responses across all AAP endpoints. Contains: error (machine-readable code), error_description (human-readable), hint (what to do next), and request_id. One shape everywhere — agents can parse errors programmatically without per-endpoint handling.
error field must be from the §16.2 catalogue.A time range (earliest / latest in ISO 8601) in the consent receipt that bounds when a background or offline agent task may execute. Treats consent as a window rather than a moment. The agent may only act within this window. The user sets it explicitly when approving the task.
consent_expired. Agent must abort if it detects it has run past latest.G
An object in the service account delegation token that precisely describes what a Mode B agent was authorised to access at bootstrap time. Contains a human-readable description, an explicit list of resources, the permitted scopes, and the crosses_users flag. Authoritative — the agent may not exceed this scope regardless of what the service's general API permits.
granted_scope.resources, return 403 boundary_violation. See §19.5.H
A periodic status signal sent by an agent running a long-duration background task to inform the user that the task is still running and still within declared scope. Required for tasks expected to run longer than 30 minutes. The user must be able to abort from the heartbeat notification without being at a computer.
heartbeat_interval (seconds) in the consent receipt. Delivery is the operator's responsibility — the protocol does not define the channel (push notification, email, etc.).I
A token type defined in the WorkOS auth.md proposal (the precursor that inspired AAP). AAP does not use ID-JAG — it uses a standard JWT profile instead. Included here because implementers reading auth.md alongside this spec may encounter the term.
The fundamental choice of how an agent presents itself: as a specific user (Mode A — User-Delegated) or as a fixed non-human identity (Mode B — Service Account). Must be declared in every registration request. No default. Governs the entire trust chain, bootstrap process, audit requirements, and user disclosures.
mode field in POST /agent/register. Values: "user_delegated" or "service_account". Absent mode returns 400 mode_missing.A human-readable description of why an agent is making a specific API call, carried in the X-Agent-Intent (Mode A, free text, operator-held) or X-Agent-Intent-Type (Mode B, structured vocabulary, service-logged) headers. Creates an auditable trail without the service needing to know the sensitive details of what the user asked for.
A class of operations (delete, send, pay, publish) that cannot be undone or have significant consequences if performed in error. AAP Tier F requires a pre-action confirmation signal (X-Agent-Confirm: true) for irreversible operations, even if the scope is already granted. Undo windows (30s) apply where the service supports them.
403 irreversible_requires_confirm without the confirm header. See §14.1.J
A JSON document containing one or more public keys used to verify JWT signatures. Operators host their JWKS at the URL declared in their identity manifest's signing_keys field. Services fetch and cache this to verify operator JWTs. During key rotation, both old and new keys must coexist in the JWKS for at least 2× the service's cache TTL.
kid. Services cache with max 1-hour TTL. On kid mismatch, re-fetch once before rejecting. Use ES256 keys recommended. See §20.The token format used for all AAP credentials: operator JWTs, delegation tokens, consent receipts, and DPoP proofs. A JWT is a base64url-encoded JSON payload with a cryptographic signature. The signature can be verified by anyone with the signer's public key — no call back to the issuer needed.
L
A plain-language statement shown to users or admins at bootstrap time, answering: what can this agent do, what can't it do, and who do you contact if something goes wrong. Required for Tier F. Must be under a defined word limit and written in plain language — not legal boilerplate. Returned in the /agent/delegate response and displayed before the delegation token is issued.
/agent/delegate response. Must be shown to the user before they can approve. Service decides content; operator displays it.M
A session in which a single agent task uses both Mode A (user-delegated) and Mode B (service account) credentials for different API calls. Both modes must be declared and registered at session start using the same session_id. Each request must carry X-Agent-Mode. The audit log correlates all actions under the shared session ID.
/agent/register once per mode with the same session_id. Modes not declared at start are rejected. Partial revocation does not terminate the whole session. See §21.An identity mode in which the agent acts as a specific named user, using that user's delegated credentials. The agent sees only what that user can see. Different users get different responses. Requires per-user bootstrap and consent receipt. Revocable by the user.
mode: "user_delegated". Requires delegation token with user sub, consent receipt with user sub, and operator JWT. See §11.An identity mode in which the agent uses fixed, non-human credentials authorised by an admin. All users interacting with the agent get consistent responses. Scope is defined precisely at bootstrap — may be narrow or wide. Not inherently org-wide. Revocable only by an admin.
mode: "service_account". Requires org delegation token with granted_scope object and crosses_users flag. No consent receipt. See §11.O
The company or developer who built and deployed the agent. Identified by their domain name (DNS-anchored). Responsible for the agent's actions. Holds the signing keys used to produce operator JWTs and consent receipts. The accountability anchor in the AAP trust chain — the party that can be contacted, held responsible, and whose access can be terminated.
/.well-known/agent-identity.json and /.well-known/agent-jwks.json. Must sign operator JWTs and consent receipts with declared keys. See §4.A short-lived JWT signed by the operator's private key, presented at every /agent/register call. Proves the request genuinely originates from the declared operator domain. Verified by the service against the operator's JWKS. Contains iss (operator domain), aud (target service), kid, iat, and exp.
kid, verifies signature. Invalid signature returns 401 operator_jwt_invalid.A delegation token issued for a Mode B service account, where the subject is an organisation (via org_id) rather than an individual user. The scope is defined by granted_scope and may be narrow or wide. Distinct from a user delegation token — no individual user's identity is asserted.
sub is the service account ID (svc_* pattern). authorized_by records the admin email. Revocable via /agent/revoke by admin only. See §19.5.P
The distance between formal consent (a user tapped approve) and informed consent (a user understood what they were approving). AAP's protocol layer can enforce that disclosures were presented; it cannot enforce that they were read or understood. Closing the perception gap requires UX design, progressive disclosure, and comprehension signals — elements that are recommended but not protocol-enforceable.
An optional JSON document hosted by a model provider (e.g. Anthropic, OpenAI) at /.well-known/agent-operators.json, listing operators they have verified are legitimately using their API. Services requiring T4 trust can cross-reference this list. Providers are not required to participate — T4 is an optional upgrade, not a baseline requirement.
standing field — reject operators with "suspended" or "revoked" status.R
The act of cancelling an agent's delegated authority, preventing all further access until re-authorised. In AAP, revocation is first-class and designed for speed: short-lived access tokens mean revocation propagates within one TTL window without push infrastructure. Explicit revocation via /agent/revoke is also supported. Auto-revoke policies provide declarative revocation without human action.
/agent/revoke as idempotent. On refresh attempt with revoked delegation, return 401 delegation_revoked. Notify operator via webhook (Tier F). See §8.S
A named permission that grants an agent the ability to perform a class of operations. Defined by the service in the discovery manifest with explicit allows and explicitly_excludes action enumerations. Service scope definitions are authoritative — operators may narrow them but not redefine them. Any action not in allows is implicitly excluded.
[resource].[action] (e.g. calendar.write). Must enumerate allowed actions explicitly — not just a label. See §12.3 and §19.1.When an agent mid-task determines it needs a scope beyond what the current consent receipt granted. Behaviour is governed by the service's declared scope_expansion policy: reject (return 403), notify (push notification to user, agent polls for approval), or silent (grant if within delegation token scopes, log only). Default is notify.
403 consent_scope_exceeded. If policy is notify, agent must poll /agent/scope-approval?session_id= for user response before retrying.A non-human identity used by a Mode B agent. Has fixed credentials not tied to any individual user. Authorised by an admin. Its scope is precisely defined at bootstrap via granted_scope. All callers get consistent results because the agent always operates with the same credentials. Not inherently org-wide — scope is whatever the admin approved.
sub matching ^svc_[a-z0-9_]{3,64}$ pattern in delegation token. Revocable by admin only. Audit entries may omit user context but must include data_subjects when crosses_users: true.A cryptographically random identifier that ties all API calls within a single agent task together. Stable for the lifetime of the consent receipt. Used for audit correlation — especially important in mixed-mode sessions where both Mode A and Mode B calls share the same session ID. Must be unguessable and not reusable across different users or delegation IDs.
X-Agent-Session header on every call. Included in all audit log entries.T
The cryptographic binding of an access token to a specific keypair (via DPoP), so that the token cannot be used by anyone who doesn't hold the corresponding private key. Prevents bearer token replay attacks — an attacker who intercepts a token cannot use it without the private key that stays with the legitimate agent. Required from Tier E.
token_type: "DPoP" and bound to the DPoP public key presented at registration.The root of a verification chain — the thing whose trustworthiness everything else depends on. AAP has three trust anchors: DNS control (for operator identity), service-issued tokens (for user delegation), and operator-signed receipts (for consent). An optional fourth anchor — model provider registries — is available for T4.
One of four levels (T1–T4) describing how much of the AAP trust chain has been verified for a given interaction. T1: operator identity only. T2: operator + user delegation. T3: operator + delegation + consent receipt. T4: T3 + model provider registry. Services declare their required minimum tier. Higher tiers provide stronger guarantees at the cost of more setup.
minimum_tier (1–4) in the discovery manifest. Requests not meeting the minimum tier are rejected with appropriate 401/403 errors.U
A short period (default 30 seconds) after a write operation during which the action can be reversed by the agent or user. Similar to "undo send" in email. Services supporting undo windows must expose a cancel endpoint for the specific resource created or modified. Required for Tier E+ on supported operation types.
undo_url in the response body of eligible write operations. Agent surfaces the undo option to the user for the window duration. See §14.1.Optional constraints on a consent receipt that cap how much an agent can do within its authorised scope: requests_per_hour, writes_per_session, max_data_egress_kb. Prevents a misbehaving or compromised agent from causing damage at scale even with valid credentials. Exhausting limits returns 429 usage_limit_reached and triggers re-consent.
delegation_id. On exhaustion: return 429 usage_limit_reached with a Retry-After header indicating window reset time.V
The mechanism by which agents and services agree on which AAP version to use. Agents declare their version in the Aap-Version request header. Services respond with Aap-Version-Served and may advertise all accepted versions via Aap-Version-Accepted. Version mismatches return 400 spec_version_unsupported — never silently degrade.
Aap-Version: 2.0 on all requests. Handle 400 spec_version_unsupported by checking Aap-Version-Accepted header and retrying at a compatible version. See §22.W
A push notification from the service to the operator when an auto-revoke policy triggers. Delivered to the webhook_url in the operator's identity manifest. Payload signed with HMAC-SHA256 using a shared secret. Required for Tier F. Polling /agent/audit is the fallback for Tier C and E.
HMAC-SHA256(timestamp + "." + body). See §17.8.A standardised path prefix (RFC 8615) for hosting machine-readable metadata about a domain. AAP uses three well-known paths: /.well-known/agent-auth.json (service discovery manifest), /.well-known/agent-identity.json (operator identity manifest), and /.well-known/agent-jwks.json (operator signing keys). The same convention used by OpenID Connect and OAuth metadata.
application/json), and be served without authentication. Cache-Control headers recommended.