Errors and idempotency
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.
| Code | HTTP | What the caller should understand |
|---|---|---|
| enrollment_failed | 400 | Enrollment failed; the Service suppresses a more precise reason. |
| invalid_request | 400 | Correct malformed JSON, missing fields, types, or incompatible field combinations. |
| unsupported_grant_type | 400 | The requested Grant or Revoke type was not advertised; recheck Inspect. |
| not_recognized | 401 | Recognition or assertion validation failed; no finer reason is disclosed. |
| authentication_required | 401 | The protected resource needs an advertised authentication method. |
| unsupported_authentication_method | 401 | The protected resource does not accept the method presented. |
| identity_suspended | 403 | A recognized identity is temporarily disabled by Service action. |
| identity_terminated | 403 | A recognized identity is permanently de-registered. |
| identity_unavailable | 403 | A recognized identity is temporarily unavailable under Service policy. |
| verification_pending | 403 | Enrollment or required verification has not completed. |
| insufficient_scope | 403 | Authentication succeeded, but authorization scope is insufficient. |
| idempotency_conflict | 409 | The key was reused for changed command or request content. |
| requirements_unmet | 422 | Required claims are missing or invalid. |
| verification_timeout | 422 | Required asynchronous verification exceeded the Service's policy window. |
| rate_limited | 429 | The 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.