# Schemas

Every named object the published API uses, with its fields. Type names throughout the reference link here.

## OAuthAuthorizationRequest

- `client_id` · uuid · required
- `redirect_uri` · uri · required
- `state` · string
- `code_challenge` · string · required
- `code_challenge_method` · enum · required One of `S256`.
- `scope` · string: Space-separated permission keys the client asked for.
- `resource` · uri
- `response_type` · enum One of `code`.

```json
{
  "client_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "redirect_uri": "https://example.com/webhooks/withhuman",
  "state": "string",
  "code_challenge": "string",
  "code_challenge_method": "S256",
  "scope": "string",
  "resource": "https://example.com/webhooks/withhuman",
  "response_type": "code"
}
```

## OAuthAuthorizationPreview

- `client` · object · required
  - `id` · uuid · required
  - `name` · string · required
  - `client_uri` · string
  - `redirect_uri` · uri · required
- `requested_permissions` · array<string> · required
- `bearable_permissions` · array<APIKeyPermission> · required
  - `key` · string · required
  - `area` · string · required
  - `description` · string · required
  - `dangerous` · boolean · required: Minting a key that carries it needs a fresh, strong session.
- `scoping_enabled` · boolean · required
- `personal_api_keys_allowed` · boolean · required

```json
{
  "client": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "name": "string",
    "client_uri": "string",
    "redirect_uri": "https://example.com/webhooks/withhuman"
  },
  "requested_permissions": [
    "string"
  ],
  "bearable_permissions": [
    {
      "key": "pipeline.write",
      "area": "pipeline",
      "description": "string",
      "dangerous": true
    }
  ],
  "scoping_enabled": true,
  "personal_api_keys_allowed": true
}
```

## OAuthAuthorizationDecision

- `client_id` · uuid · required
- `redirect_uri` · uri · required
- `state` · string
- `code_challenge` · string · required
- `code_challenge_method` · enum · required One of `S256`.
- `scope` · string: Space-separated permission keys the client asked for.
- `resource` · uri
- `response_type` · enum One of `code`.
- `approve` · boolean · required
- `permissions` · array<string>: The permissions to place on the key; omit to inherit everything the person holds.
- `expires_at` · date-time

```json
{
  "client_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "redirect_uri": "https://example.com/webhooks/withhuman",
  "state": "string",
  "code_challenge": "string",
  "code_challenge_method": "S256",
  "scope": "string",
  "resource": "https://example.com/webhooks/withhuman",
  "response_type": "code",
  "approve": true,
  "permissions": [
    "string"
  ],
  "expires_at": "2026-09-08T12:02:11Z"
}
```

## ToolDefinition

- `name` · string · required: Exact tool name sent in approval requests
- `display_name` · string
- `description` · string
- `input_schema` · object: Original JSON Schema, at most 256 KiB. Absence means unknown.
- `annotations` · OnboardingToolAnnotations
  - `title` · string
  - `read_only_hint` · boolean
  - `destructive_hint` · boolean
  - `idempotent_hint` · boolean
  - `open_world_hint` · boolean

```json
{
  "name": "string",
  "display_name": "string",
  "description": "string",
  "input_schema": {},
  "annotations": {
    "title": "string",
    "read_only_hint": true,
    "destructive_hint": true,
    "idempotent_hint": true,
    "open_world_hint": true
  }
}
```

## ToolCatalogSourceReport

- `source` · enum · required One of `builtin`, `mcp`.
- `server` · string
- `complete` · boolean · required
- `error` · string
- `tools` · array<ToolDefinition> · required
  - `name` · string · required: Exact tool name sent in approval requests
  - `display_name` · string
  - `description` · string
  - `input_schema` · object: Original JSON Schema, at most 256 KiB. Absence means unknown.
  - `annotations` · OnboardingToolAnnotations
    - `title` · string
    - `read_only_hint` · boolean
    - `destructive_hint` · boolean
    - `idempotent_hint` · boolean
    - `open_world_hint` · boolean

```json
{
  "source": "builtin",
  "server": "string",
  "complete": true,
  "error": "string",
  "tools": [
    {
      "name": "string",
      "display_name": "string",
      "description": "string",
      "input_schema": {},
      "annotations": {
        "title": "string",
        "read_only_hint": true,
        "destructive_hint": true,
        "idempotent_hint": true,
        "open_world_hint": true
      }
    }
  ]
}
```

## ToolCatalogReport

- `runtime` · string · required
- `discovery_context` · string · required: Stable opaque identifier for this local project or runtime context
- `complete` · boolean · required: Whether all configured sources in this context were enumerated
- `sources` · array<ToolCatalogSourceReport> · required
  - `source` · enum · required One of `builtin`, `mcp`.
  - `server` · string
  - `complete` · boolean · required
  - `error` · string
  - `tools` · array<ToolDefinition> · required
    - `name` · string · required: Exact tool name sent in approval requests
    - `display_name` · string
    - `description` · string
    - `input_schema` · object: Original JSON Schema, at most 256 KiB. Absence means unknown.
    - `annotations` · OnboardingToolAnnotations
      - `title` · string
      - `read_only_hint` · boolean
      - `destructive_hint` · boolean
      - `idempotent_hint` · boolean
      - `open_world_hint` · boolean

```json
{
  "runtime": "string",
  "discovery_context": "string",
  "complete": true,
  "sources": [
    {
      "source": "builtin",
      "server": "string",
      "complete": true,
      "error": "string",
      "tools": [
        {
          "name": "string",
          "display_name": "string",
          "description": "string",
          "input_schema": {},
          "annotations": {
            "title": "string",
            "read_only_hint": true,
            "destructive_hint": true,
            "idempotent_hint": true,
            "open_world_hint": true
          }
        }
      ]
    }
  ]
}
```

## ToolCatalogEntry

- `name` · string · required
- `display_name` · string
- `description` · string
- `input_schema` · object
- `annotations` · OnboardingToolAnnotations
  - `title` · string
  - `read_only_hint` · boolean
  - `destructive_hint` · boolean
  - `idempotent_hint` · boolean
  - `open_world_hint` · boolean
- `id` · uuid · required
- `agent_slug` · string · required
- `agent_instance_id` · uuid
- `instance_name` · string
- `discovery_context` · string · required
- `runtime` · string · required
- `source` · string · required: `builtin`, `mcp`, or a source an edition adds (the hosted gateway's `gateway`)
- `server` · string
- `available` · boolean · required
- `stale` · boolean · required
- `error` · string
- `schema_fingerprint` · string · required
- `last_seen_at` · date-time · required
- `last_attempt_at` · date-time · required

```json
{
  "name": "string",
  "display_name": "string",
  "description": "string",
  "input_schema": {},
  "annotations": {
    "title": "string",
    "read_only_hint": true,
    "destructive_hint": true,
    "idempotent_hint": true,
    "open_world_hint": true
  },
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "string",
  "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "instance_name": "string",
  "discovery_context": "string",
  "runtime": "string",
  "source": "string",
  "server": "string",
  "available": true,
  "stale": true,
  "error": "string",
  "schema_fingerprint": "string",
  "last_seen_at": "2026-09-08T12:02:11Z",
  "last_attempt_at": "2026-09-08T12:02:11Z"
}
```

## ToolCatalogEntryDefinition

- `name` · string · required
- `display_name` · string
- `description` · string
- `input_schema` · object
- `annotations` · OnboardingToolAnnotations
  - `title` · string
  - `read_only_hint` · boolean
  - `destructive_hint` · boolean
  - `idempotent_hint` · boolean
  - `open_world_hint` · boolean

```json
{
  "name": "string",
  "display_name": "string",
  "description": "string",
  "input_schema": {},
  "annotations": {
    "title": "string",
    "read_only_hint": true,
    "destructive_hint": true,
    "idempotent_hint": true,
    "open_world_hint": true
  }
}
```

## ToolCatalogSource

- `id` · uuid · required
- `agent_slug` · string · required
- `agent_instance_id` · uuid
- `runtime` · string · required
- `discovery_context` · string · required
- `source` · string · required: `builtin`, `mcp`, or a source an edition adds (the hosted gateway's `gateway`)
- `server` · string
- `last_attempt_at` · date-time · required
- `last_success_at` · date-time · required
- `error` · string

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "string",
  "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "runtime": "string",
  "discovery_context": "string",
  "source": "string",
  "server": "string",
  "last_attempt_at": "2026-09-08T12:02:11Z",
  "last_success_at": "2026-09-08T12:02:11Z",
  "error": "string"
}
```

## ToolCatalogPage

- `tools` · array<ToolCatalogEntryDefinition> · required
- `sources` · array<ToolCatalogSource> · required
  - `id` · uuid · required
  - `agent_slug` · string · required
  - `agent_instance_id` · uuid
  - `runtime` · string · required
  - `discovery_context` · string · required
  - `source` · string · required: `builtin`, `mcp`, or a source an edition adds (the hosted gateway's `gateway`)
  - `server` · string
  - `last_attempt_at` · date-time · required
  - `last_success_at` · date-time · required
  - `error` · string
- `total` · integer · required
- `next_offset` · integer · required

```json
{
  "tools": [
    {
      "name": "string",
      "display_name": "string",
      "description": "string",
      "input_schema": {},
      "annotations": {
        "title": "string",
        "read_only_hint": true,
        "destructive_hint": true,
        "idempotent_hint": true,
        "open_world_hint": true
      },
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "agent_slug": "string",
      "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "instance_name": "string",
      "discovery_context": "string",
      "runtime": "string",
      "source": "string",
      "server": "string",
      "available": true,
      "stale": true,
      "error": "string",
      "schema_fingerprint": "string",
      "last_seen_at": "2026-09-08T12:02:11Z",
      "last_attempt_at": "2026-09-08T12:02:11Z"
    }
  ],
  "sources": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "agent_slug": "string",
      "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "runtime": "string",
      "discovery_context": "string",
      "source": "string",
      "server": "string",
      "last_attempt_at": "2026-09-08T12:02:11Z",
      "last_success_at": "2026-09-08T12:02:11Z",
      "error": "string"
    }
  ],
  "total": 0,
  "next_offset": 1
}
```

## OnboardingToolAnnotations

- `title` · string
- `read_only_hint` · boolean
- `destructive_hint` · boolean
- `idempotent_hint` · boolean
- `open_world_hint` · boolean

```json
{
  "title": "string",
  "read_only_hint": true,
  "destructive_hint": true,
  "idempotent_hint": true,
  "open_world_hint": true
}
```

## ErrorResponse

- `error` · object · required
  - `type` · enum · required One of `invalid_request`, `authentication`, `authorization`, `not_found`, `conflict`, `rate_limit`, `dependency`, `internal`.
  - `code` · string · required
  - `message` · string · required
  - `param` · string
  - `request_id` · string · required
  - `details` · QuotaExceededDetails | ForbiddenDetails | RoutingRejectionDetails | object: Structured detail for codes that carry one. quota_exceeded carries QuotaExceededDetails; a forbidden error from a permission check carries ForbiddenDetails; a decision the routing rule refused carries RoutingRejectionDetails.

```json
{
  "error": {
    "type": "invalid_request",
    "code": "string",
    "message": "string",
    "param": "string",
    "request_id": "string",
    "details": {
      "dimension": "string",
      "limit": 0,
      "used": 0
    }
  }
}
```

## QuotaExceededDetails

The details object on a quota_exceeded error, so a client can offer the purchase that resolves it.

- `dimension` · string · required: The plan dimension that is full, such as seats or agents.
- `limit` · int64 · required
- `used` · int64 · required

```json
{
  "dimension": "string",
  "limit": 0,
  "used": 0
}
```

## ForbiddenDetails

The details on a 403 from a permission check. They name the permission that was needed and the thing it was checked against.

- `permission` · string · required: The permission you needed.
- `resource_kind` · enum · required: What kind of thing the check was about. One of `organization`, `team`, `agent`, `request`.
- `resource_id` · string: The team, agent slug, or request the check was about. Absent for the organization.

```json
{
  "permission": "request.decide",
  "resource_kind": "organization",
  "resource_id": "string"
}
```

## RoutingRejectionDetails

The details object on a forbidden error raised by the routing rule at decision time. not_targeted means the request was routed to other people; break_glass_only means the organization sends unrouted requests to holders of request.decide.unrouted; outside_routing_required means the reviewer holds break glass but did not send outside_routing.

- `reason` · enum · required One of `not_targeted`, `break_glass_only`, `outside_routing_required`.

```json
{
  "reason": "not_targeted"
}
```

## APIKey

An API key without its secret. Personal prefixes start with `whk_`; organization prefixes start with `who_`.

- `grants` · array<Grant> · required: Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
  - `permission` · string · required: A registry key such as request.decide
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
- `effective_grants` · array<Grant>: On detail responses, currently allowed organization-key grants.
  - `permission` · string · required: A registry key such as request.decide
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
- `usable` · boolean · required: Whether the key is active and not blocked by organization policy or status.
- `blocked_reason` · enum One of `organization_inactive`, `api_keys_disabled`.
- `id` · uuid · required
- `prefix` · string · required
- `kind` · enum · required: Personal keys act as a member; organization keys act as themselves. One of `personal`, `organization`.
- `membership_id` · uuid: The member a personal key acts as.
- `created_by` · object: Who minted it. Present on the organization-wide list.
  - `membership_id` · uuid · required
  - `display_name` · string · required
  - `email` · string · required
- `name` · string · required
- `permissions` · array<string> · required: The narrowing list, or `null` when the key inherits everything its member holds. The key's effective permissions are always this list intersected with the member's current grants.
- `assurance_at_issue` · string · required: The assurance of the session that minted the key (`strong`, `single_factor`), presented as the key's own when it decides.
- `oauth_client` · object: The MCP client the key was minted for through the consent page. Absent for a key made by hand.
  - `id` · uuid · required
  - `name` · string · required
- `status` · enum · required One of `active`, `expired`, `revoked`.
- `created_at` · date-time · required
- `expires_at` · date-time · required: `null` when the key lives until revoked.
- `last_used_at` · date-time · required
- `revoked_at` · date-time · required
- `revocation_reason` · enum One of `manual`, `membership_deprovisioned`.

```json
{
  "grants": [
    {
      "permission": "request.decide",
      "scope_kind": "organization",
      "scope_id": "string"
    }
  ],
  "effective_grants": [
    {
      "permission": "request.decide",
      "scope_kind": "organization",
      "scope_id": "string"
    }
  ],
  "usable": true,
  "blocked_reason": "organization_inactive",
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "prefix": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31",
  "kind": "personal",
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_by": {
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "display_name": "string",
    "email": "string"
  },
  "name": "string",
  "permissions": [
    "string"
  ],
  "assurance_at_issue": "string",
  "oauth_client": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "name": "string"
  },
  "status": "active",
  "created_at": "2026-09-08T12:02:11Z",
  "expires_at": "2026-09-08T12:02:11Z",
  "last_used_at": "2026-09-08T12:02:11Z",
  "revoked_at": "2026-09-08T12:02:11Z",
  "revocation_reason": "manual"
}
```

## APIKeyPermission

One permission the caller could place on a key.

- `key` · string · required
- `area` · string · required
- `description` · string · required
- `dangerous` · boolean · required: Minting a key that carries it needs a fresh, strong session.

```json
{
  "key": "pipeline.write",
  "area": "pipeline",
  "description": "string",
  "dangerous": true
}
```

## MyAPIKeysEnvelope

- `keys` · array<APIKey> · required
  - `grants` · array<Grant> · required: Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `effective_grants` · array<Grant>: On detail responses, currently allowed organization-key grants.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `usable` · boolean · required: Whether the key is active and not blocked by organization policy or status.
  - `blocked_reason` · enum One of `organization_inactive`, `api_keys_disabled`.
  - `id` · uuid · required
  - `prefix` · string · required
  - `kind` · enum · required: Personal keys act as a member; organization keys act as themselves. One of `personal`, `organization`.
  - `membership_id` · uuid: The member a personal key acts as.
  - `created_by` · object: Who minted it. Present on the organization-wide list.
    - `membership_id` · uuid · required
    - `display_name` · string · required
    - `email` · string · required
  - `name` · string · required
  - `permissions` · array<string> · required: The narrowing list, or `null` when the key inherits everything its member holds. The key's effective permissions are always this list intersected with the member's current grants.
  - `assurance_at_issue` · string · required: The assurance of the session that minted the key (`strong`, `single_factor`), presented as the key's own when it decides.
  - `oauth_client` · object: The MCP client the key was minted for through the consent page. Absent for a key made by hand.
    - `id` · uuid · required
    - `name` · string · required
  - `status` · enum · required One of `active`, `expired`, `revoked`.
  - `created_at` · date-time · required
  - `expires_at` · date-time · required: `null` when the key lives until revoked.
  - `last_used_at` · date-time · required
  - `revoked_at` · date-time · required
  - `revocation_reason` · enum One of `manual`, `membership_deprovisioned`.
- `personal_api_keys_allowed` · boolean · required: The organization's switch.
- `scoping_enabled` · boolean · required: Whether a key may carry a permission list here. False in the open edition, where keys inherit the member's role.
- `bearable_permissions` · array<APIKeyPermission> · required
  - `key` · string · required
  - `area` · string · required
  - `description` · string · required
  - `dangerous` · boolean · required: Minting a key that carries it needs a fresh, strong session.

```json
{
  "keys": [
    {
      "grants": [
        {
          "permission": "request.decide",
          "scope_kind": "organization",
          "scope_id": "string"
        }
      ],
      "effective_grants": [
        {
          "permission": "request.decide",
          "scope_kind": "organization",
          "scope_id": "string"
        }
      ],
      "usable": true,
      "blocked_reason": "organization_inactive",
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "prefix": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31",
      "kind": "personal",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "created_by": {
        "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "display_name": "string",
        "email": "string"
      },
      "name": "string",
      "permissions": [
        "string"
      ],
      "assurance_at_issue": "string",
      "oauth_client": {
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "name": "string"
      },
      "status": "active",
      "created_at": "2026-09-08T12:02:11Z",
      "expires_at": "2026-09-08T12:02:11Z",
      "last_used_at": "2026-09-08T12:02:11Z",
      "revoked_at": "2026-09-08T12:02:11Z",
      "revocation_reason": "manual"
    }
  ],
  "personal_api_keys_allowed": true,
  "scoping_enabled": true,
  "bearable_permissions": [
    {
      "key": "pipeline.write",
      "area": "pipeline",
      "description": "string",
      "dangerous": true
    }
  ]
}
```

## APIKeysEnvelope

- `keys` · array<APIKey> · required
  - `grants` · array<Grant> · required: Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `effective_grants` · array<Grant>: On detail responses, currently allowed organization-key grants.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `usable` · boolean · required: Whether the key is active and not blocked by organization policy or status.
  - `blocked_reason` · enum One of `organization_inactive`, `api_keys_disabled`.
  - `id` · uuid · required
  - `prefix` · string · required
  - `kind` · enum · required: Personal keys act as a member; organization keys act as themselves. One of `personal`, `organization`.
  - `membership_id` · uuid: The member a personal key acts as.
  - `created_by` · object: Who minted it. Present on the organization-wide list.
    - `membership_id` · uuid · required
    - `display_name` · string · required
    - `email` · string · required
  - `name` · string · required
  - `permissions` · array<string> · required: The narrowing list, or `null` when the key inherits everything its member holds. The key's effective permissions are always this list intersected with the member's current grants.
  - `assurance_at_issue` · string · required: The assurance of the session that minted the key (`strong`, `single_factor`), presented as the key's own when it decides.
  - `oauth_client` · object: The MCP client the key was minted for through the consent page. Absent for a key made by hand.
    - `id` · uuid · required
    - `name` · string · required
  - `status` · enum · required One of `active`, `expired`, `revoked`.
  - `created_at` · date-time · required
  - `expires_at` · date-time · required: `null` when the key lives until revoked.
  - `last_used_at` · date-time · required
  - `revoked_at` · date-time · required
  - `revocation_reason` · enum One of `manual`, `membership_deprovisioned`.

```json
{
  "keys": [
    {
      "grants": [
        {
          "permission": "request.decide",
          "scope_kind": "organization",
          "scope_id": "string"
        }
      ],
      "effective_grants": [
        {
          "permission": "request.decide",
          "scope_kind": "organization",
          "scope_id": "string"
        }
      ],
      "usable": true,
      "blocked_reason": "organization_inactive",
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "prefix": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31",
      "kind": "personal",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "created_by": {
        "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "display_name": "string",
        "email": "string"
      },
      "name": "string",
      "permissions": [
        "string"
      ],
      "assurance_at_issue": "string",
      "oauth_client": {
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "name": "string"
      },
      "status": "active",
      "created_at": "2026-09-08T12:02:11Z",
      "expires_at": "2026-09-08T12:02:11Z",
      "last_used_at": "2026-09-08T12:02:11Z",
      "revoked_at": "2026-09-08T12:02:11Z",
      "revocation_reason": "manual"
    }
  ]
}
```

## CreateAPIKeyRequest

- `name` · string · required
- `permissions` · array<string>: Narrow the key to these permissions. Omit to inherit everything you hold. Each must be a permission you hold; only in the hosted edition.
- `expires_at` · date-time: When the key stops working. Omit for a key that lives until revoked, unless the deployment caps key lifetime.

```json
{
  "name": "terraform",
  "permissions": [
    "pipeline.read",
    "pipeline.write",
    "pipeline.activate"
  ],
  "expires_at": "2026-09-08T12:02:11Z"
}
```

## IssuedAPIKeyEnvelope

- `key` · APIKey · required: An API key without its secret. Personal prefixes start with `whk_`; organization prefixes start with `who_`.
  - `grants` · array<Grant> · required: Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `effective_grants` · array<Grant>: On detail responses, currently allowed organization-key grants.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `usable` · boolean · required: Whether the key is active and not blocked by organization policy or status.
  - `blocked_reason` · enum One of `organization_inactive`, `api_keys_disabled`.
  - `id` · uuid · required
  - `prefix` · string · required
  - `kind` · enum · required: Personal keys act as a member; organization keys act as themselves. One of `personal`, `organization`.
  - `membership_id` · uuid: The member a personal key acts as.
  - `created_by` · object: Who minted it. Present on the organization-wide list.
    - `membership_id` · uuid · required
    - `display_name` · string · required
    - `email` · string · required
  - `name` · string · required
  - `permissions` · array<string> · required: The narrowing list, or `null` when the key inherits everything its member holds. The key's effective permissions are always this list intersected with the member's current grants.
  - `assurance_at_issue` · string · required: The assurance of the session that minted the key (`strong`, `single_factor`), presented as the key's own when it decides.
  - `oauth_client` · object: The MCP client the key was minted for through the consent page. Absent for a key made by hand.
    - `id` · uuid · required
    - `name` · string · required
  - `status` · enum · required One of `active`, `expired`, `revoked`.
  - `created_at` · date-time · required
  - `expires_at` · date-time · required: `null` when the key lives until revoked.
  - `last_used_at` · date-time · required
  - `revoked_at` · date-time · required
  - `revocation_reason` · enum One of `manual`, `membership_deprovisioned`.
- `token` · string · required: The raw key, shown once: `whk_<id>_<secret>`.

```json
{
  "key": {
    "grants": [
      {
        "permission": "request.decide",
        "scope_kind": "organization",
        "scope_id": "string"
      }
    ],
    "effective_grants": [
      {
        "permission": "request.decide",
        "scope_kind": "organization",
        "scope_id": "string"
      }
    ],
    "usable": true,
    "blocked_reason": "organization_inactive",
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "prefix": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31",
    "kind": "personal",
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "created_by": {
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "email": "string"
    },
    "name": "string",
    "permissions": [
      "string"
    ],
    "assurance_at_issue": "string",
    "oauth_client": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "name": "string"
    },
    "status": "active",
    "created_at": "2026-09-08T12:02:11Z",
    "expires_at": "2026-09-08T12:02:11Z",
    "last_used_at": "2026-09-08T12:02:11Z",
    "revoked_at": "2026-09-08T12:02:11Z",
    "revocation_reason": "manual"
  },
  "token": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31_x5nH9v…"
}
```

## VerifiedAPIKeyEnvelope

- `effective_grants` · array<Grant>
  - `permission` · string · required: A registry key such as request.decide
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
- `key` · APIKey · required: An API key without its secret. Personal prefixes start with `whk_`; organization prefixes start with `who_`.
  - `grants` · array<Grant> · required: Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `effective_grants` · array<Grant>: On detail responses, currently allowed organization-key grants.
    - `permission` · string · required: A registry key such as request.decide
    - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
  - `usable` · boolean · required: Whether the key is active and not blocked by organization policy or status.
  - `blocked_reason` · enum One of `organization_inactive`, `api_keys_disabled`.
  - `id` · uuid · required
  - `prefix` · string · required
  - `kind` · enum · required: Personal keys act as a member; organization keys act as themselves. One of `personal`, `organization`.
  - `membership_id` · uuid: The member a personal key acts as.
  - `created_by` · object: Who minted it. Present on the organization-wide list.
    - `membership_id` · uuid · required
    - `display_name` · string · required
    - `email` · string · required
  - `name` · string · required
  - `permissions` · array<string> · required: The narrowing list, or `null` when the key inherits everything its member holds. The key's effective permissions are always this list intersected with the member's current grants.
  - `assurance_at_issue` · string · required: The assurance of the session that minted the key (`strong`, `single_factor`), presented as the key's own when it decides.
  - `oauth_client` · object: The MCP client the key was minted for through the consent page. Absent for a key made by hand.
    - `id` · uuid · required
    - `name` · string · required
  - `status` · enum · required One of `active`, `expired`, `revoked`.
  - `created_at` · date-time · required
  - `expires_at` · date-time · required: `null` when the key lives until revoked.
  - `last_used_at` · date-time · required
  - `revoked_at` · date-time · required
  - `revocation_reason` · enum One of `manual`, `membership_deprovisioned`.
- `effective_permissions` · array<string> · required: What the key may do right now, after narrowing and the member's current roles.

```json
{
  "effective_grants": [
    {
      "permission": "request.decide",
      "scope_kind": "organization",
      "scope_id": "string"
    }
  ],
  "key": {
    "grants": [
      {
        "permission": "request.decide",
        "scope_kind": "organization",
        "scope_id": "string"
      }
    ],
    "effective_grants": [
      {
        "permission": "request.decide",
        "scope_kind": "organization",
        "scope_id": "string"
      }
    ],
    "usable": true,
    "blocked_reason": "organization_inactive",
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "prefix": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31",
    "kind": "personal",
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "created_by": {
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "email": "string"
    },
    "name": "string",
    "permissions": [
      "string"
    ],
    "assurance_at_issue": "string",
    "oauth_client": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "name": "string"
    },
    "status": "active",
    "created_at": "2026-09-08T12:02:11Z",
    "expires_at": "2026-09-08T12:02:11Z",
    "last_used_at": "2026-09-08T12:02:11Z",
    "revoked_at": "2026-09-08T12:02:11Z",
    "revocation_reason": "manual"
  },
  "effective_permissions": [
    "string"
  ]
}
```

## RequestApprovalPipelineSummary

One pipeline and a summary of its history.

- `scope` · enum · required: Which requests a pipeline applies to. The organization pipeline runs first, for every request. An agent's pipeline runs after it, for that agent's requests. One of `organization`, `agent`.
- `agent_slug` · string: The agent's slug. Present for the `agent` scope only.
- `active_revision` · int64: The revision in use. Absent only for an archived pipeline.
- `latest_revision` · int64 · required: The newest revision, active or not.
- `revision_count` · int64 · required
- `latest_created_at` · date-time · required: When the newest revision was created.
- `archived_at` · date-time: Present when the pipeline was archived with its agent: readable, never active again until the agent is restored.

```json
{
  "scope": "organization",
  "agent_slug": "string",
  "active_revision": 1,
  "latest_revision": 1,
  "revision_count": 1,
  "latest_created_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z"
}
```

## RequestApprovalPipelineRevisionSummary

One pipeline revision, without its blocks.

- `id` · uuid · required
- `scope` · enum · required: Which requests a pipeline applies to. The organization pipeline runs first, for every request. An agent's pipeline runs after it, for that agent's requests. One of `organization`, `agent`.
- `agent_slug` · string: The agent's slug. Present for the `agent` scope only.
- `revision` · int64 · required: The revision number. Revisions count up from 1.
- `is_active` · boolean · required: Whether this is the revision in use.
- `block_count` · integer · required: Every block in the revision, including blocks inside branches.
- `default_escalation_path` · string: The escalation path for requests that reach a person without a block naming a path.
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the revision.
- `created_at` · date-time · required
- `archived_at` · date-time: Present when the revision was archived with its agent. An archived revision can be read but not activated until the agent is restored.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "scope": "organization",
  "agent_slug": "string",
  "revision": 1,
  "is_active": true,
  "block_count": 0,
  "default_escalation_path": "string",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z"
}
```

