# 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

<table><thead><tr><th width="217.24609375">Environment</th><th>URL</th></tr></thead><tbody><tr><td>Production (managed)</td><td><code>https://api.zeroid.io</code></td></tr><tr><td>Self-hosted</td><td>Your deployment URL</td></tr><tr><td>Local dev</td><td><code>http://localhost:8899</code></td></tr></tbody></table>

### 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:

<table><thead><tr><th width="225.97265625">Header</th><th>Description</th></tr></thead><tbody><tr><td><code>Authorization</code></td><td><code>Bearer zid_sk_...</code> (API key) or <code>Bearer &#x3C;jwt></code></td></tr><tr><td><code>X-Account-ID</code></td><td>Account identifier (multi-tenant)</td></tr><tr><td><code>X-Project-ID</code></td><td>Project identifier (multi-tenant)</td></tr></tbody></table>

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 |

```bash
curl -X POST "http://localhost:8899/oauth2/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "api_key",
    "api_key": "zid_sk_demo_abc123xyz456"
  }'
```

**`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                         |

```bash
curl -X POST "http://localhost:8899/oauth2/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "my-orchestrator",
    "client_secret": "cs_demo_secret",
    "account_id": "acct-demo",
    "project_id": "proj-demo",
    "scope": "read write"
  }'
```

**`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                      |

```bash
curl -X POST "http://localhost:8899/oauth2/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "subject": "eyJhbGciOiJFUzI1NiJ9..."
  }'
```

**`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                                            |

```bash
curl -X POST "http://localhost:8899/oauth2/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "subject_token": "eyJhbGciOiJFUzI1NiJ9...",
    "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
    "actor_token": "eyJhbGciOiJFUzI1NiJ9...",
    "scope": "read"
  }'
```

**`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_*`) |

```bash
curl -X POST "http://localhost:8899/oauth2/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "refresh_token",
    "refresh_token": "zid_rt_demo_refresh456"
  }'
```

Example response (all grant types):

```json
{
  "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InppcC1rZXktMSJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read write",
  "jti": "tok_01hx3b2c4d5e6f7g8h9i",
  "iat": 1743849600,
  "account_id": "acct-demo",
  "project_id": "proj-demo",
  "external_id": "my-agent-001",
  "refresh_token": "zid_rt_abc123xyz456"
}
```

Error response (RFC 6749 §5.2):

```json
{
  "error": "invalid_client",
  "error_description": "API key not found or revoked"
}
```

***

### 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 |

```bash
curl -X POST "http://localhost:8899/oauth2/token/introspect" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InppcC1rZXktMSJ9..."
  }'
```

Example response (active token):

```json
{
  "active": true,
  "iss": "https://zeroid.acct-demo.proj-demo",
  "sub": "spiffe://zeroid.highflame.ai/acct-demo/proj-demo/agent/my-agent-001",
  "aud": ["https://api.highflame.ai"],
  "iat": 1743849600,
  "exp": 1743853200,
  "jti": "tok_01hx3b2c4d5e6f7g8h9i",
  "account_id": "acct-demo",
  "project_id": "proj-demo",
  "external_id": "my-agent-001",
  "identity_type": "agent",
  "sub_type": "orchestrator",
  "trust_level": "first_party",
  "grant_type": "api_key",
  "scopes": ["read", "write"],
  "name": "My Demo Orchestrator",
  "framework": "langchain",
  "version": "1.2.0"
}
```

Example response (inactive token):

```json
{
  "active": false
}
```

***

### 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 |

```bash
curl -X POST "http://localhost:8899/oauth2/token/revoke" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InppcC1rZXktMSJ9..."
  }'
```

Example response:

```json
{
  "revoked": true
}
```

***

### 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)      |

```bash
# nginx config snippet
# auth_request      /oauth2/token/verify;
# auth_request_set  $forwarded_user $upstream_http_x_forwarded_user;

curl -X GET "http://localhost:8899/oauth2/token/verify" \
  -H "Authorization: Bearer eyJhbGciOiJFUzI1NiJ9..."
```

Example response (200 OK):

```json
{"active": true}
```

***

### 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`).

```bash
curl -X GET "http://localhost:8899/.well-known/jwks.json"
```

Example response:

```json
{
  "keys": [
    {
      "kty": "EC",
      "kid": "zid-key-1",
      "crv": "P-256",
      "alg": "ES256",
      "use": "sig",
      "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
      "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
    }
  ]
}
```

***

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

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

