Agent Enrollment Protocol

A protocol to onboard and authenticate agents as net-new customers

From an unauthenticated call to authenticated access

A 401 challenge points to the Inspect document. The agent uses a signed, single-use proof to authenticate directly and can request a reusable session credential when the Service offers one.

0--Trigger

An unauthenticated call returns a 401 challengeAn agent calls a protected endpoint with no credentials. The service returns 401, and the challenge carries two fields: the Service DID and the address of its Inspect document.

401-challenge.http
< HTTP/1.1 401 Unauthorized
$ curl -v https://api.example.com/v1/orders

> GET /v1/orders HTTP/1.1
> Host: api.example.com

< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: AEP service_did="did:web:api.example.com"
<   inspect="https://api.example.com/.well-known/aep"
1--
Inspect

The Inspect document lists the requirements before any signatureThe Agent requests the Inspect document at /.well-known/aep without credentials. It specifies the accepted signing algorithms, identity methods, session credential types, and protected-resource authentication methods. Its Service DID must match the one from the challenge.

inspect.http
"required": ["contact.email"]
$ curl -v https://api.example.com/.well-known/aep

> GET /.well-known/aep HTTP/1.1
> Host: api.example.com
> Accept: application/aep+json

< HTTP/1.1 200 OK
< Content-Type: application/aep+json

{
  "aep_version": "1.0",
  "authentication": {
    "methods": ["aep-jwt", "oauth-bearer"]
  },
  "bindings": {
    "supported": ["http"]
  },
  "claims": {
    "required": ["contact.email"]
  },
  "commands": {
    "grant_types": ["oauth-bearer"],
    "grant_types_config": {
      "oauth-bearer": {
        "supports_per_credential_revoke": "true"
      }
    },
    "supported": ["enroll", "grant", "inspect", "revoke", "status"]
  },
  "core": {
    "signing_algorithms": ["EdDSA", "ES256"]
  },
  "http": {
    "endpoint_base": "/aep/"
  },
  "identity": {
    "methods": ["did:web"]
  },
  "service": {
    "did": "did:web:api.example.com"
  }
}
2--
Enroll

A signed proof establishes the Agent's identityThe Agent signs a statement with its private key. The Service verifies it against the public key at a web address the Agent controls without transmitting a shared secret. Enrollment then returns the Agent's current lifecycle status.

enroll.http
"contact.email": "ops@example.com"
$ curl -v -X POST https://api.example.com/aep/enroll \
  -H 'Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...' \
  -H 'Content-Type: application/aep+json' \
  -H 'Idempotency-Key: 9f8a4d2e-1c3b-4f5e-8b7a-000000000000' \
  -d @enroll.json

> POST /aep/enroll HTTP/1.1
> Host: api.example.com
> Content-Type: application/aep+json
> Idempotency-Key: 9f8a4d2e-1c3b-4f5e-8b7a-000000000000
> Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...

{
  "agent_did": "did:web:agent.example.com:agents:123",
  "claims": {
    "contact.email": "ops@example.com"
  }
}

< HTTP/1.1 200 OK
< Content-Type: application/aep+json

{
  "status": "active"
}
3--
Grant

Grant can issue a reusable session credentialWhen the Inspect document advertises Grant, the Agent can use another signed proof to request an API key, OAuth token, or HTTP Basic credential. The Agent can then use that credential on protected resources that advertise the corresponding authentication method.

grant.http
"grant_type": "oauth-bearer"
$ curl -v -X POST https://api.example.com/aep/grant \
  -H 'Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...' \
  -H 'Content-Type: application/aep+json' \
  -H 'Idempotency-Key: 5cb95539-251d-46b4-9a36-8de6efd6b771' \
  -d @grant.json

> POST /aep/grant HTTP/1.1
> Host: api.example.com
> Content-Type: application/aep+json
> Idempotency-Key: 5cb95539-251d-46b4-9a36-8de6efd6b771
> Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...

{
  "grant_type": "oauth-bearer"
}

< HTTP/1.1 200 OK
< Content-Type: application/aep+json