## RequestApprovalPipelineRevision

One pipeline revision with its blocks in the order they run.

- `id` · uuid · required
- `scope` · enum · required: Which requests a pipeline applies to. The organization pipeline runs first, for every request. An agent's pipeline runs after it, for that agent's requests. One of `organization`, `agent`.
- `agent_slug` · string: The agent's slug. Present for the `agent` scope only.
- `revision` · int64 · required: The revision number. Revisions count up from 1.
- `is_active` · boolean · required: Whether this is the revision in use.
- `block_count` · integer · required: Every block in the revision, including blocks inside branches.
- `default_escalation_path` · string: The escalation path for requests that reach a person without a block naming a path.
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the revision.
- `created_at` · date-time · required
- `blocks` · array<StoredAlwaysBlock | StoredCELBlock | StoredWebhookBlock | StoredBranchBlock | StoredLLMJudgeBlock> · required: The blocks, in the order they run. A branch holds its own blocks in `config.blocks`.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "scope": "organization",
  "agent_slug": "string",
  "revision": 1,
  "is_active": true,
  "block_count": 0,
  "default_escalation_path": "string",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "blocks": [
    {
      "id": "review-remaining-requests",
      "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "name": "Review remaining requests",
      "type": "always",
      "enabled": true,
      "timeout": "50ms",
      "max_attempts": 1,
      "config": {
        "outcome": "human",
        "reason": "Requests not handled by earlier blocks need human review."
      },
      "config_version": 1,
      "content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    }
  ]
}
```

## RequestApprovalPipelineDocument

The complete content of a new pipeline revision.

- `blocks` · array<AlwaysBlockDefinition | CELBlockDefinition | WebhookBlockDefinition | BranchBlockDefinition | LLMJudgeBlockDefinition> · required: The blocks, in the order they should run. A branch holds its own blocks in `config.blocks`, and the pipeline may hold at most 1,000 blocks counting those. An empty list passes every request through to a person.
- `default_escalation_path` · string: The escalation path for requests that reach a person without a block naming a path. That includes requests that reach the end of the pipeline. The agent's default wins over the organization's. With neither, any reviewer who can decide may take the request from the queue. The path must exist when the revision is created, and be active when it is activated.

```json
{
  "blocks": [
    {
      "id": "review-remaining-requests",
      "name": "Review remaining requests",
      "type": "always",
      "enabled": true,
      "timeout": "50ms",
      "max_attempts": 1,
      "config": {
        "outcome": "human",
        "reason": "Requests not handled by earlier blocks need human review."
      }
    }
  ],
  "default_escalation_path": "string"
}
```

## AlwaysBlockDefinition

A block that returns its configured outcome for every request that reaches it.

- `id` · string · required: A key for the block, unique within the pipeline including blocks inside branches.
- `name` · string · required: A display name.
- `type` · "always" · required: Always `always`.
- `enabled` · true · required: Always `true`. Leave disabled blocks out of the document.
- `timeout` · string · required: How long execution may take, as a duration. At most `1s`.
- `max_attempts` · 1 · required: Always 1.
- `config` · AlwaysBlockConfig · required: Returns outcome for every request that reaches this block. Later blocks do not run.
  - `outcome` · enum · required: The outcome returned unconditionally. One of `approve`, `deny`, `human`.
  - `reason` · string · required: The note recorded on decisions this block makes.
  - `escalation_path` · string: Where the request goes for a `human` outcome. Allowed only when `outcome` is `human`. Defaults to the pipeline's default path.

```json
{
  "id": "review-remaining-requests",
  "name": "Review remaining requests",
  "type": "always",
  "enabled": true,
  "timeout": "50ms",
  "max_attempts": 1,
  "config": {
    "outcome": "human",
    "reason": "Requests not handled by earlier blocks need human review."
  }
}
```

## CELBlockDefinition

A block that tests the request against a condition.

- `id` · string · required: A key for the block, unique within the pipeline including blocks inside branches.
- `name` · string · required: A display name.
- `type` · "cel" · required: Always `cel`.
- `enabled` · true · required: Always `true`. Leave disabled blocks out of the document.
- `timeout` · string · required: How long the condition may take, as a duration. At most `1s`.
- `max_attempts` · 1 · required: Always 1.
- `config` · CELBlockConfig · required: When the condition matches, the block returns on_match. Otherwise the request passes to the next block.
  - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `on_match` · enum · required: What to do on a match: `approve`, `deny`, or `human`. One of `approve`, `deny`, `human`.
  - `reason` · string · required: The note recorded on decisions this block makes.
  - `escalation_path` · string: Where the request goes on a `human` match. Allowed only when `on_match` is `human`. Defaults to the pipeline's default path.

```json
{
  "id": "string",
  "name": "string",
  "type": "cel",
  "enabled": true,
  "timeout": "50ms",
  "max_attempts": 1,
  "config": {
    "when": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    },
    "on_match": "approve",
    "reason": "string",
    "escalation_path": "string"
  }
}
```

## StoredAlwaysBlock

An unconditional block as saved in a revision.

- `id` · string · required: The block's key, unique within the pipeline including blocks inside branches.
- `snapshot_id` · uuid · required: The id of the saved copy.
- `name` · string · required: The display name.
- `type` · "always" · required: Always `always`.
- `enabled` · true · required: Always `true`.
- `timeout` · string · required: How long execution may take.
- `max_attempts` · 1 · required: Always 1.
- `config` · AlwaysBlockConfig · required: Returns outcome for every request that reaches this block. Later blocks do not run.
  - `outcome` · enum · required: The outcome returned unconditionally. One of `approve`, `deny`, `human`.
  - `reason` · string · required: The note recorded on decisions this block makes.
  - `escalation_path` · string: Where the request goes for a `human` outcome. Allowed only when `outcome` is `human`. Defaults to the pipeline's default path.
- `config_version` · 1 · required: The version of the config format. Always 1.
- `content_hash` · string · required: A SHA-256 hash of the block's content.

```json
{
  "id": "review-remaining-requests",
  "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "Review remaining requests",
  "type": "always",
  "enabled": true,
  "timeout": "50ms",
  "max_attempts": 1,
  "config": {
    "outcome": "human",
    "reason": "Requests not handled by earlier blocks need human review."
  },
  "config_version": 1,
  "content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
```

## StoredCELBlock

A condition block as saved in a revision.

- `id` · string · required: The block's key, unique within the pipeline including blocks inside branches.
- `snapshot_id` · uuid · required: The id of the saved copy.
- `name` · string · required: The display name.
- `type` · "cel" · required: Always `cel`.
- `enabled` · true · required: Always `true`.
- `timeout` · string · required: How long the condition may take.
- `max_attempts` · 1 · required: Always 1.
- `config` · CELBlockConfig · required: When the condition matches, the block returns on_match. Otherwise the request passes to the next block.
  - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `on_match` · enum · required: What to do on a match: `approve`, `deny`, or `human`. One of `approve`, `deny`, `human`.
  - `reason` · string · required: The note recorded on decisions this block makes.
  - `escalation_path` · string: Where the request goes on a `human` match. Allowed only when `on_match` is `human`. Defaults to the pipeline's default path.
- `config_version` · 1 · required: The version of the config format. Always 1.
- `content_hash` · string · required: A SHA-256 hash of the block's content.

```json
{
  "id": "string",
  "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "string",
  "type": "cel",
  "enabled": true,
  "timeout": "50ms",
  "max_attempts": 1,
  "config": {
    "when": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    },
    "on_match": "approve",
    "reason": "string",
    "escalation_path": "string"
  },
  "config_version": 1,
  "content_hash": "string"
}
```

## WebhookBlockDefinition

A block that asks a webhook endpoint of yours for an outcome.

- `id` · string · required: A key for the block, unique within the pipeline including blocks inside branches.
- `name` · string · required: A display name.
- `type` · "webhook" · required: Always `webhook`.
- `enabled` · true · required: Always `true`. Leave disabled blocks out of the document.
- `timeout` · string · required: How long one attempt may take, from connecting to the full response, as a duration. At most `30s`.
- `max_attempts` · integer · required: How many times to try, from 1 to 3.
- `config` · WebhookBlockConfig · required: The block posts the request to a webhook endpoint and the endpoint answers with an outcome. The endpoint is a separate resource, named here by its key. Deliveries go to the key's active revision. If the answer is not in allowed_outcomes, or the call fails, the request goes to a person.
  - `endpoint_key` · string · required: The webhook endpoint to call.
  - `allowed_outcomes` · array<enum> · required: The outcomes the endpoint may return. Any other answer sends the request to a person. `approve` and `deny` must be listed explicitly. The editor defaults to `next` and `human`.
  - `include` · object · required: What to send with the request.
    - `agent_reasoning` · boolean · required: Include the agent's own explanation of the call.
    - `context` · boolean · required: Include the runtime metadata the adapter reported.
    - `previous_metadata` · boolean · required: Include the metadata earlier blocks returned.
  - `reason` · string · required: The note recorded on decisions this block makes. The endpoint's own reason is kept as evidence.
  - `escalation_path` · string: Where the request goes when the endpoint answers `human`, or when the call fails. Defaults to the pipeline's default path.

```json
{
  "id": "string",
  "name": "string",
  "type": "webhook",
  "enabled": true,
  "timeout": "5s",
  "max_attempts": 1,
  "config": {
    "endpoint_key": "string",
    "allowed_outcomes": [
      "next"
    ],
    "include": {
      "agent_reasoning": true,
      "context": true,
      "previous_metadata": true
    },
    "reason": "string",
    "escalation_path": "string"
  }
}
```

## StoredWebhookBlock

A webhook block as saved in a revision.

- `id` · string · required: The block's key, unique within the pipeline including blocks inside branches.
- `snapshot_id` · uuid · required: The id of the saved copy.
- `name` · string · required: The display name.
- `type` · "webhook" · required: Always `webhook`.
- `enabled` · true · required: Always `true`.
- `timeout` · string · required: How long one attempt may take.
- `max_attempts` · integer · required: How many times to try.
- `config` · WebhookBlockConfig · required: The block posts the request to a webhook endpoint and the endpoint answers with an outcome. The endpoint is a separate resource, named here by its key. Deliveries go to the key's active revision. If the answer is not in allowed_outcomes, or the call fails, the request goes to a person.
  - `endpoint_key` · string · required: The webhook endpoint to call.
  - `allowed_outcomes` · array<enum> · required: The outcomes the endpoint may return. Any other answer sends the request to a person. `approve` and `deny` must be listed explicitly. The editor defaults to `next` and `human`.
  - `include` · object · required: What to send with the request.
    - `agent_reasoning` · boolean · required: Include the agent's own explanation of the call.
    - `context` · boolean · required: Include the runtime metadata the adapter reported.
    - `previous_metadata` · boolean · required: Include the metadata earlier blocks returned.
  - `reason` · string · required: The note recorded on decisions this block makes. The endpoint's own reason is kept as evidence.
  - `escalation_path` · string: Where the request goes when the endpoint answers `human`, or when the call fails. Defaults to the pipeline's default path.
- `config_version` · 1 · required: The version of the config format. Always 1.
- `content_hash` · string · required: A SHA-256 hash of the block's content.

```json
{
  "id": "string",
  "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "string",
  "type": "webhook",
  "enabled": true,
  "timeout": "5s",
  "max_attempts": 1,
  "config": {
    "endpoint_key": "string",
    "allowed_outcomes": [
      "next"
    ],
    "include": {
      "agent_reasoning": true,
      "context": true,
      "previous_metadata": true
    },
    "reason": "string",
    "escalation_path": "string"
  },
  "config_version": 1,
  "content_hash": "string"
}
```

## BranchBlockDefinition

A block that runs its own blocks for the requests its condition matches and is skipped by every other request. A request that enters a branch is decided inside it, or goes to a person if none of its blocks decides; nothing after the branch runs for that request.

- `id` · string · required: A key for the block, unique within the pipeline including blocks inside branches.
- `name` · string · required: A display name.
- `type` · "branch" · required: Always `branch`.
- `enabled` · true · required: Always `true`. Leave disabled blocks out of the document.
- `timeout` · string · required: How long the condition may take, as a duration. At most `1s`.
- `max_attempts` · 1 · required: Always 1.
- `config` · BranchBlockConfig · required: When the condition matches, the branch's blocks run in order and the first to decide ends the pipeline. If none decides, the request goes to a person with this reason and escalation path. When the condition does not match, the branch is skipped and the request passes to the next block. A condition that cannot be evaluated sends the request to a person on the pipeline's default path.
  - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `blocks` · array<AlwaysBlockDefinition | CELBlockDefinition | WebhookBlockDefinition | BranchBlockDefinition | LLMJudgeBlockDefinition> · required: The branch's blocks, in the order they run. Any type, including further branches. They count toward the pipeline's 1,000 blocks.
  - `reason` · string · required: The note recorded when none of the branch's blocks decides.
  - `escalation_path` · string: Where the request goes when none of the branch's blocks decides. Defaults to the pipeline's default path. Blocks inside the branch that name no path of their own use the pipeline's default, not this one.

```json
{
  "id": "refunds",
  "name": "Refunds",
  "type": "branch",
  "enabled": true,
  "timeout": "50ms",
  "max_attempts": 1,
  "config": {
    "when": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    },
    "blocks": [
      {
        "id": "review-remaining-requests",
        "name": "Review remaining requests",
        "type": "always",
        "enabled": true,
        "timeout": "50ms",
        "max_attempts": 1,
        "config": {
          "outcome": "human",
          "reason": "Requests not handled by earlier blocks need human review."
        }
      }
    ],
    "reason": "string",
    "escalation_path": "string"
  }
}
```

## StoredBranchBlock

A branch as saved in a revision, with its blocks as saved.

- `id` · string · required: The block's key, unique within the pipeline including blocks inside branches.
- `snapshot_id` · uuid · required: The id of the saved copy.
- `name` · string · required: The display name.
- `type` · "branch" · required: Always `branch`.
- `enabled` · true · required: Always `true`.
- `timeout` · string · required: How long the condition may take.
- `max_attempts` · 1 · required: Always 1.
- `config` · StoredBranchBlockConfig · required: A branch configuration as saved, with its blocks as saved.
  - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `blocks` · array<StoredAlwaysBlock | StoredCELBlock | StoredWebhookBlock | StoredBranchBlock | StoredLLMJudgeBlock> · required: The branch's blocks, in the order they run.
  - `reason` · string · required: The note recorded when none of the branch's blocks decides.
  - `escalation_path` · string: Where the request goes when none of the branch's blocks decides.
- `config_version` · 1 · required: The version of the config format. Always 1.
- `content_hash` · string · required: A SHA-256 hash of the branch's own content. Each of its blocks has its own.

```json
{
  "id": "string",
  "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "string",
  "type": "branch",
  "enabled": true,
  "timeout": "50ms",
  "max_attempts": 1,
  "config": {
    "when": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    },
    "blocks": [
      {
        "id": "review-remaining-requests",
        "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "name": "Review remaining requests",
        "type": "always",
        "enabled": true,
        "timeout": "50ms",
        "max_attempts": 1,
        "config": {
          "outcome": "human",
          "reason": "Requests not handled by earlier blocks need human review."
        },
        "config_version": 1,
        "content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
      }
    ],
    "reason": "string",
    "escalation_path": "string"
  },
  "config_version": 1,
  "content_hash": "string"
}
```

## BranchBlockConfig

When the condition matches, the branch's blocks run in order and the first to decide ends the pipeline. If none decides, the request goes to a person with this reason and escalation path. When the condition does not match, the branch is skipped and the request passes to the next block. A condition that cannot be evaluated sends the request to a person on the pipeline's default path.

- `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
- `blocks` · array<AlwaysBlockDefinition | CELBlockDefinition | WebhookBlockDefinition | BranchBlockDefinition | LLMJudgeBlockDefinition> · required: The branch's blocks, in the order they run. Any type, including further branches. They count toward the pipeline's 1,000 blocks.
- `reason` · string · required: The note recorded when none of the branch's blocks decides.
- `escalation_path` · string: Where the request goes when none of the branch's blocks decides. Defaults to the pipeline's default path. Blocks inside the branch that name no path of their own use the pipeline's default, not this one.

