Schemas
Every named object the published API uses, with its fields. Type names throughout the reference link here.
OAuthAuthorizationRequest
One of S256
Space-separated permission keys the client asked for.
One of code
{
"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": {
"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
One of S256
Space-separated permission keys the client asked for.
One of code
The permissions to place on the key; omit to inherit everything the person holds.
{
"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
Exact tool name sent in approval requests
Original JSON Schema, at most 256 KiB. Absence means unknown.
{
"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": "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
Stable opaque identifier for this local project or runtime context
Whether all configured sources in this context were enumerated
{
"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
builtin, mcp, or a source an edition adds (the hosted gateway's gateway)
{
"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",
"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": "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": [
{
"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": true,
"destructive_hint": true,
"idempotent_hint": true,
"open_world_hint": true
}ErrorResponse
{
"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.
The plan dimension that is full, such as seats or agents.
{
"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.
The permission you needed.
What kind of thing the check was about.
One of organization, team, agent, request
The team, agent slug, or request the check was about. Absent for the organization.
{
"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.
One of not_targeted, break_glass_only, outside_routing_required
{
"reason": "not_targeted"
}APIKey
An API key without its secret. Personal prefixes start with whk_; organization prefixes start with who_.
Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
On detail responses, currently allowed organization-key grants.
Whether the key is active and not blocked by organization policy or status.
One of organization_inactive, api_keys_disabled
Personal keys act as a member; organization keys act as themselves.
One of personal, organization
The member a personal key acts as.
Who minted it. Present on the organization-wide list.
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.
The assurance of the session that minted the key (strong, single_factor), presented as the key's own when it decides.
The MCP client the key was minted for through the consent page. Absent for a key made by hand.
One of active, expired, revoked
null when the key lives until revoked.
One of manual, membership_deprovisioned
{
"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.
Minting a key that carries it needs a fresh, strong session.
{
"key": "pipeline.write",
"area": "pipeline",
"description": "string",
"dangerous": true
}MyAPIKeysEnvelope
The organization's switch.
Whether a key may carry a permission list here. False in the open edition, where keys inherit the member's role.
{
"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": [
{
"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
Narrow the key to these permissions. Omit to inherit everything you hold. Each must be a permission you hold; only in the hosted edition.
When the key stops working. Omit for a key that lives until revoked, unless the deployment caps key lifetime.
{
"name": "terraform",
"permissions": [
"pipeline.read",
"pipeline.write",
"pipeline.activate"
],
"expires_at": "2026-09-08T12:02:11Z"
}IssuedAPIKeyEnvelope
An API key without its secret. Personal prefixes start with whk_; organization prefixes start with who_.
The raw key, shown once: whk_<id>_<secret>.
{
"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
An API key without its secret. Personal prefixes start with whk_; organization prefixes start with who_.
What the key may do right now, after narrowing and the member's current roles.
{
"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.
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
The agent's slug. Present for the agent scope only.
The revision in use. Absent only for an archived pipeline.
The newest revision, active or not.
When the newest revision was created.
Present when the pipeline was archived with its agent: readable, never active again until the agent is restored.
{
"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.
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
The agent's slug. Present for the agent scope only.
The revision number. Revisions count up from 1.
Whether this is the revision in use.
Every block in the revision, including blocks inside branches.
The escalation path for requests that reach a person without a block naming a path.
The membership or organization-key actor that created the revision.
Present when the revision was archived with its agent. An archived revision can be read but not activated until the agent is restored.
{
"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.
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
The agent's slug. Present for the agent scope only.
The revision number. Revisions count up from 1.
Whether this is the revision in use.
Every block in the revision, including blocks inside branches.
The escalation path for requests that reach a person without a block naming a path.
The membership or organization-key actor that created the revision.
The blocks, in the order they run. A branch holds its own blocks in config.blocks.
{
"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.
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.
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.
{
"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.
A key for the block, unique within the pipeline including blocks inside branches.
A display name.
Always always.
Always true. Leave disabled blocks out of the document.
How long execution may take, as a duration. At most 1s.
Always 1.
Returns outcome for every request that reaches this block. Later blocks do not run.
{
"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.
A key for the block, unique within the pipeline including blocks inside branches.
A display name.
Always cel.
Always true. Leave disabled blocks out of the document.
How long the condition may take, as a duration. At most 1s.
Always 1.
When the condition matches, the block returns on_match. Otherwise the request passes to the next block.
{
"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.
The block's key, unique within the pipeline including blocks inside branches.
The id of the saved copy.
The display name.
Always always.
Always true.
How long execution may take.
Always 1.
Returns outcome for every request that reaches this block. Later blocks do not run.
The version of the config format. Always 1.
A SHA-256 hash of the block's content.
{
"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.
The block's key, unique within the pipeline including blocks inside branches.
The id of the saved copy.
The display name.
Always cel.
Always true.
How long the condition may take.
Always 1.
When the condition matches, the block returns on_match. Otherwise the request passes to the next block.
The version of the config format. Always 1.
A SHA-256 hash of the block's content.
{
"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.
A key for the block, unique within the pipeline including blocks inside branches.
A display name.
Always webhook.
Always true. Leave disabled blocks out of the document.
How long one attempt may take, from connecting to the full response, as a duration. At most 30s.
How many times to try, from 1 to 3.
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.
{
"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.
The block's key, unique within the pipeline including blocks inside branches.
The id of the saved copy.
The display name.
Always webhook.
Always true.
How long one attempt may take.
How many times to try.
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.
The version of the config format. Always 1.
A SHA-256 hash of the block's content.
{
"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.
A key for the block, unique within the pipeline including blocks inside branches.
A display name.
Always branch.
Always true. Leave disabled blocks out of the document.
How long the condition may take, as a duration. At most 1s.
Always 1.
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.
{
"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.
The block's key, unique within the pipeline including blocks inside branches.
The id of the saved copy.
The display name.
Always branch.
Always true.
How long the condition may take.
Always 1.
A branch configuration as saved, with its blocks as saved.
The version of the config format. Always 1.
A SHA-256 hash of the branch's own content. Each of its blocks has its own.
{
"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.
A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
The branch's blocks, in the order they run. Any type, including further branches. They count toward the pipeline's 1,000 blocks.
The note recorded when none of the branch's blocks decides.
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.
{
"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.
A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
The branch's blocks, in the order they run.
The note recorded when none of the branch's blocks decides.
Where the request goes when none of the branch's blocks decides.
{
"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.
The webhook endpoint to call.
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.
What to send with the request.
The note recorded on decisions this block makes. The endpoint's own reason is kept as evidence.
Where the request goes when the endpoint answers human, or when the call fails. Defaults to the pipeline's default path.
{
"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.
The note recorded on decisions this block makes.
Where the request goes for a human outcome. Allowed only when outcome is human. Defaults to the pipeline's default path.
{
"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.
A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
The note recorded on decisions this block makes.
Where the request goes on a human match. Allowed only when on_match is human. Defaults to the pipeline's default path.
{
"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.
An id for the condition, unique within the pipeline.
A JSON pointer into the request, such as /request/tool or /request/arguments/amount_cents.
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
The value to compare with. Not used by operators that take none, such as exists.
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"field": "/request/arguments/amount_cents",
"operator": "exists",
"value": {}
}CELAllCondition
Holds when every nested condition holds.
An id for the condition, unique within the pipeline.
The conditions that must all hold.
{
"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.
An id for the condition, unique within the pipeline.
The conditions, of which at least one must hold.
{
"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.
An id for the condition, unique within the pipeline.
A list field and the condition each of its items is tested against.
{
"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.
An id for the condition, unique within the pipeline.
A list field and the condition each of its items is tested against.
{
"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.
A JSON pointer to a list field.
A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
{
"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.
The blocks, in the order they should run.
The default escalation path, as in a document. Reported in the result when the outcome is human.
The request to run the pipeline against.
{
"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.
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
The block that ended the run: for end_of_branch, the branch. Absent when the request passed every block.
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.
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.
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
{
"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.
The block's key.
The block's position in the pipeline, from 0, counting in document order with each branch before the blocks it holds.
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
The note the block recorded.
The metadata the block returned, if any.
The cost the block recorded.
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).
For webhook blocks. What was called and what came back. Never the bodies, never the secret.
How the condition was evaluated, node by node. For condition blocks and branches.
Present and true on the step that closes a branch the request entered when none of its blocks decided. The outcome is human.
{
"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.
The secret the machine keeps. Send it to the exchange endpoint. Never show it to the person.
The short code the person enters in the browser. Three letters, a hyphen, and four letters or digits. Case does not matter.
The page where the person enters the user code.
When both codes stop working. Start a new enrollment after this.
{
"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
Identifies the credential, for example when revoking it.
The secret. It appears only in this response and cannot be retrieved again.
When the credential stops working.
{
"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.
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
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.
The organization every call is scoped to.
The person's membership in the organization. Present for a person only.
The instance the credential belongs to. Present for an agent only.
The agent's slug. Present for an agent only.
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.
Every permission the actor holds, each with the scope it applies at. Flattened from the role assignments.
The teams the member belongs to. Escalation paths can route requests to a team.
How a principal was signed in when it acted.
{
"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
A registry key such as request.decide
One of organization, team, agent
The team id or agent slug the grant applies to; absent at organization scope
{
"permission": "request.decide",
"scope_kind": "organization",
"scope_id": "string"
}AuthContext
How a principal was signed in when it acted.
How the principal signed in, for example local_password, sso, or social_google.
The strength of that sign-in: single_factor or strong.
When the sign-in happened.
The session that acted, when a person did.
The credential that acted, when an agent did.
{
"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.
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
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
When the agent was archived. Present exactly when status is archived.
{
"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.
The slug of the agent this is an instance of.
Unique within the agent.
The JSON stored when the instance was created.
The last time this instance called the API.
{
"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.
When the credential stops working.
The last authenticated call made with it.
When it was revoked. Absent while it is not.
{
"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.
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
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
When the agent was archived. Present exactly when status is archived.
How many instances the agent has.
How many instances are active and hold a credential that is neither expired nor revoked.
The newest authenticated call from any instance. Absent when the agent has no instances.
Requests made in the last seven days.
Requests still waiting for a decision.
The recent requests split into one bucket per day, oldest first. The last entry is the past 24 hours.
{
"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.
The slug of the agent this is an instance of.
Unique within the agent.
The JSON stored when the instance was created.
The last time this instance called the API.
Every credential the instance has held, newest first. Tokens are never included.
Requests this instance made in the last seven days.
The instance's recent requests split into one bucket per day, oldest first. The last entry is the past 24 hours.
{
"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.
The agent's identity, chosen once at creation. It never changes and is never reused, not even after the agent is archived.
The membership or organization-key actor that created the agent.
The display name. Reviewers see it on every request. Editable, and unique among the organization's live agents.
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
When the agent was archived. Present exactly when status is archived.
All provisioner tokens owned by this agent.
Tokens matching the current identifier and status filters before pagination.
All instances owned by this agent.
Active instances with at least one live credential across all pages.
Instances matching the current search and status filters before pagination.
All requests made by this agent.
One of human, api_key
The name of the human or organization key that created the agent.
The requested page of instances, sorted by last seen, creation time, and id, newest first.
The requested page of provisioner tokens, sorted by issued time and id, newest first. Secrets are never included.
Requests made in the last seven days.
Requests still waiting for a decision.
{
"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.
An agent identity. The slug identifies it everywhere: URLs, role scopes, pipeline scopes, audit data and conditions. The name is a label for people.
One running copy of an agent.
{
"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.
One running copy of an agent.
{
"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.
The slug of the agent the token creates instances of.
The membership or organization-key actor that created the token.
When the token stops working.
The last time the token created an instance.
When it was revoked. Absent while it is not.
{
"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.
A provisioner token as the agent page shows it. The token is never included.
{
"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.
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).
Replaces the humanised key.
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
Labels a code field for the reader.
For diff: the pointer to the text before the change. Requires after; excludes path.
For diff: the pointer to the text after the change.
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.
primary fields make up the block a reviewer reads first; detail fields fold away underneath it.
One of primary, detail
{
"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.
organization for one of the organization's own entries, built_in for the shipped catalog.
One of built_in, organization
The organization entry, when the source is organization.
{
"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
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.
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.
The exact arguments the tool will run with if approved. Reviewers see this as the description of the action, so it must be complete.
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.
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.
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.
{
"tool": "issue_refund",
"server": "stripe",
"arguments": {
"amount": 4900,
"reason": "duplicate_charge"
},
"agent_reasoning": "Refunding the duplicate charge for [email protected].",
"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": "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.
One of pending, approved, denied, expired, cancelled
Immutable decision. Approved calls must start within five minutes of decided_at. Reads and retries never extend expires_at.
{
"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
The slug of the agent that made the request.
The running copy of the agent that made the request.
The agent's name when the request was made.
The instance's name when the request was made.
The tool the agent wants to call, as the server that defines it names it.
The MCP server that defines the tool, as the adapter reported it. Absent for a runtime's built-in tools.
The exact arguments the tool will run with if approved.
The agent's own explanation, if it gave one.
Where the call comes from, as recorded by the adapter.
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
When the request expires if nobody has decided: created_at plus the timeout.
{
"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 [email protected].",
"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": "[email protected]",
"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.
The slug of the agent that made the request.
The running copy of the agent that made the request.
The agent's name when the request was made.
The instance's name when the request was made.
The tool the agent wants to call, as the server that defines it names it.
The MCP server that defines the tool, as the adapter reported it. Absent for a runtime's built-in tools.
The exact arguments the tool will run with if approved.
The agent's own explanation, if it gave one.
Where the call comes from, as recorded by the adapter.
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
When the request expires if nobody has decided: created_at plus the timeout.
Moves on every change a reviewer should notice, escalation progress included, so a client refetches the review when it changes.
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.
The presentation a request renders with and where it came from.
{
"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 [email protected].",
"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": "[email protected]",
"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.
The reviewer holding the claim.
The reviewer's name.
When the claim lapses on its own.
{
"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.
The member who decided. Absent when the pipeline decided, the request expired, or the agent cancelled it.
One of approved, denied, expired, cancelled
A note the reviewer left for the agent, if any.
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.
The reviewer's name, when a person decided.
The reviewer's email, when a person decided.
How a principal was signed in when it acted.
The key the decision was recorded with.
{
"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": "[email protected]",
"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.
The operation's id. The decision itself has its own.
The request that was decided.
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
Why the decision was rejected. Set for rejected only.
The rejection in plain text. Set for rejected only.
When the decision was stored.
{
"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": "[email protected]",
"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.
The membership id. Other endpoints refer to a member by this id.
The person's user id, shared across organizations.
active can sign in and act. suspended cannot until reactivated. deprovisioned was removed and is kept for the record.
One of active, suspended, deprovisioned
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
Whether your directory owns this membership. Status changes here are refused.
The roles the member holds, directly and through their teams.
The teams the member is on.
{
"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.
The membership id. Other endpoints refer to a member by this id.
active can sign in and act. suspended cannot until reactivated. deprovisioned was removed and is kept for the record.
One of active, suspended, deprovisioned
The roles the member holds, directly and through their teams.
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
Whether your directory owns this membership. Status changes here are refused.
The teams the member is on.
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"display_name": "Ada Lovelace",
"email": "[email protected]",
"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.
The name from the latest revision.
The URL from the latest revision.
The revision deliveries go to. Absent while the endpoint is archived or no revision has been activated yet.
When the endpoint was archived. Absent for a live endpoint, including one whose revisions are all drafts. Cleared when a revision is activated again.
The newest revision, active or not.
When the newest revision was created.
When the signing secret was last created or rotated.
The active pipeline revisions that post to the endpoint. Empty when it can be archived.
{
"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.
The revision number. Revisions count up from 1.
Whether deliveries go to this revision.
The display name.
Where deliveries go.
The membership or organization-key actor that created the revision.
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.
{
"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.
A display name. Pipeline authors see it when they pick an endpoint.
An absolute public HTTPS URL. It is checked against the egress policy, which refuses private and internal addresses.
{
"name": "Fraud check",
"url": "https://fraud.example.com/approvals"
}WebhookEndpointSecret
A freshly rotated signing secret.
The new secret. It appears only in this response. Starts with whsec_. Your receiver uses it to verify the WithHuman-Signature header.
When the secret was rotated.
{
"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.
The agent's slug. Present for the agent scope only.
The active revision number.
The block that uses the object. Absent when the revision names an escalation path as its default.
{
"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.
The HTTP status the endpoint returned.
How long the call took, in milliseconds.
The outcome the endpoint answered with, when its answer was valid.
One of next, human, approve, deny
The reason the endpoint gave, if any.
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
Details about the problem, in plain words.
{
"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.
Whether your directory owns this team. Such a team cannot be renamed or archived here.
How many members the team has, from every source.
The roles the team holds. Every member inherits them.
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.
When the team was archived. Null for a live team.
{
"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": 1,
"latest_revision": 1
}TeamMember
One member of a team.
How the person got on the team: added by hand, by your directory, or both.
{
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"display_name": "Ada Lovelace",
"email": "[email protected]",
"status": "active",
"sources": [
"manual"
]
}Invitation
An invitation to join the organization.
The address the invitation was sent to.
The roles the person receives when they accept.
The member who sent it.
pending can still be accepted. accepted and revoked are final. expired passed its expiry without being accepted.
One of pending, accepted, revoked, expired
When the link stops working. Seven days from creation or the last resend.
The membership created on acceptance. Present for accepted only.
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"email": "[email protected]",
"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.
An invitation to join the organization.
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.
{
"invitation": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"email": "[email protected]",
"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.
The membership, team or agent that holds it.
The team id or the agent slug. Absent for organization.
The team's name, for a team scope.
The directory group that produced it. Present for directory only.
The member who granted it. Present for manual only.
The team the role is inherited through. Present when a member holds it through a team rather than directly.
That team's name.
{
"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.
The team id or the agent slug. Absent for organization.
{
"scope_kind": "organization",
"scope_id": "string",
"permissions": [
"string"
]
}PermissionDescriptor
One permission a role can carry.
The permission key, shaped resource.action.
The key's first segment, for grouping.
What the permission allows.
Who can hold it: people, agents, or the gateway.
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
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.
{
"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.
The name from the latest revision.
The revision requests are routed with. Absent while no revision is active, which is always the case for an archived path.
The newest revision, active or not.
When the newest revision was created.
When the path was archived. Absent for a live path.
{
"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.
The revision number. Revisions count up from 1.
Whether requests are routed with this revision.
The display name.
The membership or organization-key actor that created the revision.
When the path was archived. Absent for a revision of a live path; an archived revision can no longer be activated.
{
"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.
The revision number. Revisions count up from 1.
Whether requests are routed with this revision.
The display name.
The membership or organization-key actor that created the revision.
When the path was archived. Absent for a revision of a live path; an archived revision can no longer be activated.
The named working-hours sets the nodes refer to.
The nodes, in order.
Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
The version of the document format.
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.
{
"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.
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
Members of the team who cannot decide requests.
Active members of the team.
{
"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.
A display name. Reviewers see it on the requests the path routes.
Named working-hours sets that if_else and defer nodes refer to by id. May be empty.
The nodes, in order. At least one.
Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
{
"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.
Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
{
"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.
A key for the set, unique within the document.
A display name.
An IANA timezone name, such as Europe/London.
The open windows. At least one.
{
"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.
The days the window applies to.
When the window opens, as HH:MM in 24-hour time.
When the window closes, as HH:MM in 24-hour time.
{
"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.
An id for the node, unique within the path.
level only. The people and teams to notify.
level only. How long to wait for a decision before moving to the next node. A duration from 1m to 168h.
level only. Raise the request to interrupt urgency before notifying.
if_else only. Every condition must hold for then to run. From 1 to 10.
if_else only. The nodes to run when every condition holds. An empty list moves on to the next node.
if_else only. The nodes to run otherwise. An empty list moves on to the next node.
defer only. The working-hours set to wait for.
defer only. Let interrupt requests skip the wait.
{
"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.
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 only. The set to test.
working_hours only. true tests that the window is open, false that it is closed.
A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
{
"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": "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.
Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
{
"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": "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
Run the path again from the top when the nodes run out without a decision. Every condition is evaluated again on each run.
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.
{
"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
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
The departed target. Present for target_not_member only.
The departed target's name, while the roster still lists them under another status.
Members of the team who cannot decide requests; 0 for target_not_member.
Active members of the team; 0 for target_not_member.
{
"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": "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.
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
The node the walker stands on; absent once exhausted.
1-based position of the current level in walk order; 0 before the first level.
Level nodes in the document.
How many times the program has been repeated so far.
When the armed timer fires; absent while paused, exhausted, or waiting on nothing.
{
"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.
Monotonic tick counter; a later tick always carries a higher value.
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
The request's current urgency, raised by a level that asked to.
One of standard, interrupt
The path node the walker stands on; absent once exhausted.
1-based position of the current level in walk order; 0 before the first level.
Level nodes in the path document.
How many times the path has been repeated so far.
When the path walker's armed timer fires; absent while paused, exhausted, or waiting on nothing.
{
"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.
How many extra runs, from 1 to 9.
How long to wait before each extra run. A duration from 1m to 168h.
{
"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.
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
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
The urgency the request was routed with. Always standard today.
One of standard, interrupt
The path that took the request. Present for escalated only.
The name of the path revision that took the request. Present for escalated only.
The path revision that was active at the time. Present for escalated only.
The pipeline revision that routed the request. Present for escalated only.
The pipeline block that named the path. Present for block_escalation only.
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.
People added to the request's routing after the snapshot was pinned, oldest first.
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.
The escalation executor's current position. Null before its first tick and for default_queue.
When the request entered human review.
{
"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.
The person's display name or the team's name.
{
"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": "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": 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.
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.
People added to the request's routing after it was pinned, oldest first. The same list the routing carries.
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.
{
"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
One of organization, team, agent
Empty for organization scope; otherwise the selected team IDs or agent slugs.
{
"scope_kind": "organization",
"resource_ids": [
"string"
],
"permissions": [
"string"
]
}PermissionPolicyInput
One of organization, team, agent
Empty for organization scope; otherwise the selected team IDs or agent slugs.
Required when updating an existing policy.
{
"scope_kind": "organization",
"resource_ids": [
"string"
],
"permissions": [
"string"
],
"revision": 1
}PermissionResource
{
"id": "string",
"name": "string",
"kind": "organization"
}PermissionPolicy
One of organization, team, agent
Empty for organization scope; otherwise the selected team IDs or agent slugs.
One of manual, directory
{
"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",
"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_.
Returned only in the first creation response.
Current organization-key grants projected from RBAC permission policies. Empty for personal keys.
On detail responses, currently allowed organization-key grants.
Whether the key is active and not blocked by organization policy or status.
One of organization_inactive, api_keys_disabled
Personal keys act as a member; organization keys act as themselves.
One of personal, organization
The member a personal key acts as.
Who minted it. Present on the organization-wide list.
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.
The assurance of the session that minted the key (strong, single_factor), presented as the key's own when it decides.
One of active, expired, revoked
null when the key lives until revoked.
One of manual, membership_deprovisioned
{
"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
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.
{
"name": "string",
"permission_policies": [
{
"scope_kind": "organization",
"resource_ids": [
"string"
],
"permissions": [
"string"
]
}
],
"expires_at": "2026-09-08T12:02:11Z"
}OrganizationAPIKeyOptions
Maximum lifetime as a duration. 0s means no cap.
{
"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
One of none, selected, all
Exact downstream tool names, meaningful only in selected mode. Empty for none or all; no wildcard matching.
Expected current version; zero for an unconfigured agent/server pair.
{
"mode": "none",
"tools": [
"string"
],
"revision": 0
}AgentToolAccess
One of none, selected, all
{
"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.
The event's position in the organization's log. Later events have higher numbers.
What happened, as a dotted key such as membership.suspended.
Who did it: human, agent, system, or api_key.
The person, agent, or key that acted. Absent for system.
The actor's name at the time.
What kind of thing the event is about, such as a request or a membership.
The thing the event is about.
Details specific to the event type, as JSON.
When it happened.
{
"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.
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.
What the escalation executor did for the request, oldest first. Empty for a default queue request.
How many audit events the request has in total, for the link into the audit log.
{
"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.
One of approve, deny, human
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.
One of always, cel, llm_judge, webhook, branch
The sentence the pipeline author configured on the terminal block, written for the reviewer.
A webhook endpoint's own explanation of its answer, when the terminal block was a webhook that gave one.
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.
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.
{
"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.
One of approve, deny, escalate
One of low, medium, high
The judge model's name when it judged the request.
True when the confidence fell below the block's threshold and the verdict was treated as escalate.
{
"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"
}LLMJudgeBlockDefinitionHosted edition
A block that asks a model to review the request against your written policy.
A key for the block, unique within the pipeline including blocks inside branches.
A display name.
Always llm_judge.
Always true. Leave disabled blocks out of the document.
How long one attempt may take, as a duration. At most 60s.
How many times to try, from 1 to 3.
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.
{
"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"
}
}StoredLLMJudgeBlockHosted edition
A model review block as saved in a revision.
The block's key, unique within the pipeline including blocks inside branches.
The id of the saved copy.
The display name.
Always llm_judge.
Always true.
How long one attempt may take.
How many times to try.
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.
The version of the config format. Always 1.
A SHA-256 hash of the block's content.
{
"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.
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.
How much reasoning the model spends: low, medium, or high.
One of low, medium, high
Your review policy, in plain language, up to 8,192 characters. It goes into the system prompt after withHuman's fixed framing.
What to show the model.
What the pipeline does with each verdict.
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
The note recorded on decisions this block makes. The model's reasoning is kept as evidence.
Where the request goes when a verdict maps to human. Defaults to the pipeline's default path.
{
"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": "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.
One of level_entered, urgency_raised, deferred, repeated, exhausted
The level's position within its own document, one-based (level_entered only).
How many levels the document has (level_entered only).
The urgency the level was entered at, or the urgency raised to.
One of standard, interrupt
When a deferred walker resumes (deferred only).
Which repeat of the document is starting (repeated only).
{
"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"
}