{
  "access_token": "ya29.example",
  "credential_id": "tok_01HZY8W7Q2F8J7D3P9G9Z1N6TT",
  "expires_at": "2026-09-15T22:00:00Z",
  "token_type": "Bearer"
}


$ curl -v https://api.example.com/v1/orders

> GET /v1/orders HTTP/1.1
> Host: api.example.com
> Authorization: Bearer ya29.example

< HTTP/1.1 200 OK
Status+Revoke

Enrollment stays checkable, access stays revocableStatus reads the agent's current enrollment state, active or pending, and lists any requirement still outstanding. Revoke invalidates a session credential and requires the same signed proof as Enroll and Grant, never the credential it's cutting off. The enrollment underneath stays intact.

status.http
> GET /aep/status HTTP/1.1
$ curl -v https://api.example.com/aep/status

> GET /aep/status HTTP/1.1
> Host: api.example.com
> Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...

< HTTP/1.1 200 OK

{
  "requirements_pending": ["contact.email"],
  "status": "pending"
}
revoke.http
> POST /aep/revoke HTTP/1.1
$ curl -v -X POST https://api.example.com/aep/revoke \
  -H 'Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...' \
  -H 'Content-Type: application/aep+json' \
  -H 'Idempotency-Key: e29c88b1-8f56-421f-89b0-c2882353a36f' \
  -d @revoke.json

> POST /aep/revoke HTTP/1.1
> Host: api.example.com
> Content-Type: application/aep+json
> Idempotency-Key: e29c88b1-8f56-421f-89b0-c2882353a36f
> Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...

{
  "credential_id": "tok_01HZY8W7Q2F8J7D3P9G9Z1N6TT",
  "grant_type": "oauth-bearer"
}

< HTTP/1.1 200 OK
< Content-Type: application/aep+json

{}

One enrollment turns every visit into recognizable revenue

1

Signed identity means fewer chargebacks and disputes

Enrollment ties one identity to a private-key signature. That identity backs a chargeback dispute to avoid fraud and deter bad actors.

2

Seller Policies use enrollment details to enforce access and transaction rules

A seller reads an agent's declared location and its enrollment status to decide: blocked outright, waits for review, or transacts without restriction.

3

Satisfy Travel Rule's requirements and stay in compliance

Travel Rule needs a verified name, address, and date of birth before a transfer clears. AEP's claim catalog collects that exact shape at enrollment.

4

Recognized once, remembered on every call after

Every call from a recognized agent adds to one per-agent usage record. A seller reads that record for upsell timing, term changes, or retention signals.

↳ view docs
Discovery [ODP]
discover.http
> GET /odp/offerings/
$ curl -v https://api.example.com/odp/offerings/neural-search-api

> GET /odp/offerings/neural-search-api HTTP/1.1
> Host: api.example.com

< HTTP/1.1 200 OK
< Content-Type: application/odp+json

{
  "odp_version": "1.0",
  "id": "neural-search-api",
  "name": "Neural Search API",
  "price": {
    "amount": "0.003",
    "currency": "USDC",
    "type": "fixed"
  }
}

Find what a Service sells, before AEP runsODP is the most agent-compatible, deterministic way for agents to find services. A protected service is routed to AEP for enrollment.

↳ learn more
Pay [InFlow CLI]
settle.http
$ inflow mpp pay
> GET /v1/orders HTTP/1.1
> Host: api.example.com
> AEP-Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...

< HTTP/1.1 402 Payment Required
< WWW-Authenticate: Payment id="challenge-1", realm="api.example.com",
<   method="inflow"

$ inflow mpp pay https://api.example.com/v1/orders

> GET /v1/orders HTTP/1.1
> Host: api.example.com
> AEP-Authorization: AEP eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...
> Authorization: Payment eyJhbW91bnQiOiIwLjAwMyJ9...

< HTTP/1.1 200 OK
< Payment-Receipt: eyJzZXR0bGVkIjp0cnVlfQ...

Settle a charge once identity is resolvedx402 or MPP turns a 402 response into a completed payment. InFlow CLI is the wallet that moves the funds, for an agent AEP has already confirmed.

↳ learn more

Convert guest checkouts into registered agent customers