```json
{
  "when": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "field": "/request/arguments/amount_cents",
    "operator": "exists",
    "value": {}
  },
  "blocks": [
    {
      "id": "review-remaining-requests",
      "name": "Review remaining requests",
      "type": "always",
      "enabled": true,
      "timeout": "50ms",
      "max_attempts": 1,
      "config": {
        "outcome": "human",
        "reason": "Requests not handled by earlier blocks need human review."
      }
    }
  ],
  "reason": "string",
  "escalation_path": "string"
}
```

## StoredBranchBlockConfig

A branch configuration as saved, with its blocks as saved.

- `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
- `blocks` · array<StoredAlwaysBlock | StoredCELBlock | StoredWebhookBlock | StoredBranchBlock | StoredLLMJudgeBlock> · required: The branch's blocks, in the order they run.
- `reason` · string · required: The note recorded when none of the branch's blocks decides.
- `escalation_path` · string: Where the request goes when none of the branch's blocks decides.

```json
{
  "when": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "field": "/request/arguments/amount_cents",
    "operator": "exists",
    "value": {}
  },
  "blocks": [
    {
      "id": "review-remaining-requests",
      "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "name": "Review remaining requests",
      "type": "always",
      "enabled": true,
      "timeout": "50ms",
      "max_attempts": 1,
      "config": {
        "outcome": "human",
        "reason": "Requests not handled by earlier blocks need human review."
      },
      "config_version": 1,
      "content_hash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    }
  ],
  "reason": "string",
  "escalation_path": "string"
}
```

## WebhookBlockConfig

The block posts the request to a webhook endpoint and the endpoint answers with an outcome. The endpoint is a separate resource, named here by its key. Deliveries go to the key's active revision. If the answer is not in allowed_outcomes, or the call fails, the request goes to a person.

- `endpoint_key` · string · required: The webhook endpoint to call.
- `allowed_outcomes` · array<enum> · required: The outcomes the endpoint may return. Any other answer sends the request to a person. `approve` and `deny` must be listed explicitly. The editor defaults to `next` and `human`.
- `include` · object · required: What to send with the request.
  - `agent_reasoning` · boolean · required: Include the agent's own explanation of the call.
  - `context` · boolean · required: Include the runtime metadata the adapter reported.
  - `previous_metadata` · boolean · required: Include the metadata earlier blocks returned.
- `reason` · string · required: The note recorded on decisions this block makes. The endpoint's own reason is kept as evidence.
- `escalation_path` · string: Where the request goes when the endpoint answers `human`, or when the call fails. Defaults to the pipeline's default path.

```json
{
  "endpoint_key": "string",
  "allowed_outcomes": [
    "next"
  ],
  "include": {
    "agent_reasoning": true,
    "context": true,
    "previous_metadata": true
  },
  "reason": "string",
  "escalation_path": "string"
}
```

## AlwaysBlockConfig

Returns outcome for every request that reaches this block. Later blocks do not run.

- `outcome` · enum · required: The outcome returned unconditionally. One of `approve`, `deny`, `human`.
- `reason` · string · required: The note recorded on decisions this block makes.
- `escalation_path` · string: Where the request goes for a `human` outcome. Allowed only when `outcome` is `human`. Defaults to the pipeline's default path.

```json
{
  "outcome": "human",
  "reason": "Requests not handled by earlier blocks need human review."
}
```

## CELBlockConfig

When the condition matches, the block returns on_match. Otherwise the request passes to the next block.

- `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
- `on_match` · enum · required: What to do on a match: `approve`, `deny`, or `human`. One of `approve`, `deny`, `human`.
- `reason` · string · required: The note recorded on decisions this block makes.
- `escalation_path` · string: Where the request goes on a `human` match. Allowed only when `on_match` is `human`. Defaults to the pipeline's default path.

```json
{
  "when": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "field": "/request/arguments/amount_cents",
    "operator": "exists",
    "value": {}
  },
  "on_match": "approve",
  "reason": "string",
  "escalation_path": "string"
}
```

## CELLeafCondition

A test of one field.

- `id` · uuid · required: An id for the condition, unique within the pipeline.
- `field` · string · required: A JSON pointer into the request, such as `/request/tool` or `/request/arguments/amount_cents`.
- `operator` · enum · required: How to compare the field. `contains`, `contains_any` and `contains_all` test substrings of a string field and elements of an array field. `starts_with` takes an array of prefixes and holds when the field begins with any of them. One of `exists`, `not_exists`, `equals`, `not_equals`, `is_null`, `is_not_null`, `in`, `not_in`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex`, `greater_than`, `greater_than_or_equal`, `less_than`, `less_than_or_equal`, `is_true`, `is_false`, `contains_any`, `contains_all`, `is_empty`, `is_not_empty`.
- `value` · any: The value to compare with. Not used by operators that take none, such as `exists`.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "field": "/request/arguments/amount_cents",
  "operator": "exists",
  "value": {}
}
```

## CELAllCondition

Holds when every nested condition holds.

- `id` · uuid · required: An id for the condition, unique within the pipeline.
- `all` · array<CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition> · required: The conditions that must all hold.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "all": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    }
  ]
}
```

## CELAnyCondition

Holds when at least one nested condition holds.

- `id` · uuid · required: An id for the condition, unique within the pipeline.
- `any` · array<CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition> · required: The conditions, of which at least one must hold.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "any": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    }
  ]
}
```

## CELSomeCondition

Holds when at least one item of a list field matches.

- `id` · uuid · required: An id for the condition, unique within the pipeline.
- `some` · CELQuantifier · required: A list field and the condition each of its items is tested against.
  - `field` · string · required: A JSON pointer to a list field.
  - `where` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "some": {
    "field": "string",
    "where": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    }
  }
}
```

## CELEveryCondition

Holds when every item of a list field matches.

- `id` · uuid · required: An id for the condition, unique within the pipeline.
- `every` · CELQuantifier · required: A list field and the condition each of its items is tested against.
  - `field` · string · required: A JSON pointer to a list field.
  - `where` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "every": {
    "field": "string",
    "where": {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "field": "/request/arguments/amount_cents",
      "operator": "exists",
      "value": {}
    }
  }
}
```

## CELQuantifier

A list field and the condition each of its items is tested against.

- `field` · string · required: A JSON pointer to a list field.
- `where` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition · required: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.

```json
{
  "field": "string",
  "where": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "field": "/request/arguments/amount_cents",
    "operator": "exists",
    "value": {}
  }
}
```

## RequestApprovalPipelinePreviewRequest

A pipeline document and a sample request to run it against.

- `blocks` · array<AlwaysBlockDefinition | CELBlockDefinition | WebhookBlockDefinition | BranchBlockDefinition | LLMJudgeBlockDefinition> · required: The blocks, in the order they should run.
- `default_escalation_path` · string: The default escalation path, as in a document. Reported in the result when the outcome is `human`.
- `sample` · object · required: The request to run the pipeline against.
  - `request` · object · required: The tool call.
    - `tool` · string · required: The tool the sample agent is calling.
    - `server` · string: The MCP server that defines the tool, as an adapter would report it. Leave it out for a built-in tool.
    - `arguments` · object · required: The tool's arguments.
    - `agent_reasoning` · string: The agent's own explanation of the call. Only blocks that include it see it.
    - `context` · object: Runtime metadata, as an adapter would report it. Only blocks that include it see it.
  - `agent` · object · required: The agent making the call.
    - `slug` · string · required: The agent's slug.
    - `name` · string · required: A name for the sample agent.
  - `previous_metadata` · object: Metadata as if earlier blocks had returned it, keyed by scope and then by block key. Use it to carry the organization preview's metadata into an agent preview, so the two previews together behave like one request.
    - `organization` · object: Metadata from the organization pipeline, by block key.
    - `agent` · object: Metadata from the agent's pipeline, by block key.

```json
{
  "blocks": [
    {
      "id": "review-remaining-requests",
      "name": "Review remaining requests",
      "type": "always",
      "enabled": true,
      "timeout": "50ms",
      "max_attempts": 1,
      "config": {
        "outcome": "human",
        "reason": "Requests not handled by earlier blocks need human review."
      }
    }
  ],
  "default_escalation_path": "string",
  "sample": {
    "request": {
      "tool": "issue_refund",
      "server": "stripe",
      "arguments": {
        "amount_cents": 12000
      },
      "agent_reasoning": "string",
      "context": {}
    },
    "agent": {
      "slug": "string",
      "name": "string"
    },
    "previous_metadata": {
      "organization": {},
      "agent": {}
    }
  }
}
```

## RequestApprovalPipelinePreview

What the pipeline did with the sample.

- `outcome` · enum · required: The final outcome. One of `approve`, `deny`, `human`.
- `reason_code` · enum · required: Why the run ended. `block_outcome`: a block decided. `block_error`: a block failed. `end_of_pipeline`: the request passed every block. `end_of_branch`: the request entered a branch and none of its blocks decided. One of `block_outcome`, `block_error`, `end_of_pipeline`, `end_of_branch`.
- `terminal_block_id` · string: The block that ended the run: for `end_of_branch`, the branch. Absent when the request passed every block.
- `blocks` · array<RequestApprovalPipelinePreviewBlock> · required: What each block the request reached did, in order. A branch appears when its condition is evaluated, and once more with `end_of_branch` when none of its blocks decided.
  - `block_id` · string · required: The block's key.
  - `position` · integer · required: The block's position in the pipeline, from 0, counting in document order with each branch before the blocks it holds.
  - `outcome` · enum · required: What the block returned. A branch returns `enter` when its condition matched and `next` when it did not. One of `next`, `approve`, `deny`, `human`, `enter`.
  - `reason` · string: The note the block recorded.
  - `metadata` · object: The metadata the block returned, if any.
  - `cost` · int64 · required: The cost the block recorded.
  - `error_code` · string: Why the block failed, if it did: `invalid_input`, `evaluation_error`, `cost_limit_exceeded`, `resource_limit_exceeded`, `timeout`, `invalid_block_output`, `internal_error`, `provider_error`, `endpoint_unreachable`, `endpoint_error`, `endpoint_rejected`, `outcome_not_allowed`, `endpoint_unavailable`, or a code a kind the edition adds reports (the hosted judge's `model_unavailable`).
  - `delivery` · object: For webhook blocks. What was called and what came back. Never the bodies, never the secret.
    - `endpoint_key` · string · required: The endpoint that was called.
    - `endpoint_revision` · int64: The revision that was delivered to. Absent when the endpoint had no active revision.
    - `endpoint_host` · string · required: The host that was called.
    - `http_status` · integer: The HTTP status the endpoint returned.
    - `duration_ms` · int64 · required: How long the call took, in milliseconds.
    - `reason` · string: The endpoint's own explanation, at most 1,024 characters.
  - `trace` · array<object> · required: How the condition was evaluated, node by node. For condition blocks and branches.
    - `node_id` · uuid · required: The condition's id.
    - `matched` · boolean · required: Whether the condition held.
    - `error` · boolean · required: Whether evaluating it failed.
  - `end_of_branch` · boolean: Present and true on the step that closes a branch the request entered when none of its blocks decided. The outcome is `human`.
- `escalation_path` · string: The path a `human` outcome goes to, within this scope alone: the ending block's path, or the document's default. Absent when the outcome is automatic or no path applies.
- `escalation_path_reason` · enum: Where the path came from. `block_escalation`: the ending block named it. `pipeline_default`: the document's default. `no_escalation`: no path applies. One of `block_escalation`, `pipeline_default`, `no_escalation`.

```json
{
  "outcome": "approve",
  "reason_code": "block_outcome",
  "terminal_block_id": "string",
  "blocks": [
    {
      "block_id": "string",
      "position": 0,
      "outcome": "next",
      "reason": "string",
      "metadata": {},
      "cost": 0,
      "error_code": "string",
      "delivery": {
        "endpoint_key": "string",
        "endpoint_revision": 1,
        "endpoint_host": "string",
        "http_status": 1,
        "duration_ms": 1,
        "reason": "string"
      },
      "trace": [
        {
          "node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "matched": true,
          "error": true
        }
      ],
      "end_of_branch": true
    }
  ],
  "escalation_path": "string",
  "escalation_path_reason": "block_escalation"
}
```

## RequestApprovalPipelinePreviewBlock

What one block did in a preview.

- `block_id` · string · required: The block's key.
- `position` · integer · required: The block's position in the pipeline, from 0, counting in document order with each branch before the blocks it holds.
- `outcome` · enum · required: What the block returned. A branch returns `enter` when its condition matched and `next` when it did not. One of `next`, `approve`, `deny`, `human`, `enter`.
- `reason` · string: The note the block recorded.
- `metadata` · object: The metadata the block returned, if any.
- `cost` · int64 · required: The cost the block recorded.
- `error_code` · string: Why the block failed, if it did: `invalid_input`, `evaluation_error`, `cost_limit_exceeded`, `resource_limit_exceeded`, `timeout`, `invalid_block_output`, `internal_error`, `provider_error`, `endpoint_unreachable`, `endpoint_error`, `endpoint_rejected`, `outcome_not_allowed`, `endpoint_unavailable`, or a code a kind the edition adds reports (the hosted judge's `model_unavailable`).
- `delivery` · object: For webhook blocks. What was called and what came back. Never the bodies, never the secret.
  - `endpoint_key` · string · required: The endpoint that was called.
  - `endpoint_revision` · int64: The revision that was delivered to. Absent when the endpoint had no active revision.
  - `endpoint_host` · string · required: The host that was called.
  - `http_status` · integer: The HTTP status the endpoint returned.
  - `duration_ms` · int64 · required: How long the call took, in milliseconds.
  - `reason` · string: The endpoint's own explanation, at most 1,024 characters.
- `trace` · array<object> · required: How the condition was evaluated, node by node. For condition blocks and branches.
  - `node_id` · uuid · required: The condition's id.
  - `matched` · boolean · required: Whether the condition held.
  - `error` · boolean · required: Whether evaluating it failed.
- `end_of_branch` · boolean: Present and true on the step that closes a branch the request entered when none of its blocks decided. The outcome is `human`.

```json
{
  "block_id": "string",
  "position": 0,
  "outcome": "next",
  "reason": "string",
  "metadata": {},
  "cost": 0,
  "error_code": "string",
  "delivery": {
    "endpoint_key": "string",
    "endpoint_revision": 1,
    "endpoint_host": "string",
    "http_status": 1,
    "duration_ms": 1,
    "reason": "string"
  },
  "trace": [
    {
      "node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "matched": true,
      "error": true
    }
  ],
  "end_of_branch": true
}
```

## EnrollmentCode

The two codes of a pending enrollment.

- `enrollment_code` · string · required: The secret the machine keeps. Send it to the exchange endpoint. Never show it to the person.
- `user_code` · string · required: The short code the person enters in the browser. Three letters, a hyphen, and four letters or digits. Case does not matter.
- `verification_uri` · uri · required: The page where the person enters the user code.
- `expires_at` · date-time · required: When both codes stop working. Start a new enrollment after this.

```json
{
  "enrollment_code": "whe_7c1f2a9e-4b3d-4f2e-9a1c-2d6e8b5f0a11_3f6b9c1d0e7a4b2c",
  "user_code": "K7M-3PQ2",
  "verification_uri": "https://app.withhuman.ai/connect/authorize",
  "expires_at": "2026-09-08T12:02:11Z"
}
```

## IssuedCredential

- `id` · uuid · required: Identifies the credential, for example when revoking it.
- `token` · string · required: The secret. It appears only in this response and cannot be retrieved again.
- `expires_at` · date-time · required: When the credential stops working.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "token": "whc_live_3f6b9c1d0e7a4b2c",
  "expires_at": "2026-09-08T12:02:11Z"
}
```

## Principal

The identity the API checks permissions against. A person acting through a session, or a machine acting through a credential.

- `actor_type` · enum · required: `human` for a signed-in person, `agent` for an agent credential, `gateway` for the chokepoint MCP gateway acting for the organization with the deployment's gateway token, `system` for actions the provider takes on its own. One of `human`, `agent`, `system`, `gateway`.
- `actor_id` · uuid · required: The user's id for a person, an internal id for an agent (address agents by `agent_slug`), or the literal `gateway` for the gateway.
- `organization_id` · uuid · required: The organization every call is scoped to.
- `membership_id` · uuid: The person's membership in the organization. Present for a person only.
- `agent_instance_id` · uuid: The instance the credential belongs to. Present for an agent only.
- `agent_slug` · string: The agent's slug. Present for an agent only.
- `permissions` · array<string> · required: Every permission the actor holds at any scope, such as `request.decide`, sorted and without duplicates. Use it to decide what to show. Whether an action is allowed depends on `grants`.
- `grants` · array<Grant> · required: Every permission the actor holds, each with the scope it applies at. Flattened from the role assignments.
  - `permission` · string · required: A registry key such as request.decide
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
- `team_ids` · array<uuid>: The teams the member belongs to. Escalation paths can route requests to a team.
- `auth` · AuthContext · required: How a principal was signed in when it acted.
  - `method` · string · required: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
  - `assurance` · string · required: The strength of that sign-in: `single_factor` or `strong`.
  - `authenticated_at` · date-time · required: When the sign-in happened.
  - `session_id` · uuid: The session that acted, when a person did.
  - `credential_id` · uuid: The credential that acted, when an agent did.

```json
{
  "actor_type": "human",
  "actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "claude-code",
  "permissions": [
    "request.read",
    "request.decide",
    "agent.credential.issue"
  ],
  "grants": [
    {
      "permission": "request.decide",
      "scope_kind": "organization",
      "scope_id": "string"
    }
  ],
  "team_ids": [
    "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
  ],
  "auth": {
    "method": "local_password",
    "assurance": "strong",
    "authenticated_at": "2026-09-08T12:02:11Z",
    "session_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "credential_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
  }
}
```

## Grant

- `permission` · string · required: A registry key such as request.decide
- `scope_kind` · enum · required One of `organization`, `team`, `agent`.
- `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope

```json
{
  "permission": "request.decide",
  "scope_kind": "organization",
  "scope_id": "string"
}
```

## AuthContext

How a principal was signed in when it acted.

- `method` · string · required: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
- `assurance` · string · required: The strength of that sign-in: `single_factor` or `strong`.
- `authenticated_at` · date-time · required: When the sign-in happened.
- `session_id` · uuid: The session that acted, when a person did.
- `credential_id` · uuid: The credential that acted, when an agent did.

```json
{
  "method": "local_password",
  "assurance": "strong",
  "authenticated_at": "2026-09-08T12:02:11Z",
  "session_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "credential_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
```

## Agent

An agent identity. The slug identifies it everywhere: URLs, role scopes, pipeline scopes, audit data and conditions. The name is a label for people.

- `slug` · string · required: The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
- `organization_id` · uuid · required
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the agent.
- `name` · string · required: The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
- `status` · enum · required: A `disabled` agent's instances cannot authenticate until it is enabled again. An `archived` agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored. One of `active`, `disabled`, `archived`.
- `created_at` · date-time · required
- `updated_at` · date-time · required
- `archived_at` · date-time: When the agent was archived. Present exactly when `status` is `archived`.

```json
{
  "slug": "support-bot",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "Support bot",
  "status": "active",
  "created_at": "2026-09-08T12:02:11Z",
  "updated_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z"
}
```

## AgentInstance

One running copy of an agent.

- `id` · uuid · required
- `organization_id` · uuid · required
- `agent_slug` · string · required: The slug of the agent this is an instance of.
- `name` · string · required: Unique within the agent.
- `status` · enum · required: A disabled instance cannot create requests. One of `active`, `disabled`.
- `metadata` · object · required: The JSON stored when the instance was created.
- `created_at` · date-time · required
- `last_seen_at` · date-time · required: The last time this instance called the API.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "string",
  "name": "ci-runner-07",
  "status": "active",
  "metadata": {
    "region": "eu-west"
  },
  "created_at": "2026-09-08T12:02:11Z",
  "last_seen_at": "2026-09-08T12:02:11Z"
}
```

## AgentCredentialState

One instance credential as the agent page shows it. The token is never included. A credential is live while it is neither expired nor revoked.

- `id` · uuid · required
- `issued_at` · date-time · required
- `expires_at` · date-time · required: When the credential stops working.
- `last_used_at` · date-time: The last authenticated call made with it.
- `revoked_at` · date-time: When it was revoked. Absent while it is not.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "issued_at": "2026-09-08T12:02:11Z",
  "expires_at": "2026-09-08T12:02:11Z",
  "last_used_at": "2026-09-08T12:02:11Z",
  "revoked_at": "2026-09-08T12:02:11Z"
}
```

## AgentSummary

One row of the agent list. An agent with its instance and request counts.

- `slug` · string · required: The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
- `organization_id` · uuid · required
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the agent.
- `name` · string · required: The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
- `status` · enum · required: A `disabled` agent's instances cannot authenticate until it is enabled again. An `archived` agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored. One of `active`, `disabled`, `archived`.
- `created_at` · date-time · required
- `updated_at` · date-time · required
- `archived_at` · date-time: When the agent was archived. Present exactly when `status` is `archived`.
- `instance_count` · integer · required: How many instances the agent has.
- `live_instance_count` · integer · required: How many instances are active and hold a credential that is neither expired nor revoked.
- `last_seen_at` · date-time: The newest authenticated call from any instance. Absent when the agent has no instances.
- `recent_request_count` · integer · required: Requests made in the last seven days.
- `pending_request_count` · integer · required: Requests still waiting for a decision.
- `request_trend` · array<integer> · required: The recent requests split into one bucket per day, oldest first. The last entry is the past 24 hours.

```json
{
  "slug": "support-bot",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "Support bot",
  "status": "active",
  "created_at": "2026-09-08T12:02:11Z",
  "updated_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z",
  "instance_count": 0,
  "live_instance_count": 0,
  "last_seen_at": "2026-09-08T12:02:11Z",
  "recent_request_count": 0,
  "pending_request_count": 0,
  "request_trend": [
    0
  ]
}
```

## AgentInstanceDetail

An instance with its credential history.

