Protocol

Errors and idempotency

AEP reports an Agent's lifecycle state in successful Enroll and Status responses. It reports a failed or blocked operation with Problem Details. State-changing commands also have an idempotency contract for safe retries.

Read the response before choosing a remedy

A 200 OK Enroll or Status response with "status":"pending" reports the Service's current enrollment state. A successful Status response can also report suspended, unavailable, terminated, or another lifecycle state. These are not HTTP errors, and another Enroll request does not reset the record.

A later command or protected-resource request may be blocked because of that state and return an error such as verification_pending or identity_suspended. The error describes the attempted operation; it does not replace the Status representation. Agents should preserve this distinction when reporting what happened to a user or deciding what to do next.

Read the AEP Problem Details fields

The HTTP binding returns application/problem+json with a canonical machine-readable code. The type must equal urn:aep:error:<code> for that exact code; title is a short human-readable summary. For example:

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json
WWW-Authenticate: AEP reason="not_recognized"

{
  "code": "not_recognized",
  "status": 401,
  "title": "Not recognized",
  "type": "urn:aep:error:not_recognized"
}

not_recognized intentionally does not say whether the Agent DID exists, the signature failed, the assertion used the wrong audience or operation, its jti was replayed, or another recognition check failed. The Service must not reveal which check failed. An Agent must not probe other commands or identities to turn this umbrella error into an existence test.

After the Agent is recognized, verification_pending may carry verification_pending claim names, and requirements_unmet may carry requirements_pending names. Either may carry "owner_action_required":"true". They must not disclose claim values; not_recognized must not carry any of these details. If several errors apply, the Service chooses the least revealing one.

Use the code to choose the next action

The HTTP status gives a broad outcome; the AEP code tells the caller what happened. The core protocol defines these codes. Correct a definite request error before resending; do not treat every 403 as an authentication failure or every 401 as a reason to enroll again.

CodeHTTPWhat the caller should understand
enrollment_failed400Enrollment failed; the Service suppresses a more precise reason.
invalid_request400Correct malformed JSON, missing fields, types, or incompatible field combinations.
unsupported_grant_type400The requested Grant or Revoke type was not advertised; recheck Inspect.
not_recognized401Recognition or assertion validation failed; no finer reason is disclosed.
authentication_required401The protected resource needs an advertised authentication method.
unsupported_authentication_method401The protected resource does not accept the method presented.
identity_suspended403A recognized identity is temporarily disabled by Service action.
identity_terminated403A recognized identity is permanently de-registered.
identity_unavailable403A recognized identity is temporarily unavailable under Service policy.
verification_pending403Enrollment or required verification has not completed.
insufficient_scope403Authentication succeeded, but authorization scope is insufficient.
idempotency_conflict409The key was reused for changed command or request content.
requirements_unmet422Required claims are missing or invalid.
verification_timeout422Required asynchronous verification exceeded the Service's policy window.
rate_limited429The Service rate limit was exceeded; wait before another attempt.

Give each state-changing command a retry key

Enroll, Grant, and Revoke are POST commands. Every request needs a nonempty Idempotency-Key HTTP header. The Service rejects a missing or empty key with 400 invalid_request. Status and Inspect are GET commands and do not use this POST retry contract.

The Service retains a result for the authenticated Agent and key for at least one hour, bound to the command and a canonical hash of the request body. Repeating the same command and body with that key returns the cached response or an equivalent successful response. Reusing the key for another command or changed body returns 409 idempotency_conflict. Use a new key for a new operation.

Enroll may also place idempotency_key in the JSON body. If both body and header forms are present, their values must match. The header is still required.

Retry an unknown outcome, not a known rejection

If the network drops after an Enroll, Grant, or Revoke request, the Agent cannot infer whether the Service applied it. Retry the same command and unchanged body with the original idempotency key, but sign a fresh operation-bound client assertion with a new jti. Replaying the old assertion can fail recognition even though the idempotency key is correct. Do not generate a new key merely to check the outcome; that can start a separate operation.

A definite invalid_request, unsupported_grant_type, or idempotency_conflict requires correcting the request or key choice before trying again. A not_recognized response does not identify which recognition check failed. Handle rate limits and pending requirements according to the Service's response, rather than blindly repeating the same operation. The Enroll and Status and Grant and Revoke pages give the command-specific context.