Authdog

AuthZEN API

Last updated Aug 8, 2026
View as Markdown

Authdog answers authorization questions over the OpenID Foundation's AuthZEN Authorization API 1.0. Your application acts as the Policy Enforcement Point (PEP) and calls Authdog as the Policy Decision Point (PDP): send a subject, an action, and a resource; get back permit or deny.

Reach for AuthZEN when you want a vendor-neutral wire contract, when a gateway or sidecar in your stack already speaks it, or when you want one decision endpoint no matter which model — RBAC, ABAC, or FGA — the environment enforces.

Endpoints

The PDP is mounted at the API origin root, outside the /v1 base path, because the specification fixes these paths and interop tooling probes them verbatim.

Endpoint Purpose
POST /access/v1/evaluation One decision for one subject, action, and resource.
POST /access/v1/evaluations Boxcarred batch of up to 100 evaluations.
POST /access/v1/search/subject Who can perform this action on this resource?
POST /access/v1/search/resource What resources can this subject act on?
POST /access/v1/search/action What actions can this subject perform on this resource?
GET /.well-known/authzen-configuration Discovery document. Unauthenticated, per section 9.1.

Discovery advertises all five endpoints and honors X-Forwarded-Host and X-Forwarded-Proto, so the advertised URLs match the origin your PEP actually calls.

curl https://api.authdog.com/.well-known/authzen-configuration

Authentication

Every endpoint except discovery is authenticated with an environment API secret (adenv_...) as a bearer token. The token alone identifies the environment whose policies decide the request, so no tenant or environment appears in the path.

curl -X POST "https://api.authdog.com/access/v1/evaluation" \
  -H "Authorization: Bearer $AUTHDOG_ENVIRONMENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "subject":  { "type": "user", "id": "usr_123" },
    "action":   { "name": "can_read" },
    "resource": { "type": "document", "id": "roadmap" }
  }'

The response is the spec's decision shape:

{
  "decision": true,
  "context": {
    "reason_admin": { "reason_code": "role_grant" },
    "reason_user": { "en": "Permitted by an assigned role." }
  }
}

The environment secret is a PEP credential, not an end-user token. Keep it server-side; never ship it to a browser or mobile client.

How decisions are made

The PDP dispatches on the environment's permission model, reusing the same engines that enforce access inside Authdog — a decision served over AuthZEN can never disagree with what Authdog enforces internally.

  • RBAC: the action is normalized to a CRUD verb and checked against the subject's role grants, including roles inherited through groups.
  • ABAC: the request is converted into policy input and evaluated against the environment's enabled policies.
  • FGA: the action is mapped onto a relation and checked against the environment's authorization model and relationship tuples.

See Authorization concepts for how to choose a model, and Fine-grained authorization for the relationship model.

Action names

AuthZEN leaves the action namespace to the PDP. Authdog normalizes action names onto four coarse verbs — create, read, update, delete — so PEPs can send whichever dialect they already use:

Sent by the PEP Normalized verb
create, can_create, insert, add, write, can_write, POST create
read, can_read, view, can_view, GET, list, HEAD, search read
update, can_update, edit, can_edit, modify, PUT, PATCH update
delete, can_delete, remove, destroy delete

An unrecognized action name falls back to read, the least privileged verb. That means an unknown action can never widen access — but it also won't grant what you intended, so keep your PEP on a name from the table.

Subjects

Subject types user, identity, and subject resolve against the environment's directory, matched by user id or external id. A subject that cannot be resolved is denied with reason code subject_not_found; a resolved but deactivated user is denied with subject_inactive.

Reason codes are stable and safe to log: role_grant, no_matching_role, policy_allow, policy_deny, no_policies, relation_holds, relation_missing, subject_not_found, subject_inactive, and evaluation_error.

Batching

POST /access/v1/evaluations evaluates up to 100 items in one request. Top-level subject, action, resource, and context act as defaults; each item overrides only what it sets.

{
  "subject": { "type": "user", "id": "usr_123" },
  "resource": { "type": "document", "id": "roadmap" },
  "evaluations": [
    { "action": { "name": "can_read" } },
    { "action": { "name": "can_update" } },
    { "action": { "name": "can_delete" } }
  ],
  "options": { "evaluations_semantic": "deny_on_first_deny" }
}

Supported semantics are execute_all (the default), deny_on_first_deny, and permit_on_first_permit. When a batch short-circuits, the remaining entries are filled with the short-circuit decision rather than omitted, so the response array always lines up with the request array.

A batch shares one environment lookup and memoizes subject and grant reads, so evaluating 50 items costs a handful of database round trips, not 50.

The search endpoints answer the reverse questions — enumeration rather than a yes/no. Results are paginated forward-only: pass the next_token from a response's page block to fetch the next page.

curl -X POST "https://api.authdog.com/access/v1/search/resource" \
  -H "Authorization: Bearer $AUTHDOG_ENVIRONMENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": { "type": "user", "id": "usr_123" },
    "action":  { "name": "can_read" },
    "resource": { "type": "document" }
  }'

Search evaluates every candidate it enumerates, so it costs far more than a single evaluation. Use it for admin surfaces and access reviews, not on a hot request path.

Failure behavior

The PDP is fail-closed. An unresolvable subject, an unknown permission model, or an internal error produces decision: false with a reason — never a partial permit.

Status Meaning
400 Malformed JSON or a body that fails spec validation.
401 Missing or invalid environment API secret.
502 The decision point was unreachable or returned no decision.

Errors use one envelope on every endpoint, so a PEP can log a single shape:

{ "error": { "status": 400, "message": "subject.id is required" } }

A 502 means no decision was made. Your PEP must treat it as a deny, not as a permit.

Learn more