- `id` · uuid · required
- `organization_id` · uuid · required
- `agent_slug` · string · required: The slug of the agent this is an instance of.
- `name` · string · required: Unique within the agent.
- `status` · enum · required: A disabled instance cannot create requests. One of `active`, `disabled`.
- `metadata` · object · required: The JSON stored when the instance was created.
- `created_at` · date-time · required
- `last_seen_at` · date-time · required: The last time this instance called the API.
- `credentials` · array<AgentCredentialState> · required: Every credential the instance has held, newest first. Tokens are never included.
  - `id` · uuid · required
  - `issued_at` · date-time · required
  - `expires_at` · date-time · required: When the credential stops working.
  - `last_used_at` · date-time: The last authenticated call made with it.
  - `revoked_at` · date-time: When it was revoked. Absent while it is not.
- `recent_request_count` · integer · required: Requests this instance made in the last seven days.
- `request_trend` · array<integer> · required: The instance's recent requests split into one bucket per day, oldest first. The last entry is the past 24 hours.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "string",
  "name": "ci-runner-07",
  "status": "active",
  "metadata": {
    "region": "eu-west"
  },
  "created_at": "2026-09-08T12:02:11Z",
  "last_seen_at": "2026-09-08T12:02:11Z",
  "credentials": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "issued_at": "2026-09-08T12:02:11Z",
      "expires_at": "2026-09-08T12:02:11Z",
      "last_used_at": "2026-09-08T12:02:11Z",
      "revoked_at": "2026-09-08T12:02:11Z"
    }
  ],
  "recent_request_count": 0,
  "request_trend": [
    0
  ]
}
```

## AgentDetail

An agent with a page of instances, aggregate counts, and provisioner tokens.

- `slug` · string · required: The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
- `organization_id` · uuid · required
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the agent.
- `name` · string · required: The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
- `status` · enum · required: A `disabled` agent's instances cannot authenticate until it is enabled again. An `archived` agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored. One of `active`, `disabled`, `archived`.
- `created_at` · date-time · required
- `updated_at` · date-time · required
- `archived_at` · date-time: When the agent was archived. Present exactly when `status` is `archived`.
- `provisioner_count` · integer · required: All provisioner tokens owned by this agent.
- `matching_provisioner_count` · integer · required: Tokens matching the current identifier and status filters before pagination.
- `instance_count` · integer · required: All instances owned by this agent.
- `live_instance_count` · integer · required: Active instances with at least one live credential across all pages.
- `matching_instance_count` · integer · required: Instances matching the current search and status filters before pagination.
- `request_count` · integer · required: All requests made by this agent.
- `created_by_actor_type` · enum One of `human`, `api_key`.
- `created_by_display_name` · string · required: The name of the human or organization key that created the agent.
- `instances` · array<AgentInstance> · required: The requested page of instances, sorted by last seen, creation time, and id, newest first.
- `provisioners` · array<AgentProvisioner> · required: The requested page of provisioner tokens, sorted by issued time and id, newest first. Secrets are never included.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `agent_slug` · string · required: The slug of the agent the token creates instances of.
  - `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the token.
  - `issued_at` · date-time · required
  - `expires_at` · date-time · required: When the token stops working.
  - `last_used_at` · date-time: The last time the token created an instance.
  - `revoked_at` · date-time: When it was revoked. Absent while it is not.
- `recent_request_count` · integer · required: Requests made in the last seven days.
- `pending_request_count` · integer · required: Requests still waiting for a decision.

```json
{
  "slug": "support-bot",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "Support bot",
  "status": "active",
  "created_at": "2026-09-08T12:02:11Z",
  "updated_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z",
  "provisioner_count": 0,
  "matching_provisioner_count": 0,
  "instance_count": 0,
  "live_instance_count": 0,
  "matching_instance_count": 0,
  "request_count": 0,
  "created_by_actor_type": "human",
  "created_by_display_name": "string",
  "instances": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "agent_slug": "string",
      "name": "ci-runner-07",
      "status": "active",
      "metadata": {
        "region": "eu-west"
      },
      "created_at": "2026-09-08T12:02:11Z",
      "last_seen_at": "2026-09-08T12:02:11Z",
      "credentials": [
        {
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "issued_at": "2026-09-08T12:02:11Z",
          "expires_at": "2026-09-08T12:02:11Z",
          "last_used_at": "2026-09-08T12:02:11Z",
          "revoked_at": "2026-09-08T12:02:11Z"
        }
      ],
      "recent_request_count": 0,
      "request_trend": [
        0
      ]
    }
  ],
  "provisioners": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "agent_slug": "string",
      "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "issued_at": "2026-09-08T12:02:11Z",
      "expires_at": "2026-09-08T12:02:11Z",
      "last_used_at": "2026-09-08T12:02:11Z",
      "revoked_at": "2026-09-08T12:02:11Z"
    }
  ],
  "recent_request_count": 0,
  "pending_request_count": 0
}
```

## AgentEnrollmentCredential

The selected parent agent, its new instance, and the instance credential shown once.

- `agent` · Agent · required: An agent identity. The slug identifies it everywhere: URLs, role scopes, pipeline scopes, audit data and conditions. The name is a label for people.
  - `slug` · string · required: The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
  - `organization_id` · uuid · required
  - `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the agent.
  - `name` · string · required: The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
  - `status` · enum · required: A `disabled` agent's instances cannot authenticate until it is enabled again. An `archived` agent is retired: nothing of it authenticates, nothing can be enrolled under it, and its status cannot change until it is restored. One of `active`, `disabled`, `archived`.
  - `created_at` · date-time · required
  - `updated_at` · date-time · required
  - `archived_at` · date-time: When the agent was archived. Present exactly when `status` is `archived`.
- `agent_instance` · AgentInstance · required: One running copy of an agent.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `agent_slug` · string · required: The slug of the agent this is an instance of.
  - `name` · string · required: Unique within the agent.
  - `status` · enum · required: A disabled instance cannot create requests. One of `active`, `disabled`.
  - `metadata` · object · required: The JSON stored when the instance was created.
  - `created_at` · date-time · required
  - `last_seen_at` · date-time · required: The last time this instance called the API.
- `credential` · IssuedCredential · required
  - `id` · uuid · required: Identifies the credential, for example when revoking it.
  - `token` · string · required: The secret. It appears only in this response and cannot be retrieved again.
  - `expires_at` · date-time · required: When the credential stops working.

```json
{
  "agent": {
    "slug": "support-bot",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "name": "Support bot",
    "status": "active",
    "created_at": "2026-09-08T12:02:11Z",
    "updated_at": "2026-09-08T12:02:11Z",
    "archived_at": "2026-09-08T12:02:11Z"
  },
  "agent_instance": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "agent_slug": "string",
    "name": "ci-runner-07",
    "status": "active",
    "metadata": {
      "region": "eu-west"
    },
    "created_at": "2026-09-08T12:02:11Z",
    "last_seen_at": "2026-09-08T12:02:11Z"
  },
  "credential": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "token": "whc_live_3f6b9c1d0e7a4b2c",
    "expires_at": "2026-09-08T12:02:11Z"
  }
}
```

## AgentInstanceCredential

A newly registered instance and its credential.

- `agent_instance` · AgentInstance · required: One running copy of an agent.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `agent_slug` · string · required: The slug of the agent this is an instance of.
  - `name` · string · required: Unique within the agent.
  - `status` · enum · required: A disabled instance cannot create requests. One of `active`, `disabled`.
  - `metadata` · object · required: The JSON stored when the instance was created.
  - `created_at` · date-time · required
  - `last_seen_at` · date-time · required: The last time this instance called the API.
- `credential` · IssuedCredential · required
  - `id` · uuid · required: Identifies the credential, for example when revoking it.
  - `token` · string · required: The secret. It appears only in this response and cannot be retrieved again.
  - `expires_at` · date-time · required: When the credential stops working.

```json
{
  "agent_instance": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "agent_slug": "string",
    "name": "ci-runner-07",
    "status": "active",
    "metadata": {
      "region": "eu-west"
    },
    "created_at": "2026-09-08T12:02:11Z",
    "last_seen_at": "2026-09-08T12:02:11Z"
  },
  "credential": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "token": "whc_live_3f6b9c1d0e7a4b2c",
    "expires_at": "2026-09-08T12:02:11Z"
  }
}
```

## AgentProvisioner

A provisioner token as the agent page shows it. The token is never included.

- `id` · uuid · required
- `organization_id` · uuid · required
- `agent_slug` · string · required: The slug of the agent the token creates instances of.
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the token.
- `issued_at` · date-time · required
- `expires_at` · date-time · required: When the token stops working.
- `last_used_at` · date-time: The last time the token created an instance.
- `revoked_at` · date-time: When it was revoked. Absent while it is not.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "string",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "issued_at": "2026-09-08T12:02:11Z",
  "expires_at": "2026-09-08T12:02:11Z",
  "last_used_at": "2026-09-08T12:02:11Z",
  "revoked_at": "2026-09-08T12:02:11Z"
}
```

## AgentProvisionerCredential

A newly created provisioner and its token.

- `provisioner` · AgentProvisioner · required: A provisioner token as the agent page shows it. The token is never included.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `agent_slug` · string · required: The slug of the agent the token creates instances of.
  - `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the token.
  - `issued_at` · date-time · required
  - `expires_at` · date-time · required: When the token stops working.
  - `last_used_at` · date-time: The last time the token created an instance.
  - `revoked_at` · date-time: When it was revoked. Absent while it is not.
- `credential` · IssuedCredential · required
  - `id` · uuid · required: Identifies the credential, for example when revoking it.
  - `token` · string · required: The secret. It appears only in this response and cannot be retrieved again.
  - `expires_at` · date-time · required: When the credential stops working.

```json
{
  "provisioner": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "agent_slug": "string",
    "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "issued_at": "2026-09-08T12:02:11Z",
    "expires_at": "2026-09-08T12:02:11Z",
    "last_used_at": "2026-09-08T12:02:11Z",
    "revoked_at": "2026-09-08T12:02:11Z"
  },
  "credential": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "token": "whc_live_3f6b9c1d0e7a4b2c",
    "expires_at": "2026-09-08T12:02:11Z"
  }
}
```

## ToolPresentationField

One rendered argument. A field whose path is absent from a request's arguments is skipped; arguments no field names still show after the named ones, rendered by shape.

- `path` · string: A JSON pointer into the arguments. Required unless the field is a diff pair. Inside a list's `item` it is relative to the element (no leading slash; empty means the element itself).
- `label` · string: Replaces the humanised key.
- `kind` · enum · required: How one argument renders. `text` is a short value; `prose` long plain text; `markdown` and `html` are rendered (HTML in a sandbox that runs no script and loads nothing, with the source one click away); `code` is a mono block labelled with `language`; `shell` a command line; `diff` a unified diff at `path` or the pair at `before` and `after`; `path` a file path; `url` a link shown, never followed; `email` an address; `reference` an opaque identifier; `enum` a badge; `datetime` an instant in the reader's locale; `list` an array rendered per `item`; `json` pretty-printed structure. One of `text`, `prose`, `markdown`, `html`, `code`, `shell`, `diff`, `path`, `url`, `email`, `reference`, `enum`, `datetime`, `list`, `json`.
- `language` · string: Labels a `code` field for the reader.
- `before` · string: For `diff`: the pointer to the text before the change. Requires `after`; excludes `path`.
- `after` · string: For `diff`: the pointer to the text after the change.
- `item` · ToolPresentationField: One rendered argument. A field whose path is absent from a request's arguments is skipped; arguments no field names still show after the named ones, rendered by shape.
- `role` · enum: `primary` fields make up the block a reviewer reads first; `detail` fields fold away underneath it. One of `primary`, `detail`.

```json
{
  "path": "/body",
  "label": "Body",
  "kind": "text",
  "language": "python",
  "before": "/old_string",
  "after": "/new_string",
  "item": {},
  "role": "primary"
}
```

## ResolvedToolPresentation

The presentation a request renders with and where it came from.

- `source` · enum · required: `organization` for one of the organization's own entries, `built_in` for the shipped catalog. One of `built_in`, `organization`.
- `id` · uuid: The organization entry, when the source is `organization`.
- `title` · string
- `fields` · array<ToolPresentationField> · required
  - `path` · string: A JSON pointer into the arguments. Required unless the field is a diff pair. Inside a list's `item` it is relative to the element (no leading slash; empty means the element itself).
  - `label` · string: Replaces the humanised key.
  - `kind` · enum · required: How one argument renders. `text` is a short value; `prose` long plain text; `markdown` and `html` are rendered (HTML in a sandbox that runs no script and loads nothing, with the source one click away); `code` is a mono block labelled with `language`; `shell` a command line; `diff` a unified diff at `path` or the pair at `before` and `after`; `path` a file path; `url` a link shown, never followed; `email` an address; `reference` an opaque identifier; `enum` a badge; `datetime` an instant in the reader's locale; `list` an array rendered per `item`; `json` pretty-printed structure. One of `text`, `prose`, `markdown`, `html`, `code`, `shell`, `diff`, `path`, `url`, `email`, `reference`, `enum`, `datetime`, `list`, `json`.
  - `language` · string: Labels a `code` field for the reader.
  - `before` · string: For `diff`: the pointer to the text before the change. Requires `after`; excludes `path`.
  - `after` · string: For `diff`: the pointer to the text after the change.
  - `item` · ToolPresentationField: One rendered argument. A field whose path is absent from a request's arguments is skipped; arguments no field names still show after the named ones, rendered by shape.
  - `role` · enum: `primary` fields make up the block a reviewer reads first; `detail` fields fold away underneath it. One of `primary`, `detail`.

```json
{
  "source": "built_in",
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "title": "Edit {{/file_path}}",
  "fields": [
    {
      "path": "/body",
      "label": "Body",
      "kind": "text",
      "language": "python",
      "before": "/old_string",
      "after": "/new_string",
      "item": {},
      "role": "primary"
    }
  ]
}
```

## CreateApproval

- `tool` · string · required: The name under which the tool is defined: the name an MCP server advertises in `tools/list`, or the runtime's own name for a built-in tool such as `Bash`. Never the runtime's joined spelling such as `mcp__stripe__issue_refund`.
- `server` · string: The alias of the MCP server that defines the tool, as the runtime configured it. Present only for tools served over MCP. A label the adapter observed, not a verified identity.
- `arguments` · object · required: The exact arguments the tool will run with if approved. Reviewers see this as the description of the action, so it must be complete.
- `agent_reasoning` · string: The agent's own explanation of why it wants to do this. Reviewers see it as a claim from the agent, separate from the arguments.
- `context` · object: Where the call comes from, as observed by the adapter rather than stated by the agent: for example the runtime, session id, or working directory.
- `timeout` · string · required: How long the request may wait for a decision, as a duration such as `30m` or `24h`. Between one second and seven days. Once it passes, the request expires.

```json
{
  "tool": "issue_refund",
  "server": "stripe",
  "arguments": {
    "amount": 4900,
    "reason": "duplicate_charge"
  },
  "agent_reasoning": "Refunding the duplicate charge for jane@northwind.com.",
  "context": {
    "run_id": "4821",
    "framework": "claude-code"
  },
  "timeout": "30m"
}
```

## AAPDecision

Immutable decision. Approved calls must start within five minutes of decided_at. Reads and retries never extend expires_at.

- `status` · enum · required One of `approved`, `denied`, `expired`, `cancelled`.
- `note` · string
- `decided_at` · date-time · required
- `expires_at` · date-time

```json
{
  "status": "approved",
  "note": "Refund the duplicate charge.",
  "decided_at": "2026-09-17T12:02:00Z",
  "expires_at": "2026-09-17T12:07:00Z"
}
```

## AAPApprovalRequest

Submitted fields are immutable. Timeout preserves the submitted duration text. Request idempotency is scoped to the authenticated instance; another instance cannot read or cancel it.

- `id` · uuid · required
- `tool` · string · required
- `server` · string
- `arguments` · object · required
- `timeout` · string · required
- `agent_reasoning` · string
- `context` · object
- `status` · enum · required One of `pending`, `approved`, `denied`, `expired`, `cancelled`.
- `created_at` · date-time · required
- `deadline_at` · date-time · required
- `decision` · AAPDecision: Immutable decision. Approved calls must start within five minutes of decided_at. Reads and retries never extend expires_at.
  - `status` · enum · required One of `approved`, `denied`, `expired`, `cancelled`.
  - `note` · string
  - `decided_at` · date-time · required
  - `expires_at` · date-time

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "tool": "issue_refund",
  "arguments": {
    "amount": 4900,
    "reason": "duplicate_charge"
  },
  "timeout": "30m",
  "status": "pending",
  "created_at": "2026-09-17T12:00:00Z",
  "deadline_at": "2026-09-17T12:30:00Z"
}
```

## ApprovalRequest

- `id` · uuid · required
- `organization_id` · uuid · required
- `agent_slug` · string · required: The slug of the agent that made the request.
- `agent_instance_id` · uuid · required: The running copy of the agent that made the request.
- `agent_name` · string · required: The agent's name when the request was made.
- `agent_instance_name` · string · required: The instance's name when the request was made.
- `tool` · string · required: The tool the agent wants to call, as the server that defines it names it.
- `server` · string: The MCP server that defines the tool, as the adapter reported it. Absent for a runtime's built-in tools.
- `arguments` · any · required: The exact arguments the tool will run with if approved.
- `agent_reasoning` · string: The agent's own explanation, if it gave one.
- `context` · any: Where the call comes from, as recorded by the adapter.
- `status` · enum · required: `pending` while waiting for a decision, then one of the four final states. `cancelled` means the requesting instance withdrew it; treat it as a denial. One of `pending`, `approved`, `denied`, `expired`, `cancelled`.
- `deadline_at` · date-time · required: When the request expires if nobody has decided: `created_at` plus the timeout.
- `created_at` · date-time · required
- `decision` · Decision: The outcome of an approval request and who produced it.
  - `id` · uuid · required
  - `request_id` · uuid · required
  - `organization_id` · uuid · required
  - `membership_id` · uuid: The member who decided. Absent when the pipeline decided, the request expired, or the agent cancelled it.
  - `status` · enum · required One of `approved`, `denied`, `expired`, `cancelled`.
  - `note` · string: A note the reviewer left for the agent, if any.
  - `channel` · string · required: Where the decision was made, as reported by the client, for example `web` or `slack`. `pipeline` when the pipeline decided, `system` when the request expired, `aap` when the agent cancelled it.
  - `reviewer_display_name` · string: The reviewer's name, when a person decided.
  - `reviewer_email` · email: The reviewer's email, when a person decided.
  - `authentication` · AuthContext · required: How a principal was signed in when it acted.
    - `method` · string · required: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
    - `assurance` · string · required: The strength of that sign-in: `single_factor` or `strong`.
    - `authenticated_at` · date-time · required: When the sign-in happened.
    - `session_id` · uuid: The session that acted, when a person did.
    - `credential_id` · uuid: The credential that acted, when an agent did.
  - `idempotency_key` · string · required: The key the decision was recorded with.
  - `decided_at` · date-time · required

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "support-agent",
  "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_name": "support-agent",
  "agent_instance_name": "ci-runner-07",
  "tool": "issue_refund",
  "server": "stripe",
  "arguments": {
    "amount": 4900,
    "reason": "duplicate_charge"
  },
  "agent_reasoning": "Refunding the duplicate charge for jane@northwind.com.",
  "context": {
    "run_id": "4821",
    "framework": "claude-code"
  },
  "status": "approved",
  "deadline_at": "2026-09-09T12:00:00Z",
  "created_at": "2026-09-08T12:00:00Z",
  "decision": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "request_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "status": "approved",
    "note": "OK, but flag this account for review.",
    "channel": "web",
    "reviewer_display_name": "Chris",
    "reviewer_email": "reviewer@example.com",
    "authentication": {
      "method": "local_password",
      "assurance": "strong",
      "authenticated_at": "2026-09-08T12:02:11Z",
      "session_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "credential_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    },
    "idempotency_key": "decision-7ab8c8ec",
    "decided_at": "2026-09-08T12:02:11Z"
  }
}
```

## QueueApprovalRequest

An approval request as reviewers see it: the request itself plus the claim a reviewer holds on it, if any. Agents never see claims.

- `id` · uuid · required
- `organization_id` · uuid · required
- `agent_slug` · string · required: The slug of the agent that made the request.
- `agent_instance_id` · uuid · required: The running copy of the agent that made the request.
- `agent_name` · string · required: The agent's name when the request was made.
- `agent_instance_name` · string · required: The instance's name when the request was made.
- `tool` · string · required: The tool the agent wants to call, as the server that defines it names it.
- `server` · string: The MCP server that defines the tool, as the adapter reported it. Absent for a runtime's built-in tools.
- `arguments` · any · required: The exact arguments the tool will run with if approved.
- `agent_reasoning` · string: The agent's own explanation, if it gave one.
- `context` · any: Where the call comes from, as recorded by the adapter.
- `status` · enum · required: `pending` while waiting for a decision, then one of the four final states. `cancelled` means the requesting instance withdrew it; treat it as a denial. One of `pending`, `approved`, `denied`, `expired`, `cancelled`.
- `deadline_at` · date-time · required: When the request expires if nobody has decided: `created_at` plus the timeout.
- `created_at` · date-time · required
- `decision` · Decision: The outcome of an approval request and who produced it.
  - `id` · uuid · required
  - `request_id` · uuid · required
  - `organization_id` · uuid · required
  - `membership_id` · uuid: The member who decided. Absent when the pipeline decided, the request expired, or the agent cancelled it.
  - `status` · enum · required One of `approved`, `denied`, `expired`, `cancelled`.
  - `note` · string: A note the reviewer left for the agent, if any.
  - `channel` · string · required: Where the decision was made, as reported by the client, for example `web` or `slack`. `pipeline` when the pipeline decided, `system` when the request expired, `aap` when the agent cancelled it.
  - `reviewer_display_name` · string: The reviewer's name, when a person decided.
  - `reviewer_email` · email: The reviewer's email, when a person decided.
  - `authentication` · AuthContext · required: How a principal was signed in when it acted.
    - `method` · string · required: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
    - `assurance` · string · required: The strength of that sign-in: `single_factor` or `strong`.
    - `authenticated_at` · date-time · required: When the sign-in happened.
    - `session_id` · uuid: The session that acted, when a person did.
    - `credential_id` · uuid: The credential that acted, when an agent did.
  - `idempotency_key` · string · required: The key the decision was recorded with.
  - `decided_at` · date-time · required
- `updated_at` · date-time · required: Moves on every change a reviewer should notice, escalation progress included, so a client refetches the review when it changes.
- `claim` · Claim: A reviewer's claim on a pending request: an advisory marker that they are looking at it. Absent once released, lapsed, or the request is no longer pending.
  - `id` · uuid · required
  - `membership_id` · uuid · required: The reviewer holding the claim.
  - `display_name` · string: The reviewer's name.
  - `claimed_at` · date-time · required
  - `expires_at` · date-time · required: When the claim lapses on its own.
- `presentation` · ResolvedToolPresentation: The presentation a request renders with and where it came from.
  - `source` · enum · required: `organization` for one of the organization's own entries, `built_in` for the shipped catalog. One of `built_in`, `organization`.
  - `id` · uuid: The organization entry, when the source is `organization`.
  - `title` · string
  - `fields` · array<ToolPresentationField> · required
    - `path` · string: A JSON pointer into the arguments. Required unless the field is a diff pair. Inside a list's `item` it is relative to the element (no leading slash; empty means the element itself).
    - `label` · string: Replaces the humanised key.
    - `kind` · enum · required: How one argument renders. `text` is a short value; `prose` long plain text; `markdown` and `html` are rendered (HTML in a sandbox that runs no script and loads nothing, with the source one click away); `code` is a mono block labelled with `language`; `shell` a command line; `diff` a unified diff at `path` or the pair at `before` and `after`; `path` a file path; `url` a link shown, never followed; `email` an address; `reference` an opaque identifier; `enum` a badge; `datetime` an instant in the reader's locale; `list` an array rendered per `item`; `json` pretty-printed structure. One of `text`, `prose`, `markdown`, `html`, `code`, `shell`, `diff`, `path`, `url`, `email`, `reference`, `enum`, `datetime`, `list`, `json`.
    - `language` · string: Labels a `code` field for the reader.
    - `before` · string: For `diff`: the pointer to the text before the change. Requires `after`; excludes `path`.
    - `after` · string: For `diff`: the pointer to the text after the change.
    - `item` · ToolPresentationField: One rendered argument. A field whose path is absent from a request's arguments is skipped; arguments no field names still show after the named ones, rendered by shape.
    - `role` · enum: `primary` fields make up the block a reviewer reads first; `detail` fields fold away underneath it. One of `primary`, `detail`.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_slug": "support-agent",
  "agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "agent_name": "support-agent",
  "agent_instance_name": "ci-runner-07",
  "tool": "issue_refund",
  "server": "stripe",
  "arguments": {
    "amount": 4900,
    "reason": "duplicate_charge"
  },
  "agent_reasoning": "Refunding the duplicate charge for jane@northwind.com.",
  "context": {
    "run_id": "4821",
    "framework": "claude-code"
  },
  "status": "approved",
  "deadline_at": "2026-09-09T12:00:00Z",
  "created_at": "2026-09-08T12:00:00Z",
  "decision": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "request_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "status": "approved",
    "note": "OK, but flag this account for review.",
    "channel": "web",
    "reviewer_display_name": "Chris",
    "reviewer_email": "reviewer@example.com",
    "authentication": {
      "method": "local_password",
      "assurance": "strong",
      "authenticated_at": "2026-09-08T12:02:11Z",
      "session_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "credential_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    },
    "idempotency_key": "decision-7ab8c8ec",
    "decided_at": "2026-09-08T12:02:11Z"
  },
  "updated_at": "2026-09-08T12:05:00Z",
  "claim": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "display_name": "Chris",
    "claimed_at": "2026-09-08T12:01:30Z",
    "expires_at": "2026-09-08T12:16:30Z"
  },
  "presentation": {
    "source": "built_in",
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "title": "Edit {{/file_path}}",
    "fields": [
      {
        "path": "/body",
        "label": "Body",
        "kind": "text",
        "language": "python",
        "before": "/old_string",
        "after": "/new_string",
        "item": {},
        "role": "primary"
      }
    ]
  }
}
```

