Protocol

Enroll and Status

Enroll asks a Service to recognize an Agent identity. Status reports the Service's current decision about that identity.

Two commands, one enrollment record

After reading the Service's Inspect document, an Agent can call Enroll if the Service advertises it and accepts the Agent's identity method. The Service uses that request to make an initial enrollment decision. The decision may be immediate, or it may depend on claim verification or action by the Agent's Owner.

Status does not submit another enrollment. It reads the Service's current state for the authenticated Agent identity. If the Service advertises Status, an Agent uses it after a pending response and whenever it needs to know whether an existing enrollment remains usable. Both commands return lifecycle representations on success; an error about a blocked operation is a different response.

Send Enroll

The Agent sends POST to the advertised Enroll path, built from http.endpoint_base. It presents a fresh baseline client assertion with op set to enroll and sends a non-empty Idempotency-Key header. The example uses /aep/ as the endpoint base and a Service that requested contact.email in Inspect.

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

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

agent_did must identify the same Agent as the assertion's iss, sub, and DID portion of kid. The identity method must be one the Service advertised. The optional claims object carries values requested in Inspect; submitting a value does not mean the Service has verified it. The request body may also carry idempotency_key; when it does, it must equal the header value.

Include Owner-authorized document IDs

When Inspect advertises Service Documents, Enroll can carry acknowledged_document_ids. Send only the original IDs covered by the Owner's decision. A successful response echoes that declaration, not a cumulative history. Omission is not acceptance; an empty array explicitly declares none. Existing accepted IDs are not deleted by omission.

Interpret the Enroll result

A successful Enroll response uses 200 OK. For a new enrollment, active means the Service recognizes the Agent and it can continue; pending means the decision is not complete; rejected means the initial decision did not approve enrollment. An immediate approval needs only a status field with the value active.

When verification or Owner action is outstanding, the response may instead look like this. It tells the Agent to check Status later; it does not provide a verified claim value or grant access to a protected resource.

{
  "owner_action_required": "true",
  "status": "pending",
  "verification_pending": ["contact.email"]
}

An explicit repeated Enroll can submit updated claims or supported extension inputs for the same Agent DID. The Service may evaluate the update or return the record unchanged. Accepted updates preserve its identity, original creation time, omitted claims, unrelated lifecycle state, and credentials; Enroll does not implicitly reactivate a suspended or terminated record.

Check Status

Status is a bodyless GET for the Agent identity authenticated on that request. Its path uses the same advertised endpoint base as Enroll. Its baseline client assertion uses op equal to status. A concrete session-credential type may additionally allow its credential on Status, but every exposed Status endpoint accepts the baseline assertion.

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

A successful Status response uses 200 OK and reports one of six states. When present, since is the time of the last state transition, not the original enrollment time.

{
  "since": "2026-05-28T12:00:00Z",
  "status": "active"
}
  • active: Enrolled and operational.
  • pending: Waiting for asynchronous verification.
  • unavailable: Temporarily unavailable for a Service-defined, non-punitive reason.
  • suspended: Temporarily disabled by Service action.
  • terminated: Permanently de-registered.
  • rejected: Asynchronous verification failed.

The status field describes enrollment state. It is not an HTTP status or a promise that the application will authorize every request from an active Agent.

Understand what remains unfinished

verification_pending names submitted claims the Service has not finished verifying. requirements_pending names requirements the Agent still needs to satisfy. They are not interchangeable: a submitted email awaiting verification is different from a missing requirement. Both fields can appear in either Enroll or Status; an omitted field means its set is empty, and empty arrays are not emitted. Neither array contains claim values.

owner_action_required is independent of those two lists. Its value is a JSON string: true means the Agent's Owner must complete an out-of-band action before enrollment can become or remain active. Canonical responses omit the field unless it is true; consumers also accept an explicit false. The Agent should use these details to decide whether to wait, request information, or ask its Owner to act.

Retry Enroll without changing the request

A network failure can leave the Agent unsure whether Enroll succeeded. Retry with the same authenticated Agent, command, request body, and Idempotency-Key. The Service retains the response for that key for at least one hour and returns the cached response or an equivalent successful response. Reusing the key with a different command or body produces 409 idempotency_conflict. A missing or empty key produces 400 invalid_request.

The retry uses a fresh client assertion and jti; the idempotency key stays the same because the operation being retried has not changed. An explicit update to an existing enrollment uses a new key. The Agent must send that request rather than suppress it because an enrollment already exists. See Errors and idempotency for general retry and error rules.

Next steps