Schemas
Every named object the published API uses, with its fields. Type names throughout the reference link here.
OAuthAuthorizationRequest
A registered client's ID, or the https URL of the client's metadata document (draft-ietf-oauth-client-id-metadata-document).
One of S256
Space-separated permission keys the client asked for.
One of code
{
"client_id": "string",
"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
A registered client's ID, or the https URL of the client's metadata document (draft-ietf-oauth-client-id-metadata-document).
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": "string",
"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
The instance that reported the source, when an instance did
Whether the source's owner can serve now: the reporting instance is active with a live credential, or the edition reports the external source live
builtin, mcp, or a source an edition adds (the hosted gateway's gateway)
Why the last discovery failed, as the reporting instance or the edition described it. Empty after a successful discovery
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"agent_instance_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"instance_name": "string",
"available": true,
"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"
}ToolDefinitionUse
The runtime that offers the definition, or the source an edition adds (the hosted gateway's gateway)
The instance that reported the definition, when an instance did
{
"runtime": "string",
"instance_name": "string",
"available": true,
"stale": true
}CatalogToolDefinition
{
"name": "string",
"server": "string",
"description": "string",
"input_schema": {},
"uses": [
{
"runtime": "string",
"instance_name": "string",
"available": true,
"stale": true
}
]
}ToolDefinitionList
{
"tool_definitions": [
{
"name": "string",
"server": "string",
"description": "string",
"input_schema": {},
"uses": [
{
"runtime": "string",
"instance_name": "string",
"available": true,
"stale": true
}
]
}
],
"servers": [
"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",
"instance_name": "string",
"available": true,
"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, pipeline, request
The team, agent slug, pipeline key, 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.
The pipeline's key. entry is the pipeline every request starts in.
The pipeline's display name: the active revision's, or the latest revision's for an archived pipeline.
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: readable, never active again, its key taken for good.
Every delegate block in an active pipeline revision that hands requests to this pipeline, the entry pipeline first, then by pipeline key and block position. Empty when no request can reach it.
{
"pipeline_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",
"referenced_by": [
{
"pipeline_key": "string",
"pipeline_name": "string",
"revision": 1,
"block_key": "string",
"block_name": "string"
}
]
}PipelineDelegation
One delegate block in an active pipeline revision.
The key of the pipeline holding the delegate block.
That pipeline's name as of its active revision.
The active revision holding the block.
The delegate block's key, unique within the revision.
The delegate block's name.
{
"pipeline_key": "string",
"pipeline_name": "string",
"revision": 1,
"block_key": "string",
"block_name": "string"
}RequestApprovalPipelineRevisionSummary
One pipeline revision, without its blocks.
The pipeline's key.
The pipeline's display name as of this revision.
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 from this pipeline without a block naming a path.
Send requests that reach a person without a block naming a path to the person who owns the agent instance that made them, instead of default_escalation_path. The path, if any, is then the fallback for a request whose agent has no owner able to decide it; without one the fallback is the enclosing pipeline's default, else any authorized reviewer.
The membership or organization-key actor that created the revision.
When the revision was first active. Absent for a draft, which can still be changed.
Present when the pipeline was archived. An archived revision can be read but never activated.
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"pipeline_key": "string",
"name": "string",
"revision": 1,
"is_active": true,
"block_count": 0,
"default_escalation_path": "string",
"default_ask_agent_owner": true,
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_at": "2026-09-08T12:02:11Z",
"activated_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.
The pipeline's key.
The pipeline's display name as of this revision.
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 from this pipeline without a block naming a path.
Send requests that reach a person without a block naming a path to the person who owns the agent instance that made them, instead of default_escalation_path. The path, if any, is then the fallback for a request whose agent has no owner able to decide it; without one the fallback is the enclosing pipeline's default, else any authorized reviewer.
The membership or organization-key actor that created the revision.
When the revision was first active. Absent for a draft, which can still be changed.
Present when the pipeline was archived.
The blocks, in the order they run. A branch holds its own blocks in config.blocks; a delegate names the pipeline it hands matching requests to in config.pipeline_key.
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"pipeline_key": "string",
"name": "string",
"revision": 1,
"is_active": true,
"block_count": 0,
"default_escalation_path": "string",
"default_ask_agent_owner": true,
"created_by_actor_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"created_at": "2026-09-08T12:02:11Z",
"activated_at": "2026-09-08T12:02:11Z",
"archived_at": "2026-09-08T12:02:11Z",
"blocks": [
{
"id": "review-remaining-requests",
"snapshot_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "Review remaining requests",
"type": "outcome",
"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.
A new display name for the pipeline. Omit to keep the active revision's.
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 that enters the pipeline through to a person.
The escalation path for requests that reach a person from this pipeline without a block naming a path. That includes requests that run out of it. A delegated pipeline without one inherits the defaults of the pipelines that delegated into it, nearest first; with none anywhere, 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.
Send those requests to the person who owns the agent instance that made them instead. default_escalation_path, if set, is then the fallback for a request whose agent has no owner able to decide it (a service agent, or an owner who was deactivated); without it the fallback is the enclosing pipeline's default, else any authorized reviewer. The owner is the only reviewer: the request never moves to the path because the owner has not answered.
{
"name": "string",
"blocks": [
{
"id": "review-remaining-requests",
"name": "Review remaining requests",
"type": "outcome",
"enabled": true,
"timeout": "50ms",
"max_attempts": 1,
"config": {
"outcome": "human",
"reason": "Requests not handled by earlier blocks need human review."
}
}
],
"default_escalation_path": "string",
"default_ask_agent_owner": true
}RequestApprovalPipelineCreate
A new pipeline and the content of its first revision.
The pipeline's key. Taken for good once used, archived pipelines included; never entry.
The display name.
The first revision's blocks, in the order they should run. Omit for a pipeline that passes every request that enters it through to a person until it is edited.
The escalation path for requests that reach a person from this pipeline without a block naming a path, as in a document.
Send those requests to the person who owns the agent instance that made them instead. default_escalation_path, if set, is then the fallback for a request whose agent has no owner able to decide it (a service agent, or an owner who was deactivated); without it the fallback is the enclosing pipeline's default, else any authorized reviewer. The owner is the only reviewer: the request never moves to the path because the owner has not answered.
{
"pipeline_key": "payments",
"name": "Payments",
"blocks": [
{
"id": "review-remaining-requests",
"name": "Review remaining requests",
"type": "outcome",
"enabled": true,
"timeout": "50ms",
"max_attempts": 1,
"config": {
"outcome": "human",
"reason": "Requests not handled by earlier blocks need human review."
}
}
],
"default_escalation_path": "string",
"default_ask_agent_owner": true
}OutcomeBlockDefinition
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 outcome.
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": "outcome",
"enabled": true,
"timeout": "50ms",
"max_attempts": 1,
"config": {
"outcome": "human",
"reason": "Requests not handled by earlier blocks need human review."
}
}StoredOutcomeBlock
An outcome 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 outcome.
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": "outcome",
"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"
}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",
"ask_agent_owner": true
}
}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",
"ask_agent_owner": true
},
"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. "If this, then that" is a branch whose blocks end in an outcome block; such a branch never runs out, so it needs no reason or 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": "outcome",
"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",
"ask_agent_owner": true
}
}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": "outcome",
"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",
"ask_agent_owner": true
},
"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. "If this, then that" is a branch whose blocks end in an outcome block; such a branch never runs out, so it needs no reason or 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; empty when the branch has none.
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.
Send the request to the person who owns the agent instance that made it, instead of escalation_path. The path, if set, is then the fallback when the request has no owner able to decide it; without one the fallback is the pipeline default.
{
"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": "outcome",
"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",
"ask_agent_owner": true
}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; empty when the branch has none.
Where the request goes when none of the branch's blocks decides.
Send the request to the person who owns the agent instance that made it, instead of escalation_path. The path, if set, is then the fallback when the request has no owner able to decide it; without one the fallback is the pipeline default.
{
"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": "outcome",
"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",
"ask_agent_owner": true
}DelegateBlockDefinition
A block that hands the requests its condition matches to another pipeline, named by key, and is skipped by every other request. A request that enters the delegated pipeline is decided inside it, or goes to a person under that pipeline's own default escalation path (else the defaults of the pipelines that delegated into it) if none of its blocks decides; nothing after the delegate runs for that request. The condition belongs to the pipeline holding the block; everything after a match belongs to the target pipeline's owners.
A key for the block, unique within the pipeline including blocks inside branches.
A display name.
Always delegate.
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 request continues in the named pipeline's active revision and is settled there. When it does not match, the delegate is skipped and the request passes to the next block. A condition that cannot be evaluated sends the request to a person on the default path of the pipeline holding the block. The named pipeline must exist when the revision is created and be active when it is activated; it can never be entry, and the activation refuses a delegation that would loop.
{
"id": "to-payments",
"name": "Payments team",
"type": "delegate",
"enabled": true,
"timeout": "50ms",
"max_attempts": 1,
"config": {
"when": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"field": "/request/arguments/amount_cents",
"operator": "exists",
"value": {}
},
"pipeline_key": "payments"
}
}StoredDelegateBlock
A delegate 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 delegate.
Always true.
How long the condition may take.
Always 1.
When the condition matches, the request continues in the named pipeline's active revision and is settled there. When it does not match, the delegate is skipped and the request passes to the next block. A condition that cannot be evaluated sends the request to a person on the default path of the pipeline holding the block. The named pipeline must exist when the revision is created and be active when it is activated; it can never be entry, and the activation refuses a delegation that would loop.
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": "delegate",
"enabled": true,
"timeout": "50ms",
"max_attempts": 1,
"config": {
"when": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"field": "/request/arguments/amount_cents",
"operator": "exists",
"value": {}
},
"pipeline_key": "payments"
},
"config_version": 1,
"content_hash": "string"
}DelegateBlockConfig
When the condition matches, the request continues in the named pipeline's active revision and is settled there. When it does not match, the delegate is skipped and the request passes to the next block. A condition that cannot be evaluated sends the request to a person on the default path of the pipeline holding the block. The named pipeline must exist when the revision is created and be active when it is activated; it can never be entry, and the activation refuses a delegation that would loop.
A condition tree. A leaf tests one field. all and any combine conditions. some and every test the items of a list field.
The pipeline that takes matching requests.
{
"when": {
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"field": "/request/arguments/amount_cents",
"operator": "exists",
"value": {}
},
"pipeline_key": "payments"
}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.
Send the request to the person who owns the agent instance that made it, instead of escalation_path. The path, if set, is then the fallback when the request has no owner able to decide it; without one the fallback is the pipeline default.
{
"endpoint_key": "string",
"allowed_outcomes": [
"next"
],
"include": {
"agent_reasoning": true,
"context": true,
"previous_metadata": true
},
"reason": "string",
"escalation_path": "string",
"ask_agent_owner": true
}OutcomeBlockConfig
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.
Send the request to the person who owns the agent instance that made it, instead of escalation_path. The path, if set, is then the fallback when the request has no owner able to decide it; without one the fallback is the pipeline default.
{
"outcome": "human",
"reason": "Requests not handled by earlier blocks need human review."
}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.
Whether the default asks the agent's owner first, as in a document.
The request to run the pipeline against.
{
"blocks": [
{
"id": "review-remaining-requests",
"name": "Review remaining requests",
"type": "outcome",
"enabled": true,
"timeout": "50ms",
"max_attempts": 1,
"config": {
"outcome": "human",
"reason": "Requests not handled by earlier blocks need human review."
}
}
],
"default_escalation_path": "string",
"default_ask_agent_owner": true,
"sample": {
"request": {
"tool": "issue_refund",
"server": "stripe",
"arguments": {
"amount_cents": 12000
},
"agent_reasoning": "string",
"context": {}
},
"agent": {
"slug": "string",
"name": "string"
},
"previous_metadata": {}
}
}RequestApprovalPipelinePreview
What the pipeline did with the sample.
Why the run ended. block_outcome: a block decided. block_error, block_timeout, retries_exhausted, invalid_block_output: a block failed. end_of_pipeline: the request passed every block. end_of_branch: the request entered a branch or a delegated pipeline and none of its blocks decided.
One of block_outcome, block_error, block_timeout, retries_exhausted, invalid_block_output, end_of_pipeline, end_of_branch
The block that ended the run: for end_of_branch, the branch or the delegate. Absent when the request passed every block.
What each block the request reached did, in order, each with the pipeline it belongs to. A branch or delegate appears when its condition is evaluated, and once more with end_of_branch when none of the blocks inside decided.
The path a human outcome goes to: the ending block's path, else the default of the pipeline the request was in, else the defaults of the pipelines that delegated into it. Absent when the outcome is automatic or no path applies.
Where the path came from. block_escalation: the ending block named it. pipeline_default: a pipeline's default. no_escalation: no path applies.
One of block_escalation, pipeline_default, no_escalation
The pipeline whose block or default supplied the path. Absent with no_escalation.
Present when the route asks the agent's owner, with escalation_path as the fallback. A preview has no real agent instance, so it names the route, never a person.
{
"outcome": "approve",
"reason_code": "block_outcome",
"terminal_block_id": "string",
"blocks": [
{
"block_id": "string",
"pipeline_key": "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",
"escalation_path_pipeline_key": "string",
"ask_agent_owner": true
}RequestApprovalPipelinePreviewBlock
What one block did in a preview.
The block's key.
The pipeline the block belongs to: the previewed document's key, or a delegated pipeline's.
The block's position in the effective pipeline, from 0, counting in document order with each branch or delegate before the blocks it holds.
What the block returned. A branch or delegate 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, input_too_large, 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",
"pipeline_key": "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, pipeline
The team id, agent slug, or pipeline key 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.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
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",
"kind": "service",
"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.
The member this instance belongs to and acts for. Present exactly when the agent is personal.
Unique within the agent.
The JSON stored when the instance was created.
The last time this instance called the API.
The verified receiver of an instance using asynchronous mode, returned when the instance is created. The signing secret is never returned.
{
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"organization_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"agent_slug": "string",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
}AgentInstanceDelivery
The verified receiver of an instance using asynchronous mode, returned when the instance is created. The signing secret is never returned.
{
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}AgentInstanceDeliveryInput
A webhook receiver for asynchronous mode. Install the registration ID and signing secret on the receiver before calling: the verification message names the registration so the receiver can pick the secret before it knows the instance ID.
The only delivery type offered. Another value answers 400 with code unsupported_delivery_type.
One of webhook
The exact HTTPS receiver URL. Private, loopback and other non-public addresses are refused, and redirects are not followed.
A UUID you generate for this configuration and associate with the secret on the receiver. It is echoed in the verification message.
whsec_ followed by 32 random bytes in padded base64. Use a separate secret for each instance. It is never returned.
{
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"signing_secret": "string"
}AgentInstanceRegistration
A pending webhook registration. Configure your receiver with the registration ID and signing secret.
The ID the verification message names. Your receiver uses it to choose the signing secret.
The receiver's signing secret: whsec_ followed by 32 random bytes in padded base64. It is never returned again.
When the registration can no longer be completed.
{
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"signing_secret": "string",
"expires_at": "2026-09-08T12:02:11Z"
}AgentInstanceRegistrationDelivery
The receiver for a registration you started. It is verified before the instance is created.
The registration's ID.
The exact HTTPS receiver URL. Private, loopback and other non-public addresses are refused, and redirects are not followed.
{
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"url": "https://harness.example.com/aap/events"
}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.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
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",
"kind": "service",
"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.
The member this instance belongs to and acts for. Present exactly when the agent is personal.
Unique within the agent.
The JSON stored when the instance was created.
The last time this instance called the API.
The verified receiver of an instance using asynchronous mode, returned when the instance is created. The signing secret is never returned.
The name of the member a personal instance belongs to. Absent for a service agent's instance.
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",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
},
"owner_display_name": "string",
"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.
Whose behalf the agent's instances act on, chosen once. A service agent is one the organization runs, such as a support bot: its instances share one identity, administrators enroll them, and provisioner tokens mint them. A personal agent is one people run for themselves, such as a coding assistant: every instance belongs to the member who enrolled it, any active member may connect their own, and the instances are disabled when that member is removed. Personal agents have no provisioner tokens.
One of service, personal
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",
"kind": "service",
"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",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
},
"owner_display_name": "string",
"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",
"kind": "service",
"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",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
},
"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",
"membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}
},
"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"
}AAPInstance
An instance as the Agent Approval Protocol returns it to a provisioner. Credentials and the signing secret are never included.
Unique within the agent.
The verified receiver of an instance using asynchronous mode, returned when the instance is created. The signing secret is never returned.
{
"id": "b29a43a7-1949-4b9f-9d61-b7ae66f85d31",
"name": "ci-runner-07",
"delivery": {
"type": "webhook",
"url": "https://harness.example.com/aap/events",
"registration_id": "8246931c-4ce0-4c15-a94a-e9c078ad06e2"
}
}AAPProvisionedInstance
A provisioned instance and its credential.
An instance as the Agent Approval Protocol returns it to a provisioner. Credentials and the signing secret are never included.
{
"instance": {
"id": "b29a43a7-1949-4b9f-9d61-b7ae66f85d31",
"name": "ci-runner-07"
},
"credential": {
"token": "whc_live_3f6b9c1d0e7a4b2c",
"expires_at": "2026-09-18T12:00:00Z"
}
}AAPDecision
Immutable decision. An approved call must start before expires_at: 30 minutes after decided_at when the instance had webhook delivery at decision time, five minutes otherwise. 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. An approved call must start before expires_at: 30 minutes after decided_at when the instance had webhook delivery at decision time, five minutes otherwise. Reads and retries never extend expires_at.
The creating instance used the approval. Present only once an approved request was consumed. An adapter that finds a consumption it did not make treats the approval as used.
{
"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"
}AAPConsumption
The creating instance used the approval. Present only once an approved request was consumed. An adapter that finds a consumption it did not make treats the approval as used.
How the approved call went, as the adapter reported it. For the record only.
{
"consumed_at": "2026-09-17T12:02:04Z",
"outcome": {
"status": "succeeded",
"note": "Refund issued",
"reported_at": "2026-09-17T12:02:06Z"
}
}AAPOutcome
How the approved call went, as the adapter reported it. For the record only.
{
"status": "succeeded",
"note": "Refund issued",
"reported_at": "2026-09-17T12:02:06Z"
}ReportApprovalOutcome
A short note about the result, such as an error message or a reference.
{
"status": "succeeded",
"note": "Refund issued"
}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 member the instance acts for, when the agent is personal: the owner of the instance at the time the request was made. Absent for a service agent's request, and absent from the response that creates the request.
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.
The creating instance used the approval. Present only once an approved request was consumed. An adapter that finds a consumption it did not make treats the approval as used.
{
"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",
"requester_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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"
},
"consumption": {
"consumed_at": "2026-09-17T12:02:04Z",
"outcome": {
"status": "succeeded",
"note": "Refund issued",
"reported_at": "2026-09-17T12:02:06Z"
}
}
}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 member the instance acts for, when the agent is personal: the owner of the instance at the time the request was made. Absent for a service agent's request, and absent from the response that creates the request.
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.
The creating instance used the approval. Present only once an approved request was consumed. An adapter that finds a consumption it did not make treats the approval as used.
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 name of the member a personal instance acted for. Absent for a service agent's request.
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",
"requester_membership_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"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"
},
"consumption": {
"consumed_at": "2026-09-17T12:02:04Z",
"outcome": {
"status": "succeeded",
"note": "Refund issued",
"reported_at": "2026-09-17T12:02:06Z"
}
},
"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"
},
"requester_display_name": "Chris",
"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"
}
]
},
"summary": {
"text": "Refund $49.00 on charge ch_3N1x for a faulty bag",
"generated_at": "2026-09-08T12:00:03Z"
}
}RequestSummary
{
"text": "Refund $49.00 on charge ch_3N1x for a faulty bag",
"generated_at": "2026-09-08T12:00:03Z"
}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,
"built_in_role": "owner",
"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,
"built_in_role": "owner",
"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",
"pipeline_key": "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.
When the revision was first active. Absent for a draft, which can still be changed.
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",
"activated_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, a webhook endpoint, or another pipeline.
The pipeline whose revision uses the object.
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",
"pipeline_key": "string",
"revision": 1,
"block_key": "string"
}PipelineReferences
What one pipeline revision depends on, and whether each dependency is in place right now. activatable is false while any entry would refuse activation.
The pipelines the revision's delegate blocks hand requests to.
The escalation paths the revision names.
The webhook endpoints the revision posts to.
Blocks the deployment or the plan cannot run right now.
Whether activating the revision would succeed right now.
{
"pipelines": [
{
"pipeline_key": "string",
"name": "string",
"block_keys": [
"string"
],
"exists": true,
"active": true
}
],
"escalation_paths": [
{
"path_key": "string",
"block_keys": [
"string"
],
"default": true,
"exists": true,
"active": true
}
],
"webhook_endpoints": [
{
"endpoint_key": "string",
"name": "string",
"block_keys": [
"string"
],
"exists": true,
"active": true
}
],
"blocks": [
{
"block_key": "string",
"type": "string",
"problem": "string"
}
],
"activatable": true
}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,
"built_in_role": "owner",
"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": [
{
"role": "owner",
"role_name": "string",
"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": [
{
"role": "owner",
"role_name": "string",
"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.
A predefined role. owner is full control and only an owner may grant it; admin manages agents, pipelines, paths, members and teams; reviewer decides the requests routed to them; auditor reads everything without changing anything.
One of owner, admin, reviewer, auditor
{
"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,
"built_in_role": "owner",
"permission_policy_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71"
}Role
A predefined role. owner is full control and only an owner may grant it; admin manages agents, pipelines, paths, members and teams; reviewer decides the requests routed to them; auditor reads everything without changing anything.
One of owner, admin, reviewer, auditor
Every permission the role holds, including the ones this edition registers.
The role holds a sensitive permission, so granting it needs a fresh sign-in.
{
"key": "owner",
"name": "Reviewer",
"description": "string",
"permissions": [
"string"
],
"dangerous": true
}InvitationGrant
Permissions granted at one scope, with provenance.
A predefined role. owner is full control and only an owner may grant it; admin manages agents, pipelines, paths, members and teams; reviewer decides the requests routed to them; auditor reads everything without changing anything.
One of owner, admin, reviewer, auditor
That role's name.
The team id or the agent slug. Absent for organization.
{
"role": "owner",
"role_name": "string",
"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 revision was first active. Absent for a draft, which can still be changed.
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",
"activated_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 revision was first active. Absent for a draft, which can still be changed.
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",
"activated_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
When the revision was first active. Absent for a draft, which can still be changed.
{
"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",
"activated_at": "2026-09-08T12:02:11Z"
}TeamEscalationPolicyRevision
When the revision was first active. Absent for a draft, which can still be changed.
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",
"activated_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. agent_owner sends it to the person who owns the agent instance that made it; escalated pins an escalation path revision and the people it reaches for this request; default_queue means any authorized reviewer.
agent_owner when the agent's owner took the request; the path fields then describe the fallback pinned beside it. escalated when an escalation path took the request. default_queue when no path applied and every reviewer who can decide was asked.
One of agent_owner, escalated, default_queue
How the outcome came about. block_escalation: the pipeline block that asked for a person named the route. pipeline_default: the pipeline's default route 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, or for agent_owner the fallback pinned beside the owner. Absent for default_queue.
The name of the path revision that took the request, or the fallback revision for agent_owner. Absent for default_queue.
The path revision that was active at the time. Absent for default_queue, and for agent_owner when the fallback path had no active revision.
The pipeline revision that routed the request. Absent for default_queue.
The pipeline block that named the path. Present for block_escalation only.
One person or team the pinned path reaches for the request.
Why a route that asks the agent's owner went to its fallback path when the request arrived. no_agent_owner: a service agent made it. agent_owner_inactive: the owner was suspended or removed. agent_owner_cannot_decide: the owner may not decide requests.
One of no_agent_owner, agent_owner_inactive, agent_owner_cannot_decide
When the agent's owner was suspended or removed and the fallback path took over the request. Present for agent_owner only.
Who may claim or decide the request. For agent_owner, the owner alone until a handoff. Otherwise 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",
"agent_owner": {
"type": "user",
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "string"
},
"agent_owner_fallback": "no_agent_owner",
"agent_owner_handed_off_at": "2026-09-08T12:02:11Z",
"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. agent_owner sends it to the person who owns the agent instance that made it; 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",
"agent_owner": {
"type": "user",
"id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"name": "string"
},
"agent_owner_fallback": "no_agent_owner",
"agent_owner_handed_off_at": "2026-09-08T12:02:11Z",
"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,
"included_in_plan": 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.
The scopes the agent may call with; any granted scope works with any granted tool. Empty exactly when mode is none.
Expected current version; zero for an unconfigured agent/server pair.
{
"mode": "none",
"tools": [
"string"
],
"scopes": [
"string"
],
"revision": 0
}AgentToolAccess
One of none, selected, all
{
"server_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
"server_slug": "string",
"mode": "none",
"tools": [
"string"
],
"scopes": [
"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.
Prompts for the request that a notification window held and sent when it closed, oldest first, one per person and contact kind.
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": {
"pipeline_key": "string",
"name": "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"
}
],
"digests": [
{
"membership_id": "string",
"display_name": "string",
"kind": "string",
"request_count": 1,
"window_opened_at": "2026-09-08T12:02:11Z",
"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, delegate
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 pipeline that held the terminal block (the entry pipeline, or one a delegate handed the request to), at the revision pinned for the request. 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": {
"pipeline_key": "string",
"name": "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"
}DigestEvent
A prompt a notification window held and sent when it closed, alone (request_count 1) or in a digest naming request_count requests.
The person's name when the window closed.
The contact kind the window belongs to, such as email, slack, discord, sms, whatsapp or push.
How many requests the message named.
{
"membership_id": "string",
"display_name": "string",
"kind": "string",
"request_count": 1,
"window_opened_at": "2026-09-08T12:02:11Z",
"occurred_at": "2026-09-08T12:02:11Z"
}