## Claim

A reviewer's claim on a pending request: an advisory marker that they are looking at it. Absent once released, lapsed, or the request is no longer pending.

- `id` · uuid · required
- `membership_id` · uuid · required: The reviewer holding the claim.
- `display_name` · string: The reviewer's name.
- `claimed_at` · date-time · required
- `expires_at` · date-time · required: When the claim lapses on its own.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "display_name": "Chris",
  "claimed_at": "2026-09-08T12:01:30Z",
  "expires_at": "2026-09-08T12:16:30Z"
}
```

## Decision

The outcome of an approval request and who produced it.

- `id` · uuid · required
- `request_id` · uuid · required
- `organization_id` · uuid · required
- `membership_id` · uuid: The member who decided. Absent when the pipeline decided, the request expired, or the agent cancelled it.
- `status` · enum · required One of `approved`, `denied`, `expired`, `cancelled`.
- `note` · string: A note the reviewer left for the agent, if any.
- `channel` · string · required: Where the decision was made, as reported by the client, for example `web` or `slack`. `pipeline` when the pipeline decided, `system` when the request expired, `aap` when the agent cancelled it.
- `reviewer_display_name` · string: The reviewer's name, when a person decided.
- `reviewer_email` · email: The reviewer's email, when a person decided.
- `authentication` · AuthContext · required: How a principal was signed in when it acted.
  - `method` · string · required: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
  - `assurance` · string · required: The strength of that sign-in: `single_factor` or `strong`.
  - `authenticated_at` · date-time · required: When the sign-in happened.
  - `session_id` · uuid: The session that acted, when a person did.
  - `credential_id` · uuid: The credential that acted, when an agent did.
- `idempotency_key` · string · required: The key the decision was recorded with.
- `decided_at` · date-time · required

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "request_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "status": "approved",
  "note": "OK, but flag this account for review.",
  "channel": "web",
  "reviewer_display_name": "Chris",
  "reviewer_email": "reviewer@example.com",
  "authentication": {
    "method": "local_password",
    "assurance": "strong",
    "authenticated_at": "2026-09-08T12:02:11Z",
    "session_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "credential_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
  },
  "idempotency_key": "decision-7ab8c8ec",
  "decided_at": "2026-09-08T12:02:11Z"
}
```

## DecisionOperation

The record of one decision call and its outcome.

- `id` · uuid · required: The operation's id. The decision itself has its own.
- `organization_id` · uuid · required
- `request_id` · uuid · required: The request that was decided.
- `status` · enum · required: `applied` once the decision is stored. A `200` response always says `applied`: the call waits for the outcome, and a rejected decision comes back as an error. One of `pending`, `applied`, `rejected`.
- `error_code` · string: Why the decision was rejected. Set for `rejected` only.
- `error_message` · string: The rejection in plain text. Set for `rejected` only.
- `decision` · Decision: The outcome of an approval request and who produced it.
  - `id` · uuid · required
  - `request_id` · uuid · required
  - `organization_id` · uuid · required
  - `membership_id` · uuid: The member who decided. Absent when the pipeline decided, the request expired, or the agent cancelled it.
  - `status` · enum · required One of `approved`, `denied`, `expired`, `cancelled`.
  - `note` · string: A note the reviewer left for the agent, if any.
  - `channel` · string · required: Where the decision was made, as reported by the client, for example `web` or `slack`. `pipeline` when the pipeline decided, `system` when the request expired, `aap` when the agent cancelled it.
  - `reviewer_display_name` · string: The reviewer's name, when a person decided.
  - `reviewer_email` · email: The reviewer's email, when a person decided.
  - `authentication` · AuthContext · required: How a principal was signed in when it acted.
    - `method` · string · required: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
    - `assurance` · string · required: The strength of that sign-in: `single_factor` or `strong`.
    - `authenticated_at` · date-time · required: When the sign-in happened.
    - `session_id` · uuid: The session that acted, when a person did.
    - `credential_id` · uuid: The credential that acted, when an agent did.
  - `idempotency_key` · string · required: The key the decision was recorded with.
  - `decided_at` · date-time · required
- `created_at` · date-time · required
- `completed_at` · date-time: When the decision was stored.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "request_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "status": "applied",
  "error_code": "string",
  "error_message": "string",
  "decision": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "request_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "status": "approved",
    "note": "OK, but flag this account for review.",
    "channel": "web",
    "reviewer_display_name": "Chris",
    "reviewer_email": "reviewer@example.com",
    "authentication": {
      "method": "local_password",
      "assurance": "strong",
      "authenticated_at": "2026-09-08T12:02:11Z",
      "session_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "credential_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    },
    "idempotency_key": "decision-7ab8c8ec",
    "decided_at": "2026-09-08T12:02:11Z"
  },
  "created_at": "2026-09-08T12:02:11Z",
  "completed_at": "2026-09-08T12:02:11Z"
}
```

## Membership

A person's membership of the organization, as stored.

- `id` · uuid · required: The membership id. Other endpoints refer to a member by this id.
- `user_id` · uuid · required: The person's user id, shared across organizations.
- `organization_id` · uuid · required
- `status` · enum · required: `active` can sign in and act. `suspended` cannot until reactivated. `deprovisioned` was removed and is kept for the record. One of `active`, `suspended`, `deprovisioned`.
- `admission_source` · enum · required: How the person joined: added by hand, by accepting an invitation, on first SSO sign-in, or by directory sync. One of `manual`, `invite`, `sso_jit`, `directory`.
- `directory_managed` · boolean · required: Whether your directory owns this membership. Status changes here are refused.
- `assignments` · array<RoleAssignment> · required: The roles the member holds, directly and through their teams.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `principal_kind` · enum · required: What holds the role. One of `membership`, `team`, `agent`.
  - `principal_id` · uuid · required: The membership, team or agent that holds it.
  - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
  - `scope_name` · string: The team's name, for a team scope.
  - `source` · enum · required: Whether a person granted it or your directory did. One of `manual`, `directory`.
  - `source_key` · string: The directory group that produced it. Present for `directory` only.
  - `granted_by_actor_id` · uuid: The member who granted it. Present for `manual` only.
  - `via_team_id` · uuid: The team the role is inherited through. Present when a member holds it through a team rather than directly.
  - `via_team_name` · string: That team's name.
  - `created_at` · date-time · required
  - `permissions` · array<string> · required
  - `is_owner` · boolean · required
  - `permission_policy_id` · uuid
- `team_ids` · array<uuid>: The teams the member is on.
- `created_at` · date-time
- `updated_at` · date-time

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "user_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "status": "active",
  "admission_source": "manual",
  "directory_managed": true,
  "assignments": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "principal_kind": "membership",
      "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "scope_kind": "organization",
      "scope_id": "string",
      "scope_name": "string",
      "source": "manual",
      "source_key": "string",
      "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_name": "string",
      "created_at": "2026-09-08T12:02:11Z",
      "permissions": [
        "string"
      ],
      "is_owner": true,
      "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    }
  ],
  "team_ids": [
    "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
  ],
  "created_at": "2026-09-08T12:02:11Z",
  "updated_at": "2026-09-08T12:02:11Z"
}
```

## Member

A member as shown in the member list, with their name and email.

- `id` · uuid · required: The membership id. Other endpoints refer to a member by this id.
- `display_name` · string · required
- `email` · string · required
- `status` · enum · required: `active` can sign in and act. `suspended` cannot until reactivated. `deprovisioned` was removed and is kept for the record. One of `active`, `suspended`, `deprovisioned`.
- `assignments` · array<RoleAssignment> · required: The roles the member holds, directly and through their teams.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `principal_kind` · enum · required: What holds the role. One of `membership`, `team`, `agent`.
  - `principal_id` · uuid · required: The membership, team or agent that holds it.
  - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
  - `scope_name` · string: The team's name, for a team scope.
  - `source` · enum · required: Whether a person granted it or your directory did. One of `manual`, `directory`.
  - `source_key` · string: The directory group that produced it. Present for `directory` only.
  - `granted_by_actor_id` · uuid: The member who granted it. Present for `manual` only.
  - `via_team_id` · uuid: The team the role is inherited through. Present when a member holds it through a team rather than directly.
  - `via_team_name` · string: That team's name.
  - `created_at` · date-time · required
  - `permissions` · array<string> · required
  - `is_owner` · boolean · required
  - `permission_policy_id` · uuid
- `admission_source` · enum · required: How the person joined: added by hand, by accepting an invitation, on first SSO sign-in, or by directory sync. One of `manual`, `invite`, `sso_jit`, `directory`.
- `directory_managed` · boolean · required: Whether your directory owns this membership. Status changes here are refused.
- `team_ids` · array<uuid> · required: The teams the member is on.
- `created_at` · date-time · required

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "display_name": "Ada Lovelace",
  "email": "ada@example.com",
  "status": "active",
  "assignments": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "principal_kind": "membership",
      "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "scope_kind": "organization",
      "scope_id": "string",
      "scope_name": "string",
      "source": "manual",
      "source_key": "string",
      "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_name": "string",
      "created_at": "2026-09-08T12:02:11Z",
      "permissions": [
        "string"
      ],
      "is_owner": true,
      "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    }
  ],
  "admission_source": "manual",
  "directory_managed": true,
  "team_ids": [
    "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
  ],
  "created_at": "2026-09-08T12:02:11Z"
}
```

## WebhookEndpointSummary

One webhook endpoint and a summary of its history.

- `endpoint_key` · string · required
- `name` · string · required: The name from the latest revision.
- `url` · uri · required: The URL from the latest revision.
- `active_revision` · int64: The revision deliveries go to. Absent while the endpoint is archived or no revision has been activated yet.
- `archived_at` · date-time: When the endpoint was archived. Absent for a live endpoint, including one whose revisions are all drafts. Cleared when a revision is activated again.
- `latest_revision` · int64 · required: The newest revision, active or not.
- `revision_count` · int64 · required
- `latest_created_at` · date-time · required: When the newest revision was created.
- `secret_rotated_at` · date-time · required: When the signing secret was last created or rotated.
- `uses` · array<PipelineUse> · required: The active pipeline revisions that post to the endpoint. Empty when it can be archived.
  - `pipeline_id` · uuid · required
  - `scope` · enum · required: The pipeline's scope. One of `organization`, `agent`.
  - `agent_slug` · string: The agent's slug. Present for the `agent` scope only.
  - `revision` · int64 · required: The active revision number.
  - `block_key` · string: The block that uses the object. Absent when the revision names an escalation path as its default.

```json
{
  "endpoint_key": "string",
  "name": "string",
  "url": "https://example.com/webhooks/withhuman",
  "active_revision": 1,
  "archived_at": "2026-09-08T12:02:11Z",
  "latest_revision": 1,
  "revision_count": 1,
  "latest_created_at": "2026-09-08T12:02:11Z",
  "secret_rotated_at": "2026-09-08T12:02:11Z",
  "uses": [
    {
      "pipeline_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "scope": "organization",
      "agent_slug": "string",
      "revision": 1,
      "block_key": "string"
    }
  ]
}
```

## WebhookEndpointRevision

One revision of a webhook endpoint, a name and a URL. The signing secret belongs to the endpoint key, not to a revision. It is never included, except once, in the response that created the endpoint's first revision.

- `id` · uuid · required
- `endpoint_key` · string · required
- `revision` · int64 · required: The revision number. Revisions count up from 1.
- `is_active` · boolean · required: Whether deliveries go to this revision.
- `name` · string · required: The display name.
- `url` · uri · required: Where deliveries go.
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the revision.
- `created_at` · date-time · required
- `signing_secret` · string: Present only in the response that created the endpoint's first revision. Starts with `whsec_`. Your receiver uses it to verify the `WithHuman-Signature` header.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "endpoint_key": "string",
  "revision": 1,
  "is_active": true,
  "name": "Fraud check",
  "url": "https://fraud.example.com/approvals",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "signing_secret": "whsec_9f2c1b7e4d3a4f8b"
}
```

## WebhookEndpointDocument

The content of a new webhook endpoint revision.

- `name` · string · required: A display name. Pipeline authors see it when they pick an endpoint.
- `url` · uri · required: An absolute public HTTPS URL. It is checked against the egress policy, which refuses private and internal addresses.

```json
{
  "name": "Fraud check",
  "url": "https://fraud.example.com/approvals"
}
```

## WebhookEndpointSecret

A freshly rotated signing secret.

- `endpoint_key` · string · required
- `signing_secret` · string · required: The new secret. It appears only in this response. Starts with `whsec_`. Your receiver uses it to verify the `WithHuman-Signature` header.
- `secret_rotated_at` · date-time · required: When the secret was rotated.

```json
{
  "endpoint_key": "string",
  "signing_secret": "whsec_9f2c1b7e4d3a4f8b",
  "secret_rotated_at": "2026-09-08T12:02:11Z"
}
```

## PipelineUse

One place an active pipeline revision uses a shared object, an escalation path or a webhook endpoint.

- `pipeline_id` · uuid · required
- `scope` · enum · required: The pipeline's scope. One of `organization`, `agent`.
- `agent_slug` · string: The agent's slug. Present for the `agent` scope only.
- `revision` · int64 · required: The active revision number.
- `block_key` · string: The block that uses the object. Absent when the revision names an escalation path as its default.

```json
{
  "pipeline_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "scope": "organization",
  "agent_slug": "string",
  "revision": 1,
  "block_key": "string"
}
```

## WebhookEndpointTest

What one test delivery produced. problem is absent when the endpoint answered with a valid outcome.

- `status_code` · integer · required: The HTTP status the endpoint returned.
- `duration_ms` · int64 · required: How long the call took, in milliseconds.
- `outcome` · enum: The outcome the endpoint answered with, when its answer was valid. One of `next`, `human`, `approve`, `deny`.
- `reason` · string: The reason the endpoint gave, if any.
- `problem` · enum: What went wrong, if anything. `blocked`: the URL fails the egress policy. `response_too_large`: the body was over 64 KiB. `timeout`: no answer within 10 seconds. `unreachable`: the connection failed. `status`: the endpoint answered with a status outside 2xx. `invalid_answer`: the body was not a valid outcome document. One of `blocked`, `response_too_large`, `timeout`, `unreachable`, `status`, `invalid_answer`.
- `message` · string: Details about the problem, in plain words.

```json
{
  "status_code": 1,
  "duration_ms": 1,
  "outcome": "next",
  "reason": "string",
  "problem": "blocked",
  "message": "string"
}
```

## Team

A group of members. A team can be an escalation target, and its members inherit the roles it holds. A team may also own an escalation policy that runs when a path level names it.

- `id` · uuid · required
- `name` · string · required
- `directory_managed` · boolean · required: Whether your directory owns this team. Such a team cannot be renamed or archived here.
- `member_count` · integer · required: How many members the team has, from every source.
- `assignments` · array<RoleAssignment> · required: The roles the team holds. Every member inherits them.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `principal_kind` · enum · required: What holds the role. One of `membership`, `team`, `agent`.
  - `principal_id` · uuid · required: The membership, team or agent that holds it.
  - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
  - `scope_name` · string: The team's name, for a team scope.
  - `source` · enum · required: Whether a person granted it or your directory did. One of `manual`, `directory`.
  - `source_key` · string: The directory group that produced it. Present for `directory` only.
  - `granted_by_actor_id` · uuid: The member who granted it. Present for `manual` only.
  - `via_team_id` · uuid: The team the role is inherited through. Present when a member holds it through a team rather than directly.
  - `via_team_name` · string: That team's name.
  - `created_at` · date-time · required
  - `permissions` · array<string> · required
  - `is_owner` · boolean · required
  - `permission_policy_id` · uuid
- `escalation_policy` · TeamEscalationPolicyStatus · required: The team's escalation policy lineage. Both revisions are null when the team has never had a policy; active_revision alone is null when every revision is archived. Without an active revision a path level that targets the team notifies every member at once.
  - `active_revision` · int64 · required
  - `latest_revision` · int64 · required
- `archived_at` · date-time · required: When the team was archived. Null for a live team.
- `created_at` · date-time · required

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "Payments approvers",
  "directory_managed": true,
  "member_count": 1,
  "assignments": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "principal_kind": "membership",
      "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "scope_kind": "organization",
      "scope_id": "string",
      "scope_name": "string",
      "source": "manual",
      "source_key": "string",
      "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_name": "string",
      "created_at": "2026-09-08T12:02:11Z",
      "permissions": [
        "string"
      ],
      "is_owner": true,
      "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    }
  ],
  "escalation_policy": {
    "active_revision": 1,
    "latest_revision": 1
  },
  "archived_at": "2026-09-08T12:02:11Z",
  "created_at": "2026-09-08T12:02:11Z"
}
```

## TeamEscalationPolicyStatus

The team's escalation policy lineage. Both revisions are null when the team has never had a policy; active_revision alone is null when every revision is archived. Without an active revision a path level that targets the team notifies every member at once.

- `active_revision` · int64 · required
- `latest_revision` · int64 · required

```json
{
  "active_revision": 1,
  "latest_revision": 1
}
```

## TeamMember

One member of a team.

- `membership_id` · uuid · required
- `display_name` · string · required
- `email` · string · required
- `status` · enum · required: The member's status in the organization. One of `active`, `suspended`, `deprovisioned`.
- `sources` · array<enum> · required: How the person got on the team: added by hand, by your directory, or both.

```json
{
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "display_name": "Ada Lovelace",
  "email": "ada@example.com",
  "status": "active",
  "sources": [
    "manual"
  ]
}
```

## Invitation

An invitation to join the organization.

- `id` · uuid · required
- `organization_id` · uuid · required
- `email` · string · required: The address the invitation was sent to.
- `grants` · array<InvitationGrant> · required: The roles the person receives when they accept.
  - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
  - `permissions` · array<string> · required
- `invited_by_membership_id` · uuid · required: The member who sent it.
- `status` · enum · required: `pending` can still be accepted. `accepted` and `revoked` are final. `expired` passed its expiry without being accepted. One of `pending`, `accepted`, `revoked`, `expired`.
- `expires_at` · date-time · required: When the link stops working. Seven days from creation or the last resend.
- `accepted_membership_id` · uuid: The membership created on acceptance. Present for `accepted` only.
- `created_at` · date-time · required
- `updated_at` · date-time · required

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "email": "ada@example.com",
  "grants": [
    {
      "scope_kind": "organization",
      "scope_id": "string",
      "permissions": [
        "string"
      ]
    }
  ],
  "invited_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "status": "pending",
  "expires_at": "2026-09-08T12:02:11Z",
  "accepted_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "updated_at": "2026-09-08T12:02:11Z"
}
```

## InvitationWithURL

An invitation and, on a deployment without email delivery, the link to send. Where email is configured the link goes only to the invitee's mailbox and this field is absent.