```bash
curl -X GET "http://localhost:8899/.well-known/oauth-authorization-server"
```

Example response:

```json
{
  "issuer": "https://zeroid.acct-demo.proj-demo",
  "token_endpoint": "http://localhost:8899/oauth2/token",
  "jwks_uri": "http://localhost:8899/.well-known/jwks.json",
  "introspection_endpoint": "http://localhost:8899/oauth2/token/introspect",
  "revocation_endpoint": "http://localhost:8899/oauth2/token/revoke",
  "grant_types_supported": [
    "client_credentials",
    "urn:ietf:params:oauth:grant-type:jwt-bearer",
    "urn:ietf:params:oauth:grant-type:token-exchange",
    "api_key"
  ],
  "token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"],
  "token_endpoint_auth_signing_alg_values_supported": ["ES256", "RS256"],
  "response_types_supported": ["token"]
}
```

***

### GET /health

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

```bash
curl -X GET "http://localhost:8899/health"
```

Example response:

```json
{
  "status": "healthy",
  "service": "zeroid",
  "timestamp": "2026-04-05T12:00:00Z",
  "uptime_ms": 84600000
}
```

### GET /ready

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

```bash
curl -X GET "http://localhost:8899/ready"
```

Example response:

```json
{
  "ready": true
}
```

***

## 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>`.

```bash
# Common admin headers (used in all examples below)
-H "X-Account-ID: acct-demo" \
-H "X-Project-ID: proj-demo" \
-H "Authorization: Bearer zid_sk_demo_adminkey"
```

***

## 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`                                                         |

```bash
curl -X POST "http://localhost:8899/api/v1/agents/register" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "name": "Research Orchestrator",
    "external_id": "research-orch-001",
    "identity_type": "agent",
    "sub_type": "orchestrator",
    "trust_level": "first_party",
    "framework": "langchain",
    "version": "2.1.0",
    "description": "Coordinates literature research sub-agents",
    "labels": {"team": "research", "env": "production"},
    "created_by": "user_abc123"
  }'
```

Example response:

```json
{
  "identity": {
    "id": "idt_01hx3b2c4d5e6f7g8h9i",
    "account_id": "acct-demo",
    "project_id": "proj-demo",
    "external_id": "research-orch-001",
    "name": "Research Orchestrator",
    "wimse_uri": "spiffe://zeroid.highflame.ai/acct-demo/proj-demo/agent/research-orch-001",
    "identity_type": "agent",
    "sub_type": "orchestrator",
    "trust_level": "first_party",
    "status": "active",
    "owner_user_id": "",
    "framework": "langchain",
    "version": "2.1.0",
    "labels": {"team": "research", "env": "production"},
    "created_at": "2026-04-05T12:00:00Z",
    "updated_at": "2026-04-05T12:00:00Z"
  },
  "api_key": {
    "id": "key_01hx3b2c4d5e6f7g8h9j",
    "name": "research-orch-001",
    "key_prefix": "zid_sk",
    "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
    "account_id": "acct-demo",
    "project_id": "proj-demo",
    "state": "active",
    "created_at": "2026-04-05T12:00:00Z"
  },
  "plaintext_key": "zid_sk_abc123xyz456def789ghi012"
}
```

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)                                                                  |

```bash
curl -X GET "http://localhost:8899/api/v1/agents/registry?identity_type=agent&label=team:research&limit=10" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "agents": [
    {
      "id": "idt_01hx3b2c4d5e6f7g8h9i",
      "external_id": "research-orch-001",
      "name": "Research Orchestrator",
      "wimse_uri": "spiffe://zeroid.highflame.ai/acct-demo/proj-demo/agent/research-orch-001",
      "identity_type": "agent",
      "sub_type": "orchestrator",
      "trust_level": "first_party",
      "status": "active",
      "created_at": "2026-04-05T12:00:00Z"
    }
  ],
  "total": 1,
  "limit": 10,
  "offset": 0
}
```

***

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

Get a single agent by its identity UUID.

```bash
curl -X GET "http://localhost:8899/api/v1/agents/registry/idt_01hx3b2c4d5e6f7g8h9i" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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` |

```bash
curl -X PATCH "http://localhost:8899/api/v1/agents/registry/idt_01hx3b2c4d5e6f7g8h9i" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "version": "2.2.0",
    "trust_level": "verified_third_party",
    "labels": {"team": "research", "env": "production", "reviewed": "true"}
  }'
```

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.

