ZeroID REST APIs

Interactive reference for the ZeroID REST API. ZeroID is Highflame's open-source agent identity layer — it issues OAuth2 tokens for non-human identities (agents, applications, MCP servers, services), manages their lifecycle, and enforces credential policies.

All SDKs (Python, TypeScript, Rust) are thin clients over these endpoints.

Base URL

Environment
URL

Production (managed)

https://api.zeroid.io

Self-hosted

Your deployment URL

Local dev

http://localhost:8899

Authentication

ZeroID has two distinct API surfaces with different authentication models.

Public token endpoints (/oauth2/*, /.well-known/*, /health, /ready) require no Authorization header. Tenant context and identity are derived entirely from the credential material in the request body (API key, client credentials, JWT assertion, etc.).

Admin endpoints (/api/v1/*) manage identity registration and lifecycle. By default they have no built-in authentication — protect them at the network layer (reverse proxy, VPN, firewall). When the AdminAuth hook is configured, requests must carry:

Header
Description

Authorization

Bearer zid_sk_... (API key) or Bearer <jwt>

X-Account-ID

Account identifier (multi-tenant)

X-Project-ID

Project identifier (multi-tenant)

The X-Account-ID and X-Project-ID headers are required on every admin request — they scope all read and write operations to the correct tenant.


Public Endpoints

POST /oauth2/token

Issue an access token. The grant type determines which credential fields are required. No Authorization header or tenant headers — tenant context is resolved from the credential material.

Common fields:

Field
Type
Required
Description

grant_type

string

Yes

One of: api_key, client_credentials, urn:ietf:params:oauth:grant-type:jwt-bearer, urn:ietf:params:oauth:grant-type:token-exchange, authorization_code, refresh_token

scope

string

No

Space-delimited requested scopes

account_id

string

Depends

Required for client_credentials and authorization_code grants

project_id

string

Depends

Required for client_credentials and authorization_code grants

api_key grant — exchange a zid_sk_* API key for a short-lived JWT:

Field
Required
Description

api_key

Yes

The zid_sk_* API key

client_credentials grant — M2M token for a registered OAuth client:

Field
Required
Description

client_id

Yes

OAuth client identifier

client_secret

Yes

Client secret (confidential clients only)

account_id

Yes

Tenant account ID

project_id

Yes

Tenant project ID

urn:ietf:params:oauth:grant-type:jwt-bearer grant — present a self-signed JWT assertion (RFC 7523). The identity must have a registered public_key_pem:

Field
Required
Description

subject

Yes

Signed JWT assertion (ES256 or RS256)

scope

No

Requested scopes

urn:ietf:params:oauth:grant-type:token-exchange grant — delegate or impersonate (RFC 8693):

Field
Required
Description

subject_token

Yes

The token being exchanged

subject_token_type

Yes

URI identifying token type, e.g. urn:ietf:params:oauth:token-type:access_token

actor_token

No

Actor token for NHI delegation chains

authorization_code grant — redeem an authorization code (PKCE supported):

Field
Required
Description

code

Yes

Authorization code JWT

client_id

Yes

OAuth client identifier

redirect_uri

Yes

Must match the URI used during authorization

code_verifier

No

PKCE S256 verifier

account_id

Yes

Tenant account ID

project_id

Yes

Tenant project ID

refresh_token grant — obtain a new access token using a refresh token:

Field
Required
Description

refresh_token

Yes

Refresh token (zid_rt_*)

Example response (all grant types):

Error response (RFC 6749 §5.2):


POST /oauth2/token/introspect

Introspect a JWT per RFC 7662. Returns active: false for expired, revoked, or unrecognised tokens — never an error.

Field
Required
Description

token

Yes

JWT to introspect

Example response (active token):

Example response (inactive token):


POST /oauth2/token/revoke

Revoke a token per RFC 7009. Always returns 200 — whether the token exists or not.

Field
Required
Description

token

Yes

JWT to revoke

Example response:


GET /oauth2/token/verify

Forward-auth endpoint for reverse proxies (nginx auth_request, Caddy forward_auth, Traefik forwardAuth). Reads the Bearer JWT from Authorization, introspects it, and returns 200 with identity claims as response headers on success, or 401 on failure.

On success, the following headers are set on the response (the proxy copies them into the upstream request):

Response Header
JWT Claim
Description

X-Forwarded-User

sub

WIMSE URI of the identity

X-Zeroid-Identity-Type

identity_type

agent, application, etc.

X-Zeroid-Trust-Level

trust_level

first_party, verified_third_party, unverified

X-Zeroid-Account-ID

account_id

Tenant account ID

X-Zeroid-Project-ID

project_id

Tenant project ID

X-Zeroid-External-ID

external_id

Identity external ID

X-Zeroid-Act-Sub

act.sub

Delegating actor subject (token exchange only)

Example response (200 OK):


GET /.well-known/jwks.json

Returns the server's public key set (JWK Set). Use this to verify JWTs issued by ZeroID without calling back to the server. Keys are ECDSA P-256 (ES256) and optionally RSA 2048 (RS256).

Example response:


GET /.well-known/oauth-authorization-server

OAuth 2.0 Authorization Server Metadata (RFC 8414). Describes supported grant types, endpoints, and signing algorithms.

Example response:


GET /health

Liveness check. Always returns 200 while the process is running.

Example response:

GET /ready

Readiness check. Verifies database connectivity. Returns 503 if the database is unreachable.

Example response:


Admin Endpoints

All admin endpoints require X-Account-ID and X-Project-ID headers. When AdminAuth is configured, also include Authorization: Bearer zid_sk_... or Authorization: Bearer <jwt>.


Agents

The Agent API is a high-level convenience layer. A single registration call atomically creates an identity and issues an API key. Use this for the common case of registering an agent that authenticates with a zid_sk_* key.

POST /api/v1/agents/register

Register a new agent. Creates an identity and an API key in a single atomic operation. Returns the plaintext API key — it is shown only once.

Field
Type
Required
Description

name

string

Yes

Human-readable name

external_id

string

Yes

Unique identifier within this project

identity_type

string

No

agent (default), application, mcp_server, service

sub_type

string

No

Role within the identity type (see table below)

trust_level

string

No

unverified (default), verified_third_party, first_party

framework

string

No

Agent framework, e.g. langchain, autogen, crewai

version

string

No

Agent version string

publisher

string

No

Publisher or organization

description

string

No

Human-readable description

capabilities

JSON array

No

List of capability strings

labels

JSON object

No

Key-value labels for filtering

metadata

JSON object

No

Opaque product-specific metadata

created_by

string

No

User ID of the creator

public_key_pem

string

No

PEM-encoded EC P-256 public key for jwt_bearer and token_exchange grants

Valid sub_type values by identity_type:

identity_type
Valid sub_type values

agent

orchestrator, autonomous, tool_agent, human_proxy, evaluator

application

chatbot, assistant, api_service, code_agent, custom

mcp_server

(none)

service

llm_provider

Example response:

The plaintext_key is shown once. Store it securely — it cannot be retrieved again.


GET /api/v1/agents/registry

List agents for the current tenant. Supports filtering and pagination.

Query param
Description

identity_type

Filter by type: agent, application, mcp_server, service (comma-separated for multiple)

label

Filter by label in key:value format, e.g. team:research

trust_level

Filter by trust level

is_active

Filter by active status (true / false)

search

Search by name or external_id

limit

Items per page (default 20, max 100)

offset

Pagination offset (default 0)

Example response:


GET /api/v1/agents/registry/{id}

Get a single agent by its identity UUID.

Returns the same shape as a single item in the list response above.


PATCH /api/v1/agents/registry/{id}

Update mutable fields of an agent. All fields are optional — only provided fields are updated.

Field
Type
Description

name

string

Human-readable name

sub_type

string

Operational role

trust_level

string

Trust level

framework

string

Agent framework

version

string

Agent version

publisher

string

Publisher

description

string

Description

capabilities

JSON array

Capabilities

labels

JSON object

Key-value labels

metadata

JSON object

Opaque metadata

status

string

active, suspended, or deactivated

Returns the updated agent object.


DELETE /api/v1/agents/registry/{id}

Soft-delete an agent — deactivates the identity and revokes all its API keys. The identity record is retained for audit purposes.

Returns the final agent state with status: "deactivated".


POST /api/v1/agents/registry/{id}/activate

Activate a previously deactivated or pending agent.

Returns the updated agent with status: "active".


POST /api/v1/agents/registry/{id}/deactivate

Deactivate an agent without deleting it. The agent can be reactivated later.

Returns the updated agent with status: "deactivated".


POST /api/v1/agents/registry/{id}/rotate-key

Rotate an agent's API key. Revokes the existing key and issues a new one. Returns the plaintext key — shown once only.

Example response:


Identities

Lower-level identity management. Use the Agents API for the common registration case. Use Identities directly when you need fine-grained control or want to attach credentials separately.

POST /api/v1/identities

Create a new identity without automatically issuing credentials.

Field
Type
Required
Description

external_id

string

Yes

Unique identifier within this project

owner_user_id

string

Yes

User ID of the identity owner

name

string

No

Human-readable name

identity_type

string

No

agent, application, mcp_server, service

sub_type

string

No

Role within identity type

trust_level

string

No

unverified, verified_third_party, first_party

allowed_scopes

string array

No

OAuth scopes this identity may request

public_key_pem

string

No

PEM-encoded ECDSA P-256 public key for jwt_bearer grant

framework

string

No

Agent framework

version

string

No

Version string

publisher

string

No

Publisher or organization

description

string

No

Human-readable description

capabilities

JSON array

No

Capability list

labels

JSON object

No

Key-value labels

Example response:


GET /api/v1/identities

List identities for the current tenant.

Query param
Description

identity_type

Filter by type (comma-separated for multiple)

label

Filter by label in key:value format

trust_level

Filter by trust level

is_active

Filter by active status (true / false)

search

Search by name or external_id

limit

Items per page (default 20, max 100)

offset

Pagination offset

Example response:


GET /api/v1/identities/{id}

Get a single identity by UUID.

Returns the full identity object.


PATCH /api/v1/identities/{id}

Update mutable fields of an identity. All fields are optional.

Field
Type
Description

name

string

Human-readable name

identity_type

string

Identity type

sub_type

string

Sub-type

trust_level

string

Trust level

owner_user_id

string

Owner user ID

allowed_scopes

string array

Permitted scopes

public_key_pem

string

ECDSA public key PEM

framework

string

Framework

version

string

Version

publisher

string

Publisher

description

string

Description

capabilities

JSON array

Capabilities

labels

JSON object

Labels

status

string

active, suspended, or deactivated

Returns the updated identity object.


DELETE /api/v1/identities/{id}

Soft-delete (deactivate) an identity. Returns 204 No Content.


Credential Policies

Credential policies define governance constraints enforced at token issuance time — max TTL, allowed grant types, required trust level, and delegation depth. Policies are templates assigned to API keys.

POST /api/v1/credential-policies

Create a new credential policy.

Field
Type
Required
Description

name

string

Yes

Policy name (unique per tenant)

description

string

No

Human-readable description

max_ttl_seconds

integer

No

Maximum token TTL in seconds (default 3600)

allowed_grant_types

string array

No

Permitted grant types (e.g. ["api_key", "client_credentials"])

allowed_scopes

string array

No

Permitted OAuth scopes

required_trust_level

string

No

Minimum identity trust level required

required_attestation

string

No

Minimum attestation level required

max_delegation_depth

integer

No

Maximum delegation chain depth (default 1)

Example response:


GET /api/v1/credential-policies

List all credential policies for the current tenant.

Example response:


GET /api/v1/credential-policies/{id}

Get a single credential policy by UUID.

Returns the full policy object.


PATCH /api/v1/credential-policies/{id}

Update a credential policy. All fields are optional.

Field
Type
Description

name

string

Policy name

description

string

Description

max_ttl_seconds

integer

Max token TTL

allowed_grant_types

string array

Permitted grant types

allowed_scopes

string array

Permitted scopes

required_trust_level

string

Required trust level

required_attestation

string

Required attestation level

max_delegation_depth

integer

Max delegation depth

is_active

boolean

Enable or disable the policy

Returns the updated policy object.


DELETE /api/v1/credential-policies/{id}

Delete a credential policy. Returns 204 No Content.


API Keys

Standalone API key management. API keys are prefixed zid_sk_*, hashed with SHA-256, and shown in plaintext only at creation. Use the Agent API (/api/v1/agents/register) to create a key bound to a new identity atomically; use this API to create additional keys for existing identities or manage keys directly.

POST /api/v1/api-keys

Create a new API key.

Field
Type
Required
Description

name

string

Yes

Human-readable key name

description

string

No

Key description

identity_id

string

No

UUID of the identity to link this key to

product

string

No

Product namespace for key scoping

scopes

string array

No

Allowed OAuth scopes

environment

string

No

live (default) or test

expires_in_days

integer

No

Key expiry in days (omit for no expiry)

metadata

JSON object

No

Arbitrary JSON metadata

Example response:


GET /api/v1/api-keys

List API keys for the current tenant.

Query param
Description

product

Filter by product namespace

application_id

Filter by linked identity ID

label

Filter by identity label in key:value format

page

Page number (default 1)

limit

Items per page (default 20, max 100)

Example response:


GET /api/v1/api-keys/{id}

Get a single API key by UUID. The raw key value is never returned — only metadata.

Returns the full key metadata object.


POST /api/v1/api-keys/{id}/revoke

Revoke an API key immediately. Any tokens issued with this key remain valid until their exp claim. To also invalidate issued tokens, revoke them via POST /oauth2/token/revoke.

Field
Type
Required
Description

reason

string

No

Human-readable revocation reason

Example response:


CAE Signals

Continuous Access Evaluation (CAE) signals notify ZeroID of security-relevant events affecting an identity. Signals trigger downstream token evaluation and can cause active sessions to be terminated.

Valid signal_type values: credential_change, session_revoked, ip_change, anomalous_behavior, policy_violation, retirement, owner_change.

POST /api/v1/signals/ingest

Ingest a CAE signal for an identity.

Field
Type
Required
Description

signal_type

string

Yes

Type of the signal (see valid values above)

source

string

Yes

System or service that generated the signal

identity_id

string

No

UUID of the affected identity

severity

string

No

low (default), medium, high, critical

payload

JSON object

No

Arbitrary signal payload for context

Example response:


GET /api/v1/signals

List recent CAE signals for the current tenant.

Query param
Description

limit

Maximum signals to return (default 50, max 500)

Example response:

GET /api/v1/signals/stream

Real-time Server-Sent Events stream for CAE signals. Signals are pushed as they are ingested. Useful for building real-time dashboards or automated response systems.

Example event stream:


OAuth Clients

Register and manage OAuth2 clients (RFC 7591). Clients are global — tenant scoping happens at token issuance, not registration. Use confidential clients (confidential: true) for server-to-server M2M flows; use public PKCE clients for browser or native agent flows.

POST /api/v1/oauth/clients

Register a new OAuth2 client.

Field
Type
Required
Description

client_id

string

Yes

Globally unique client identifier

name

string

Yes

Display name

description

string

No

Human-readable description

confidential

boolean

No

If true, generates a client_secret for M2M flows

token_endpoint_auth_method

string

No

none, client_secret_basic, client_secret_post, private_key_jwt

grant_types

string array

No

Permitted OAuth grant types

scopes

string array

No

Permitted OAuth scopes

redirect_uris

string array

No

Allowed redirect URIs (required for authorization_code clients)

access_token_ttl

integer

No

Access token lifetime in seconds (0 = server default)

refresh_token_ttl

integer

No

Refresh token lifetime in seconds

jwks_uri

string

No

URL to client's public JWK Set (for private_key_jwt)

jwks

JSON object

No

Inline JWK Set (when no URI is available)

software_id

string

No

Client software identifier (RFC 7591)

software_version

string

No

Client software version

contacts

string array

No

Email addresses of responsible parties

metadata

JSON object

No

Arbitrary JSON metadata

Example response:

For public (PKCE) clients the response omits client_secret and the note reads: "Public PKCE client registered — no client_secret (use PKCE code_challenge instead)."


GET /api/v1/oauth/clients

List all registered OAuth2 clients.

Example response:


GET /api/v1/oauth/clients/{id}

Get a single OAuth2 client by its internal UUID (not client_id).

Returns the full client object. client_secret is never included.


DELETE /api/v1/oauth/clients/{id}

Delete an OAuth2 client. Any tokens issued to this client remain valid until their exp claim.

Example response:


POST /api/v1/oauth/clients/{id}/rotate-secret

Rotate a confidential client's secret. The old secret is invalidated immediately. Returns the new plaintext secret — shown once only.

Example response:


Error Responses

Public token endpoints return RFC 6749 §5.2 error bodies:

Admin endpoints return RFC 9457 problem details via Huma:

Common HTTP status codes:

Status
Meaning

200

Success

201

Resource created

204

Success, no content

400

Bad request — invalid input

401

Unauthorized — missing or invalid credentials

404

Resource not found

409

Conflict — resource already exists

503

Service unavailable — database unreachable


WIMSE Identity URIs

Every identity in ZeroID is assigned a SPIFFE/WIMSE URI used as the JWT sub claim:

Example:

This URI is stable for the lifetime of the identity and is included in every issued JWT, enabling downstream services to make access decisions without calling back to ZeroID.


SDK Reference

The ZeroID SDKs wrap these endpoints with automatic token caching, refresh, and retry logic.

Last updated