- `invitation` · Invitation · required: An invitation to join the organization.
  - `id` · uuid · required
  - `organization_id` · uuid · required
  - `email` · string · required: The address the invitation was sent to.
  - `grants` · array<InvitationGrant> · required: The roles the person receives when they accept.
    - `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
    - `scope_id` · string: The team id or the agent slug. Absent for `organization`.
    - `permissions` · array<string> · required
  - `invited_by_membership_id` · uuid · required: The member who sent it.
  - `status` · enum · required: `pending` can still be accepted. `accepted` and `revoked` are final. `expired` passed its expiry without being accepted. One of `pending`, `accepted`, `revoked`, `expired`.
  - `expires_at` · date-time · required: When the link stops working. Seven days from creation or the last resend.
  - `accepted_membership_id` · uuid: The membership created on acceptance. Present for `accepted` only.
  - `created_at` · date-time · required
  - `updated_at` · date-time · required
- `invite_url` · uri: The link to send to the person. It contains the invitation's secret token, appears only in this response, and only on a deployment with no email delivery configured.

```json
{
  "invitation": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "email": "ada@example.com",
    "grants": [
      {
        "scope_kind": "organization",
        "scope_id": "string",
        "permissions": [
          "string"
        ]
      }
    ],
    "invited_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "status": "pending",
    "expires_at": "2026-09-08T12:02:11Z",
    "accepted_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "created_at": "2026-09-08T12:02:11Z",
    "updated_at": "2026-09-08T12:02:11Z"
  },
  "invite_url": "https://example.com/webhooks/withhuman"
}
```

## RoleAssignment

Permissions granted at one scope, with provenance.

- `id` · uuid · required
- `organization_id` · uuid · required
- `principal_kind` · enum · required: What holds the role. One of `membership`, `team`, `agent`.
- `principal_id` · uuid · required: The membership, team or agent that holds it.
- `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
- `scope_id` · string: The team id or the agent slug. Absent for `organization`.
- `scope_name` · string: The team's name, for a team scope.
- `source` · enum · required: Whether a person granted it or your directory did. One of `manual`, `directory`.
- `source_key` · string: The directory group that produced it. Present for `directory` only.
- `granted_by_actor_id` · uuid: The member who granted it. Present for `manual` only.
- `via_team_id` · uuid: The team the role is inherited through. Present when a member holds it through a team rather than directly.
- `via_team_name` · string: That team's name.
- `created_at` · date-time · required
- `permissions` · array<string> · required
- `is_owner` · boolean · required
- `permission_policy_id` · uuid

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "principal_kind": "membership",
  "principal_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "scope_kind": "organization",
  "scope_id": "string",
  "scope_name": "string",
  "source": "manual",
  "source_key": "string",
  "granted_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "via_team_name": "string",
  "created_at": "2026-09-08T12:02:11Z",
  "permissions": [
    "string"
  ],
  "is_owner": true,
  "permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
```

## InvitationGrant

Permissions granted at one scope, with provenance.

- `scope_kind` · enum · required: Where the role applies. One of `organization`, `team`, `agent`.
- `scope_id` · string: The team id or the agent slug. Absent for `organization`.
- `permissions` · array<string> · required

```json
{
  "scope_kind": "organization",
  "scope_id": "string",
  "permissions": [
    "string"
  ]
}
```

## PermissionDescriptor

One permission a role can carry.

- `key` · string · required: The permission key, shaped `resource.action`.
- `area` · string · required: The key's first segment, for grouping.
- `description` · string · required: What the permission allows.
- `holders` · array<enum> · required: Who can hold it: people, agents, or the gateway.
- `resource` · enum · required: What kind of thing a check on this permission is about. A grant must cover that thing's scope. One of `organization`, `team`, `agent`, `request`, `scope`.
- `dangerous` · boolean · required: Whether granting or using it changes what other people can do, or cannot be undone. Such permissions need a fresh, strong sign-in to use and never arrive through directory sync or first SSO sign-in.

```json
{
  "key": "request.decide",
  "area": "request",
  "description": "Approve or deny requests routed to you",
  "holders": [
    "human"
  ],
  "resource": "organization",
  "dangerous": true
}
```

## EscalationPathSummary

One escalation path and a summary of its history.

- `path_key` · string · required
- `name` · string · required: The name from the latest revision.
- `active_revision` · int64: The revision requests are routed with. Absent while no revision is active, which is always the case for an archived path.
- `latest_revision` · int64 · required: The newest revision, active or not.
- `revision_count` · int64 · required
- `latest_created_at` · date-time · required: When the newest revision was created.
- `archived_at` · date-time: When the path was archived. Absent for a live path.

```json
{
  "path_key": "string",
  "name": "string",
  "active_revision": 1,
  "latest_revision": 1,
  "revision_count": 1,
  "latest_created_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z"
}
```

## EscalationPathRevisionSummary

One escalation path revision, without its document.

- `id` · uuid · required
- `path_key` · string · required
- `revision` · int64 · required: The revision number. Revisions count up from 1.
- `is_active` · boolean · required: Whether requests are routed with this revision.
- `name` · string · required: The display name.
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the revision.
- `created_at` · date-time · required
- `archived_at` · date-time: When the path was archived. Absent for a revision of a live path; an archived revision can no longer be activated.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "path_key": "string",
  "revision": 1,
  "is_active": true,
  "name": "On-call engineers",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z"
}
```

## EscalationPathRevision

One escalation path revision with its full document.

- `id` · uuid · required
- `path_key` · string · required
- `revision` · int64 · required: The revision number. Revisions count up from 1.
- `is_active` · boolean · required: Whether requests are routed with this revision.
- `name` · string · required: The display name.
- `created_by_actor_id` · uuid · required: The membership or organization-key actor that created the revision.
- `created_at` · date-time · required
- `archived_at` · date-time: When the path was archived. Absent for a revision of a live path; an archived revision can no longer be activated.
- `working_hours` · array<WorkingHoursSet> · required: The named working-hours sets the nodes refer to.
  - `id` · string · required: A key for the set, unique within the document.
  - `name` · string · required: A display name.
  - `timezone` · string · required: An IANA timezone name, such as `Europe/London`.
  - `intervals` · array<WeekdayInterval> · required: The open windows. At least one.
    - `weekdays` · array<enum> · required: The days the window applies to.
    - `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
    - `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.
- `nodes` · array<EscalationPathNode> · required: The nodes, in order.
  - `id` · uuid · required: An id for the node, unique within the path.
  - `type` · enum · required: The kind of node. One of `level`, `if_else`, `defer`.
  - `targets` · array<EscalationTarget>: `level` only. The people and teams to notify.
    - `type` · enum · required One of `user`, `team`, `broadcast`.
    - `id` · uuid: Required for user and team, absent for broadcast.
  - `escalate_after` · string: `level` only. How long to wait for a decision before moving to the next node. A duration from `1m` to `168h`.
  - `raise_urgency` · boolean: `level` only. Raise the request to `interrupt` urgency before notifying.
  - `conditions` · array<EscalationPathCondition>: `if_else` only. Every condition must hold for `then` to run. From 1 to 10.
    - `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
    - `working_hours_id` · string: `working_hours` only. The set to test.
    - `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
    - `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
    - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `then` · array<EscalationPathNode>: `if_else` only. The nodes to run when every condition holds. An empty list moves on to the next node.
  - `else` · array<EscalationPathNode>: `if_else` only. The nodes to run otherwise. An empty list moves on to the next node.
  - `working_hours_id` · string: `defer` only. The working-hours set to wait for.
  - `standard_only` · boolean: `defer` only. Let `interrupt` requests skip the wait.
- `repeat` · EscalationPathRepeat: Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
  - `times` · integer · required: How many extra runs, from 1 to 9.
  - `after` · string · required: How long to wait before each extra run. A duration from `1m` to `168h`.
- `document_version` · integer · required: The version of the document format.
- `warnings` · array<EscalationPathWarning> · required: Advisory findings about the document's targets. They are computed when a revision is created or activated, and the list is empty on plain reads. A warning never blocks anything: a path narrows who decides, it never grants.
  - `code` · enum · required: `team_members_cannot_decide`: `count` of the team's `member_count` members cannot decide requests, so routing to the team reaches fewer deciders than its size suggests. `path_may_reach_nobody`: some request's conditions leave no level to run, so such a request is routed to nobody and only break-glass reviewers can decide it; the team fields are absent. One of `team_members_cannot_decide`, `path_may_reach_nobody`.
  - `team_id` · uuid
  - `team_name` · string
  - `count` · integer: Members of the team who cannot decide requests.
  - `member_count` · integer: Active members of the team.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "path_key": "string",
  "revision": 1,
  "is_active": true,
  "name": "On-call engineers",
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "archived_at": "2026-09-08T12:02:11Z",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  },
  "document_version": 1,
  "warnings": [
    {
      "code": "team_members_cannot_decide",
      "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "team_name": "string",
      "count": 1,
      "member_count": 1
    }
  ]
}
```

## EscalationPathWarning

One advisory finding about the document. The team fields are present only for findings about a team target.

- `code` · enum · required: `team_members_cannot_decide`: `count` of the team's `member_count` members cannot decide requests, so routing to the team reaches fewer deciders than its size suggests. `path_may_reach_nobody`: some request's conditions leave no level to run, so such a request is routed to nobody and only break-glass reviewers can decide it; the team fields are absent. One of `team_members_cannot_decide`, `path_may_reach_nobody`.
- `team_id` · uuid
- `team_name` · string
- `count` · integer: Members of the team who cannot decide requests.
- `member_count` · integer: Active members of the team.

```json
{
  "code": "team_members_cannot_decide",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_name": "string",
  "count": 1,
  "member_count": 1
}
```

## EscalationPathDocument

The complete content of a new escalation path revision.

- `name` · string · required: A display name. Reviewers see it on the requests the path routes.
- `working_hours` · array<WorkingHoursSet> · required: Named working-hours sets that `if_else` and `defer` nodes refer to by id. May be empty.
  - `id` · string · required: A key for the set, unique within the document.
  - `name` · string · required: A display name.
  - `timezone` · string · required: An IANA timezone name, such as `Europe/London`.
  - `intervals` · array<WeekdayInterval> · required: The open windows. At least one.
    - `weekdays` · array<enum> · required: The days the window applies to.
    - `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
    - `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.
- `nodes` · array<EscalationPathNode> · required: The nodes, in order. At least one.
  - `id` · uuid · required: An id for the node, unique within the path.
  - `type` · enum · required: The kind of node. One of `level`, `if_else`, `defer`.
  - `targets` · array<EscalationTarget>: `level` only. The people and teams to notify.
    - `type` · enum · required One of `user`, `team`, `broadcast`.
    - `id` · uuid: Required for user and team, absent for broadcast.
  - `escalate_after` · string: `level` only. How long to wait for a decision before moving to the next node. A duration from `1m` to `168h`.
  - `raise_urgency` · boolean: `level` only. Raise the request to `interrupt` urgency before notifying.
  - `conditions` · array<EscalationPathCondition>: `if_else` only. Every condition must hold for `then` to run. From 1 to 10.
    - `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
    - `working_hours_id` · string: `working_hours` only. The set to test.
    - `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
    - `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
    - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `then` · array<EscalationPathNode>: `if_else` only. The nodes to run when every condition holds. An empty list moves on to the next node.
  - `else` · array<EscalationPathNode>: `if_else` only. The nodes to run otherwise. An empty list moves on to the next node.
  - `working_hours_id` · string: `defer` only. The working-hours set to wait for.
  - `standard_only` · boolean: `defer` only. Let `interrupt` requests skip the wait.
- `repeat` · EscalationPathRepeat: Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
  - `times` · integer · required: How many extra runs, from 1 to 9.
  - `after` · string · required: How long to wait before each extra run. A duration from `1m` to `168h`.

```json
{
  "name": "On-call engineers",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  }
}
```

## EscalationPathCreate

The first revision's document plus the key it creates.

- `path_key` · string · required
- `name` · string · required
- `working_hours` · array<WorkingHoursSet> · required
  - `id` · string · required: A key for the set, unique within the document.
  - `name` · string · required: A display name.
  - `timezone` · string · required: An IANA timezone name, such as `Europe/London`.
  - `intervals` · array<WeekdayInterval> · required: The open windows. At least one.
    - `weekdays` · array<enum> · required: The days the window applies to.
    - `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
    - `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.
- `nodes` · array<EscalationPathNode> · required
  - `id` · uuid · required: An id for the node, unique within the path.
  - `type` · enum · required: The kind of node. One of `level`, `if_else`, `defer`.
  - `targets` · array<EscalationTarget>: `level` only. The people and teams to notify.
    - `type` · enum · required One of `user`, `team`, `broadcast`.
    - `id` · uuid: Required for user and team, absent for broadcast.
  - `escalate_after` · string: `level` only. How long to wait for a decision before moving to the next node. A duration from `1m` to `168h`.
  - `raise_urgency` · boolean: `level` only. Raise the request to `interrupt` urgency before notifying.
  - `conditions` · array<EscalationPathCondition>: `if_else` only. Every condition must hold for `then` to run. From 1 to 10.
    - `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
    - `working_hours_id` · string: `working_hours` only. The set to test.
    - `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
    - `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
    - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `then` · array<EscalationPathNode>: `if_else` only. The nodes to run when every condition holds. An empty list moves on to the next node.
  - `else` · array<EscalationPathNode>: `if_else` only. The nodes to run otherwise. An empty list moves on to the next node.
  - `working_hours_id` · string: `defer` only. The working-hours set to wait for.
  - `standard_only` · boolean: `defer` only. Let `interrupt` requests skip the wait.
- `repeat` · EscalationPathRepeat: Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
  - `times` · integer · required: How many extra runs, from 1 to 9.
  - `after` · string · required: How long to wait before each extra run. A duration from `1m` to `168h`.

```json
{
  "path_key": "string",
  "name": "string",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  }
}
```

## WorkingHoursSet

A named set of weekly hours in one timezone.

- `id` · string · required: A key for the set, unique within the document.
- `name` · string · required: A display name.
- `timezone` · string · required: An IANA timezone name, such as `Europe/London`.
- `intervals` · array<WeekdayInterval> · required: The open windows. At least one.
  - `weekdays` · array<enum> · required: The days the window applies to.
  - `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
  - `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.

```json
{
  "id": "uk-office",
  "name": "UK office hours",
  "timezone": "Europe/London",
  "intervals": [
    {
      "weekdays": [
        "mon"
      ],
      "start": "09:00",
      "end": "18:00"
    }
  ]
}
```

## WeekdayInterval

One window that repeats every week.

- `weekdays` · array<enum> · required: The days the window applies to.
- `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
- `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.

```json
{
  "weekdays": [
    "mon"
  ],
  "start": "09:00",
  "end": "18:00"
}
```

## EscalationPathNode

One node of an escalation path. The type picks the kind of node, and only that kind's fields may be set. A level node notifies its targets and waits for a decision. If nobody decides before escalate_after runs out, the path moves to the next node. An if_else node runs then when every condition holds, otherwise else. A defer node parks the request until a working-hours window opens, notifying nobody.

- `id` · uuid · required: An id for the node, unique within the path.
- `type` · enum · required: The kind of node. One of `level`, `if_else`, `defer`.
- `targets` · array<EscalationTarget>: `level` only. The people and teams to notify.
  - `type` · enum · required One of `user`, `team`, `broadcast`.
  - `id` · uuid: Required for user and team, absent for broadcast.
- `escalate_after` · string: `level` only. How long to wait for a decision before moving to the next node. A duration from `1m` to `168h`.
- `raise_urgency` · boolean: `level` only. Raise the request to `interrupt` urgency before notifying.
- `conditions` · array<EscalationPathCondition>: `if_else` only. Every condition must hold for `then` to run. From 1 to 10.
  - `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
  - `working_hours_id` · string: `working_hours` only. The set to test.
  - `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
  - `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
  - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
- `then` · array<EscalationPathNode>: `if_else` only. The nodes to run when every condition holds. An empty list moves on to the next node.
- `else` · array<EscalationPathNode>: `if_else` only. The nodes to run otherwise. An empty list moves on to the next node.
- `working_hours_id` · string: `defer` only. The working-hours set to wait for.
- `standard_only` · boolean: `defer` only. Let `interrupt` requests skip the wait.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "type": "level",
  "targets": [
    {
      "type": "user",
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
    }
  ],
  "escalate_after": "15m",
  "raise_urgency": true,
  "conditions": [
    {
      "type": "working_hours",
      "working_hours_id": "string",
      "active": true,
      "urgency": "standard",
      "when": {
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "field": "/request/arguments/amount_cents",
        "operator": "exists",
        "value": {}
      }
    }
  ],
  "then": [
    {}
  ],
  "else": [
    {}
  ],
  "working_hours_id": "string",
  "standard_only": true
}
```

## EscalationPathCondition

One test in an if_else node. The type picks the kind of test, and only that kind's fields may be set.

- `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
- `working_hours_id` · string: `working_hours` only. The set to test.
- `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
- `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
- `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.

```json
{
  "type": "working_hours",
  "working_hours_id": "string",
  "active": true,
  "urgency": "standard",
  "when": {
    "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "field": "/request/arguments/amount_cents",
    "operator": "exists",
    "value": {}
  }
}
```

## EscalationTarget

Who a level notifies. user names a membership and team names a team; both carry an id. broadcast notifies every current member of the team that owns a team escalation policy and carries no id. An organization escalation path accepts user and team; a team escalation policy accepts user (members of that team only) and broadcast.

- `type` · enum · required One of `user`, `team`, `broadcast`.
- `id` · uuid: Required for user and team, absent for broadcast.

```json
{
  "type": "user",
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
```

## TeamEscalationPolicyDocument

The node program a team runs when a path level targets it. The same grammar as an escalation path document, without a name, whose levels name members of the team or broadcast to all of them.

- `working_hours` · array<WorkingHoursSet> · required
  - `id` · string · required: A key for the set, unique within the document.
  - `name` · string · required: A display name.
  - `timezone` · string · required: An IANA timezone name, such as `Europe/London`.
  - `intervals` · array<WeekdayInterval> · required: The open windows. At least one.
    - `weekdays` · array<enum> · required: The days the window applies to.
    - `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
    - `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.
- `nodes` · array<EscalationPathNode> · required
  - `id` · uuid · required: An id for the node, unique within the path.
  - `type` · enum · required: The kind of node. One of `level`, `if_else`, `defer`.
  - `targets` · array<EscalationTarget>: `level` only. The people and teams to notify.
    - `type` · enum · required One of `user`, `team`, `broadcast`.
    - `id` · uuid: Required for user and team, absent for broadcast.
  - `escalate_after` · string: `level` only. How long to wait for a decision before moving to the next node. A duration from `1m` to `168h`.
  - `raise_urgency` · boolean: `level` only. Raise the request to `interrupt` urgency before notifying.
  - `conditions` · array<EscalationPathCondition>: `if_else` only. Every condition must hold for `then` to run. From 1 to 10.
    - `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
    - `working_hours_id` · string: `working_hours` only. The set to test.
    - `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
    - `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
    - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `then` · array<EscalationPathNode>: `if_else` only. The nodes to run when every condition holds. An empty list moves on to the next node.
  - `else` · array<EscalationPathNode>: `if_else` only. The nodes to run otherwise. An empty list moves on to the next node.
  - `working_hours_id` · string: `defer` only. The working-hours set to wait for.
  - `standard_only` · boolean: `defer` only. Let `interrupt` requests skip the wait.
- `repeat` · EscalationPathRepeat: Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
  - `times` · integer · required: How many extra runs, from 1 to 9.
  - `after` · string · required: How long to wait before each extra run. A duration from `1m` to `168h`.

```json
{
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  }
}
```

## TeamEscalationPolicyRevisionSummary

- `id` · uuid · required
- `team_id` · uuid · required
- `revision` · int64 · required
- `is_active` · boolean · required
- `created_by_actor_id` · uuid · required
- `created_at` · date-time · required

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "is_active": true,
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z"
}
```

## TeamEscalationPolicyRevision

- `id` · uuid · required
- `team_id` · uuid · required
- `revision` · int64 · required
- `is_active` · boolean · required
- `created_by_actor_id` · uuid · required
- `created_at` · date-time · required
- `working_hours` · array<WorkingHoursSet> · required
  - `id` · string · required: A key for the set, unique within the document.
  - `name` · string · required: A display name.
  - `timezone` · string · required: An IANA timezone name, such as `Europe/London`.
  - `intervals` · array<WeekdayInterval> · required: The open windows. At least one.
    - `weekdays` · array<enum> · required: The days the window applies to.
    - `start` · string · required: When the window opens, as `HH:MM` in 24-hour time.
    - `end` · string · required: When the window closes, as `HH:MM` in 24-hour time.
- `nodes` · array<EscalationPathNode> · required
  - `id` · uuid · required: An id for the node, unique within the path.
  - `type` · enum · required: The kind of node. One of `level`, `if_else`, `defer`.
  - `targets` · array<EscalationTarget>: `level` only. The people and teams to notify.
    - `type` · enum · required One of `user`, `team`, `broadcast`.
    - `id` · uuid: Required for user and team, absent for broadcast.
  - `escalate_after` · string: `level` only. How long to wait for a decision before moving to the next node. A duration from `1m` to `168h`.
  - `raise_urgency` · boolean: `level` only. Raise the request to `interrupt` urgency before notifying.
  - `conditions` · array<EscalationPathCondition>: `if_else` only. Every condition must hold for `then` to run. From 1 to 10.
    - `type` · enum · required: The kind of test. `working_hours` tests whether a named window is open. `urgency` tests the request's current urgency. `request` tests the request itself. One of `working_hours`, `urgency`, `request`.
    - `working_hours_id` · string: `working_hours` only. The set to test.
    - `active` · boolean: `working_hours` only. `true` tests that the window is open, `false` that it is closed.
    - `urgency` · enum: `urgency` only. The urgency the request must have. One of `standard`, `interrupt`.
    - `when` · CELLeafCondition | CELAllCondition | CELAnyCondition | CELSomeCondition | CELEveryCondition: A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
  - `then` · array<EscalationPathNode>: `if_else` only. The nodes to run when every condition holds. An empty list moves on to the next node.
  - `else` · array<EscalationPathNode>: `if_else` only. The nodes to run otherwise. An empty list moves on to the next node.
  - `working_hours_id` · string: `defer` only. The working-hours set to wait for.
  - `standard_only` · boolean: `defer` only. Let `interrupt` requests skip the wait.