```bash
curl -X DELETE "http://localhost:8899/api/v1/agents/registry/idt_01hx3b2c4d5e6f7g8h9i" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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

***

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

Activate a previously deactivated or pending agent.

```bash
curl -X POST "http://localhost:8899/api/v1/agents/registry/idt_01hx3b2c4d5e6f7g8h9i/activate" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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.

```bash
curl -X POST "http://localhost:8899/api/v1/agents/registry/idt_01hx3b2c4d5e6f7g8h9i/deactivate" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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.

```bash
curl -X POST "http://localhost:8899/api/v1/agents/registry/idt_01hx3b2c4d5e6f7g8h9i/rotate-key" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "identity": { "...": "same shape as register response" },
  "api_key": {
    "id": "key_01hx3b2c4d5e6f7g8h9k",
    "name": "research-orch-001",
    "key_prefix": "zid_sk",
    "state": "active",
    "created_at": "2026-04-05T13:00:00Z"
  },
  "plaintext_key": "zid_sk_newkey789abc123def456ghi"
}
```

***

## 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                                          |

```bash
curl -X POST "http://localhost:8899/api/v1/identities" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "external_id": "tool-agent-web-search",
    "owner_user_id": "user_abc123",
    "name": "Web Search Tool Agent",
    "identity_type": "agent",
    "sub_type": "tool_agent",
    "trust_level": "first_party",
    "allowed_scopes": ["search:read"],
    "framework": "custom",
    "labels": {"product": "research-platform"}
  }'
```

Example response:

```json
{
  "id": "idt_01hx4c3d5e6f7g8h9i0j",
  "account_id": "acct-demo",
  "project_id": "proj-demo",
  "external_id": "tool-agent-web-search",
  "name": "Web Search Tool Agent",
  "wimse_uri": "spiffe://zeroid.highflame.ai/acct-demo/proj-demo/agent/tool-agent-web-search",
  "identity_type": "agent",
  "sub_type": "tool_agent",
  "trust_level": "first_party",
  "status": "active",
  "owner_user_id": "user_abc123",
  "allowed_scopes": ["search:read"],
  "labels": {"product": "research-platform"},
  "capabilities": null,
  "created_at": "2026-04-05T12:00:00Z",
  "updated_at": "2026-04-05T12:00:00Z"
}
```

***

### 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                             |

```bash
curl -X GET "http://localhost:8899/api/v1/identities?identity_type=agent,application&limit=20&offset=0" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "identities": [
    {
      "id": "idt_01hx4c3d5e6f7g8h9i0j",
      "external_id": "tool-agent-web-search",
      "name": "Web Search Tool Agent",
      "identity_type": "agent",
      "status": "active",
      "trust_level": "first_party"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}
```

***

### GET /api/v1/identities/{id}

Get a single identity by UUID.

```bash
curl -X GET "http://localhost:8899/api/v1/identities/idt_01hx4c3d5e6f7g8h9i0j" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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` |

```bash
curl -X PATCH "http://localhost:8899/api/v1/identities/idt_01hx4c3d5e6f7g8h9i0j" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "trust_level": "first_party",
    "allowed_scopes": ["search:read", "search:write"]
  }'
```

Returns the updated identity object.

***

### DELETE /api/v1/identities/{id}

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

```bash
curl -X DELETE "http://localhost:8899/api/v1/identities/idt_01hx4c3d5e6f7g8h9i0j" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

***

## 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)                       |

```bash
curl -X POST "http://localhost:8899/api/v1/credential-policies" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "name": "production-agents",
    "description": "Strict policy for production agent identities",
    "max_ttl_seconds": 900,
    "allowed_grant_types": ["api_key", "urn:ietf:params:oauth:grant-type:jwt-bearer"],
    "allowed_scopes": ["read", "write"],
    "required_trust_level": "first_party",
    "max_delegation_depth": 2
  }'
```

Example response:

```json
{
  "id": "pol_01hx5d4e6f7g8h9i0j1k",
  "account_id": "acct-demo",
  "project_id": "proj-demo",
  "name": "production-agents",
  "description": "Strict policy for production agent identities",
  "max_ttl_seconds": 900,
  "allowed_grant_types": ["api_key", "urn:ietf:params:oauth:grant-type:jwt-bearer"],
  "allowed_scopes": ["read", "write"],
  "required_trust_level": "first_party",
  "max_delegation_depth": 2,
  "is_active": true,
  "created_at": "2026-04-05T12:00:00Z",
  "updated_at": "2026-04-05T12:00:00Z"
}
```

***

### GET /api/v1/credential-policies

List all credential policies for the current tenant.