- `repeat` · EscalationPathRepeat: Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
  - `times` · integer · required: How many extra runs, from 1 to 9.
  - `after` · string · required: How long to wait before each extra run. A duration from `1m` to `168h`.
- `document_version` · integer · required
- `warnings` · array<TeamEscalationPolicyWarning> · required: Advisory findings about the document against the team's roster and grants. Never a refusal: the policy narrows who is asked within the team, it never grants. Creation and activation report members who cannot decide; reads of the active revision report that and every target who left the team; reads of a historical revision report departed targets only.
  - `code` · enum · required: team_members_cannot_decide means count of the team's member_count members hold no request.decide grant. target_not_member means the user target in membership_id is no longer an active member of the team, so the level naming them skips them. One of `team_members_cannot_decide`, `target_not_member`.
  - `membership_id` · uuid: The departed target. Present for target_not_member only.
  - `display_name` · string: The departed target's name, while the roster still lists them under another status.
  - `count` · integer · required: Members of the team who cannot decide requests; 0 for target_not_member.
  - `member_count` · integer · required: Active members of the team; 0 for target_not_member.

```json
{
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "is_active": true,
  "created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-08T12:02:11Z",
  "working_hours": [
    {
      "id": "uk-office",
      "name": "UK office hours",
      "timezone": "Europe/London",
      "intervals": [
        {
          "weekdays": [
            "mon"
          ],
          "start": "09:00",
          "end": "18:00"
        }
      ]
    }
  ],
  "nodes": [
    {
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "type": "level",
      "targets": [
        {
          "type": "user",
          "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
        }
      ],
      "escalate_after": "15m",
      "raise_urgency": true,
      "conditions": [
        {
          "type": "working_hours",
          "working_hours_id": "string",
          "active": true,
          "urgency": "standard",
          "when": {
            "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
            "field": "/request/arguments/amount_cents",
            "operator": "exists",
            "value": {}
          }
        }
      ],
      "then": [
        {}
      ],
      "else": [
        {}
      ],
      "working_hours_id": "string",
      "standard_only": true
    }
  ],
  "repeat": {
    "times": 1,
    "after": "1h"
  },
  "document_version": 1,
  "warnings": [
    {
      "code": "team_members_cannot_decide",
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "count": 1,
      "member_count": 1
    }
  ]
}
```

## TeamEscalationPolicyWarning

- `code` · enum · required: team_members_cannot_decide means count of the team's member_count members hold no request.decide grant. target_not_member means the user target in membership_id is no longer an active member of the team, so the level naming them skips them. One of `team_members_cannot_decide`, `target_not_member`.
- `membership_id` · uuid: The departed target. Present for target_not_member only.
- `display_name` · string: The departed target's name, while the roster still lists them under another status.
- `count` · integer · required: Members of the team who cannot decide requests; 0 for target_not_member.
- `member_count` · integer · required: Active members of the team; 0 for target_not_member.

```json
{
  "code": "team_members_cannot_decide",
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "display_name": "string",
  "count": 1,
  "member_count": 1
}
```

## PinnedTeamPolicy

One team escalation policy revision an escalated request runs inline when its pinned path reaches the team.

- `team_id` · uuid · required
- `team_name` · string · required
- `policy_revision_id` · uuid · required
- `revision` · int64 · required

```json
{
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "team_name": "string",
  "policy_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1
}
```

## EscalationWalkerProgress

One team policy walker's position while its policy runs inline.

- `team_id` · uuid · required
- `phase` · enum · required: Where a walker stands. `level`: a level is notified and its timer armed. `deferred`: waiting for a working hours window to open. `repeat_wait`: the program ran out and is waiting to repeat. `paused`: a claim holds escalation still. `exhausted`: nothing left to do. One of `level`, `deferred`, `repeat_wait`, `paused`, `exhausted`.
- `current_node_id` · uuid: The node the walker stands on; absent once exhausted.
- `level_ordinal` · integer · required: 1-based position of the current level in walk order; 0 before the first level.
- `level_count` · integer · required: Level nodes in the document.
- `repeat` · integer · required: How many times the program has been repeated so far.
- `next_escalation_at` · date-time: When the armed timer fires; absent while paused, exhausted, or waiting on nothing.

```json
{
  "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "phase": "level",
  "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "level_ordinal": 1,
  "level_count": 1,
  "repeat": 1,
  "next_escalation_at": "2026-09-08T12:02:11Z"
}
```

## EscalationProgress

Where the escalation machine stands after its latest tick. The organization path's walker is flattened onto the object; teams carries one entry per team policy running inline.

- `sequence` · int64 · required: Monotonic tick counter; a later tick always carries a higher value.
- `phase` · enum · required: Where a walker stands. `level`: a level is notified and its timer armed. `deferred`: waiting for a working hours window to open. `repeat_wait`: the program ran out and is waiting to repeat. `paused`: a claim holds escalation still. `exhausted`: nothing left to do. One of `level`, `deferred`, `repeat_wait`, `paused`, `exhausted`.
- `urgency` · enum · required: The request's current urgency, raised by a level that asked to. One of `standard`, `interrupt`.
- `current_node_id` · uuid: The path node the walker stands on; absent once exhausted.
- `level_ordinal` · integer · required: 1-based position of the current level in walk order; 0 before the first level.
- `level_count` · integer · required: Level nodes in the path document.
- `repeat` · integer · required: How many times the path has been repeated so far.
- `next_escalation_at` · date-time: When the path walker's armed timer fires; absent while paused, exhausted, or waiting on nothing.
- `updated_at` · date-time · required
- `teams` · array<EscalationWalkerProgress> · required
  - `team_id` · uuid · required
  - `phase` · enum · required: Where a walker stands. `level`: a level is notified and its timer armed. `deferred`: waiting for a working hours window to open. `repeat_wait`: the program ran out and is waiting to repeat. `paused`: a claim holds escalation still. `exhausted`: nothing left to do. One of `level`, `deferred`, `repeat_wait`, `paused`, `exhausted`.
  - `current_node_id` · uuid: The node the walker stands on; absent once exhausted.
  - `level_ordinal` · integer · required: 1-based position of the current level in walk order; 0 before the first level.
  - `level_count` · integer · required: Level nodes in the document.
  - `repeat` · integer · required: How many times the program has been repeated so far.
  - `next_escalation_at` · date-time: When the armed timer fires; absent while paused, exhausted, or waiting on nothing.

```json
{
  "sequence": 1,
  "phase": "level",
  "urgency": "standard",
  "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "level_ordinal": 1,
  "level_count": 1,
  "repeat": 1,
  "next_escalation_at": "2026-09-08T12:02:11Z",
  "updated_at": "2026-09-08T12:02:11Z",
  "teams": [
    {
      "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "phase": "level",
      "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "level_ordinal": 1,
      "level_count": 1,
      "repeat": 1,
      "next_escalation_at": "2026-09-08T12:02:11Z"
    }
  ]
}
```

## EscalationPathRepeat

Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.

- `times` · integer · required: How many extra runs, from 1 to 9.
- `after` · string · required: How long to wait before each extra run. A duration from `1m` to `168h`.

```json
{
  "times": 1,
  "after": "1h"
}
```

## ApprovalRouting

Who was asked to review a request when it entered human review. escalated pins an escalation path revision and the people it reaches for this request; default_queue means any authorized reviewer.

- `outcome` · enum · required: `escalated` when an escalation path took the request. `default_queue` when no path applied and every reviewer who can decide was asked. One of `escalated`, `default_queue`.
- `reason` · enum · required: How the outcome came about. `block_escalation`: the pipeline block that asked for a person named the path. `pipeline_default`: the pipeline's default path took it. `no_escalation`: the pipeline named no path. `path_inactive`: the named path had no active revision. `evaluation_error`: the path could not be resolved. The last three mean no path applied. One of `block_escalation`, `pipeline_default`, `no_escalation`, `path_inactive`, `evaluation_error`.
- `urgency` · enum · required: The urgency the request was routed with. Always `standard` today. One of `standard`, `interrupt`.
- `escalation_path_key` · string: The path that took the request. Present for `escalated` only.
- `escalation_path_name` · string: The name of the path revision that took the request. Present for `escalated` only.
- `escalation_path_revision_id` · uuid: The path revision that was active at the time. Present for `escalated` only.
- `pipeline_revision_id` · uuid: The pipeline revision that routed the request. Present for `escalated` only.
- `block_id` · uuid: The pipeline block that named the path. Present for `block_escalation` only.
- `targets` · array<ApprovalRoutingTarget> · required: The people and teams the pinned path reaches for this request, in path order: the levels whose request conditions hold for it, plus both branches of any working-hours or urgency test, at every level regardless of timing. Only these people, and `added_reviewers`, may claim or decide. Empty for `default_queue`. Empty for `escalated` means the path reaches nobody for this request and only a break-glass reviewer may decide it.
  - `type` · enum · required: `user` names a membership, `team` a team. One of `user`, `team`.
  - `id` · uuid · required
  - `name` · string · required: The person's display name or the team's name.
- `added_reviewers` · array<ApprovalReviewer> · required: People added to the request's routing after the snapshot was pinned, oldest first.
  - `membership_id` · uuid · required
  - `display_name` · string · required
  - `added_by_membership_id` · uuid · required
  - `added_by_display_name` · string · required
  - `created_at` · date-time · required
- `team_policies` · array<PinnedTeamPolicy> · required: The team escalation policy revisions pinned beside the path, one per team the path reaches for this request that had an active policy at routing time. A team absent here is notified all at once when a level reaches it. Empty for `default_queue`.
  - `team_id` · uuid · required
  - `team_name` · string · required
  - `policy_revision_id` · uuid · required
  - `revision` · int64 · required
- `escalation` · EscalationProgress | null · required: The escalation executor's current position. Null before its first tick and for `default_queue`.
- `created_at` · date-time · required: When the request entered human review.

```json
{
  "outcome": "escalated",
  "reason": "block_escalation",
  "urgency": "standard",
  "escalation_path_key": "oncall",
  "escalation_path_name": "On-call engineers",
  "escalation_path_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "pipeline_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "block_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "targets": [
    {
      "type": "user",
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "name": "string"
    }
  ],
  "added_reviewers": [
    {
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "added_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "added_by_display_name": "string",
      "created_at": "2026-09-08T12:02:11Z"
    }
  ],
  "team_policies": [
    {
      "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "team_name": "string",
      "policy_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "revision": 1
    }
  ],
  "escalation": {
    "sequence": 1,
    "phase": "level",
    "urgency": "standard",
    "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "level_ordinal": 1,
    "level_count": 1,
    "repeat": 1,
    "next_escalation_at": "2026-09-08T12:02:11Z",
    "updated_at": "2026-09-08T12:02:11Z",
    "teams": [
      {
        "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "phase": "level",
        "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "level_ordinal": 1,
        "level_count": 1,
        "repeat": 1,
        "next_escalation_at": "2026-09-08T12:02:11Z"
      }
    ]
  },
  "created_at": "2026-09-08T12:00:00Z"
}
```

## ApprovalRoutingTarget

One person or team the pinned path reaches for the request.

- `type` · enum · required: `user` names a membership, `team` a team. One of `user`, `team`.
- `id` · uuid · required
- `name` · string · required: The person's display name or the team's name.

```json
{
  "type": "user",
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "string"
}
```

## ApprovalReviewer

A person pulled into a pending request's routing after the fact by someone it was routed to.

- `membership_id` · uuid · required
- `display_name` · string · required
- `added_by_membership_id` · uuid · required
- `added_by_display_name` · string · required
- `created_at` · date-time · required

```json
{
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "display_name": "string",
  "added_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "added_by_display_name": "string",
  "created_at": "2026-09-08T12:02:11Z"
}
```

## ReviewViewer

The reading person's own standing on the request. targeted says whether the pinned path reaches them for this request (directly, through a team, or by a later widening). decidable says whether a decision from them would pass the routing rule outright (yes), only with the outside_routing acknowledgement and request.decide.unrouted (break_glass), or not at all (no). reason names the rule in the way; it is absent when decidable is yes or the request is no longer pending. The decision itself still checks membership status, self-approval, and authentication freshness.

- `targeted` · boolean · required
- `decidable` · enum · required One of `yes`, `break_glass`, `no`.
- `reason` · enum One of `not_targeted`, `break_glass_only`, `no_permission`.

```json
{
  "targeted": true,
  "decidable": "yes",
  "reason": "not_targeted"
}
```

## ApprovalReview

The request's routing and the reader's standing on it. routing is absent for requests the pipeline decided automatically.

- `routing` · ApprovalRouting: Who was asked to review a request when it entered human review. escalated pins an escalation path revision and the people it reaches for this request; default_queue means any authorized reviewer.
  - `outcome` · enum · required: `escalated` when an escalation path took the request. `default_queue` when no path applied and every reviewer who can decide was asked. One of `escalated`, `default_queue`.
  - `reason` · enum · required: How the outcome came about. `block_escalation`: the pipeline block that asked for a person named the path. `pipeline_default`: the pipeline's default path took it. `no_escalation`: the pipeline named no path. `path_inactive`: the named path had no active revision. `evaluation_error`: the path could not be resolved. The last three mean no path applied. One of `block_escalation`, `pipeline_default`, `no_escalation`, `path_inactive`, `evaluation_error`.
  - `urgency` · enum · required: The urgency the request was routed with. Always `standard` today. One of `standard`, `interrupt`.
  - `escalation_path_key` · string: The path that took the request. Present for `escalated` only.
  - `escalation_path_name` · string: The name of the path revision that took the request. Present for `escalated` only.
  - `escalation_path_revision_id` · uuid: The path revision that was active at the time. Present for `escalated` only.
  - `pipeline_revision_id` · uuid: The pipeline revision that routed the request. Present for `escalated` only.
  - `block_id` · uuid: The pipeline block that named the path. Present for `block_escalation` only.
  - `targets` · array<ApprovalRoutingTarget> · required: The people and teams the pinned path reaches for this request, in path order: the levels whose request conditions hold for it, plus both branches of any working-hours or urgency test, at every level regardless of timing. Only these people, and `added_reviewers`, may claim or decide. Empty for `default_queue`. Empty for `escalated` means the path reaches nobody for this request and only a break-glass reviewer may decide it.
    - `type` · enum · required: `user` names a membership, `team` a team. One of `user`, `team`.
    - `id` · uuid · required
    - `name` · string · required: The person's display name or the team's name.
  - `added_reviewers` · array<ApprovalReviewer> · required: People added to the request's routing after the snapshot was pinned, oldest first.
    - `membership_id` · uuid · required
    - `display_name` · string · required
    - `added_by_membership_id` · uuid · required
    - `added_by_display_name` · string · required
    - `created_at` · date-time · required
  - `team_policies` · array<PinnedTeamPolicy> · required: The team escalation policy revisions pinned beside the path, one per team the path reaches for this request that had an active policy at routing time. A team absent here is notified all at once when a level reaches it. Empty for `default_queue`.
    - `team_id` · uuid · required
    - `team_name` · string · required
    - `policy_revision_id` · uuid · required
    - `revision` · int64 · required
  - `escalation` · EscalationProgress | null · required: The escalation executor's current position. Null before its first tick and for `default_queue`.
  - `created_at` · date-time · required: When the request entered human review.
- `added_reviewers` · array<ApprovalReviewer> · required: People added to the request's routing after it was pinned, oldest first. The same list the routing carries.
  - `membership_id` · uuid · required
  - `display_name` · string · required
  - `added_by_membership_id` · uuid · required
  - `added_by_display_name` · string · required
  - `created_at` · date-time · required
- `viewer` · ReviewViewer · required: The reading person's own standing on the request. targeted says whether the pinned path reaches them for this request (directly, through a team, or by a later widening). decidable says whether a decision from them would pass the routing rule outright (yes), only with the outside_routing acknowledgement and request.decide.unrouted (break_glass), or not at all (no). reason names the rule in the way; it is absent when decidable is yes or the request is no longer pending. The decision itself still checks membership status, self-approval, and authentication freshness.
  - `targeted` · boolean · required
  - `decidable` · enum · required One of `yes`, `break_glass`, `no`.
  - `reason` · enum One of `not_targeted`, `break_glass_only`, `no_permission`.

```json
{
  "routing": {
    "outcome": "escalated",
    "reason": "block_escalation",
    "urgency": "standard",
    "escalation_path_key": "oncall",
    "escalation_path_name": "On-call engineers",
    "escalation_path_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "pipeline_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "block_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "targets": [
      {
        "type": "user",
        "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "name": "string"
      }
    ],
    "added_reviewers": [
      {
        "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "display_name": "string",
        "added_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "added_by_display_name": "string",
        "created_at": "2026-09-08T12:02:11Z"
      }
    ],
    "team_policies": [
      {
        "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "team_name": "string",
        "policy_revision_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
        "revision": 1
      }
    ],
    "escalation": {
      "sequence": 1,
      "phase": "level",
      "urgency": "standard",
      "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "level_ordinal": 1,
      "level_count": 1,
      "repeat": 1,
      "next_escalation_at": "2026-09-08T12:02:11Z",
      "updated_at": "2026-09-08T12:02:11Z",
      "teams": [
        {
          "team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "phase": "level",
          "current_node_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
          "level_ordinal": 1,
          "level_count": 1,
          "repeat": 1,
          "next_escalation_at": "2026-09-08T12:02:11Z"
        }
      ]
    },
    "created_at": "2026-09-08T12:00:00Z"
  },
  "added_reviewers": [
    {
      "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "display_name": "string",
      "added_by_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "added_by_display_name": "string",
      "created_at": "2026-09-08T12:02:11Z"
    }
  ],
  "viewer": {
    "targeted": true,
    "decidable": "yes",
    "reason": "not_targeted"
  }
}
```

## PermissionPolicySpec

- `scope_kind` · enum · required One of `organization`, `team`, `agent`.
- `resource_ids` · array<string> · required: Empty for organization scope; otherwise the selected team IDs or agent slugs.
- `permissions` · array<string> · required

```json
{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ]
}
```

## PermissionPolicyInput

- `scope_kind` · enum · required One of `organization`, `team`, `agent`.
- `resource_ids` · array<string> · required: Empty for organization scope; otherwise the selected team IDs or agent slugs.
- `permissions` · array<string> · required
- `revision` · int64 · required: Required when updating an existing policy.

```json
{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ],
  "revision": 1
}
```

## PermissionResource

- `id` · string · required
- `name` · string · required
- `kind` · enum · required One of `organization`, `team`, `agent`.

```json
{
  "id": "string",
  "name": "string",
  "kind": "organization"
}
```

## PermissionPolicy

- `scope_kind` · enum · required One of `organization`, `team`, `agent`.
- `resource_ids` · array<string> · required: Empty for organization scope; otherwise the selected team IDs or agent slugs.
- `permissions` · array<string> · required
- `id` · uuid · required
- `revision` · int64 · required
- `resources` · array<PermissionResource> · required
  - `id` · string · required
  - `name` · string · required
  - `kind` · enum · required One of `organization`, `team`, `agent`.
- `source` · enum · required One of `manual`, `directory`.
- `via_team_id` · uuid
- `via_team_name` · string
- `can_edit` · boolean · required
- `read_only_reason` · string

```json
{
  "scope_kind": "organization",
  "resource_ids": [
    "string"
  ],
  "permissions": [
    "string"
  ],
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "revision": 1,
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "source": "manual",
  "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "via_team_name": "string",
  "can_edit": true,
  "read_only_reason": "string"
}
```

## PermissionPoliciesPage

- `name` · string · required
- `policies` · array<PermissionPolicy> · required
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `resource_ids` · array<string> · required: Empty for organization scope; otherwise the selected team IDs or agent slugs.
  - `permissions` · array<string> · required
  - `id` · uuid · required
  - `revision` · int64 · required
  - `resources` · array<PermissionResource> · required
    - `id` · string · required
    - `name` · string · required
    - `kind` · enum · required One of `organization`, `team`, `agent`.
  - `source` · enum · required One of `manual`, `directory`.
  - `via_team_id` · uuid
  - `via_team_name` · string
  - `can_edit` · boolean · required
  - `read_only_reason` · string
- `resources` · array<PermissionResource> · required
  - `id` · string · required
  - `name` · string · required
  - `kind` · enum · required One of `organization`, `team`, `agent`.
- `permissions` · array<PermissionDescriptor> · required
  - `key` · string · required: The permission key, shaped `resource.action`.
  - `area` · string · required: The key's first segment, for grouping.
  - `description` · string · required: What the permission allows.
  - `holders` · array<enum> · required: Who can hold it: people, agents, or the gateway.
  - `resource` · enum · required: What kind of thing a check on this permission is about. A grant must cover that thing's scope. One of `organization`, `team`, `agent`, `request`, `scope`.
  - `dangerous` · boolean · required: Whether granting or using it changes what other people can do, or cannot be undone. Such permissions need a fresh, strong sign-in to use and never arrive through directory sync or first SSO sign-in.
- `can_create` · boolean · required
- `is_owner` · boolean · required
- `read_only_reason` · string

```json
{
  "name": "string",
  "policies": [
    {
      "scope_kind": "organization",
      "resource_ids": [
        "string"
      ],
      "permissions": [
        "string"
      ],
      "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "revision": 1,
      "resources": [
        {
          "id": "string",
          "name": "string",
          "kind": "organization"
        }
      ],
      "source": "manual",
      "via_team_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
      "via_team_name": "string",
      "can_edit": true,
      "read_only_reason": "string"
    }
  ],
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "permissions": [
    {
      "key": "request.decide",
      "area": "request",
      "description": "Approve or deny requests routed to you",
      "holders": [
        "human"
      ],
      "resource": "organization",
      "dangerous": true
    }
  ],
  "can_create": true,
  "is_owner": true,
  "read_only_reason": "string"
}
```

## OrganizationAPIKeyCreated

An API key without its secret. Personal prefixes start with `whk_`; organization prefixes start with `who_`.

- `secret_available` · boolean · required
- `token` · string: Returned only in the first creation response.
- `grants` · array<Grant> · required: Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
  - `permission` · string · required: A registry key such as request.decide
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
- `effective_grants` · array<Grant>: On detail responses, currently allowed organization-key grants.
  - `permission` · string · required: A registry key such as request.decide
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `scope_id` · string: The team id or agent slug the grant applies to; absent at organization scope
- `usable` · boolean · required: Whether the key is active and not blocked by organization policy or status.
- `blocked_reason` · enum One of `organization_inactive`, `api_keys_disabled`.
- `id` · uuid · required
- `prefix` · string · required
- `kind` · enum · required: Personal keys act as a member; organization keys act as themselves. One of `personal`, `organization`.
- `membership_id` · uuid: The member a personal key acts as.
- `created_by` · object: Who minted it. Present on the organization-wide list.
  - `membership_id` · uuid · required
  - `display_name` · string · required
  - `email` · string · required
- `name` · string · required
- `permissions` · array<string> · required: The narrowing list, or `null` when the key inherits everything its member holds. The key's effective permissions are always this list intersected with the member's current grants.
- `assurance_at_issue` · string · required: The assurance of the session that minted the key (`strong`, `single_factor`), presented as the key's own when it decides.
- `status` · enum · required One of `active`, `expired`, `revoked`.
- `created_at` · date-time · required
- `expires_at` · date-time · required: `null` when the key lives until revoked.
- `last_used_at` · date-time · required
- `revoked_at` · date-time · required
- `revocation_reason` · enum One of `manual`, `membership_deprovisioned`.

```json
{
  "secret_available": true,
  "token": "string",
  "grants": [
    {
      "permission": "request.decide",
      "scope_kind": "organization",
      "scope_id": "string"
    }
  ],
  "effective_grants": [
    {
      "permission": "request.decide",
      "scope_kind": "organization",
      "scope_id": "string"
    }
  ],
  "usable": true,
  "blocked_reason": "organization_inactive",
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "prefix": "whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31",
  "kind": "personal",
  "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_by": {
    "membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
    "display_name": "string",
    "email": "string"
  },
  "name": "string",
  "permissions": [
    "string"
  ],
  "assurance_at_issue": "string",
  "status": "active",
  "created_at": "2026-09-08T12:02:11Z",
  "expires_at": "2026-09-08T12:02:11Z",
  "last_used_at": "2026-09-08T12:02:11Z",
  "revoked_at": "2026-09-08T12:02:11Z",
  "revocation_reason": "manual"
}
```

## CreateOrganizationAPIKeyRequest

- `name` · string · required
- `permission_policies` · array<PermissionPolicySpec> · required: Policies assigned to the key through RBAC. Every permission must allow organization keys and be held by the issuer at each selected resource or wider.
  - `scope_kind` · enum · required One of `organization`, `team`, `agent`.
  - `resource_ids` · array<string> · required: Empty for organization scope; otherwise the selected team IDs or agent slugs.
  - `permissions` · array<string> · required
- `expires_at` · date-time

```json
{
  "name": "string",
  "permission_policies": [
    {
      "scope_kind": "organization",
      "resource_ids": [
        "string"
      ],
      "permissions": [
        "string"
      ]
    }
  ],
  "expires_at": "2026-09-08T12:02:11Z"
}
```

## OrganizationAPIKeyOptions

- `organization_api_keys_allowed` · boolean · required
- `resources` · array<PermissionResource> · required
  - `id` · string · required
  - `name` · string · required
  - `kind` · enum · required One of `organization`, `team`, `agent`.
- `permissions` · array<PermissionDescriptor> · required
  - `key` · string · required: The permission key, shaped `resource.action`.
  - `area` · string · required: The key's first segment, for grouping.
  - `description` · string · required: What the permission allows.
  - `holders` · array<enum> · required: Who can hold it: people, agents, or the gateway.
  - `resource` · enum · required: What kind of thing a check on this permission is about. A grant must cover that thing's scope. One of `organization`, `team`, `agent`, `request`, `scope`.
  - `dangerous` · boolean · required: Whether granting or using it changes what other people can do, or cannot be undone. Such permissions need a fresh, strong sign-in to use and never arrive through directory sync or first SSO sign-in.
- `max_ttl` · string · required: Maximum lifetime as a duration. 0s means no cap.

```json
{
  "organization_api_keys_allowed": true,
  "resources": [
    {
      "id": "string",
      "name": "string",
      "kind": "organization"
    }
  ],
  "permissions": [
    {
      "key": "request.decide",
      "area": "request",
      "description": "Approve or deny requests routed to you",
      "holders": [
        "human"
      ],
      "resource": "organization",
      "dangerous": true
    }
  ],
  "max_ttl": "string"
}
```

## AgentToolAccessInput

- `mode` · enum · required One of `none`, `selected`, `all`.
- `tools` · array<string> · required: Exact downstream tool names, meaningful only in selected mode. Empty for none or all; no wildcard matching.
- `revision` · int64 · required: Expected current version; zero for an unconfigured agent/server pair.

```json
{
  "mode": "none",
  "tools": [
    "string"
  ],
  "revision": 0
}
```

## AgentToolAccess

- `server_id` · uuid · required
- `server_slug` · string · required
- `mode` · enum · required One of `none`, `selected`, `all`.
- `tools` · array<string> · required
- `revision` · int64 · required

```json
{
  "server_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "server_slug": "string",
  "mode": "none",
  "tools": [
    "string"
  ],
  "revision": 1
}
```

## AuditEvent

One entry in the audit log. Entries are never changed or deleted.

- `sequence` · int64 · required: The event's position in the organization's log. Later events have higher numbers.
- `id` · uuid · required
- `organization_id` · uuid · required
- `event_type` · string · required: What happened, as a dotted key such as `membership.suspended`.
- `actor_type` · string · required: Who did it: `human`, `agent`, `system`, or `api_key`.
- `actor_id` · uuid: The person, agent, or key that acted. Absent for `system`.
- `actor_display_name` · string: The actor's name at the time.
- `subject_type` · string · required: What kind of thing the event is about, such as a request or a membership.
- `subject_id` · uuid · required: The thing the event is about.
- `data` · any · required: Details specific to the event type, as JSON.
- `occurred_at` · date-time · required: When it happened.

```json
{
  "sequence": 1,
  "id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "event_type": "string",
  "actor_type": "string",
  "actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "actor_display_name": "string",
  "subject_type": "string",
  "subject_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "data": {},
  "occurred_at": "2026-09-08T12:02:11Z"
}
```

## RequestTimeline

The request's story from its audit events. pipeline is absent while a suspended pipeline is still waiting on a judge.

- `pipeline` · PipelineReview: The completed evaluation as a reviewer reads it. The terminal block is the one whose outcome ended the pipeline; its fields are absent when the pipeline fell through its end or never ran.
  - `outcome` · enum · required One of `approve`, `deny`, `human`.
  - `reason_code` · string · required: Why the pipeline stopped, for example block_outcome, block_timeout, retries_exhausted, end_of_branch (the request entered a branch and none of its blocks decided), no_active_revision.
  - `blocks_evaluated` · integer · required
  - `terminal_block_key` · string
  - `terminal_block_name` · string
  - `terminal_block_type` · enum One of `always`, `cel`, `llm_judge`, `webhook`, `branch`.
  - `terminal_block_reason` · string: The sentence the pipeline author configured on the terminal block, written for the reviewer.
  - `endpoint_reason` · string: A webhook endpoint's own explanation of its answer, when the terminal block was a webhook that gave one.
  - `terminal_block_error_code` · string: Present when the terminal block failed instead of answering, for example `evaluation_error` or `timeout`. A failed block always sends the request to a human, so a reviewer should not read it as a match.
  - `terminal_pipeline` · object: The section of the effective pipeline that held the terminal block, at the revision that ran. Enough to read that revision and find the block in it.
    - `scope` · enum · required One of `organization`, `agent`.
    - `agent_slug` · string: Present when the scope is `agent`.
    - `revision` · int64 · required
  - `completed_at` · date-time · required
- `assessments` · array<JudgeAssessment> · required
  - `block_key` · string · required
  - `block_name` · string
  - `verdict` · enum · required One of `approve`, `deny`, `escalate`.
  - `confidence` · enum · required One of `low`, `medium`, `high`.
  - `rationale` · string · required
  - `model_label` · string: The judge model's name when it judged the request.
  - `provider` · string
  - `model` · string
  - `downgraded_for_confidence` · boolean · required: True when the confidence fell below the block's threshold and the verdict was treated as escalate.
  - `evaluated_at` · date-time · required
- `claims` · array<ClaimEvent> · required
  - `kind` · enum · required One of `claimed`, `released`, `lapsed`.
  - `actor_display_name` · string
  - `occurred_at` · date-time · required
- `escalations` · array<EscalationEvent> · required: What the escalation executor did for the request, oldest first. Empty for a default queue request.
  - `kind` · enum · required One of `level_entered`, `urgency_raised`, `deferred`, `repeated`, `exhausted`.
  - `team_id` · string
  - `node_id` · string
  - `level_ordinal` · integer: The level's position within its own document, one-based (level_entered only).
  - `level_count` · integer: How many levels the document has (level_entered only).
  - `urgency` · enum: The urgency the level was entered at, or the urgency raised to. One of `standard`, `interrupt`.
  - `until` · date-time: When a deferred walker resumes (deferred only).
  - `iteration` · integer: Which repeat of the document is starting (repeated only).
  - `occurred_at` · date-time · required
- `audit_event_count` · integer · required: How many audit events the request has in total, for the link into the audit log.

```json
{
  "pipeline": {
    "outcome": "approve",
    "reason_code": "string",
    "blocks_evaluated": 1,
    "terminal_block_key": "string",
    "terminal_block_name": "string",
    "terminal_block_type": "always",
    "terminal_block_reason": "string",
    "endpoint_reason": "string",
    "terminal_block_error_code": "string",
    "terminal_pipeline": {
      "scope": "organization",
      "agent_slug": "string",
      "revision": 1
    },
    "completed_at": "2026-09-08T12:02:11Z"
  },
  "assessments": [
    {
      "block_key": "string",
      "block_name": "string",
      "verdict": "approve",
      "confidence": "low",
      "rationale": "string",
      "model_label": "string",
      "provider": "string",
      "model": "string",
      "downgraded_for_confidence": true,
      "evaluated_at": "2026-09-08T12:02:11Z"
    }
  ],
  "claims": [
    {
      "kind": "claimed",
      "actor_display_name": "string",
      "occurred_at": "2026-09-08T12:02:11Z"
    }
  ],
  "escalations": [
    {
      "kind": "level_entered",
      "team_id": "string",
      "node_id": "string",
      "level_ordinal": 1,
      "level_count": 1,
      "urgency": "standard",
      "until": "2026-09-08T12:02:11Z",
      "iteration": 1,
      "occurred_at": "2026-09-08T12:02:11Z"
    }
  ],
  "audit_event_count": 1
}
```

## PipelineReview

The completed evaluation as a reviewer reads it. The terminal block is the one whose outcome ended the pipeline; its fields are absent when the pipeline fell through its end or never ran.

- `outcome` · enum · required One of `approve`, `deny`, `human`.
- `reason_code` · string · required: Why the pipeline stopped, for example block_outcome, block_timeout, retries_exhausted, end_of_branch (the request entered a branch and none of its blocks decided), no_active_revision.
- `blocks_evaluated` · integer · required
- `terminal_block_key` · string
- `terminal_block_name` · string
- `terminal_block_type` · enum One of `always`, `cel`, `llm_judge`, `webhook`, `branch`.
- `terminal_block_reason` · string: The sentence the pipeline author configured on the terminal block, written for the reviewer.
- `endpoint_reason` · string: A webhook endpoint's own explanation of its answer, when the terminal block was a webhook that gave one.
- `terminal_block_error_code` · string: Present when the terminal block failed instead of answering, for example `evaluation_error` or `timeout`. A failed block always sends the request to a human, so a reviewer should not read it as a match.
- `terminal_pipeline` · object: The section of the effective pipeline that held the terminal block, at the revision that ran. Enough to read that revision and find the block in it.
  - `scope` · enum · required One of `organization`, `agent`.
  - `agent_slug` · string: Present when the scope is `agent`.
  - `revision` · int64 · required
- `completed_at` · date-time · required

```json
{
  "outcome": "approve",
  "reason_code": "string",
  "blocks_evaluated": 1,
  "terminal_block_key": "string",
  "terminal_block_name": "string",
  "terminal_block_type": "always",
  "terminal_block_reason": "string",
  "endpoint_reason": "string",
  "terminal_block_error_code": "string",
  "terminal_pipeline": {
    "scope": "organization",
    "agent_slug": "string",
    "revision": 1
  },
  "completed_at": "2026-09-08T12:02:11Z"
}
```

## JudgeAssessment

One judge block's verdict on the request. Evidence for the reviewer, never a decision.

- `block_key` · string · required
- `block_name` · string
- `verdict` · enum · required One of `approve`, `deny`, `escalate`.
- `confidence` · enum · required One of `low`, `medium`, `high`.
- `rationale` · string · required
- `model_label` · string: The judge model's name when it judged the request.
- `provider` · string
- `model` · string
- `downgraded_for_confidence` · boolean · required: True when the confidence fell below the block's threshold and the verdict was treated as escalate.
- `evaluated_at` · date-time · required

```json
{
  "block_key": "string",
  "block_name": "string",
  "verdict": "approve",
  "confidence": "low",
  "rationale": "string",
  "model_label": "string",
  "provider": "string",
  "model": "string",
  "downgraded_for_confidence": true,
  "evaluated_at": "2026-09-08T12:02:11Z"
}
```

## LLMJudgeBlockDefinition

A block that asks a model to review the request against your written policy.

- `id` · string · required: A key for the block, unique within the pipeline including blocks inside branches.
- `name` · string · required: A display name.
- `type` · "llm_judge" · required: Always `llm_judge`.
- `enabled` · true · required: Always `true`. Leave disabled blocks out of the document.
- `timeout` · string · required: How long one attempt may take, as a duration. At most `60s`.
- `max_attempts` · integer · required: How many times to try, from 1 to 3.
- `config` · LLMJudgeBlockConfig · required: The model answers with a verdict of approve, deny, or escalate. The on_verdict table maps each verdict to what the pipeline does. A verdict can never map to the opposite decision, and escalate never decides on its own. The deployment decides which judge models are offered, and each decision records the model that made it.
  - `model` · string: The key of the judge model to use, such as `gpt-5.6-terra`. Leave it out to use the deployment's default model, which follows the deployment when its default changes. A key the deployment does not offer is refused with `judge_model_unavailable`.
  - `effort` · enum · required: How much reasoning the model spends: `low`, `medium`, or `high`. One of `low`, `medium`, `high`.
  - `instructions` · string · required: Your review policy, in plain language, up to 8,192 characters. It goes into the system prompt after withHuman's fixed framing.
  - `include` · object · required: What to show the model.
    - `agent_reasoning` · boolean · required: Include the agent's own explanation of the call.
    - `context` · boolean · required: Include the runtime metadata the adapter reported.
    - `previous_metadata` · boolean · required: Include the metadata earlier blocks returned.
  - `on_verdict` · object · required: What the pipeline does with each verdict.
    - `approve` · enum · required: What an `approve` verdict does. One of `approve`, `next`, `human`.
    - `deny` · enum · required: What a `deny` verdict does. One of `deny`, `next`, `human`.
    - `escalate` · enum · required: What an `escalate` verdict does. One of `human`, `next`.
  - `automatic_decisions_require_confidence` · enum · required: The confidence the model must report before its verdict can decide on its own. Below it, the verdict is treated as `escalate`. One of `high`, `medium`, `low`.
  - `reason` · string · required: The note recorded on decisions this block makes. The model's reasoning is kept as evidence.
  - `escalation_path` · string: Where the request goes when a verdict maps to `human`. Defaults to the pipeline's default path.

```json
{
  "id": "string",
  "name": "string",
  "type": "llm_judge",
  "enabled": true,
  "timeout": "30s",
  "max_attempts": 1,
  "config": {
    "model": "string",
    "effort": "low",
    "instructions": "string",
    "include": {
      "agent_reasoning": true,
      "context": true,
      "previous_metadata": true
    },
    "on_verdict": {
      "approve": "approve",
      "deny": "deny",
      "escalate": "human"
    },
    "automatic_decisions_require_confidence": "high",
    "reason": "string",
    "escalation_path": "string"
  }
}
```

## StoredLLMJudgeBlock

A model review block as saved in a revision.

- `id` · string · required: The block's key, unique within the pipeline including blocks inside branches.
- `snapshot_id` · uuid · required: The id of the saved copy.
- `name` · string · required: The display name.
- `type` · "llm_judge" · required: Always `llm_judge`.
- `enabled` · true · required: Always `true`.
- `timeout` · string · required: How long one attempt may take.
- `max_attempts` · integer · required: How many times to try.
- `config` · LLMJudgeBlockConfig · required: The model answers with a verdict of approve, deny, or escalate. The on_verdict table maps each verdict to what the pipeline does. A verdict can never map to the opposite decision, and escalate never decides on its own. The deployment decides which judge models are offered, and each decision records the model that made it.
  - `model` · string: The key of the judge model to use, such as `gpt-5.6-terra`. Leave it out to use the deployment's default model, which follows the deployment when its default changes. A key the deployment does not offer is refused with `judge_model_unavailable`.
  - `effort` · enum · required: How much reasoning the model spends: `low`, `medium`, or `high`. One of `low`, `medium`, `high`.
  - `instructions` · string · required: Your review policy, in plain language, up to 8,192 characters. It goes into the system prompt after withHuman's fixed framing.
  - `include` · object · required: What to show the model.
    - `agent_reasoning` · boolean · required: Include the agent's own explanation of the call.
    - `context` · boolean · required: Include the runtime metadata the adapter reported.
    - `previous_metadata` · boolean · required: Include the metadata earlier blocks returned.
  - `on_verdict` · object · required: What the pipeline does with each verdict.
    - `approve` · enum · required: What an `approve` verdict does. One of `approve`, `next`, `human`.
    - `deny` · enum · required: What a `deny` verdict does. One of `deny`, `next`, `human`.
    - `escalate` · enum · required: What an `escalate` verdict does. One of `human`, `next`.
  - `automatic_decisions_require_confidence` · enum · required: The confidence the model must report before its verdict can decide on its own. Below it, the verdict is treated as `escalate`. One of `high`, `medium`, `low`.
  - `reason` · string · required: The note recorded on decisions this block makes. The model's reasoning is kept as evidence.
  - `escalation_path` · string: Where the request goes when a verdict maps to `human`. Defaults to the pipeline's default path.
- `config_version` · 1 · required: The version of the config format. Always 1.
- `content_hash` · string · required: A SHA-256 hash of the block's content.

```json
{
  "id": "string",
  "snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "name": "string",
  "type": "llm_judge",
  "enabled": true,
  "timeout": "30s",
  "max_attempts": 1,
  "config": {
    "model": "string",
    "effort": "low",
    "instructions": "string",
    "include": {
      "agent_reasoning": true,
      "context": true,
      "previous_metadata": true
    },
    "on_verdict": {
      "approve": "approve",
      "deny": "deny",
      "escalate": "human"
    },
    "automatic_decisions_require_confidence": "high",
    "reason": "string",
    "escalation_path": "string"
  },
  "config_version": 1,
  "content_hash": "string"
}
```

## LLMJudgeBlockConfig

The model answers with a verdict of approve, deny, or escalate. The on_verdict table maps each verdict to what the pipeline does. A verdict can never map to the opposite decision, and escalate never decides on its own. The deployment decides which judge models are offered, and each decision records the model that made it.

- `model` · string: The key of the judge model to use, such as `gpt-5.6-terra`. Leave it out to use the deployment's default model, which follows the deployment when its default changes. A key the deployment does not offer is refused with `judge_model_unavailable`.
- `effort` · enum · required: How much reasoning the model spends: `low`, `medium`, or `high`. One of `low`, `medium`, `high`.
- `instructions` · string · required: Your review policy, in plain language, up to 8,192 characters. It goes into the system prompt after withHuman's fixed framing.
- `include` · object · required: What to show the model.
  - `agent_reasoning` · boolean · required: Include the agent's own explanation of the call.
  - `context` · boolean · required: Include the runtime metadata the adapter reported.
  - `previous_metadata` · boolean · required: Include the metadata earlier blocks returned.
- `on_verdict` · object · required: What the pipeline does with each verdict.
  - `approve` · enum · required: What an `approve` verdict does. One of `approve`, `next`, `human`.
  - `deny` · enum · required: What a `deny` verdict does. One of `deny`, `next`, `human`.
  - `escalate` · enum · required: What an `escalate` verdict does. One of `human`, `next`.
- `automatic_decisions_require_confidence` · enum · required: The confidence the model must report before its verdict can decide on its own. Below it, the verdict is treated as `escalate`. One of `high`, `medium`, `low`.
- `reason` · string · required: The note recorded on decisions this block makes. The model's reasoning is kept as evidence.
- `escalation_path` · string: Where the request goes when a verdict maps to `human`. Defaults to the pipeline's default path.

```json
{
  "model": "string",
  "effort": "low",
  "instructions": "string",
  "include": {
    "agent_reasoning": true,
    "context": true,
    "previous_metadata": true
  },
  "on_verdict": {
    "approve": "approve",
    "deny": "deny",
    "escalate": "human"
  },
  "automatic_decisions_require_confidence": "high",
  "reason": "string",
  "escalation_path": "string"
}
```

## ClaimEvent

One moment in the request's claim history.

- `kind` · enum · required One of `claimed`, `released`, `lapsed`.
- `actor_display_name` · string
- `occurred_at` · date-time · required

```json
{
  "kind": "claimed",
  "actor_display_name": "string",
  "occurred_at": "2026-09-08T12:02:11Z"
}
```

## EscalationEvent

One step of the request's escalation. Only the fields the kind carries are present. team_id marks a team policy's step and is absent for the organization path's.

- `kind` · enum · required One of `level_entered`, `urgency_raised`, `deferred`, `repeated`, `exhausted`.
- `team_id` · string
- `node_id` · string
- `level_ordinal` · integer: The level's position within its own document, one-based (level_entered only).
- `level_count` · integer: How many levels the document has (level_entered only).
- `urgency` · enum: The urgency the level was entered at, or the urgency raised to. One of `standard`, `interrupt`.
- `until` · date-time: When a deferred walker resumes (deferred only).
- `iteration` · integer: Which repeat of the document is starting (repeated only).
- `occurred_at` · date-time · required

```json
{
  "kind": "level_entered",
  "team_id": "string",
  "node_id": "string",
  "level_ordinal": 1,
  "level_count": 1,
  "urgency": "standard",
  "until": "2026-09-08T12:02:11Z",
  "iteration": 1,
  "occurred_at": "2026-09-08T12:02:11Z"
}
```