```bash
curl -X GET "http://localhost:8899/api/v1/credential-policies" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "credential_policies": [
    {
      "id": "pol_01hx5d4e6f7g8h9i0j1k",
      "name": "production-agents",
      "max_ttl_seconds": 900,
      "is_active": true,
      "created_at": "2026-04-05T12:00:00Z"
    },
    {
      "id": "pol_default",
      "name": "default",
      "description": "System default credential policy — applied to agents when no explicit policy is specified",
      "max_ttl_seconds": 3600,
      "allowed_grant_types": ["api_key", "client_credentials"],
      "max_delegation_depth": 1,
      "is_active": true,
      "created_at": "2026-04-05T11:00:00Z"
    }
  ],
  "total": 2
}
```

***

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

Get a single credential policy by UUID.

```bash
curl -X GET "http://localhost:8899/api/v1/credential-policies/pol_01hx5d4e6f7g8h9i0j1k" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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 |

```bash
curl -X PATCH "http://localhost:8899/api/v1/credential-policies/pol_01hx5d4e6f7g8h9i0j1k" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "max_ttl_seconds": 1800,
    "required_trust_level": "verified_third_party"
  }'
```

Returns the updated policy object.

***

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

Delete a credential policy. Returns 204 No Content.

```bash
curl -X DELETE "http://localhost:8899/api/v1/credential-policies/pol_01hx5d4e6f7g8h9i0j1k" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

***

## 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                  |

```bash
curl -X POST "http://localhost:8899/api/v1/api-keys" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "name": "research-agent-prod-key",
    "description": "Production key for research orchestrator",
    "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
    "scopes": ["read", "write"],
    "environment": "live",
    "expires_in_days": 90
  }'
```

Example response:

```json
{
  "key": {
    "id": "key_01hx6e5f7g8h9i0j1k2l",
    "name": "research-agent-prod-key",
    "key_prefix": "zid_sk",
    "account_id": "acct-demo",
    "project_id": "proj-demo",
    "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
    "scopes": ["read", "write"],
    "environment": "live",
    "state": "active",
    "expires_at": "2026-07-04T12:00:00Z",
    "usage_count": 0,
    "created_at": "2026-04-05T12:00:00Z"
  },
  "plaintext_key": "zid_sk_newkey789abc123def456ghi"
}
```

***

### 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)           |

```bash
curl -X GET "http://localhost:8899/api/v1/api-keys?environment=live&limit=20" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "keys": [
    {
      "id": "key_01hx6e5f7g8h9i0j1k2l",
      "name": "research-agent-prod-key",
      "key_prefix": "zid_sk",
      "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
      "state": "active",
      "usage_count": 42,
      "last_used_at": "2026-04-05T11:55:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 20
}
```

***

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

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

```bash
curl -X GET "http://localhost:8899/api/v1/api-keys/key_01hx6e5f7g8h9i0j1k2l" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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 |

```bash
curl -X POST "http://localhost:8899/api/v1/api-keys/key_01hx6e5f7g8h9i0j1k2l/revoke" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "reason": "key compromised — rotated during security review"
  }'
```

Example response:

```json
{
  "message": "API key revoked"
}
```

***

## 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          |

```bash
curl -X POST "http://localhost:8899/api/v1/signals/ingest" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "signal_type": "anomalous_behavior",
    "source": "shield-guardrails",
    "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
    "severity": "high",
    "payload": {
      "reason": "repeated prompt injection attempts",
      "injection_score": 97,
      "session_id": "sess_demo_001"
    }
  }'
```

Example response:

```json
{
  "id": "sig_01hx7f6g8h9i0j1k2l3m",
  "account_id": "acct-demo",
  "project_id": "proj-demo",
  "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
  "signal_type": "anomalous_behavior",
  "severity": "high",
  "source": "shield-guardrails",
  "payload": {
    "reason": "repeated prompt injection attempts",
    "injection_score": 97,
    "session_id": "sess_demo_001"
  },
  "created_at": "2026-04-05T12:00:00Z"
}
```

***

### GET /api/v1/signals

List recent CAE signals for the current tenant.

| Query param | Description                                     |
| ----------- | ----------------------------------------------- |
| `limit`     | Maximum signals to return (default 50, max 500) |

```bash
curl -X GET "http://localhost:8899/api/v1/signals?limit=20" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "signals": [
    {
      "id": "sig_01hx7f6g8h9i0j1k2l3m",
      "identity_id": "idt_01hx3b2c4d5e6f7g8h9i",
      "signal_type": "anomalous_behavior",
      "severity": "high",
      "source": "shield-guardrails",
      "created_at": "2026-04-05T12:00:00Z"
    }
  ],
  "total": 1
}
```

### 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.

```bash
curl -N -X GET "http://localhost:8899/api/v1/signals/stream" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example event stream:

```
event: connected
data: {}

event: signal
data: {"id":"sig_01hx7f6g8h9i0j1k2l3m","identity_id":"idt_01hx3b2c4d5e6f7g8h9i","signal_type":"anomalous_behavior","severity":"high","source":"shield-guardrails","created_at":"2026-04-05T12:00:00Z"}
```

***

## 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                                                |

```bash
curl -X POST "http://localhost:8899/api/v1/oauth/clients" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey" \
  -d '{
    "client_id": "my-orchestrator-client",
    "name": "Orchestrator M2M Client",
    "description": "Confidential client for the research orchestrator service",
    "confidential": true,
    "token_endpoint_auth_method": "client_secret_post",
    "grant_types": ["client_credentials"],
    "scopes": ["read", "write"],
    "access_token_ttl": 900,
    "contacts": ["platform-team@example.com"],
    "software_id": "orchestrator-svc",
    "software_version": "2.1.0"
  }'
```

Example response:

```json
{
  "client": {
    "id": "cli_01hx8g7h9i0j1k2l3m4n",
    "client_id": "my-orchestrator-client",
    "name": "Orchestrator M2M Client",
    "client_type": "confidential",
    "token_endpoint_auth_method": "client_secret_post",
    "grant_types": ["client_credentials"],
    "scopes": ["read", "write"],
    "access_token_ttl": 900,
    "is_active": true,
    "created_at": "2026-04-05T12:00:00Z",
    "updated_at": "2026-04-05T12:00:00Z"
  },
  "client_secret": "cs_abc123xyz456def789ghi012",
  "note": "Save client_secret now — it will not be shown again."
}
```

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.

```bash
curl -X GET "http://localhost:8899/api/v1/oauth/clients" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "clients": [
    {
      "id": "cli_01hx8g7h9i0j1k2l3m4n",
      "client_id": "my-orchestrator-client",
      "name": "Orchestrator M2M Client",
      "client_type": "confidential",
      "grant_types": ["client_credentials"],
      "is_active": true,
      "created_at": "2026-04-05T12:00:00Z"
    }
  ],
  "total": 1
}
```

***

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

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

```bash
curl -X GET "http://localhost:8899/api/v1/oauth/clients/cli_01hx8g7h9i0j1k2l3m4n" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

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.

```bash
curl -X DELETE "http://localhost:8899/api/v1/oauth/clients/cli_01hx8g7h9i0j1k2l3m4n" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "deleted": true,
  "id": "cli_01hx8g7h9i0j1k2l3m4n"
}
```

***

### 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.

```bash
curl -X POST "http://localhost:8899/api/v1/oauth/clients/cli_01hx8g7h9i0j1k2l3m4n/rotate-secret" \
  -H "Content-Type: application/json" \
  -H "X-Account-ID: acct-demo" \
  -H "X-Project-ID: proj-demo" \
  -H "Authorization: Bearer zid_sk_demo_adminkey"
```

Example response:

```json
{
  "client": {
    "id": "cli_01hx8g7h9i0j1k2l3m4n",
    "client_id": "my-orchestrator-client",
    "name": "Orchestrator M2M Client",
    "updated_at": "2026-04-05T13:00:00Z"
  },
  "client_secret": "cs_newrotated789abc123def456",
  "note": "Save client_secret now — it will not be shown again."
}
```

***

## Error Responses

Public token endpoints return RFC 6749 §5.2 error bodies:

```json
{
  "error": "invalid_client",
  "error_description": "API key not found or revoked"
}
```

Admin endpoints return RFC 9457 problem details via Huma:

```json
{
  "title": "Not Found",
  "status": 404,
  "detail": "resource not found"
}
```

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:

```
spiffe://{domain}/{account_id}/{project_id}/{identity_type}/{external_id}
```

Example:

```
spiffe://zeroid.highflame.ai/acct-demo/proj-demo/agent/research-orch-001
```

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.

* [Python SDK](/api-reference/sdk/zeroid.md)
* [TypeScript SDK](/api-reference/sdk/zeroid.md)
* [Rust SDK](/api-reference/sdk/zeroid.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.highflame.ai/api-reference/rest-endpoints/zeroid-rest-apis.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
