DocsAPI reference
OpenAPI schema
Read or download the OpenAPI contract for the published withHuman API.
Updated Sep 23, 2026
openapi.yaml
openapi: 3.1.0
info:
title: withHuman provider API
version: 0.1.0
description: Agent Approval Protocol and reviewer product endpoints.
servers:
- url: /
tags:
- name: Agent Approval Protocol
description: What an adapter calls on behalf of a running agent. Create an instance and get its
credential, ask for approval of a tool call, and wait for the answer.
- name: Review queue
description: The review queue is where approval requests wait for a person to approve or deny them.
These endpoints list the queue, retrieve one request, claim it while you look at it, and
record your decision.
- name: Agent enrollment
description: Connect an agent from a developer machine. The agent asks for a code, a person approves
it in the browser, and the agent exchanges the code for a credential. A withHuman extension,
not part of the protocol.
- name: Account
description: Who the session belongs to. Read the current member, their organization and permissions.
- name: Agents
description: Agents are the AI systems that ask for approval. Each agent has instances, and each
instance holds a credential it uses to call the API.
- name: Approval pipelines
description: The ordered list of blocks every approval request runs through. A block can decide the
request itself, ask a service of yours, hand it to a person, or, as a branch, run blocks of
its own for the requests that match its condition. The hosted edition adds a block that asks a
model.
- name: Escalation paths
description: An escalation path decides who reviews a request once the pipeline hands it to a
person, and what happens when nobody answers in time.
- name: Webhook endpoints
description: A webhook endpoint is a URL on a service of yours that a pipeline's webhook block can
ask for an outcome.
- name: Members
description: The people in the organization, their status, and their permissions.
- name: Teams
description: Groups of members. A team can be an escalation target, and its members inherit the
team's permissions.
- name: Invitations
description: Invite people to the organization by email.
- name: API keys
description: "Personal API keys let a script act as you outside the browser: a whk_ bearer that
carries a subset of your own permissions, changes with your permissions, and dies with your
membership. Mint one, list yours, revoke it, and verify what a key can do."
- name: Audit
description: The organization's append-only log of what happened and who did it, and the request
timeline it tells.
paths:
/auth/agent_enrollments:
post:
tags:
- Agent enrollment
summary: Start an enrollment
description: Starts an enrollment and returns two codes. The `enrollment_code` stays on the machine.
The `user_code` is for a person to enter at the `verification_uri`. Both expire at
`expires_at`, ten minutes after this call. The person selects an existing agent or creates
one in the browser, then authorizes the instance. An optional requested agent name fixes the
target within the organization they sign into.
operationId: beginAgentEnrollment
x-withhuman-permission: public
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- runtime
- instance_name
properties:
runtime:
type: string
minLength: 1
example: claude-code
description: The runtime being installed, independent of its parent agent.
requested_agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: codex
description: Optional slug of an existing agent. The browser must authorize this target; it cannot
create or substitute another agent.
instance_name:
type: string
example: eces-macbook
description: A name for this instance, unique within the agent. The CLI defaults to the machine's
hostname plus a unique suffix. Reviewers see it next to the agent's name.
instance_metadata:
type: object
additionalProperties: true
example:
hostname: eces-macbook
os: darwin
description: Any JSON object to store with the instance, such as the hostname or operating system.
responses:
"201":
description: The codes and where to use them
content:
application/json:
schema:
$ref: "#/components/schemas/EnrollmentCode"
"400":
description: A required field is missing or blank
/auth/agent_enrollments/token:
post:
tags:
- Agent enrollment
summary: Exchange an enrollment code
description: Exchanges an authorized enrollment code for the new instance and its credential. Call
this every few seconds after starting an enrollment. It returns `404` until a person has
authorized the user code. Once it succeeds, the code is used up and a second call returns
`404` as well. The credential's token appears only in this response, so store it right away.
operationId: exchangeAgentEnrollment
x-withhuman-permission: public
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- enrollment_code
properties:
enrollment_code:
type: string
example: whe_7c1f2a9e-4b3d-4f2e-9a1c-2d6e8b5f0a11_3f6b9c1d0e7a4b2c
description: The `enrollment_code` returned when the enrollment started.
responses:
"200":
description: The instance and its credential. The credential's token appears only in this response.
content:
application/json:
schema:
$ref: "#/components/schemas/AgentEnrollmentCredential"
"401":
description: The enrollment code is not in the expected format
"403":
description: The agent or the instance has been disabled since it was authorized
"404":
description: The code has not been authorized yet, has expired, or has already been exchanged
/api/aap/v1/instances:
post:
summary: Create an agent instance
description: Creates a new instance of the agent the provisioner token belongs to and returns it
together with its agent credential. Use this from a fleet or platform that starts agent
replicas itself, so each replica gets a credential of its own. The bearer token must be a
provisioner token (`whp_`); an agent credential is rejected.
operationId: mintAgentInstance
x-withhuman-permission: public
tags:
- Agent Approval Protocol
security:
- bearerAuth: []
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
instance_name:
type: string
example: ci-runner-07
description: A name for the instance, unique within the agent. Reviewers see it next to the agent's
name.
instance_metadata:
type: object
additionalProperties: true
example:
region: eu-west
description: Any JSON object to store with the instance, such as a region or pod name.
ttl_seconds:
type: integer
format: int64
minimum: 0
example: 86400
description: How long the credential stays valid, in seconds. A value above the provider's default
is reduced to the default.
responses:
"201":
description: The new instance and its credential. The credential's token appears only in this
response.
content:
application/json:
schema:
$ref: "#/components/schemas/AgentInstanceCredential"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"409":
description: An instance with this name already exists for the agent
/api/aap/v1/requests:
post:
summary: Create an approval request
operationId: createApprovalRequest
x-withhuman-permission: request.create
description: "Asks for approval to run a tool call. The organization's approval pipeline evaluates
the request first and may approve or deny it on the spot, in which case the response already
carries the decision. Otherwise the request goes to reviewers and comes back as `pending`;
retrieve it with a `wait` to learn the outcome. A request nobody decides before its timeout
becomes `expired`: the adapter does not run the call and tells the agent the approval timed
out. An adapter that stops waiting should cancel the request so reviewers stop seeing it.
Requires an `Idempotency-Key` header scoped to this operation and the authenticated
instance. Reusing it with changed input returns 409."
tags:
- Agent Approval Protocol
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateApproval"
responses:
"201":
description: The request, either already decided by the pipeline or pending review
content:
application/json:
schema:
$ref: "#/components/schemas/AAPApprovalRequest"
examples:
pending:
$ref: "#/components/examples/AAPPendingRequest"
approved:
$ref: "#/components/examples/AAPApprovedRequest"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"409":
description: The idempotency key was already used for a different request
"429":
description: The organization has reached its limit of pending requests
/api/aap/v1/requests/{id}:
get:
summary: Retrieve an approval request
operationId: getApprovalDecision
x-withhuman-permission: request.read
description: Returns the request and, once it has been decided, its decision. Pass `wait` to hold
the response until the request is decided or the wait runs out, whichever comes first. Only
the instance that created the request may retrieve it. A request stops changing once it is
`approved`, `denied`, `expired`, or `cancelled`.
tags:
- Agent Approval Protocol
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
- name: wait
in: query
description: How long to hold the response for a decision, as a duration such as `30s`. Capped at 30
seconds. Omit to return immediately.
schema:
type: string
example: 30s
responses:
"200":
description: The request in its current state
content:
application/json:
schema:
$ref: "#/components/schemas/AAPApprovalRequest"
examples:
pending:
$ref: "#/components/examples/AAPPendingRequest"
approved:
$ref: "#/components/examples/AAPApprovedRequest"
"401":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
delete:
summary: Cancel an approval request
operationId: cancelApprovalRequest
x-withhuman-permission: request.create
description: "Withdraws a pending request. Use it when the runtime that asked stops waiting for the
answer: it was interrupted, timed out locally, or is shutting down. Reviewers stop seeing
the request, notifications stop, and anyone holding it is released. Only the instance that
created the request may cancel it; another instance of the same agent gets a `403`. A
cancelled request is final and answers `cancelled` with a protocol decision, so a long poll
in flight returns at once. Cancelling again returns the same cancelled request. A request
that was already `approved`, `denied`, or `expired` is unchanged and answers `409` with code
`already_terminal`. This is an optional capability of the Agent Approval Protocol: adapters
must treat a `404` or `405` from a provider that does not offer it as nothing to do."
tags:
- Agent Approval Protocol
security:
- bearerAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
responses:
"200":
description: The request, now cancelled
content:
application/json:
schema:
$ref: "#/components/schemas/AAPApprovalRequest"
examples:
cancelled:
$ref: "#/components/examples/AAPCancelledRequest"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
description: The request was already decided or expired (code `already_terminal`)
/api/v1/requests:
get:
summary: List the review queue
description: >-
Returns the list of approval requests that reached human review: pending, decided by a
person, or expired while waiting. Approval requests the pipeline decided automatically are
not included. They stay in the audit log and can still be retrieved by id. Requests are
returned newest first.
Pass `agent_slug` to list one agent's requests instead. That list includes the requests the
pipeline decided automatically.
operationId: listReviewerQueue
x-withhuman-permission: request.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Review queue
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: limit
in: query
description: How many requests to return. Defaults to 50, at most 200.
schema:
type: integer
minimum: 1
maximum: 200
- name: agent_slug
in: query
required: false
description: Show one agent's requests instead of the queue, including the ones the pipeline decided.
schema:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
- name: offset
in: query
description: Number of visible matching requests to skip before returning this page.
schema:
type: integer
minimum: 0
default: 0
- name: q
in: query
description: Case-insensitive literal substring of the tool or instance name.
schema:
type: string
- name: status
in: query
description: Return only requests with this outcome.
schema:
type: string
enum:
- pending
- approved
- denied
- expired
- cancelled
- name: scope
in: query
description: "Which layer of the queue to return, from the caller's standpoint. `notified` is what
the caller was told about: requests a notification addressed to them, or a colleague
handed them. `decidable` is what the caller may decide outright: requests routed to them
directly, through a team, or by being added, plus unrouted requests where any reviewer
may take them. `visible` (the default) is everything the caller may see, whoever decides
it. Each layer contains the one before it. Ignored for API keys, which have no
membership."
schema:
type: string
enum:
- notified
- decidable
- visible
responses:
"200":
description: The requests, newest first
content:
application/json:
schema:
type: object
required:
- requests
properties:
requests:
type: array
items:
$ref: "#/components/schemas/QueueApprovalRequest"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
description: You cannot read requests
/api/v1/me:
get:
summary: Retrieve the current member
description: "Returns who the session belongs to. The `principal` is what the API checks permissions
against on every call. The rest is for display: the person's name and email, the
organization, and the role assignments the membership holds. Call this after signing in to
learn what the session can do."
operationId: currentPrincipal
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Account
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: The member behind the session
content:
application/json:
schema:
type: object
required:
- principal
properties:
principal:
$ref: "#/components/schemas/Principal"
user:
type: object
description: The person who signed in.
properties:
display_name:
type: string
example: Ada Lovelace
description: The name shown in the product.
email:
type: string
example: [email protected]
description: The primary email address on the account.
has_password:
type: boolean
example: true
description: Whether the account has a password. False for an account that only signs in through an
identity provider; such an account cannot change or reset a password.
organization:
type: object
description: The organization the session is scoped to.
properties:
name:
type: string
example: Northwind
description: The organization's display name.
slug:
type: string
example: northwind
description: The organization's URL-safe identifier.
assignments:
type: array
description: "The membership's role assignments: its own, plus the ones it inherits through its
teams. The `grants` in the principal are flattened from these."
items:
$ref: "#/components/schemas/RoleAssignment"
onboarding:
type: object
description: Whether the product's guided setup should open for this person.
required:
- required
- completed_at
properties:
required:
type: boolean
example: false
description: "`true` while nobody in the organization has finished or skipped the guided setup and
this person has the permissions to run it."
completed_at:
type:
- string
- "null"
format: date-time
description: When the organization finished or skipped the guided setup. `null` until then.
"401":
description: No session cookie, or the session has expired or been signed out
/api/v1/requests/{id}:
get:
summary: Retrieve a request
description: Returns one approval request. The response includes its current status, its decision
once there is one, and any claim a reviewer holds on it. Pass `wait` to hold the response
until the request is decided.
operationId: getProductApprovalRequest
x-withhuman-permission: request.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Review queue
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
- name: wait
in: query
description: How long to hold the response for a decision, as a duration such as `30s`. Capped at 30
seconds. Omit to return immediately.
schema:
type: string
example: 30s
responses:
"200":
description: The request in its current state
content:
application/json:
schema:
$ref: "#/components/schemas/QueueApprovalRequest"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
description: You cannot read requests
"404":
$ref: "#/components/responses/Error"
description: No such request
/api/v1/requests/{id}/claim:
post:
summary: Claim a request
operationId: claimApprovalRequest
x-withhuman-permission: request.claim
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Review queue
description: >-
Marks a pending request as being looked at by you. Other reviewers see who has it, and its
notifications pause. A claim is advisory: anyone allowed to decide the request can still
decide it.
A claim lapses after the organization's claim timeout, 15 minutes by default. It also lapses
at the request's deadline if that comes first. Claiming a request you already hold returns
the same claim. You cannot claim a request another reviewer holds. Wait for their claim to
lapse, or for them to release it.
Owners and reviewers can claim. If the request was routed to an escalation path, only the
people on that path can claim it. Owners can always claim.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
responses:
"200":
description: The claim you hold
content:
application/json:
schema:
type: object
required:
- claim
properties:
claim:
$ref: "#/components/schemas/Claim"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
description: You cannot claim this request. Your membership is inactive, you lack permission to
decide, or the request was routed to other people
"404":
$ref: "#/components/responses/Error"
description: No such request
"409":
$ref: "#/components/responses/Error"
description: Another reviewer holds the claim, or the request is no longer pending
delete:
summary: Release a claim
operationId: releaseApprovalRequestClaim
x-withhuman-permission: request.claim
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Review queue
description: Releases your claim on a request. Other reviewers then see it as unclaimed. Only the
reviewer holding a claim can release it. Other reviewers' claims lapse on their own.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
responses:
"204":
description: The claim is released
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
description: Another reviewer holds the claim
"404":
$ref: "#/components/responses/Error"
description: No live claim on this request
/api/v1/requests/{id}/decision:
post:
summary: Approve or deny a request
operationId: decideApprovalRequest
x-withhuman-permission: request.decide
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Review queue
description: >-
Records your decision on a pending request and returns once it is durably stored. The agent
waiting on the request sees the decision on its next read.
Every call needs an `Idempotency-Key` header. Repeating a call with the same key returns the
decision already recorded.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- status
properties:
status:
type: string
enum:
- approved
- denied
description: The decision.
note:
type: string
example: OK, but flag this account for review.
description: A note for the agent. It is returned with the decision.
channel:
type: string
default: web
example: web
description: Where the decision was made, recorded on the decision, for example `web` or `slack`.
Defaults to `web`.
outside_routing:
type: boolean
default: false
description: The reviewer's explicit acknowledgement that they are deciding outside the request's
routing (break glass). Required, alongside request.decide.unrouted, when routing
did not hand them the request; without it such a decision is refused with reason
outside_routing_required. Part of the decision's intent under the idempotency
key.
responses:
"200":
description: The recorded decision
content:
application/json:
schema:
$ref: "#/components/schemas/DecisionOperation"
"400":
$ref: "#/components/responses/Error"
description: The `Idempotency-Key` header is missing, or `status` is not `approved` or `denied`
"401":
$ref: "#/components/responses/Error"
"403":
description: You cannot decide this request. Your membership is inactive, you lack permission to
decide, the request was routed to other people, or your sign-in is too old or not strong
enough. A routing refusal carries RoutingRejectionDetails in error.details, whose reason
is not_targeted, break_glass_only, or outside_routing_required.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
$ref: "#/components/responses/Error"
description: No such request
"409":
$ref: "#/components/responses/Error"
description: The request already has a decision or has expired, or the idempotency key was already
used with different parameters
/api/v1/requests/{id}/routing:
get:
summary: Retrieve a request's routing
operationId: getApprovalRouting
x-withhuman-permission: request.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Review queue
description: >-
Returns who was asked to review a request when it entered human review. If an escalation
path took the request, the people on that path were notified, and only they can decide it.
If no path applied, every reviewer who can decide was notified, and any of them can decide
it. Routing is fixed when the request enters human review. Later changes to pipelines or
paths do not move it.
Requests the pipeline decided automatically never entered human review and have no routing.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
responses:
"200":
description: Where the request was routed
content:
application/json:
schema:
$ref: "#/components/schemas/ApprovalRouting"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
description: You cannot read requests
"404":
$ref: "#/components/responses/Error"
description: No such request, or it never reached human review
/api/v1/requests/{id}/reviewer-candidates:
get:
summary: List who a request could be handed to
operationId: listReviewerCandidates
x-withhuman-permission: request.decide
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Review queue
description: Active members who hold request.decide and are not yet reviewers of the request. Gated
like deciding rather than by membership.read, so a targeted reviewer without the members
page can still hand off.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
responses:
"200":
description: Candidates sorted by name
content:
application/json:
schema:
type: object
required:
- candidates
properties:
candidates:
type: array
items:
type: object
required:
- membership_id
- display_name
- email
properties:
membership_id:
type: string
format: uuid
display_name:
type: string
email:
type: string
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
/api/v1/requests/{id}/reviewers:
post:
summary: Add a reviewer to a pending request
operationId: addApprovalReviewer
x-withhuman-permission: request.decide
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Review queue
description: The handoff. Someone the request was routed to (or a holder of request.decide.unrouted)
pulls one more active member who may decide into the request's routing. Widening never
removes anyone and is audited as request.reviewer_added. Refused when the request is not
pending or was never routed (409), when the actor is not targeted (403), or when the member
is inactive or cannot decide (400).
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- membership_id
properties:
membership_id:
type: string
format: uuid
responses:
"200":
description: The request's review after the widening
content:
application/json:
schema:
$ref: "#/components/schemas/ApprovalReview"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
/api/v1/agents:
get:
summary: List agents
description: >-
Returns the organization's agents. Each entry carries the agent's instance counts, when it
was last seen, and how many requests it has made. Active agents come first, then disabled
ones, then archived ones, each group sorted by name. Archived agents are left out unless
`status` says otherwise.
An agent is live when one of its instances holds a credential that is neither expired nor
revoked. `last_seen_at` is the newest authenticated call from any of its instances. Agents
only call in when they have something to approve, so a quiet agent is not a broken one.
operationId: listAgents
x-withhuman-permission: agent.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: status
in: query
required: false
description: Which agents to return. `live` (the default) returns active and disabled agents,
`archived` returns only archived ones, and `all` returns every agent.
schema:
type: string
enum:
- live
- archived
- all
default: live
- name: limit
in: query
description: How many agents to return. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The agents
content:
application/json:
schema:
type: object
required:
- agents
properties:
agents:
type: array
items:
$ref: "#/components/schemas/AgentSummary"
"400":
$ref: "#/components/responses/Error"
description: "`status` is not live, archived, or all"
"403":
$ref: "#/components/responses/Error"
description: You cannot read agents
post:
summary: Create an agent
description: >-
Creates an agent. An agent is a stable identity for one AI system, such as a support bot or
a deploy assistant. Its slug is the identity: chosen here, never changed, never reused.
Every agent owns one approval pipeline, created empty with the agent.
Creating an agent does not issue a credential. Register an instance, or create a provisioner
token, to let it call the API.
operationId: createAgent
x-withhuman-permission: agent.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- slug
- name
properties:
slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: support-bot
description: The agent's identity. Lowercase letters, digits, dots, underscores and hyphens, up to
63 characters. It cannot change and is never reused, so a slug an archived agent
holds is refused.
name:
type: string
example: Support bot
description: A display name. Reviewers see it on every request the agent makes. It can be changed
later and must be unique among the organization's live agents.
responses:
"201":
description: The new agent
content:
application/json:
schema:
$ref: "#/components/schemas/Agent"
"403":
$ref: "#/components/responses/Error"
description: You cannot create agents
"409":
$ref: "#/components/responses/Error"
description: The slug is taken by a live or archived agent (`agent_slug_taken`), or a live agent
already has the name (`agent_name_taken`)
"429":
description: The organization is at its agent limit
/api/v1/agents/{slug}:
get:
summary: Retrieve an agent
description: Returns one agent with a page of instances, each instance's credentials, and its
provisioner tokens. Secrets are never included. Instances are sorted by last seen, then
creation time and id, newest first. Counts and connection state cover all instances,
independent of the page and filters.
operationId: getAgent
x-withhuman-permission: agent.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlug"
- name: instance_limit
in: query
description: Number of instances in this page.
schema:
type: integer
minimum: 1
maximum: 200
default: 25
- name: instance_offset
in: query
description: Number of matching instances to skip.
schema:
type: integer
minimum: 0
default: 0
- name: instance_q
in: query
description: Case-insensitive literal substring of the instance name.
schema:
type: string
- name: instance_status
in: query
description: Filter by whether an instance is enabled or disabled, independently of its credentials.
schema:
type: string
enum:
- active
- disabled
- name: provisioner_limit
in: query
description: Number of provisioner tokens in this page, sorted by issued time and id, newest first.
schema:
type: integer
minimum: 1
maximum: 200
default: 25
- name: provisioner_offset
in: query
description: Number of matching provisioner tokens to skip.
schema:
type: integer
minimum: 0
default: 0
- name: provisioner_q
in: query
description: Case-insensitive literal substring of the token identifier or displayed identifier
suffix. Searches identifiers, never secrets.
schema:
type: string
- name: provisioner_status
in: query
description: Token state, with revocation taking precedence over expiry.
schema:
type: string
enum:
- active
- expired
- revoked
responses:
"200":
description: The agent
content:
application/json:
schema:
$ref: "#/components/schemas/AgentDetail"
"400":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
description: You cannot read agents
"404":
$ref: "#/components/responses/Error"
description: No such agent
patch:
summary: Update an agent
operationId: updateAgent
x-withhuman-permission: agent.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Changes an agent's status or its display name, the two fields that can change after
creation. The slug never changes. Who manages an agent is decided by the role assignments at
the agent, not by a property of it.
Disabling an agent keeps its instances and credentials in place, but none of them can
authenticate until the agent is enabled again. An archived agent is past both switches;
restore it first.
parameters:
- $ref: "#/components/parameters/AgentSlug"
requestBody:
required: true
content:
application/json:
schema:
type: object
minProperties: 1
properties:
status:
type: string
enum:
- active
- disabled
description: "`active` or `disabled`."
name:
type: string
example: Support bot
description: A new display name, unique among the organization's live agents. Audited as
`agent.renamed`.
responses:
"200":
description: The updated agent
content:
application/json:
schema:
$ref: "#/components/schemas/Agent"
"400":
$ref: "#/components/responses/Error"
description: Neither status nor name was given, or the status is invalid
"403":
$ref: "#/components/responses/Error"
description: You cannot change agents
"404":
$ref: "#/components/responses/Error"
description: No such agent
"409":
$ref: "#/components/responses/Error"
description: The agent is archived (`agent_archived`), or a live agent already has the name
(`agent_name_taken`)
delete:
summary: Archive an agent
operationId: archiveAgent
x-withhuman-permission: agent.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Archives the agent. Every live credential and provisioner token is revoked. Nothing is
deleted: the agent is still listed with `status=archived` or `all`, it can still be
retrieved with its instances and revoked credentials, and its requests and audit trail stay
readable. But no instance or provisioner token can be created for it and its status cannot
change. Its name is freed for a new agent; its slug is not, and never will be.
The agent's approval pipeline is archived with it: every revision is stamped and the active
one deactivated. The archived revisions stay readable but cannot be activated. Restore the
agent to bring the pipeline back.
parameters:
- $ref: "#/components/parameters/AgentSlug"
responses:
"204":
description: The agent is archived
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot change agents
"404":
$ref: "#/components/responses/Error"
description: No such agent
"409":
$ref: "#/components/responses/Error"
description: The agent is already archived (`agent_archived`)
/api/v1/agents/{slug}/restore:
post:
summary: Restore an archived agent
operationId: restoreAgent
x-withhuman-permission: agent.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
description: "Brings an archived agent back to `active`, together with its approval pipeline: every
archived revision is unfrozen and the latest one becomes active again. Credentials and
provisioner tokens revoked when the agent was archived stay revoked; register instances or
connect them again to let it call the API. The name must still be free among the
organization's live agents."
parameters:
- $ref: "#/components/parameters/AgentSlug"
responses:
"200":
description: The restored agent
content:
application/json:
schema:
$ref: "#/components/schemas/Agent"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot change agents
"404":
$ref: "#/components/responses/Error"
description: No such agent
"409":
$ref: "#/components/responses/Error"
description: The agent is not archived (`agent_not_archived`), or a live agent took its name
meanwhile (`agent_name_taken`)
/api/v1/agent_instances/{id}:
patch:
summary: Disable or enable an instance
operationId: setAgentInstanceStatus
x-withhuman-permission: agent.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
description: Disables or re-enables one instance. A disabled instance keeps its credentials, but
none of them can authenticate until it is enabled again. The agent and its other instances
are not affected.
parameters:
- $ref: "#/components/parameters/AgentInstanceID"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- status
properties:
status:
type: string
enum:
- active
- disabled
description: "`active` or `disabled`."
responses:
"200":
description: The updated instance
content:
application/json:
schema:
$ref: "#/components/schemas/AgentInstance"
"400":
$ref: "#/components/responses/Error"
description: "`status` is not `active` or `disabled`"
"403":
$ref: "#/components/responses/Error"
description: You cannot change agents
"404":
$ref: "#/components/responses/Error"
description: No such instance
"409":
$ref: "#/components/responses/Error"
description: The instance's agent is archived (`agent_archived`)
/api/v1/agents/{slug}/instances:
post:
summary: Register an instance
description: >-
Registers an instance of an agent and issues its credential. An instance is one running copy
of the agent, such as a CI runner or a pod. The credential's token is returned once, in this
response. Store it where the instance can read it. It cannot be retrieved again.
Use this when you set up an instance by hand. For a fleet that starts replicas itself,
create a provisioner token instead and let each replica create its own instance through the
Agent Approval Protocol (AAP).
operationId: createAgentInstance
x-withhuman-permission: agent.credential.issue
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlug"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: ci-runner-07
description: A name for the instance, unique within the agent. Reviewers see it next to the agent's
name.
metadata:
type: object
additionalProperties: true
example:
region: eu-west
description: Any JSON object to store with the instance, such as a region or pod name.
responses:
"201":
description: The new instance and its credential. The token appears only in this response.
content:
application/json:
schema:
$ref: "#/components/schemas/AgentInstanceCredential"
"403":
$ref: "#/components/responses/Error"
description: You cannot issue credentials
"404":
$ref: "#/components/responses/Error"
description: No such agent
"409":
$ref: "#/components/responses/Error"
description: The agent is archived (`agent_archived`)
/api/v1/agents/{slug}/provisioners:
post:
summary: Create a provisioner token
description: >-
Creates a provisioner token for an agent. A provisioner token lets a fleet or platform
create instances of this agent on its own, each with a credential of its own, through the
Agent Approval Protocol (AAP). It cannot create approval requests itself.
The token is returned once, in this response. It cannot be retrieved again.
operationId: createAgentProvisioner
x-withhuman-permission: agent.credential.issue
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlug"
responses:
"201":
description: The provisioner and its token. The token appears only in this response.
content:
application/json:
schema:
$ref: "#/components/schemas/AgentProvisionerCredential"
"403":
$ref: "#/components/responses/Error"
description: You cannot issue credentials
"404":
$ref: "#/components/responses/Error"
description: No such agent
"409":
$ref: "#/components/responses/Error"
description: The agent is archived (`agent_archived`)
/api/v1/agent-provisioners/{id}:
delete:
summary: Revoke a provisioner token
description: Revokes a provisioner token. It stops working immediately. The instances it already
created, and their credentials, are not affected.
operationId: revokeAgentProvisioner
x-withhuman-permission: agent.credential.revoke
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/ProvisionerID"
responses:
"204":
description: The token is revoked
"403":
$ref: "#/components/responses/Error"
description: You cannot revoke credentials
"404":
$ref: "#/components/responses/Error"
description: No such provisioner
/api/v1/agent-credentials/{id}:
delete:
summary: Revoke a credential
description: Revokes one instance credential. It stops working immediately. The instance stays
registered, and the agent and its other instances are not affected.
operationId: revokeAgentCredential
x-withhuman-permission: agent.credential.revoke
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Agents
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/CredentialID"
responses:
"204":
description: The credential is revoked
"403":
$ref: "#/components/responses/Error"
description: You cannot revoke credentials
"404":
$ref: "#/components/responses/Error"
description: No such credential
/api/v1/oauth_authorizations/preview:
post:
summary: Preview an MCP client's authorization request
operationId: previewOAuthAuthorization
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
description: "What the consent page shows for the request an MCP client sent the browser to: the
client, the permissions it asked for that the person could grant, the person's full
vocabulary, and whether keys may be scoped or are allowed at all. The whole request is
validated first, so a redirect URI the client did not register is refused before anything is
shown."
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthAuthorizationRequest"
responses:
"200":
description: The consent preview
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthAuthorizationPreview"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
/api/v1/oauth_authorizations:
post:
summary: Approve or decline an MCP client
operationId: createOAuthAuthorization
x-withhuman-permission: api_key.issue
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
description: The person's decision on the consent page. Approval mints a personal API key named
after the client, narrowed to the chosen permissions where scoping is available, under the
same freshness and MFA rules as a hand-made key, and answers the client's redirect URI
carrying a single-use code. Declining answers the redirect URI carrying access_denied. The
browser navigates to the answer.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OAuthAuthorizationDecision"
responses:
"200":
description: Where the browser goes next
content:
application/json:
schema:
type: object
required:
- redirect_url
properties:
redirect_url:
type: string
format: uri
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
/api/v1/request-approval-pipelines:
get:
summary: List pipelines
description: >-
Returns every pipeline in the organization: the organization pipeline and one pipeline per
agent. Each entry carries the active revision number and a summary of its history.
A pipeline exists as soon as the organization or the agent does, and it always has an active
revision. An empty revision passes every request through to a person. The one exception is
an archived agent: its pipeline is archived with it, has no active revision, carries
`archived_at`, and is left out unless `status` says otherwise. Restoring the agent brings it
back.
operationId: listRequestApprovalPipelines
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: status
in: query
required: false
description: Which pipelines to return. `live` (the default) leaves archived pipelines out,
`archived` returns only them, and `all` returns both.
schema:
type: string
enum:
- live
- archived
- all
default: live
- name: limit
in: query
description: How many pipelines to return. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The pipelines
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- request_approval_pipelines
properties:
request_approval_pipelines:
type: array
items:
$ref: "#/components/schemas/RequestApprovalPipelineSummary"
"400":
$ref: "#/components/responses/Error"
description: "`status` is not live, archived, or all"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
/api/v1/request-approval-pipelines/organization:
get:
summary: Retrieve the active organization pipeline
description: >-
Returns the organization pipeline's active revision, with its blocks in the order they run.
The organization pipeline runs first, for every request, before the pipeline of the
request's agent.
The `ETag` header carries the active revision number. Pass it as `If-Match` when you
activate another revision.
operationId: getActiveOrganizationRequestApprovalPipeline
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: The active revision
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
"404":
$ref: "#/components/responses/Error"
description: No revision is active
/api/v1/request-approval-pipelines/organization/preview:
post:
summary: Preview an organization pipeline
operationId: previewOrganizationRequestApprovalPipeline
x-withhuman-permission: pipeline.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
description: >-
Runs a pipeline document against a sample request and returns what each block would do and
the final outcome. Nothing is saved: not the document, not the sample, not the result. Use
it to test a revision before you create it.
The sample names an agent by slug and name. For the organization pipeline the slug can be
any valid slug; for an agent's pipeline it must be that agent's.
security:
- cookieAuth: []
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelinePreviewRequest"
responses:
"200":
description: What the pipeline would do with the sample
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelinePreview"
"400":
$ref: "#/components/responses/Error"
description: The document or the sample is invalid
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit pipelines
/api/v1/request-approval-pipelines/organization/revisions:
get:
summary: List organization pipeline revisions
description: >-
Returns the organization pipeline's revisions, newest first, without their blocks.
Paging is by cursor. When more revisions follow, the response carries `next_cursor`. Pass it
back as `cursor` to get the next page. The last page has no `next_cursor`. `total_count` is
how many revisions there are across every page.
operationId: listOrganizationRequestApprovalPipelineRevisions
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: cursor
in: query
description: The `next_cursor` from the previous page.
schema:
type: string
- name: limit
in: query
description: How many revisions to return per page. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 200
responses:
"200":
description: The revisions, newest first
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- revisions
- total_count
properties:
revisions:
type: array
description: The revisions on this page, newest first.
items:
$ref: "#/components/schemas/RequestApprovalPipelineRevisionSummary"
next_cursor:
type: string
description: Present when another page follows.
total_count:
type: integer
format: int64
description: How many revisions the pipeline has, across every page.
"400":
$ref: "#/components/responses/Error"
description: "`cursor` is not a cursor this endpoint issued"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
post:
summary: Create an organization pipeline revision
operationId: createOrganizationRequestApprovalPipelineRevision
x-withhuman-permission: pipeline.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
description: Creates a new revision of the organization pipeline from a complete document. The
document is validated and compiled first. The revision is created inactive, and it never
changes. Activate it to put it into use.
security:
- cookieAuth: []
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineDocument"
responses:
"201":
description: The new revision, inactive
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"400":
$ref: "#/components/responses/Error"
description: The document is invalid
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit pipelines
/api/v1/request-approval-pipelines/organization/revisions/{revision}:
get:
summary: Retrieve an organization pipeline revision
description: Returns one revision of the organization pipeline, active or not, with its blocks in
the order they run.
operationId: getOrganizationRequestApprovalPipelineRevision
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PipelineRevision"
responses:
"200":
description: The revision
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` is not a positive integer"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
"404":
$ref: "#/components/responses/Error"
description: No such revision
/api/v1/request-approval-pipelines/organization/revisions/{revision}/activate:
post:
summary: Activate an organization pipeline revision
operationId: activateOrganizationRequestApprovalPipelineRevision
x-withhuman-permission: pipeline.activate
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
description: >-
Makes a revision the active one. New requests use it from then on. Activating an older
revision is how you roll back.
Pass the revision you expect to be active in `If-Match`, quoted, as returned in `ETag`. If
someone activated another revision in the meantime, the call fails with 412 and nothing
changes.
Every escalation path the revision names, and every webhook endpoint its blocks post to,
must have an active revision. Otherwise the call fails with 409 and nothing changes.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PipelineRevision"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"200":
description: The revision, now active
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` or `If-Match` is malformed"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot activate pipelines
"404":
$ref: "#/components/responses/Error"
description: No such revision
"409":
$ref: "#/components/responses/Error"
description: An escalation path or webhook endpoint the revision uses has no active revision.
`error.code` is `escalation_path_inactive` or `webhook_endpoint_inactive`, and
`error.details` lists the keys
"412":
$ref: "#/components/responses/PreconditionFailed"
description: "`If-Match` does not match the active revision"
"428":
$ref: "#/components/responses/PreconditionRequired"
description: The `If-Match` header is missing
/api/v1/request-approval-pipelines/agents/{agent_slug}:
get:
summary: Retrieve an agent's active pipeline
description: >-
Returns the active revision of one agent's pipeline, with its blocks in the order they run.
It runs after the organization pipeline, for every request this agent makes.
The `ETag` header carries the active revision number. Pass it as `If-Match` when you
activate another revision.
operationId: getActiveRequestApprovalPipeline
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlugScope"
responses:
"200":
description: The active revision
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
"404":
$ref: "#/components/responses/Error"
description: No agent with this slug exists, or the agent and its pipeline are archived
/api/v1/request-approval-pipelines/agents/{agent_slug}/preview:
post:
summary: Preview an agent's pipeline
operationId: previewRequestApprovalPipeline
x-withhuman-permission: pipeline.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
description: >-
Runs a pipeline document against a sample request and returns what each block would do and
the final outcome. Nothing is saved: not the document, not the sample, not the result. Use
it to test a revision before you create it.
The preview runs this document alone. To see what a request would meet end to end, preview
the organization pipeline first and pass its metadata in `previous_metadata`.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlugScope"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelinePreviewRequest"
responses:
"200":
description: What the pipeline would do with the sample
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelinePreview"
"400":
$ref: "#/components/responses/Error"
description: The document or the sample is invalid
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit pipelines
/api/v1/request-approval-pipelines/agents/{agent_slug}/revisions:
get:
summary: List an agent's pipeline revisions
description: >-
Returns the revisions of one agent's pipeline, newest first, without their blocks.
Paging is by cursor. When more revisions follow, the response carries `next_cursor`. Pass it
back as `cursor` to get the next page. The last page has no `next_cursor`. `total_count` is
how many revisions there are across every page.
operationId: listRequestApprovalPipelineRevisions
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlugScope"
- name: cursor
in: query
description: The `next_cursor` from the previous page.
schema:
type: string
- name: limit
in: query
description: How many revisions to return per page. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 200
responses:
"200":
description: The revisions, newest first
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- revisions
- total_count
properties:
revisions:
type: array
description: The revisions on this page, newest first.
items:
$ref: "#/components/schemas/RequestApprovalPipelineRevisionSummary"
next_cursor:
type: string
description: Present when another page follows.
total_count:
type: integer
format: int64
description: How many revisions the pipeline has, across every page.
"400":
$ref: "#/components/responses/Error"
description: "`cursor` is not a cursor this endpoint issued"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
post:
summary: Create an agent's pipeline revision
operationId: createRequestApprovalPipelineRevision
x-withhuman-permission: pipeline.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
description: >-
Creates a new revision of one agent's pipeline from a complete document. The document is
validated and compiled first. The revision is created inactive, and it never changes.
Activate it to put it into use.
The pipeline exists from the moment the agent is created.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlugScope"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineDocument"
responses:
"201":
description: The new revision, inactive
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"400":
$ref: "#/components/responses/Error"
description: The document is invalid
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit pipelines
"404":
$ref: "#/components/responses/Error"
description: No agent with this slug exists
"409":
$ref: "#/components/responses/Error"
description: The agent and its pipeline are archived (`request_approval_pipeline_archived`)
/api/v1/request-approval-pipelines/agents/{agent_slug}/revisions/{revision}:
get:
summary: Retrieve an agent's pipeline revision
description: Returns one revision of an agent's pipeline, active or not, with its blocks in the
order they run.
operationId: getRequestApprovalPipelineRevision
x-withhuman-permission: pipeline.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlugScope"
- $ref: "#/components/parameters/PipelineRevision"
responses:
"200":
description: The revision
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` is not a positive integer"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read pipelines
"404":
$ref: "#/components/responses/Error"
description: No such revision
/api/v1/request-approval-pipelines/agents/{agent_slug}/revisions/{revision}/activate:
post:
summary: Activate an agent's pipeline revision
operationId: activateRequestApprovalPipelineRevision
x-withhuman-permission: pipeline.activate
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Approval pipelines
description: >-
Makes a revision the active one. New requests use it from then on. Activating an older
revision is how you roll back.
Pass the revision you expect to be active in `If-Match`, quoted, as returned in `ETag`. If
someone activated another revision in the meantime, the call fails with 412 and nothing
changes.
Every escalation path the revision names, and every webhook endpoint its blocks post to,
must have an active revision. Otherwise the call fails with 409 and nothing changes.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AgentSlugScope"
- $ref: "#/components/parameters/PipelineRevision"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"200":
description: The revision, now active
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/RequestApprovalPipelineRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` or `If-Match` is malformed"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot activate pipelines
"404":
$ref: "#/components/responses/Error"
description: No such revision
"409":
$ref: "#/components/responses/Error"
description: An escalation path or webhook endpoint the revision uses has no active revision
(`escalation_path_inactive` or `webhook_endpoint_inactive`, with the keys in
`error.details`), or the revision or the pipeline was archived with the type's last
agent (`request_approval_pipeline_archived`)
"412":
$ref: "#/components/responses/PreconditionFailed"
description: "`If-Match` does not match the active revision"
"428":
$ref: "#/components/responses/PreconditionRequired"
description: The `If-Match` header is missing
/api/v1/escalation-paths:
get:
summary: List escalation paths
operationId: listEscalationPaths
x-withhuman-permission: escalation_path.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
description: Returns the organization's escalation paths, one entry per key, with the active
revision number and a summary of the history. Live paths are returned unless `status` says
otherwise; an archived path carries `archived_at`.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: status
in: query
required: false
description: Which paths to return. `live` (the default) leaves archived paths out, `archived`
returns only them, and `all` returns both.
schema:
type: string
enum:
- live
- archived
- all
default: live
- name: limit
in: query
description: How many paths to return. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The paths
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- escalation_paths
properties:
escalation_paths:
type: array
items:
$ref: "#/components/schemas/EscalationPathSummary"
"400":
$ref: "#/components/responses/Error"
description: "`status` is not live, archived, or all"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read escalation paths
post:
summary: Create an escalation path
operationId: createEscalationPath
x-withhuman-permission: escalation_path.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
description: >-
Creates a path and its first revision in one call, from a complete document plus the key
pipelines will reference. The document is validated first, including that every person and
team it targets exists. The revision is created inactive, and it never changes. Activate it
to put it into use.
A key can be used once. A key that belongs to a path, archived or not, is refused with 409
`escalation_path_exists`.
The response may carry warnings, for example a team whose members cannot decide requests. A
warning never blocks creation.
security:
- cookieAuth: []
- apiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathCreate"
responses:
"201":
description: The path's first revision, inactive
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathRevision"
"400":
$ref: "#/components/responses/Error"
description: The document is invalid, a target does not exist, or the key is malformed
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit escalation paths
"409":
$ref: "#/components/responses/Error"
description: The key is already in use
/api/v1/escalation-paths/{path_key}:
get:
summary: Retrieve an escalation path
description: >-
Returns the path's active revision with its full document. This is the revision new requests
are routed with.
The `ETag` header carries the active revision number. Pass it as `If-Match` when you
activate another revision.
operationId: getActiveEscalationPath
x-withhuman-permission: escalation_path.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PathKey"
responses:
"200":
description: The active revision
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathRevision"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read escalation paths
"404":
$ref: "#/components/responses/Error"
description: No such path, or no revision is active (an archived path never has one)
delete:
summary: Archive an escalation path
operationId: archiveEscalationPath
x-withhuman-permission: escalation_path.activate
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
description: >-
Archives the path and every revision it has. Nothing is deleted: the path is still listed
with `status=archived` or `all`, its revisions can still be retrieved, and a request already
routed with one of them keeps it. But the path is never active again, it takes no new
revisions, and no pipeline can name it. Its key cannot be used for a new path.
A path that an active pipeline revision names, in a block or as the pipeline default, cannot
be archived. The call fails with 409 `escalation_path_in_use` and lists those revisions in
`details.uses`; replace them first.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PathKey"
responses:
"204":
description: The path is archived
"400":
$ref: "#/components/responses/Error"
description: "`path_key` is malformed"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot archive escalation paths
"404":
$ref: "#/components/responses/Error"
description: No such path
"409":
$ref: "#/components/responses/Error"
description: An active pipeline revision names the path (`escalation_path_in_use`), or the path is
already archived (`escalation_path_archived`)
/api/v1/escalation-paths/{path_key}/revisions:
get:
summary: List escalation path revisions
description: >-
Returns the path's revisions, newest first, without their documents. An archived path's
revisions carry `archived_at`.
Paging is by cursor. When more revisions follow, the response carries `next_cursor`. Pass it
back as `cursor` to get the next page. The last page has no `next_cursor`. `total_count` is
how many revisions there are across every page.
operationId: listEscalationPathRevisions
x-withhuman-permission: escalation_path.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PathKey"
- name: cursor
in: query
description: The `next_cursor` from the previous page.
schema:
type: string
- name: limit
in: query
description: How many revisions to return per page. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 200
responses:
"200":
description: The revisions, newest first
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- revisions
- total_count
properties:
revisions:
type: array
description: The revisions on this page, newest first.
items:
$ref: "#/components/schemas/EscalationPathRevisionSummary"
next_cursor:
type: string
description: Present when another page follows.
total_count:
type: integer
format: int64
description: How many revisions the path has, across every page.
"400":
$ref: "#/components/responses/Error"
description: "`cursor` is not a cursor this endpoint issued"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read escalation paths
post:
summary: Create an escalation path revision
operationId: createEscalationPathRevision
x-withhuman-permission: escalation_path.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
description: >-
Creates a new revision of an existing path from a complete document. The document is
validated first, including that every person and team it targets exists. The revision is
created inactive, and it never changes. Activate it to put it into use.
The path must exist and not be archived. A new path is created with `POST
/api/v1/escalation-paths`.
The response may carry warnings, for example a team whose members cannot decide requests. A
warning never blocks creation.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PathKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathDocument"
responses:
"201":
description: The new revision, inactive
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathRevision"
"400":
$ref: "#/components/responses/Error"
description: The document is invalid, or a target does not exist
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit escalation paths
"404":
$ref: "#/components/responses/Error"
description: No such path
"409":
$ref: "#/components/responses/Error"
description: The path is archived
/api/v1/escalation-paths/{path_key}/revisions/{revision}:
get:
summary: Retrieve an escalation path revision
description: Returns one revision of a path, active or not, with its full document. Revisions of an
archived path can still be retrieved.
operationId: getEscalationPathRevision
x-withhuman-permission: escalation_path.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PathKey"
- $ref: "#/components/parameters/PipelineRevision"
responses:
"200":
description: The revision
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` is not a positive integer"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read escalation paths
"404":
$ref: "#/components/responses/Error"
description: No such revision
/api/v1/escalation-paths/{path_key}/revisions/{revision}/activate:
post:
summary: Activate an escalation path revision
operationId: activateEscalationPathRevision
x-withhuman-permission: escalation_path.activate
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Escalation paths
description: >-
Makes a revision the active one. Requests that reach a person from then on are routed with
it. A request already waiting for review stays with the revision it was routed with, so its
reviewers and timers do not change. Activating an older revision is how you roll back.
Pass the revision you expect to be active in `If-Match`, quoted, as returned in `ETag`. Pass
`"0"` if no revision is active. If the active revision changed in the meantime, the call
fails with 412 and nothing changes.
A revision of an archived path cannot be activated; the call fails with 409
`escalation_path_archived`.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/PathKey"
- $ref: "#/components/parameters/PipelineRevision"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"200":
description: The revision, now active
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/EscalationPathRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` or `If-Match` is malformed"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot activate escalation paths
"404":
$ref: "#/components/responses/Error"
description: No such revision
"409":
$ref: "#/components/responses/Error"
description: The path is archived
"412":
$ref: "#/components/responses/PreconditionFailed"
description: "`If-Match` does not match the active revision"
"428":
$ref: "#/components/responses/PreconditionRequired"
description: The `If-Match` header is missing
/api/v1/me/api_keys:
get:
summary: List your API keys
description: "Every personal API key you minted, newest first, with its status, and what the create
form needs: whether the organization allows personal keys, whether this deployment and plan
let a key carry a permission list, and the permissions you could place on one. Never the
secret."
operationId: listMyAPIKeys
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
responses:
"200":
description: Your keys
content:
application/json:
schema:
$ref: "#/components/schemas/MyAPIKeysEnvelope"
"401":
$ref: "#/components/responses/Error"
post:
summary: Create an API key
description: Mints a personal API key bound to your membership. The raw token is returned exactly
once; withHuman stores only its hash. Without `permissions` the key inherits everything you
hold, live, so a role change reaches the key on its next request. With `permissions` (hosted
edition) the key is narrowed to that list; every entry must be a permission you hold, and
the list can only remove, never add. A key that could decide requests or use a dangerous
permission is minted only from a session that passes the organization's re-authentication
and MFA rules right now, and the key keeps that session's assurance for later decisions. 403
api_keys_disabled when the organization has turned personal keys off, 403
api_key_scoping_unavailable when this edition cannot scope keys, 403 plan_required when the
plan cannot.
operationId: createAPIKey
x-withhuman-permission: api_key.issue
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateAPIKeyRequest"
responses:
"201":
description: The key and, once, its token
content:
application/json:
schema:
$ref: "#/components/schemas/IssuedAPIKeyEnvelope"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
/api/v1/api_keys/verify:
get:
summary: Verify an API key
description: The first call a script makes with a fresh key. Answers the key's own row and the
permissions it holds right now, after narrowing and your current roles. Only a request
authenticated with a key can verify one; a session answers 400.
operationId: verifyAPIKey
x-withhuman-permission: self
x-withhuman-credential-kinds:
- personal_api_key
- organization_api_key
tags:
- API keys
security:
- apiKeyAuth: []
responses:
"200":
description: The key behind this request
content:
application/json:
schema:
$ref: "#/components/schemas/VerifiedAPIKeyEnvelope"
"400":
$ref: "#/components/responses/Error"
description: The request did not authenticate with an API key
"401":
$ref: "#/components/responses/Error"
/api/v1/me/api_keys/{id}:
delete:
summary: Revoke one of your API keys
description: Revokes a key you minted. It stops working immediately. Revoking an already revoked key
answers 204 again.
operationId: revokeMyAPIKey
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
parameters:
- $ref: "#/components/parameters/APIKeyID"
responses:
"204":
description: The key is revoked
"401":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
description: No such key of yours
/api/v1/api_keys:
get:
summary: List every API key in the organization
description: The oversight list for owners, admins, and auditors. Both personal and organization
keys, newest first, with creator provenance. Never the secret.
operationId: listOrganizationAPIKeys
x-withhuman-permission: api_key.read
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
parameters:
- name: kind
in: query
schema:
type: string
enum:
- personal
- organization
description: Omit to return both key kinds.
responses:
"200":
description: The organization's keys
content:
application/json:
schema:
$ref: "#/components/schemas/APIKeysEnvelope"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
post:
summary: Create an organization API key
description: Delegates explicit scoped grants to an organization-owned key. Requires a fresh human
session. The first response shows the secret once; an idempotent retry returns metadata
without the secret. Keys survive creator offboarding.
operationId: createOrganizationAPIKey
x-withhuman-permission: api_key.organization.issue
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
parameters:
- $ref: "#/components/parameters/IdempotencyKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateOrganizationAPIKeyRequest"
responses:
"200":
description: Already created. secret_available is false; the secret cannot be recovered.
content:
application/json:
schema:
$ref: "#/components/schemas/OrganizationAPIKeyCreated"
"201":
description: Created, including the one-time secret. Cache-Control is no-store.
content:
application/json:
schema:
$ref: "#/components/schemas/OrganizationAPIKeyCreated"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
description: Idempotency key reused for different intent
x-withhuman-edition: hosted
/api/v1/api_keys/{id}:
get:
summary: Inspect an API key
description: Returns metadata, configured grants, effective organization-key grants, and policy
availability. Never returns a secret.
operationId: getAPIKey
x-withhuman-permission: api_key.read
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
parameters:
- $ref: "#/components/parameters/APIKeyID"
responses:
"200":
description: Key metadata
content:
application/json:
schema:
$ref: "#/components/schemas/APIKey"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
delete:
summary: Revoke any API key
description: Revokes anyone's key. It stops working immediately and the audit log records who revoked it.
operationId: revokeOrganizationAPIKey
x-withhuman-permission: api_key.revoke
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
parameters:
- $ref: "#/components/parameters/APIKeyID"
responses:
"204":
description: The key is revoked
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
description: No such key
/api/v1/members:
get:
summary: List members
description: Returns every member of the organization, with their status and the roles they hold,
directly or through their teams.
operationId: listMembers
x-withhuman-permission: membership.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Members
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: limit
in: query
description: How many members to return. Defaults to 200.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The members
content:
application/json:
schema:
type: object
required:
- members
properties:
members:
type: array
items:
$ref: "#/components/schemas/Member"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read members
/api/v1/members/{id}:
patch:
summary: Suspend or reactivate a member
operationId: updateMember
x-withhuman-permission: membership.write
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Members
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Sets a member's status to `suspended` or `active`. A suspended member cannot sign in or act
until reactivated. Permissions are changed through the member's permission policies.
You cannot change your own membership. Changing a member who holds the owner role requires
holding it yourself. Members managed by your directory cannot be changed here, and the
organization must keep at least one active owner.
Suspending a member that an active escalation path or team escalation policy still targets
is refused with `membership_in_escalation_path`; the paths are listed in
`details.escalation_paths` and the policies in `details.team_escalation_policies`. Pass
`force` to suspend anyway. Those targets then reach nobody until the path or policy is
edited.
parameters:
- $ref: "#/components/parameters/MembershipID"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- status
properties:
status:
type: string
enum:
- active
- suspended
description: "`active` or `suspended`."
force:
type: boolean
default: false
description: Suspend even while an active escalation path targets the member.
responses:
"200":
description: The updated membership
content:
application/json:
schema:
type: object
required:
- member
properties:
member:
$ref: "#/components/schemas/Membership"
"400":
$ref: "#/components/responses/Error"
description: "`status` is not `active` or `suspended`"
"403":
$ref: "#/components/responses/Error"
description: You cannot change members, this is your own membership, or the member holds the owner
role and you do not
"404":
$ref: "#/components/responses/Error"
description: No such member
"409":
$ref: "#/components/responses/Error"
description: "The member is managed by your directory, is already removed, or is the last active
owner. Or an active escalation path targets the member: `error.code` is
`membership_in_escalation_path` and `error.details.escalation_paths` lists the paths"
delete:
summary: Remove a member
operationId: removeMember
x-withhuman-permission: membership.write
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Members
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Removes a member from the organization. Their sessions are revoked at once. The membership
record is kept with status `deprovisioned`, because decisions and audit events refer to it.
You cannot remove yourself. Removing a member who holds the owner role requires holding it
yourself, and the organization must keep at least one active owner.
Removing a member that an active escalation path or team escalation policy still targets is
refused with `membership_in_escalation_path`; the paths are listed in
`details.escalation_paths` and the policies in `details.team_escalation_policies`. Pass
`force=true` to remove anyway. Those targets then reach nobody until the path or policy is
edited, and the audit event records which paths and policies were left behind.
parameters:
- $ref: "#/components/parameters/MembershipID"
- name: force
in: query
required: false
schema:
type: boolean
default: false
description: Remove even while an active escalation path targets the member.
responses:
"204":
description: The member is removed
"403":
$ref: "#/components/responses/Error"
description: You cannot change members, this is your own membership, or the member holds the owner
role and you do not
"404":
$ref: "#/components/responses/Error"
description: No such member
"409":
$ref: "#/components/responses/Error"
description: "The member is managed by your directory or is the last active owner. Or an active
escalation path targets the member: `error.code` is `membership_in_escalation_path` and
`error.details.escalation_paths` lists the paths"
/api/v1/permissions:
get:
summary: List permissions
operationId: listPermissions
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Members
security:
- cookieAuth: []
- apiKeyAuth: []
description: Human permission vocabulary. Requires permission-policy read or write access at any scope.
responses:
"200":
description: The permissions
content:
application/json:
schema:
type: object
required:
- permissions
properties:
permissions:
type: array
items:
$ref: "#/components/schemas/PermissionDescriptor"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read roles
/api/v1/webhook_endpoints:
get:
summary: List webhook endpoints
operationId: listWebhookEndpoints
x-withhuman-permission: webhook.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns every webhook endpoint in the organization, one entry per key. Each entry
carries the latest revision's name and URL, the active revision number, and the active
pipeline revisions that post to it. Signing secrets are never included.
parameters:
- name: limit
in: query
description: How many endpoints to return. Defaults to 200.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The endpoints
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- webhook_endpoints
properties:
webhook_endpoints:
type: array
items:
$ref: "#/components/schemas/WebhookEndpointSummary"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read webhook endpoints
"501":
$ref: "#/components/responses/Error"
description: This deployment has no secrets key, so webhook endpoints are unavailable
/api/v1/webhook_endpoints/{endpoint_key}:
get:
summary: Retrieve a webhook endpoint
operationId: getActiveWebhookEndpoint
x-withhuman-permission: webhook.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Returns the endpoint's active revision. This is where deliveries go right now.
The `ETag` header carries the active revision number. Pass it as `If-Match` when you
activate another revision or archive the endpoint.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
responses:
"200":
description: The active revision
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEndpointRevision"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read webhook endpoints
"404":
$ref: "#/components/responses/Error"
description: No such endpoint, or no revision is active
/api/v1/webhook_endpoints/{endpoint_key}/revisions:
get:
summary: List webhook endpoint revisions
operationId: listWebhookEndpointRevisions
x-withhuman-permission: webhook.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Returns the endpoint's revisions, newest first. Signing secrets are never included.
Paging is by cursor. When more revisions follow, the response carries `next_cursor`. Pass it
back as `cursor` to get the next page. The last page has no `next_cursor`. `total_count` is
how many revisions there are across every page.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
- name: cursor
in: query
description: The `next_cursor` from the previous page.
schema:
type: string
- name: limit
in: query
description: How many revisions to return per page. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 200
responses:
"200":
description: The revisions, newest first
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- revisions
- total_count
properties:
revisions:
type: array
description: The revisions on this page, newest first.
items:
$ref: "#/components/schemas/WebhookEndpointRevision"
next_cursor:
type: string
description: Present when another page follows.
total_count:
type: integer
format: int64
description: How many revisions the endpoint has, across every page.
"400":
$ref: "#/components/responses/Error"
description: "`cursor` is not a cursor this endpoint issued"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read webhook endpoints
post:
summary: Create a webhook endpoint revision
operationId: createWebhookEndpointRevision
x-withhuman-permission: webhook.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Creates a new revision of an endpoint from a name and a URL. If the key is new, this creates
the endpoint and its signing secret. The secret is returned once, in this response, as
`signing_secret`. Store it where your receiver can read it. It cannot be retrieved again.
Later revisions change the name or the URL only. The secret stays the same, so your receiver
keeps working. The URL must be a public HTTPS address and is checked against the egress
policy before the revision is created. The revision is created inactive. Activate it to put
it into use.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEndpointDocument"
responses:
"201":
description: The new revision, inactive. `signing_secret` is present only for the endpoint's first
revision
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEndpointRevision"
"400":
$ref: "#/components/responses/Error"
description: The name is empty, or the URL fails the egress policy
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit webhook endpoints
"501":
$ref: "#/components/responses/Error"
description: This deployment has no secrets key, so webhook endpoints are unavailable
/api/v1/webhook_endpoints/{endpoint_key}/revisions/{revision}:
get:
summary: Retrieve a webhook endpoint revision
operationId: getWebhookEndpointRevision
x-withhuman-permission: webhook.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns one revision of an endpoint, active or not. The signing secret is never included.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
- $ref: "#/components/parameters/PipelineRevision"
responses:
"200":
description: The revision
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEndpointRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` is not a positive integer"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read webhook endpoints
"404":
$ref: "#/components/responses/Error"
description: No such revision
/api/v1/webhook_endpoints/{endpoint_key}/revisions/{revision}/activate:
post:
summary: Activate a webhook endpoint revision
operationId: activateWebhookEndpointRevision
x-withhuman-permission: webhook.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Makes a revision the active one. Deliveries go to its URL from the next request on. A
request that is paused on a webhook block and resumes later delivers to whichever revision
is active at that moment. Activating an older revision is how you roll back.
Pass the revision you expect to be active in `If-Match`, quoted, as returned in `ETag`. Pass
`"0"` if no revision is active. If the active revision changed in the meantime, the call
fails with 412 and nothing changes.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
- $ref: "#/components/parameters/PipelineRevision"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"200":
description: The revision, now active
headers:
ETag:
description: The active revision number, quoted
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEndpointRevision"
"400":
$ref: "#/components/responses/Error"
description: "`revision` or `If-Match` is malformed"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit webhook endpoints
"404":
$ref: "#/components/responses/Error"
description: No such revision
"412":
$ref: "#/components/responses/PreconditionFailed"
description: "`If-Match` does not match the active revision"
"428":
$ref: "#/components/responses/PreconditionRequired"
description: The `If-Match` header is missing
"501":
$ref: "#/components/responses/Error"
description: This deployment has no secrets key, so webhook endpoints are unavailable
/api/v1/webhook_endpoints/{endpoint_key}/revisions/{revision}/test:
post:
summary: Test a webhook endpoint revision
operationId: testWebhookEndpointRevision
x-withhuman-permission: webhook.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Sends one signed test delivery to the revision's URL and reports what came back. The payload
has type `webhook_endpoint.test` and is flagged as a preview, so your receiver can tell it
from a real request. It is signed with the endpoint's secret, so the test also checks your
signature verification. The call waits up to 10 seconds for an answer.
You can test a revision before activating it. Each test is recorded in the audit log.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
- $ref: "#/components/parameters/PipelineRevision"
responses:
"200":
description: What the endpoint answered
content:
application/json:
schema:
type: object
required:
- test
properties:
test:
$ref: "#/components/schemas/WebhookEndpointTest"
"400":
$ref: "#/components/responses/Error"
description: "`revision` is not a positive integer"
"403":
$ref: "#/components/responses/Error"
description: You cannot edit webhook endpoints
"404":
$ref: "#/components/responses/Error"
description: No such revision
"501":
$ref: "#/components/responses/Error"
description: This deployment has no secrets key, so webhook endpoints are unavailable
/api/v1/webhook_endpoints/{endpoint_key}/uses:
get:
summary: List where a webhook endpoint is used
operationId: listWebhookEndpointUses
x-withhuman-permission: webhook.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns the active pipeline revisions that post to this endpoint, one entry per webhook
block. While this list is not empty, the endpoint cannot be archived.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
responses:
"200":
description: The active uses. Empty when the endpoint can be archived
content:
application/json:
schema:
type: object
required:
- uses
properties:
uses:
type: array
items:
$ref: "#/components/schemas/PipelineUse"
"400":
$ref: "#/components/responses/Error"
description: "`endpoint_key` is malformed"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read webhook endpoints
/api/v1/webhook_endpoints/{endpoint_key}/active:
delete:
summary: Archive a webhook endpoint
operationId: archiveWebhookEndpoint
x-withhuman-permission: webhook.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Deactivates the endpoint's current revision, so the endpoint has no active revision. Its
history and its signing secret are kept, and you can activate a revision again later.
Archiving is refused while an active pipeline revision posts to the endpoint. Activate
pipeline revisions without that block first. While the endpoint is archived, no pipeline
revision that posts to it can be activated. A request that resumes onto it goes to a person,
with the block error `endpoint_unavailable`.
Pass the active revision in `If-Match`, quoted, as returned in `ETag`.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"204":
description: The endpoint is archived
"400":
$ref: "#/components/responses/Error"
description: '`If-Match` is malformed or `"0"`'
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot edit webhook endpoints
"409":
$ref: "#/components/responses/Error"
description: An active pipeline revision posts to this endpoint. `error.code` is
`webhook_endpoint_in_use`, and `error.details.uses` lists the revisions
"412":
$ref: "#/components/responses/PreconditionFailed"
description: "`If-Match` does not match the active revision"
"428":
$ref: "#/components/responses/PreconditionRequired"
description: The `If-Match` header is missing
/api/v1/webhook_endpoints/{endpoint_key}/rotate_secret:
post:
summary: Rotate the signing secret
operationId: rotateWebhookEndpointSecret
x-withhuman-permission: webhook.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Webhook endpoints
security:
- cookieAuth: []
- apiKeyAuth: []
description: Replaces the endpoint's signing secret with a new one. The old secret stops working at
once, for every revision of the endpoint. The new secret is returned once, in this response.
Update your receiver right away, or its signature checks will fail. Each rotation is
recorded in the audit log.
parameters:
- $ref: "#/components/parameters/WebhookEndpointKey"
responses:
"200":
description: The new secret. It appears only in this response
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEndpointSecret"
"403":
$ref: "#/components/responses/Error"
description: You cannot edit webhook endpoints
"404":
$ref: "#/components/responses/Error"
description: No such endpoint
"501":
$ref: "#/components/responses/Error"
description: This deployment has no secrets key, so webhook endpoints are unavailable
/api/v1/teams:
get:
summary: List teams
operationId: listTeams
x-withhuman-permission: team.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns the teams you can read. With an organization-wide grant, that is every team.
With a grant scoped to a team, only that team.
parameters:
- name: status
in: query
description: Select live teams (the default), archived teams, or all teams.
schema:
type: string
enum:
- live
- archived
- all
default: live
- name: limit
in: query
description: How many teams to return. Defaults to 200.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The teams
content:
application/json:
schema:
type: object
required:
- teams
properties:
teams:
type: array
items:
$ref: "#/components/schemas/Team"
"400":
$ref: "#/components/responses/Error"
description: The status filter is invalid
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read teams
post:
summary: Create a team
operationId: createTeam
x-withhuman-permission: team.write
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
description: Creates an empty team. Add members and roles afterwards. Names are unique among live
teams in the organization.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: Payments approvers
description: The team's name. Unique among live teams in the organization.
responses:
"201":
description: The new team
content:
application/json:
schema:
type: object
required:
- team
properties:
team:
$ref: "#/components/schemas/Team"
"400":
$ref: "#/components/responses/Error"
description: The name is empty
"403":
$ref: "#/components/responses/Error"
description: You cannot create teams
"409":
$ref: "#/components/responses/Error"
description: A team with this name already exists
/api/v1/teams/{id}:
get:
summary: Retrieve a team
operationId: getTeam
x-withhuman-permission: team.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns one team with its members. The list includes members added by hand and members
added by your directory, and says which for each.
parameters:
- $ref: "#/components/parameters/TeamID"
responses:
"200":
description: The team and its members
content:
application/json:
schema:
type: object
required:
- team
- members
properties:
team:
$ref: "#/components/schemas/Team"
members:
type: array
items:
$ref: "#/components/schemas/TeamMember"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"404":
$ref: "#/components/responses/Error"
description: No such team
patch:
summary: Rename a team
operationId: renameTeam
x-withhuman-permission: team.write
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
description: Changes a team's name. Teams managed by your directory cannot be renamed here, because
the directory owns their names.
parameters:
- $ref: "#/components/parameters/TeamID"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: Payments approvers
description: The new name. Unique among live teams in the organization.
responses:
"200":
description: The renamed team
content:
application/json:
schema:
type: object
required:
- team
properties:
team:
$ref: "#/components/schemas/Team"
"400":
$ref: "#/components/responses/Error"
description: The name is empty
"403":
$ref: "#/components/responses/Error"
description: You cannot change teams
"404":
$ref: "#/components/responses/Error"
description: No such team
"409":
$ref: "#/components/responses/Error"
description: The team is archived or managed by your directory, or a live team with this name
already exists
delete:
summary: Archive a team
operationId: archiveTeam
x-withhuman-permission: team.write
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Archives a team permanently. Its identity, roster, and policy history stay readable. Its
members lose inherited roles and the active policy is deactivated. Archived teams cannot be
edited or targeted by routing. Repeating this operation succeeds without changing the
archive date.
Archiving is refused while the team is managed by your directory, is targeted by an active
escalation path, is targeted by the path revision of a request still waiting for review.
parameters:
- $ref: "#/components/parameters/TeamID"
responses:
"204":
description: The team is archived
"403":
$ref: "#/components/responses/Error"
description: You cannot archive teams
"404":
$ref: "#/components/responses/Error"
description: No such team
"409":
$ref: "#/components/responses/Error"
description: The team is managed by your directory, is targeted by an escalation path or a pending
request
/api/v1/teams/{id}/members:
put:
summary: Replace a team's members
operationId: setTeamMembers
x-withhuman-permission: team.member.write
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
description: >-
Replaces the members added by hand with the given set. Send an empty list to remove them
all. Members added by your directory are not touched. A team's members are the union of
both.
Removing a member that the team's active escalation policy still names is refused with
`team_member_in_escalation_policy`; `details` carries `team_id`, `revision`, and the
`membership_ids` being removed. Pass `force` to remove them anyway. The level naming them
then skips them until the policy is edited.
parameters:
- $ref: "#/components/parameters/TeamID"
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- membership_ids
properties:
membership_ids:
type: array
description: The membership ids of the people to add by hand.
items:
type: string
format: uuid
force:
type: boolean
default: false
description: Remove members the active escalation policy still names.
responses:
"200":
description: The team's members after the change
content:
application/json:
schema:
type: object
required:
- members
properties:
members:
type: array
items:
$ref: "#/components/schemas/TeamMember"
"400":
$ref: "#/components/responses/Error"
description: A membership id does not belong to this organization
"403":
$ref: "#/components/responses/Error"
description: You cannot change team members
"404":
$ref: "#/components/responses/Error"
description: No such team
"409":
$ref: "#/components/responses/Error"
description: "The team is archived, or its active escalation policy still names a member being
removed: `error.code` is `team_member_in_escalation_policy`"
/api/v1/teams/{id}/escalation-policy:
get:
summary: Read the team's active escalation policy revision
operationId: getActiveTeamEscalationPolicy
x-withhuman-permission: team.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
description: The revision a path level that targets the team runs. Warnings name user targets who
are no longer active members and members who cannot decide. 404 while no revision is active,
in which case a level that targets the team notifies every member at once.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/TeamID"
responses:
"200":
description: Active immutable revision with its full document
headers:
ETag:
description: Quoted active revision for a subsequent If-Match mutation
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/TeamEscalationPolicyRevision"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
/api/v1/teams/{id}/escalation-policy/revisions:
get:
summary: List immutable team escalation policy revisions
operationId: listTeamEscalationPolicyRevisions
x-withhuman-permission: team.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/TeamID"
- name: limit
in: query
description: How many revisions to return. Defaults to 50.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: Revision history ordered newest first
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- revisions
properties:
revisions:
type: array
items:
$ref: "#/components/schemas/TeamEscalationPolicyRevisionSummary"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
post:
summary: Create an inactive immutable team escalation policy revision
operationId: createTeamEscalationPolicyRevision
x-withhuman-permission: team.escalation.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
description: Validates the complete document before creating the next inactive revision. Levels name
members of the team by membership id or broadcast to the whole team; a user target who is
not an active member of the team is refused, and team targets belong to organization
escalation paths.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/TeamID"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/TeamEscalationPolicyDocument"
responses:
"201":
description: Created inactive immutable revision
content:
application/json:
schema:
$ref: "#/components/schemas/TeamEscalationPolicyRevision"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
description: The team is archived
/api/v1/teams/{id}/escalation-policy/revisions/{revision}:
get:
summary: Read one immutable team escalation policy revision
operationId: getTeamEscalationPolicyRevision
x-withhuman-permission: team.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
description: Warnings name user targets who are no longer active members, so a rollback candidate
can be judged before it is activated.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/TeamID"
- $ref: "#/components/parameters/PipelineRevision"
responses:
"200":
description: Historical revision with its full document
content:
application/json:
schema:
$ref: "#/components/schemas/TeamEscalationPolicyRevision"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
/api/v1/teams/{id}/escalation-policy/revisions/{revision}/activate:
post:
summary: Activate or roll back to a team escalation policy revision
operationId: activateTeamEscalationPolicyRevision
x-withhuman-permission: team.escalation.activate
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
description: Atomically switches the team's active revision. If-Match must be "0" when no revision
is active. The stored document is checked against the roster again, so a revision naming
someone who has since left the team is refused. Requests already routed keep the revision
they pinned.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/TeamID"
- $ref: "#/components/parameters/PipelineRevision"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"200":
description: Activated revision
headers:
ETag:
description: Quoted active revision
schema:
type: string
example: '"3"'
content:
application/json:
schema:
$ref: "#/components/schemas/TeamEscalationPolicyRevision"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
description: The team is archived
"412":
$ref: "#/components/responses/PreconditionFailed"
"428":
$ref: "#/components/responses/PreconditionRequired"
/api/v1/teams/{id}/escalation-policy/active:
delete:
summary: Archive the team's escalation policy by deactivating its current revision
operationId: archiveTeamEscalationPolicy
x-withhuman-permission: team.escalation.activate
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Teams
description: Refused when the team is archived. Otherwise, a path level that targets the team falls
back to notifying every member at once; requests already routed keep their pinned revision.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/TeamID"
- $ref: "#/components/parameters/ActiveRevisionIfMatch"
responses:
"204":
description: Current revision deactivated
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
description: The team is archived
"412":
$ref: "#/components/responses/PreconditionFailed"
"428":
$ref: "#/components/responses/PreconditionRequired"
/api/v1/invitations:
get:
summary: List invitations
operationId: listInvitations
x-withhuman-permission: membership.invite
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Invitations
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns the organization's invitations in every status, newest first.
parameters:
- name: limit
in: query
description: How many invitations to return. Defaults to 100.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: The invitations
content:
application/json:
schema:
type: object
required:
- invitations
properties:
invitations:
type: array
items:
$ref: "#/components/schemas/Invitation"
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot invite people
post:
summary: Invite a person
operationId: createInvitation
x-withhuman-permission: membership.invite
x-withhuman-credential-kinds:
- session
tags:
- Invitations
security:
- cookieAuth: []
description: Invite a member with explicit permission policies. Every permission must be held by the
inviter at the selected scope or wider.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
- permission_policies
properties:
email:
type: string
format: email
description: Where to send the invitation. The person must accept with this address.
permission_policies:
type: array
items:
$ref: "#/components/schemas/PermissionPolicySpec"
additionalProperties: false
responses:
"201":
description: The invitation and its link. The link appears only in this response
content:
application/json:
schema:
$ref: "#/components/schemas/InvitationWithURL"
"403":
$ref: "#/components/responses/Error"
description: You cannot invite people, or a grant exceeds what you hold or needs a fresher sign-in
"409":
$ref: "#/components/responses/Error"
description: A member or a pending invitation with this email already exists, or the organization is
at its seat limit
/api/v1/invitations/{id}/resend:
post:
summary: Resend an invitation
operationId: resendInvitation
x-withhuman-permission: membership.invite
x-withhuman-credential-kinds:
- session
tags:
- Invitations
security:
- cookieAuth: []
description: Issues a new link for a pending invitation and extends its expiry by seven days. The
previous link stops working. The new link appears only in this response.
parameters:
- $ref: "#/components/parameters/InvitationID"
responses:
"200":
description: The invitation and its new link. The link appears only in this response
content:
application/json:
schema:
$ref: "#/components/schemas/InvitationWithURL"
"403":
$ref: "#/components/responses/Error"
description: You cannot invite people
"404":
$ref: "#/components/responses/Error"
description: No such invitation, or it is no longer pending
/api/v1/invitations/{id}:
delete:
summary: Revoke an invitation
operationId: revokeInvitation
x-withhuman-permission: membership.invite
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Invitations
security:
- cookieAuth: []
- apiKeyAuth: []
description: Revokes a pending invitation. Its link stops working. A revoked invitation cannot be
resent. Invite the person again instead.
parameters:
- $ref: "#/components/parameters/InvitationID"
responses:
"204":
description: The invitation is revoked
"403":
$ref: "#/components/responses/Error"
description: You cannot invite people
"404":
$ref: "#/components/responses/Error"
description: No such invitation, or it is no longer pending
/api/v1/members/{id}/permission_policies:
get:
operationId: getMembersPermissionPolicies
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Members
summary: Get members permission policies
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPoliciesPage"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
post:
operationId: postMembersPermissionPolicies
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- Members
summary: Post members permission policies
security:
- cookieAuth: []
responses:
"201":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicySpec"
/api/v1/members/{id}/permission_policies/{policy_id}:
get:
operationId: getMembersPermissionPolicy
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Members
summary: Get members permission policy
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
put:
operationId: putMembersPermissionPolicy
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- Members
summary: Put members permission policy
security:
- cookieAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicyInput"
delete:
operationId: deleteMembersPermissionPolicy
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- Members
summary: Delete members permission policy
security:
- cookieAuth: []
responses:
"204":
description: Policy operation completed
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
- name: revision
in: query
required: true
schema:
type: integer
minimum: 1
/api/v1/teams/{id}/permission_policies:
get:
operationId: getTeamsPermissionPolicies
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Teams
summary: Get teams permission policies
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPoliciesPage"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
post:
operationId: postTeamsPermissionPolicies
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- Teams
summary: Post teams permission policies
security:
- cookieAuth: []
responses:
"201":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicySpec"
/api/v1/teams/{id}/permission_policies/{policy_id}:
get:
operationId: getTeamsPermissionPolicy
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
- personal_api_key
tags:
- Teams
summary: Get teams permission policy
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
put:
operationId: putTeamsPermissionPolicy
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- Teams
summary: Put teams permission policy
security:
- cookieAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicyInput"
delete:
operationId: deleteTeamsPermissionPolicy
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- Teams
summary: Delete teams permission policy
security:
- cookieAuth: []
responses:
"204":
description: Policy operation completed
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
- name: revision
in: query
required: true
schema:
type: integer
minimum: 1
/api/v1/agent_instances/self/tool_catalog:
put:
tags:
- Agents
summary: Report the calling instance's tool catalogue
operationId: reportToolCatalog
x-withhuman-permission: self
x-withhuman-credential-kinds:
- agent_instance
security:
- bearerAuth: []
description: Replaces complete source snapshots in one discovery context. Failed sources retain
their last successful definitions. The instance credential supplies organization and
instance identity. No arguments or credentials belong in a report. This product endpoint is
outside AAP. Maximum body size is 8 MiB.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ToolCatalogReport"
responses:
"204":
description: Catalogue report recorded
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
/api/v1/tool_catalog:
get:
tags:
- Agents
summary: List known tool definitions
operationId: listToolCatalog
x-withhuman-permission: agent.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
security:
- cookieAuth: []
- apiKeyAuth: []
description: Returns definitions within the caller's agent scope. An edition's own tool source (the
hosted gateway) is refreshed on read through a five-minute shared cache, with that edition's
live grants applied. Failed refreshes retain the last successful definitions and mark their
source stale. Catalogue information is advisory and never changes approval decisions.
parameters:
- name: agent_slug
in: query
schema:
type: string
- name: q
in: query
schema:
type: string
maxLength: 200
- name: source
in: query
schema:
type: string
description: "Only tools from this source: `builtin`, `mcp`, or a source an edition adds (the hosted
gateway's `gateway`)."
- name: status
in: query
schema:
type: string
enum:
- available
- unavailable
- stale
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 500
default: 50
- name: offset
in: query
schema:
type: integer
minimum: 0
default: 0
- name: refresh
in: query
schema:
type: boolean
default: false
description: Bypass the edition's tool source discovery cache. Local tools require a CLI report.
responses:
"200":
description: Known definitions and source discovery status
content:
application/json:
schema:
$ref: "#/components/schemas/ToolCatalogPage"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
/api/v1/tool_catalog/{id}:
get:
tags:
- Agents
summary: Read a tool definition
operationId: getToolCatalogEntry
x-withhuman-permission: agent.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: agent_slug
in: query
schema:
type: string
responses:
"200":
description: Definition and its discovery provenance
content:
application/json:
schema:
$ref: "#/components/schemas/ToolCatalogEntry"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
/api/v1/audit:
get:
summary: List audit events
description: >-
Returns one page of the organization's audit log, newest first by default. Every filter is
optional and they combine.
Paging is by cursor. When more events follow, the response carries `next_cursor`. Pass it
back as `cursor` with the same sort, order and filters to get the next page. The last page
has no `next_cursor`. `total_count` is how many events match the filters across every page.
operationId: listAuditEvents
x-withhuman-permission: audit.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Audit
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- name: sort
in: query
description: What to sort by. Events with the same value are ordered newest first.
schema:
type: string
enum:
- occurred
- event_type
- actor
default: occurred
- name: order
in: query
description: "`desc` for newest first, `asc` for oldest first."
schema:
type: string
enum:
- asc
- desc
default: desc
- name: cursor
in: query
description: The `next_cursor` from the previous page. Use the same sort, order and filters.
schema:
type: string
- name: q
in: query
description: Free text. Matches the event type, the actor's name and id, the subject id, the event
id, and the event's data. Case does not matter.
schema:
type: string
- name: event_type
in: query
description: Only events of these types. Repeat the parameter to pass several.
schema:
type: array
items:
type: string
style: form
explode: true
- name: actor_type
in: query
description: Only events by this kind of actor.
schema:
type: string
enum:
- human
- agent
- system
- api_key
- name: actor_id
in: query
description: Only events by one actor. A membership id also matches events recorded under its user
id, and the other way round. An agent is given by its slug.
schema:
type: string
- name: subject_type
in: query
description: Only events about this kind of thing, such as a request or a membership.
schema:
type: string
- name: subject_id
in: query
description: Only events about one subject. Requires `subject_type`. An agent subject is given by
its slug; every other subject by its id.
schema:
type: string
- name: outcome
in: query
description: Only events whose action ended this way. Resolved from the event's data, so you do not
need to know its shape.
schema:
type: string
enum:
- approved
- denied
- expired
- cancelled
- human
- blocked
- name: from_at
in: query
description: Only events at or after this time.
schema:
type: string
format: date-time
- name: to_at
in: query
description: Only events before this time.
schema:
type: string
format: date-time
- name: limit
in: query
description: How many events to return. Defaults to 100.
schema:
type: integer
minimum: 1
maximum: 500
responses:
"200":
description: One page of events
content:
application/json:
schema:
type: object
required:
- events
- total_count
properties:
events:
type: array
description: The events on this page.
items:
$ref: "#/components/schemas/AuditEvent"
next_cursor:
type: string
description: Present when another page follows.
total_count:
type: integer
format: int64
description: How many events match the filters, across every page.
"400":
$ref: "#/components/responses/Error"
description: A filter is malformed, or `subject_id` was given without `subject_type`
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read the audit log
x-withhuman-edition: hosted
/api/v1/audit/{id}:
get:
summary: Retrieve an audit event
description: One entry of the organization's audit log by id, the record behind a deep link. An id
from another organization is reported as not found.
operationId: getAuditEvent
x-withhuman-permission: audit.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Audit
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/AuditEventID"
responses:
"200":
description: The event
content:
application/json:
schema:
$ref: "#/components/schemas/AuditEvent"
"400":
$ref: "#/components/responses/Error"
description: The id is not a UUID
"401":
$ref: "#/components/responses/Error"
description: You are not signed in
"403":
$ref: "#/components/responses/Error"
description: You cannot read the audit log
"404":
$ref: "#/components/responses/Error"
description: No such event in this organization
x-withhuman-edition: hosted
/api/v1/audit/event_types:
get:
summary: List the event types this organization has recorded
description: The vocabulary for the audit log's event filter, sorted. Unlike a fixed list it can
never name a type that does not occur.
operationId: listAuditEventTypes
x-withhuman-permission: audit.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Audit
security:
- cookieAuth: []
- apiKeyAuth: []
responses:
"200":
description: Distinct event types
content:
application/json:
schema:
type: object
required:
- event_types
properties:
event_types:
type: array
items:
type: string
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
x-withhuman-edition: hosted
/api/v1/requests/{id}/timeline:
get:
summary: Read why a request needs a human and what has happened to it since
operationId: getRequestTimeline
x-withhuman-permission: request.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
tags:
- Review queue
description: Product read only, gated by request.read alone. The pipeline's conclusion and the block
that asked for a human, every judge assessment, the escalation steps, and the claim history,
told from the request's own audit events, so a reviewer does not need audit.read to
understand why they are being asked. Raw evaluation traces stay in the audit log. Routing,
added reviewers, and the reader's standing are the review (`GET
/api/v1/requests/{id}/review`), which every edition serves.
security:
- cookieAuth: []
- apiKeyAuth: []
parameters:
- $ref: "#/components/parameters/RequestID"
responses:
"200":
description: The request's timeline
content:
application/json:
schema:
$ref: "#/components/schemas/RequestTimeline"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
x-withhuman-edition: hosted
/api/v1/agents/{slug}/tool-access:
get:
summary: Read an agent's gateway tool access
operationId: listAgentToolAccess
tags:
- Agents
x-withhuman-permission: gateway.read
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
security:
- cookieAuth: []
- apiKeyAuth: []
description: Requires gateway.read and agent.read for the named agent. Unconfigured servers grant no
access. All instances inherit these grants.
parameters:
- name: slug
in: path
required: true
schema:
type: string
responses:
"200":
description: Saved grants, including no-access grants retained for revision checks
content:
application/json:
schema:
type: object
required:
- grants
properties:
grants:
type: array
items:
$ref: "#/components/schemas/AgentToolAccess"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"501":
$ref: "#/components/responses/Error"
x-withhuman-edition: hosted
/api/v1/agents/{slug}/tool-access/{server_slug}:
put:
summary: Replace an agent's access to a server's tools
operationId: setAgentToolAccess
tags:
- Agents
x-withhuman-permission: gateway.write
x-withhuman-credential-kinds:
- session
- personal_api_key
- organization_api_key
security:
- cookieAuth: []
- apiKeyAuth: []
description: Requires gateway.write and agent.read for the named agent. Grants exact tool names on
this server, all current and future tools, or no tools. Supply the current revision, or zero
for an unconfigured pair. A stale revision returns 412; an identical retry of the last write
returns its committed result without another audit event. This version is the retry
identity, so no Idempotency-Key is required. Archived agents and servers refuse edits.
Inactive servers can be configured. The next MCP tool listing reflects changes; calls check
live access before execution, including after approval. Already dispatched calls cannot be
undone.
parameters:
- name: slug
in: path
required: true
schema:
type: string
- name: server_slug
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AgentToolAccessInput"
responses:
"200":
description: Saved grant with its new revision
content:
application/json:
schema:
$ref: "#/components/schemas/AgentToolAccess"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
"412":
$ref: "#/components/responses/Error"
"501":
$ref: "#/components/responses/Error"
x-withhuman-edition: hosted
/api/v1/api_keys/options:
get:
summary: Read organization key creation choices
description: Grantable permissions and scopes, policy availability, and the deployment lifetime cap.
Does not require permission to read the key inventory.
operationId: organizationAPIKeyOptions
x-withhuman-permission: api_key.organization.issue
x-withhuman-credential-kinds:
- session
tags:
- API keys
security:
- cookieAuth: []
responses:
"200":
description: Available creation choices
content:
application/json:
schema:
$ref: "#/components/schemas/OrganizationAPIKeyOptions"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
x-withhuman-edition: hosted
/api/v1/api_keys/{id}/permission_policies:
get:
operationId: getAPIKeyPermissionPolicies
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
tags:
- API keys
summary: Get organization key permission policies
security:
- cookieAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPoliciesPage"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
description: Manage permission policies assigned directly to an organization key. Requires a human
session, policy authority at each affected scope, and api_key.organization.issue for
mutations. Mutations require fresh authentication and the current MFA policy. Expired and
revoked keys are read-only.
x-withhuman-edition: hosted
post:
operationId: postAPIKeyPermissionPolicies
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- API keys
summary: Post organization key permission policies
security:
- cookieAuth: []
responses:
"201":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicySpec"
description: Manage permission policies assigned directly to an organization key. Requires a human
session, policy authority at each affected scope, and api_key.organization.issue for
mutations. Mutations require fresh authentication and the current MFA policy. Expired and
revoked keys are read-only.
x-withhuman-edition: hosted
/api/v1/api_keys/{id}/permission_policies/{policy_id}:
get:
operationId: getAPIKeyPermissionPolicy
x-withhuman-permission: self
x-withhuman-credential-kinds:
- session
tags:
- API keys
summary: Get organization key permission policy
security:
- cookieAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
description: Manage permission policies assigned directly to an organization key. Requires a human
session, policy authority at each affected scope, and api_key.organization.issue for
mutations. Mutations require fresh authentication and the current MFA policy. Expired and
revoked keys are read-only.
x-withhuman-edition: hosted
put:
operationId: putAPIKeyPermissionPolicy
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- API keys
summary: Put organization key permission policy
security:
- cookieAuth: []
responses:
"200":
description: Policy operation completed
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicy"
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PermissionPolicyInput"
description: Manage permission policies assigned directly to an organization key. Requires a human
session, policy authority at each affected scope, and api_key.organization.issue for
mutations. Mutations require fresh authentication and the current MFA policy. Expired and
revoked keys are read-only.
x-withhuman-edition: hosted
delete:
operationId: deleteAPIKeyPermissionPolicy
x-withhuman-permission: permission_policy.write
x-withhuman-credential-kinds:
- session
tags:
- API keys
summary: Delete organization key permission policy
security:
- cookieAuth: []
responses:
"204":
description: Policy operation completed
"400":
$ref: "#/components/responses/Error"
"401":
$ref: "#/components/responses/Error"
"403":
$ref: "#/components/responses/Error"
"404":
$ref: "#/components/responses/Error"
"409":
$ref: "#/components/responses/Error"
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
- name: policy_id
in: path
required: true
schema:
type: string
format: uuid
- name: Idempotency-Key
in: header
required: true
schema:
type: string
minLength: 1
maxLength: 200
- name: revision
in: query
required: true
schema:
type: integer
minimum: 1
description: Manage permission policies assigned directly to an organization key. Requires a human
session, policy authority at each affected scope, and api_key.organization.issue for
mutations. Mutations require fresh authentication and the current MFA policy. Expired and
revoked keys are read-only.
x-withhuman-edition: hosted
components:
schemas:
EnrollmentCode:
type: object
description: The two codes of a pending enrollment.
required:
- enrollment_code
- user_code
- verification_uri
- expires_at
properties:
enrollment_code:
type: string
example: whe_7c1f2a9e-4b3d-4f2e-9a1c-2d6e8b5f0a11_3f6b9c1d0e7a4b2c
description: The secret the machine keeps. Send it to the exchange endpoint. Never show it to the
person.
user_code:
type: string
example: K7M-3PQ2
description: The short code the person enters in the browser. Three letters, a hyphen, and four
letters or digits. Case does not matter.
verification_uri:
type: string
format: uri
example: https://app.withhuman.ai/connect/authorize
description: The page where the person enters the user code.
expires_at:
type: string
format: date-time
description: When both codes stop working. Start a new enrollment after this.
AgentEnrollmentCredential:
type: object
description: The selected parent agent, its new instance, and the instance credential shown once.
required:
- agent
- agent_instance
- credential
properties:
agent:
$ref: "#/components/schemas/Agent"
agent_instance:
$ref: "#/components/schemas/AgentInstance"
credential:
$ref: "#/components/schemas/IssuedCredential"
Agent:
type: object
description: "An agent identity. The slug identifies it everywhere: URLs, role scopes, pipeline
scopes, audit data and conditions. The name is a label for people."
required:
- slug
- organization_id
- created_by_actor_id
- name
- status
- created_at
- updated_at
properties:
slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: support-bot
description: The agent's identity, chosen once at creation. It never changes and is never reused,
not even after the agent is archived.
organization_id:
type: string
format: uuid
created_by_actor_id:
type: string
format: uuid
description: The membership or organization-key actor that created the agent.
name:
type: string
example: Support bot
description: The display name. Reviewers see it on every request. Editable, and unique among the
organization's live agents.
status:
type: string
enum:
- active
- disabled
- archived
description: "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."
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
archived_at:
type: string
format: date-time
description: When the agent was archived. Present exactly when `status` is `archived`.
AgentInstance:
type: object
description: One running copy of an agent.
required:
- id
- organization_id
- agent_slug
- name
- status
- metadata
- created_at
- last_seen_at
properties:
id:
type: string
format: uuid
organization_id:
type: string
format: uuid
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The slug of the agent this is an instance of.
name:
type: string
example: ci-runner-07
description: Unique within the agent.
status:
type: string
enum:
- active
- disabled
description: A disabled instance cannot create requests.
metadata:
type: object
additionalProperties: true
example:
region: eu-west
description: The JSON stored when the instance was created.
created_at:
type: string
format: date-time
last_seen_at:
type: string
format: date-time
description: The last time this instance called the API.
IssuedCredential:
type: object
required:
- id
- token
- expires_at
properties:
id:
type: string
format: uuid
description: Identifies the credential, for example when revoking it.
token:
type: string
description: The secret. It appears only in this response and cannot be retrieved again.
example: whc_live_3f6b9c1d0e7a4b2c
expires_at:
type: string
format: date-time
description: When the credential stops working.
AgentInstanceCredential:
type: object
description: A newly registered instance and its credential.
required:
- agent_instance
- credential
properties:
agent_instance:
$ref: "#/components/schemas/AgentInstance"
credential:
$ref: "#/components/schemas/IssuedCredential"
ErrorResponse:
type: object
additionalProperties: false
required:
- error
properties:
error:
type: object
additionalProperties: false
required:
- type
- code
- message
- request_id
properties:
type:
type: string
enum:
- invalid_request
- authentication
- authorization
- not_found
- conflict
- rate_limit
- dependency
- internal
code:
type: string
message:
type: string
param:
type: string
request_id:
type: string
details:
description: Structured detail for codes that carry one. quota_exceeded carries
QuotaExceededDetails; a forbidden error from a permission check carries
ForbiddenDetails; a decision the routing rule refused carries
RoutingRejectionDetails.
anyOf:
- $ref: "#/components/schemas/QuotaExceededDetails"
- $ref: "#/components/schemas/ForbiddenDetails"
- $ref: "#/components/schemas/RoutingRejectionDetails"
- type: object
QuotaExceededDetails:
description: The details object on a quota_exceeded error, so a client can offer the purchase that
resolves it.
type: object
additionalProperties: false
required:
- dimension
- limit
- used
properties:
dimension:
type: string
description: The plan dimension that is full, such as seats or agents.
limit:
type: integer
format: int64
minimum: 0
used:
type: integer
format: int64
minimum: 0
ForbiddenDetails:
description: The details on a 403 from a permission check. They name the permission that was needed
and the thing it was checked against.
type: object
additionalProperties: false
required:
- permission
- resource_kind
properties:
permission:
type: string
description: The permission you needed.
example: request.decide
resource_kind:
type: string
enum:
- organization
- team
- agent
- request
description: What kind of thing the check was about.
resource_id:
type: string
description: The team, agent slug, or request the check was about. Absent for the organization.
RoutingRejectionDetails:
description: 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.
type: object
additionalProperties: false
required:
- reason
properties:
reason:
type: string
enum:
- not_targeted
- break_glass_only
- outside_routing_required
CreateApproval:
type: object
additionalProperties: false
required:
- tool
- arguments
- timeout
properties:
tool:
type: string
example: issue_refund
description: "The name under which the tool is defined: the name an MCP server advertises in
`tools/list`, or the runtime's own name for a built-in tool such as `Bash`. Never the
runtime's joined spelling such as `mcp__stripe__issue_refund`."
server:
type: string
example: stripe
description: The alias of the MCP server that defines the tool, as the runtime configured it.
Present only for tools served over MCP. A label the adapter observed, not a verified
identity.
arguments:
type: object
additionalProperties: true
description: The exact arguments the tool will run with if approved. Reviewers see this as the
description of the action, so it must be complete.
example:
amount: 4900
reason: duplicate_charge
agent_reasoning:
type: string
description: 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.
example: Refunding the duplicate charge for [email protected].
context:
type: object
additionalProperties: true
description: "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."
example:
run_id: "4821"
framework: claude-code
timeout:
type: string
example: 30m
description: 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.
AAPApprovalRequest:
example:
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
type: object
additionalProperties: false
required:
- id
- tool
- arguments
- timeout
- status
- created_at
- deadline_at
description: 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.
properties:
id:
type: string
format: uuid
tool:
type: string
server:
type: string
arguments:
type: object
additionalProperties: true
timeout:
type: string
example: 300s
agent_reasoning:
type: string
context:
type: object
additionalProperties: true
status:
type: string
enum:
- pending
- approved
- denied
- expired
- cancelled
created_at:
type: string
format: date-time
deadline_at:
type: string
format: date-time
decision:
$ref: "#/components/schemas/AAPDecision"
oneOf:
- properties:
status:
const: pending
decision: false
- required:
- decision
properties:
status:
const: approved
decision:
properties:
status:
const: approved
- required:
- decision
properties:
status:
const: denied
decision:
properties:
status:
const: denied
- required:
- decision
properties:
status:
const: expired
decision:
properties:
status:
const: expired
- required:
- decision
properties:
status:
const: cancelled
decision:
properties:
status:
const: cancelled
AAPDecision:
example:
status: approved
note: Refund the duplicate charge.
decided_at: 2026-09-17T12:02:00Z
expires_at: 2026-09-17T12:07:00Z
type: object
additionalProperties: false
required:
- status
- decided_at
description: Immutable decision. Approved calls must start within five minutes of decided_at. Reads
and retries never extend expires_at.
properties:
status:
type: string
enum:
- approved
- denied
- expired
- cancelled
note:
type: string
decided_at:
type: string
format: date-time
expires_at:
type: string
format: date-time
oneOf:
- properties:
status:
const: approved
required:
- expires_at
- properties:
status:
enum:
- denied
- expired
- cancelled
expires_at: false
QueueApprovalRequest:
description: "An approval request as reviewers see it: the request itself plus the claim a reviewer
holds on it, if any. Agents never see claims."
allOf:
- $ref: "#/components/schemas/ApprovalRequest"
- type: object
required:
- updated_at
properties:
updated_at:
type: string
format: date-time
example: 2026-09-08T12:05:00Z
description: Moves on every change a reviewer should notice, escalation progress included, so a
client refetches the review when it changes.
claim:
description: The live claim on the request. Absent when nobody holds one.
$ref: "#/components/schemas/Claim"
presentation:
description: "How to show the arguments: the resolved tool presentation, from the organization's own
entries or the shipped catalog. Absent when neither names the tool, in which case
the client renders the arguments by their shape. A reading aid only: the arguments
stay the complete description of the action, and a presentation never hides one."
$ref: "#/components/schemas/ResolvedToolPresentation"
ApprovalRequest:
type: object
required:
- id
- organization_id
- agent_slug
- agent_instance_id
- agent_name
- agent_instance_name
- tool
- arguments
- status
- deadline_at
- created_at
properties:
id:
type: string
format: uuid
organization_id:
type: string
format: uuid
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: support-agent
description: The slug of the agent that made the request.
agent_instance_id:
type: string
format: uuid
description: The running copy of the agent that made the request.
agent_name:
type: string
example: support-agent
description: The agent's name when the request was made.
agent_instance_name:
type: string
example: ci-runner-07
description: The instance's name when the request was made.
tool:
type: string
example: issue_refund
description: The tool the agent wants to call, as the server that defines it names it.
server:
type: string
example: stripe
description: The MCP server that defines the tool, as the adapter reported it. Absent for a
runtime's built-in tools.
arguments:
example:
amount: 4900
reason: duplicate_charge
description: The exact arguments the tool will run with if approved.
agent_reasoning:
type: string
example: Refunding the duplicate charge for [email protected].
description: The agent's own explanation, if it gave one.
context:
example:
run_id: "4821"
framework: claude-code
description: Where the call comes from, as recorded by the adapter.
status:
type: string
enum:
- pending
- approved
- denied
- expired
- cancelled
example: approved
description: "`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."
deadline_at:
type: string
format: date-time
example: 2026-09-09T12:00:00Z
description: "When the request expires if nobody has decided: `created_at` plus the timeout."
created_at:
type: string
format: date-time
example: 2026-09-08T12:00:00Z
decision:
description: Present once the request has been decided.
$ref: "#/components/schemas/Decision"
Decision:
type: object
description: The outcome of an approval request and who produced it.
required:
- id
- request_id
- organization_id
- status
- channel
- authentication
- idempotency_key
- decided_at
properties:
id:
type: string
format: uuid
request_id:
type: string
format: uuid
organization_id:
type: string
format: uuid
membership_id:
type: string
format: uuid
description: The member who decided. Absent when the pipeline decided, the request expired, or the
agent cancelled it.
status:
type: string
enum:
- approved
- denied
- expired
- cancelled
note:
type: string
example: OK, but flag this account for review.
description: A note the reviewer left for the agent, if any.
channel:
type: string
example: web
description: Where the decision was made, as reported by the client, for example `web` or `slack`.
`pipeline` when the pipeline decided, `system` when the request expired, `aap` when the
agent cancelled it.
reviewer_display_name:
type: string
example: Chris
description: The reviewer's name, when a person decided.
reviewer_email:
type: string
format: email
description: The reviewer's email, when a person decided.
authentication:
$ref: "#/components/schemas/AuthContext"
description: How the reviewer was signed in at the time.
idempotency_key:
type: string
example: decision-7ab8c8ec
description: The key the decision was recorded with.
decided_at:
type: string
format: date-time
AuthContext:
type: object
description: How a principal was signed in when it acted.
required:
- method
- assurance
- authenticated_at
properties:
method:
type: string
example: local_password
description: How the principal signed in, for example `local_password`, `sso`, or `social_google`.
assurance:
type: string
example: strong
description: "The strength of that sign-in: `single_factor` or `strong`."
authenticated_at:
type: string
format: date-time
description: When the sign-in happened.
session_id:
type: string
format: uuid
description: The session that acted, when a person did.
credential_id:
type: string
format: uuid
description: The credential that acted, when an agent did.
Claim:
type: object
description: "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."
required:
- id
- membership_id
- claimed_at
- expires_at
properties:
id:
type: string
format: uuid
membership_id:
type: string
format: uuid
description: The reviewer holding the claim.
display_name:
type: string
example: Chris
description: The reviewer's name.
claimed_at:
type: string
format: date-time
example: 2026-09-08T12:01:30Z
expires_at:
type: string
format: date-time
example: 2026-09-08T12:16:30Z
description: When the claim lapses on its own.
ResolvedToolPresentation:
type: object
description: The presentation a request renders with and where it came from.
required:
- source
- fields
properties:
source:
type: string
enum:
- built_in
- organization
description: "`organization` for one of the organization's own entries, `built_in` for the shipped
catalog."
id:
type: string
format: uuid
description: The organization entry, when the source is `organization`.
title:
type: string
example: Edit {{/file_path}}
fields:
type: array
items:
$ref: "#/components/schemas/ToolPresentationField"
ToolPresentationField:
type: object
description: 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.
required:
- kind
properties:
path:
type: string
example: /body
description: A JSON pointer into the arguments. Required unless the field is a diff pair. Inside a
list's `item` it is relative to the element (no leading slash; empty means the element
itself).
label:
type: string
example: Body
description: Replaces the humanised key.
kind:
$ref: "#/components/schemas/ToolPresentationKind"
language:
type: string
example: python
description: Labels a `code` field for the reader.
before:
type: string
example: /old_string
description: "For `diff`: the pointer to the text before the change. Requires `after`; excludes
`path`."
after:
type: string
example: /new_string
description: "For `diff`: the pointer to the text after the change."
item:
description: "For `list`: how each element renders, with paths relative to the element. Omitted,
each element is text. An item cannot itself be a list of items."
$ref: "#/components/schemas/ToolPresentationField"
role:
type: string
enum:
- primary
- detail
default: primary
description: "`primary` fields make up the block a reviewer reads first; `detail` fields fold away
underneath it."
ToolPresentationKind:
type: string
description: 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.
enum:
- text
- prose
- markdown
- html
- code
- shell
- diff
- path
- url
- email
- reference
- enum
- datetime
- list
- json
Principal:
type: object
description: The identity the API checks permissions against. A person acting through a session, or
a machine acting through a credential.
required:
- actor_type
- actor_id
- organization_id
- permissions
- grants
- auth
properties:
actor_type:
type: string
enum:
- human
- agent
- system
- gateway
example: human
description: "`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."
actor_id:
type: string
format: uuid
description: The user's id for a person, an internal id for an agent (address agents by
`agent_slug`), or the literal `gateway` for the gateway.
organization_id:
type: string
format: uuid
description: The organization every call is scoped to.
membership_id:
type: string
format: uuid
description: The person's membership in the organization. Present for a person only.
agent_instance_id:
type: string
format: uuid
description: The instance the credential belongs to. Present for an agent only.
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: claude-code
description: The agent's slug. Present for an agent only.
permissions:
type: array
description: 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`.
items:
type: string
example:
- request.read
- request.decide
- agent.credential.issue
grants:
type: array
description: Every permission the actor holds, each with the scope it applies at. Flattened from the
role assignments.
items:
$ref: "#/components/schemas/Grant"
team_ids:
type: array
description: The teams the member belongs to. Escalation paths can route requests to a team.
items:
type: string
format: uuid
auth:
$ref: "#/components/schemas/AuthContext"
Grant:
type: object
required:
- permission
- scope_kind
properties:
permission:
type: string
description: A registry key such as request.decide
example: request.decide
scope_kind:
type: string
enum:
- organization
- team
- agent
scope_id:
type: string
description: The team id or agent slug the grant applies to; absent at organization scope
RoleAssignment:
type: object
description: Permissions granted at one scope, with provenance.
required:
- id
- organization_id
- principal_kind
- principal_id
- scope_kind
- source
- created_at
- permissions
- is_owner
properties:
id:
type: string
format: uuid
organization_id:
type: string
format: uuid
principal_kind:
type: string
enum:
- membership
- team
- agent
description: What holds the role.
principal_id:
type: string
format: uuid
description: The membership, team or agent that holds it.
scope_kind:
type: string
enum:
- organization
- team
- agent
description: Where the role applies.
scope_id:
type: string
description: The team id or the agent slug. Absent for `organization`.
scope_name:
type: string
description: The team's name, for a team scope.
source:
type: string
enum:
- manual
- directory
description: Whether a person granted it or your directory did.
source_key:
type: string
description: The directory group that produced it. Present for `directory` only.
granted_by_actor_id:
type: string
format: uuid
description: The member who granted it. Present for `manual` only.
via_team_id:
type: string
format: uuid
description: The team the role is inherited through. Present when a member holds it through a team
rather than directly.
via_team_name:
type: string
description: That team's name.
created_at:
type: string
format: date-time
permissions:
type: array
items:
type: string
is_owner:
type: boolean
permission_policy_id:
type: string
format: uuid
DecisionOperation:
type: object
description: The record of one decision call and its outcome.
required:
- id
- organization_id
- request_id
- status
- created_at
properties:
id:
type: string
format: uuid
description: The operation's id. The decision itself has its own.
organization_id:
type: string
format: uuid
request_id:
type: string
format: uuid
description: The request that was decided.
status:
type: string
enum:
- pending
- applied
- rejected
example: applied
description: "`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."
error_code:
type: string
description: Why the decision was rejected. Set for `rejected` only.
error_message:
type: string
description: The rejection in plain text. Set for `rejected` only.
decision:
description: The stored decision.
$ref: "#/components/schemas/Decision"
created_at:
type: string
format: date-time
example: 2026-09-08T12:02:11Z
completed_at:
type: string
format: date-time
example: 2026-09-08T12:02:11Z
description: When the decision was stored.
ApprovalRouting:
type: object
additionalProperties: false
description: Who was asked to review a request when it entered human review. escalated pins an
escalation path revision and the people it reaches for this request; default_queue means any
authorized reviewer.
required:
- outcome
- reason
- urgency
- targets
- added_reviewers
- team_policies
- escalation
- created_at
properties:
outcome:
type: string
enum:
- escalated
- default_queue
example: escalated
description: "`escalated` when an escalation path took the request. `default_queue` when no path
applied and every reviewer who can decide was asked."
reason:
type: string
enum:
- block_escalation
- pipeline_default
- no_escalation
- path_inactive
- evaluation_error
example: block_escalation
description: "How the outcome came about. `block_escalation`: the pipeline block that asked for a
person named the path. `pipeline_default`: the pipeline's default path took it.
`no_escalation`: the pipeline named no path. `path_inactive`: the named path had no
active revision. `evaluation_error`: the path could not be resolved. The last three mean
no path applied."
urgency:
type: string
enum:
- standard
- interrupt
example: standard
description: The urgency the request was routed with. Always `standard` today.
escalation_path_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: oncall
description: The path that took the request. Present for `escalated` only.
escalation_path_name:
type: string
example: On-call engineers
description: The name of the path revision that took the request. Present for `escalated` only.
escalation_path_revision_id:
type: string
format: uuid
description: The path revision that was active at the time. Present for `escalated` only.
pipeline_revision_id:
type: string
format: uuid
description: The pipeline revision that routed the request. Present for `escalated` only.
block_id:
type: string
format: uuid
description: The pipeline block that named the path. Present for `block_escalation` only.
targets:
type: array
description: "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."
items:
$ref: "#/components/schemas/ApprovalRoutingTarget"
added_reviewers:
type: array
description: People added to the request's routing after the snapshot was pinned, oldest first.
items:
$ref: "#/components/schemas/ApprovalReviewer"
team_policies:
type: array
description: 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`.
items:
$ref: "#/components/schemas/PinnedTeamPolicy"
escalation:
description: The escalation executor's current position. Null before its first tick and for
`default_queue`.
oneOf:
- $ref: "#/components/schemas/EscalationProgress"
- type: "null"
created_at:
type: string
format: date-time
example: 2026-09-08T12:00:00Z
description: When the request entered human review.
ApprovalRoutingTarget:
type: object
additionalProperties: false
description: One person or team the pinned path reaches for the request.
required:
- type
- id
- name
properties:
type:
type: string
enum:
- user
- team
description: "`user` names a membership, `team` a team."
id:
type: string
format: uuid
name:
type: string
description: The person's display name or the team's name.
ApprovalReviewer:
type: object
additionalProperties: false
description: A person pulled into a pending request's routing after the fact by someone it was routed to.
required:
- membership_id
- display_name
- added_by_membership_id
- added_by_display_name
- created_at
properties:
membership_id:
type: string
format: uuid
display_name:
type: string
added_by_membership_id:
type: string
format: uuid
added_by_display_name:
type: string
created_at:
type: string
format: date-time
PinnedTeamPolicy:
type: object
additionalProperties: false
description: One team escalation policy revision an escalated request runs inline when its pinned
path reaches the team.
required:
- team_id
- team_name
- policy_revision_id
- revision
properties:
team_id:
type: string
format: uuid
team_name:
type: string
policy_revision_id:
type: string
format: uuid
revision:
type: integer
format: int64
minimum: 1
EscalationProgress:
type: object
additionalProperties: false
description: 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.
required:
- sequence
- phase
- urgency
- level_ordinal
- level_count
- repeat
- updated_at
- teams
properties:
sequence:
type: integer
format: int64
description: Monotonic tick counter; a later tick always carries a higher value.
phase:
$ref: "#/components/schemas/EscalationPhase"
urgency:
type: string
enum:
- standard
- interrupt
description: The request's current urgency, raised by a level that asked to.
current_node_id:
type: string
format: uuid
description: The path node the walker stands on; absent once exhausted.
level_ordinal:
type: integer
description: 1-based position of the current level in walk order; 0 before the first level.
level_count:
type: integer
description: Level nodes in the path document.
repeat:
type: integer
description: How many times the path has been repeated so far.
next_escalation_at:
type: string
format: date-time
description: When the path walker's armed timer fires; absent while paused, exhausted, or waiting on
nothing.
updated_at:
type: string
format: date-time
teams:
type: array
items:
$ref: "#/components/schemas/EscalationWalkerProgress"
EscalationPhase:
type: string
enum:
- level
- deferred
- repeat_wait
- paused
- exhausted
description: "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."
EscalationWalkerProgress:
type: object
additionalProperties: false
description: One team policy walker's position while its policy runs inline.
required:
- team_id
- phase
- level_ordinal
- level_count
- repeat
properties:
team_id:
type: string
format: uuid
phase:
$ref: "#/components/schemas/EscalationPhase"
current_node_id:
type: string
format: uuid
description: The node the walker stands on; absent once exhausted.
level_ordinal:
type: integer
description: 1-based position of the current level in walk order; 0 before the first level.
level_count:
type: integer
description: Level nodes in the document.
repeat:
type: integer
description: How many times the program has been repeated so far.
next_escalation_at:
type: string
format: date-time
description: When the armed timer fires; absent while paused, exhausted, or waiting on nothing.
ApprovalReview:
type: object
additionalProperties: false
description: The request's routing and the reader's standing on it. routing is absent for requests
the pipeline decided automatically.
required:
- added_reviewers
- viewer
properties:
routing:
$ref: "#/components/schemas/ApprovalRouting"
added_reviewers:
type: array
description: People added to the request's routing after it was pinned, oldest first. The same list
the routing carries.
items:
$ref: "#/components/schemas/ApprovalReviewer"
viewer:
$ref: "#/components/schemas/ReviewViewer"
ReviewViewer:
type: object
additionalProperties: false
description: 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.
required:
- targeted
- decidable
properties:
targeted:
type: boolean
decidable:
type: string
enum:
- yes
- break_glass
- no
reason:
type: string
enum:
- not_targeted
- break_glass_only
- no_permission
AgentSummary:
description: One row of the agent list. An agent with its instance and request counts.
allOf:
- $ref: "#/components/schemas/Agent"
- type: object
required:
- instance_count
- live_instance_count
- recent_request_count
- pending_request_count
- request_trend
properties:
instance_count:
type: integer
minimum: 0
description: How many instances the agent has.
live_instance_count:
type: integer
minimum: 0
description: How many instances are active and hold a credential that is neither expired nor
revoked.
last_seen_at:
type: string
format: date-time
description: The newest authenticated call from any instance. Absent when the agent has no
instances.
recent_request_count:
type: integer
minimum: 0
description: Requests made in the last seven days.
pending_request_count:
type: integer
minimum: 0
description: Requests still waiting for a decision.
request_trend:
type: array
minItems: 7
maxItems: 7
items:
type: integer
minimum: 0
description: The recent requests split into one bucket per day, oldest first. The last entry is the
past 24 hours.
AgentDetail:
description: An agent with a page of instances, aggregate counts, and provisioner tokens.
allOf:
- $ref: "#/components/schemas/Agent"
- type: object
required:
- created_by_display_name
- instances
- provisioners
- provisioner_count
- matching_provisioner_count
- instance_count
- live_instance_count
- matching_instance_count
- request_count
- recent_request_count
- pending_request_count
properties:
provisioner_count:
type: integer
minimum: 0
description: All provisioner tokens owned by this agent.
matching_provisioner_count:
type: integer
minimum: 0
description: Tokens matching the current identifier and status filters before pagination.
instance_count:
type: integer
minimum: 0
description: All instances owned by this agent.
live_instance_count:
type: integer
minimum: 0
description: Active instances with at least one live credential across all pages.
matching_instance_count:
type: integer
minimum: 0
description: Instances matching the current search and status filters before pagination.
request_count:
type: integer
minimum: 0
description: All requests made by this agent.
created_by_actor_type:
type: string
enum:
- human
- api_key
created_by_display_name:
type: string
description: The name of the human or organization key that created the agent.
instances:
type: array
description: The requested page of instances, sorted by last seen, creation time, and id, newest
first.
items:
$ref: "#/components/schemas/AgentInstanceDetail"
provisioners:
type: array
description: The requested page of provisioner tokens, sorted by issued time and id, newest first.
Secrets are never included.
items:
$ref: "#/components/schemas/AgentProvisioner"
recent_request_count:
type: integer
minimum: 0
description: Requests made in the last seven days.
pending_request_count:
type: integer
minimum: 0
description: Requests still waiting for a decision.
AgentInstanceDetail:
description: An instance with its credential history.
allOf:
- $ref: "#/components/schemas/AgentInstance"
- type: object
required:
- credentials
- recent_request_count
- request_trend
properties:
credentials:
description: Every credential the instance has held, newest first. Tokens are never included.
type: array
items:
$ref: "#/components/schemas/AgentCredentialState"
recent_request_count:
type: integer
minimum: 0
description: Requests this instance made in the last seven days.
request_trend:
type: array
minItems: 7
maxItems: 7
items:
type: integer
minimum: 0
description: The instance's recent requests split into one bucket per day, oldest first. The last
entry is the past 24 hours.
AgentCredentialState:
type: object
description: 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.
required:
- id
- issued_at
- expires_at
properties:
id:
type: string
format: uuid
issued_at:
type: string
format: date-time
expires_at:
type: string
format: date-time
description: When the credential stops working.
last_used_at:
type: string
format: date-time
description: The last authenticated call made with it.
revoked_at:
type: string
format: date-time
description: When it was revoked. Absent while it is not.
AgentProvisioner:
type: object
description: A provisioner token as the agent page shows it. The token is never included.
required:
- id
- organization_id
- agent_slug
- created_by_actor_id
- issued_at
- expires_at
properties:
id:
type: string
format: uuid
organization_id:
type: string
format: uuid
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The slug of the agent the token creates instances of.
created_by_actor_id:
type: string
format: uuid
description: The membership or organization-key actor that created the token.
issued_at:
type: string
format: date-time
expires_at:
type: string
format: date-time
description: When the token stops working.
last_used_at:
type: string
format: date-time
description: The last time the token created an instance.
revoked_at:
type: string
format: date-time
description: When it was revoked. Absent while it is not.
AgentProvisionerCredential:
type: object
description: A newly created provisioner and its token.
required:
- provisioner
- credential
properties:
provisioner:
$ref: "#/components/schemas/AgentProvisioner"
credential:
$ref: "#/components/schemas/IssuedCredential"
OAuthAuthorizationRequest:
type: object
required:
- client_id
- redirect_uri
- code_challenge
- code_challenge_method
properties:
client_id:
type: string
format: uuid
redirect_uri:
type: string
format: uri
state:
type: string
code_challenge:
type: string
code_challenge_method:
type: string
enum:
- S256
scope:
type: string
description: Space-separated permission keys the client asked for.
resource:
type: string
format: uri
response_type:
type: string
enum:
- code
OAuthAuthorizationPreview:
type: object
required:
- client
- requested_permissions
- bearable_permissions
- scoping_enabled
- personal_api_keys_allowed
properties:
client:
type: object
required:
- id
- name
- redirect_uri
properties:
id:
type: string
format: uuid
name:
type: string
client_uri:
type: string
redirect_uri:
type: string
format: uri
requested_permissions:
type: array
items:
type: string
bearable_permissions:
type: array
items:
$ref: "#/components/schemas/APIKeyPermission"
scoping_enabled:
type: boolean
personal_api_keys_allowed:
type: boolean
APIKeyPermission:
type: object
additionalProperties: false
description: One permission the caller could place on a key.
required:
- key
- area
- description
- dangerous
properties:
key:
type: string
example: pipeline.write
area:
type: string
example: pipeline
description:
type: string
dangerous:
type: boolean
description: Minting a key that carries it needs a fresh, strong session.
OAuthAuthorizationDecision:
allOf:
- $ref: "#/components/schemas/OAuthAuthorizationRequest"
- type: object
required:
- approve
properties:
approve:
type: boolean
permissions:
type: array
items:
type: string
description: The permissions to place on the key; omit to inherit everything the person holds.
expires_at:
type: string
format: date-time
RequestApprovalPipelineSummary:
type: object
additionalProperties: false
description: One pipeline and a summary of its history.
required:
- scope
- latest_revision
- revision_count
- latest_created_at
properties:
scope:
$ref: "#/components/schemas/PipelineScope"
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The agent's slug. Present for the `agent` scope only.
active_revision:
type: integer
format: int64
minimum: 1
description: The revision in use. Absent only for an archived pipeline.
latest_revision:
type: integer
format: int64
minimum: 1
description: The newest revision, active or not.
revision_count:
type: integer
format: int64
minimum: 1
latest_created_at:
type: string
format: date-time
description: When the newest revision was created.
archived_at:
type: string
format: date-time
description: "Present when the pipeline was archived with its agent: readable, never active again
until the agent is restored."
PipelineScope:
description: Which requests a pipeline applies to. The organization pipeline runs first, for every
request. An agent's pipeline runs after it, for that agent's requests.
type: string
enum:
- organization
- agent
RequestApprovalPipelineRevision:
type: object
additionalProperties: false
description: One pipeline revision with its blocks in the order they run.
required:
- id
- scope
- revision
- is_active
- block_count
- created_by_actor_id
- created_at
- blocks
properties:
id:
type: string
format: uuid
scope:
$ref: "#/components/schemas/PipelineScope"
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The agent's slug. Present for the `agent` scope only.
revision:
type: integer
format: int64
minimum: 1
description: The revision number. Revisions count up from 1.
is_active:
type: boolean
description: Whether this is the revision in use.
block_count:
type: integer
minimum: 0
maximum: 1000
description: Every block in the revision, including blocks inside branches.
default_escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The escalation path for requests that reach a person without a block naming a path.
created_by_actor_id:
type: string
format: uuid
description: The membership or organization-key actor that created the revision.
created_at:
type: string
format: date-time
blocks:
type: array
maxItems: 1000
description: The blocks, in the order they run. A branch holds its own blocks in `config.blocks`.
items:
$ref: "#/components/schemas/StoredRequestApprovalBlock"
StoredRequestApprovalBlock:
description: One block as saved in a revision. The same shape as its definition, plus the fields of
the saved copy.
oneOf:
- $ref: "#/components/schemas/StoredAlwaysBlock"
- $ref: "#/components/schemas/StoredCELBlock"
- $ref: "#/components/schemas/StoredWebhookBlock"
- $ref: "#/components/schemas/StoredBranchBlock"
- $ref: "#/components/schemas/StoredLLMJudgeBlock"
discriminator:
propertyName: type
mapping:
always: "#/components/schemas/StoredAlwaysBlock"
cel: "#/components/schemas/StoredCELBlock"
webhook: "#/components/schemas/StoredWebhookBlock"
branch: "#/components/schemas/StoredBranchBlock"
llm_judge: "#/components/schemas/StoredLLMJudgeBlock"
StoredAlwaysBlock:
type: object
additionalProperties: false
description: An unconditional block as saved in a revision.
required:
- id
- snapshot_id
- name
- type
- enabled
- timeout
- max_attempts
- config
- config_version
- content_hash
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: review-remaining-requests
description: The block's key, unique within the pipeline including blocks inside branches.
snapshot_id:
type: string
format: uuid
description: The id of the saved copy.
name:
type: string
minLength: 1
example: Review remaining requests
description: The display name.
type:
type: string
const: always
description: Always `always`.
enabled:
type: boolean
const: true
description: Always `true`.
timeout:
type: string
example: 50ms
description: How long execution may take.
max_attempts:
type: integer
const: 1
description: Always 1.
config:
$ref: "#/components/schemas/AlwaysBlockConfig"
config_version:
type: integer
const: 1
description: The version of the config format. Always 1.
content_hash:
type: string
pattern: ^[0-9a-f]{64}$
example: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
description: A SHA-256 hash of the block's content.
AlwaysBlockConfig:
type: object
additionalProperties: false
description: Returns outcome for every request that reaches this block. Later blocks do not run.
required:
- outcome
- reason
example:
outcome: human
reason: Requests not handled by earlier blocks need human review.
dependentSchemas:
escalation_path:
properties:
outcome:
const: human
properties:
outcome:
type: string
enum:
- approve
- deny
- human
description: The outcome returned unconditionally.
reason:
type: string
minLength: 1
description: The note recorded on decisions this block makes.
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: Where the request goes for a `human` outcome. Allowed only when `outcome` is `human`.
Defaults to the pipeline's default path.
StoredCELBlock:
type: object
additionalProperties: false
description: A condition block as saved in a revision.
required:
- id
- snapshot_id
- name
- type
- enabled
- timeout
- max_attempts
- config
- config_version
- content_hash
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The block's key, unique within the pipeline including blocks inside branches.
snapshot_id:
type: string
format: uuid
description: The id of the saved copy.
name:
type: string
minLength: 1
description: The display name.
type:
type: string
const: cel
description: Always `cel`.
enabled:
type: boolean
const: true
description: Always `true`.
timeout:
type: string
example: 50ms
description: How long the condition may take.
max_attempts:
type: integer
const: 1
description: Always 1.
config:
$ref: "#/components/schemas/CELBlockConfig"
config_version:
type: integer
const: 1
description: The version of the config format. Always 1.
content_hash:
type: string
pattern: ^[0-9a-f]{64}$
description: A SHA-256 hash of the block's content.
CELBlockConfig:
type: object
additionalProperties: false
description: When the condition matches, the block returns on_match. Otherwise the request passes to
the next block.
required:
- when
- on_match
- reason
properties:
when:
$ref: "#/components/schemas/CELCondition"
on_match:
type: string
enum:
- approve
- deny
- human
description: "What to do on a match: `approve`, `deny`, or `human`."
reason:
type: string
minLength: 1
description: The note recorded on decisions this block makes.
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: Where the request goes on a `human` match. Allowed only when `on_match` is `human`.
Defaults to the pipeline's default path.
CELCondition:
description: A condition tree. A leaf tests one field. all and any combine conditions. some and
every test the items of a list field.
oneOf:
- $ref: "#/components/schemas/CELLeafCondition"
- $ref: "#/components/schemas/CELAllCondition"
- $ref: "#/components/schemas/CELAnyCondition"
- $ref: "#/components/schemas/CELSomeCondition"
- $ref: "#/components/schemas/CELEveryCondition"
CELLeafCondition:
type: object
additionalProperties: false
description: A test of one field.
required:
- id
- field
- operator
properties:
id:
type: string
format: uuid
description: An id for the condition, unique within the pipeline.
field:
type: string
example: /request/arguments/amount_cents
description: A JSON pointer into the request, such as `/request/tool` or
`/request/arguments/amount_cents`.
operator:
type: string
enum:
- 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
description: 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.
value:
description: The value to compare with. Not used by operators that take none, such as `exists`.
CELAllCondition:
type: object
additionalProperties: false
description: Holds when every nested condition holds.
required:
- id
- all
properties:
id:
type: string
format: uuid
description: An id for the condition, unique within the pipeline.
all:
type: array
minItems: 1
description: The conditions that must all hold.
items:
$ref: "#/components/schemas/CELCondition"
CELAnyCondition:
type: object
additionalProperties: false
description: Holds when at least one nested condition holds.
required:
- id
- any
properties:
id:
type: string
format: uuid
description: An id for the condition, unique within the pipeline.
any:
type: array
minItems: 1
description: The conditions, of which at least one must hold.
items:
$ref: "#/components/schemas/CELCondition"
CELSomeCondition:
type: object
additionalProperties: false
description: Holds when at least one item of a list field matches.
required:
- id
- some
properties:
id:
type: string
format: uuid
description: An id for the condition, unique within the pipeline.
some:
$ref: "#/components/schemas/CELQuantifier"
CELQuantifier:
type: object
additionalProperties: false
description: A list field and the condition each of its items is tested against.
required:
- field
- where
properties:
field:
type: string
description: A JSON pointer to a list field.
where:
$ref: "#/components/schemas/CELCondition"
CELEveryCondition:
type: object
additionalProperties: false
description: Holds when every item of a list field matches.
required:
- id
- every
properties:
id:
type: string
format: uuid
description: An id for the condition, unique within the pipeline.
every:
$ref: "#/components/schemas/CELQuantifier"
StoredWebhookBlock:
type: object
additionalProperties: false
description: A webhook block as saved in a revision.
required:
- id
- snapshot_id
- name
- type
- enabled
- timeout
- max_attempts
- config
- config_version
- content_hash
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The block's key, unique within the pipeline including blocks inside branches.
snapshot_id:
type: string
format: uuid
description: The id of the saved copy.
name:
type: string
minLength: 1
description: The display name.
type:
type: string
const: webhook
description: Always `webhook`.
enabled:
type: boolean
const: true
description: Always `true`.
timeout:
type: string
example: 5s
description: How long one attempt may take.
max_attempts:
type: integer
minimum: 1
maximum: 3
description: How many times to try.
config:
$ref: "#/components/schemas/WebhookBlockConfig"
config_version:
type: integer
const: 1
description: The version of the config format. Always 1.
content_hash:
type: string
pattern: ^[0-9a-f]{64}$
description: A SHA-256 hash of the block's content.
WebhookBlockConfig:
type: object
additionalProperties: false
required:
- endpoint_key
- allowed_outcomes
- include
- reason
description: 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.
properties:
endpoint_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The webhook endpoint to call.
allowed_outcomes:
type: array
minItems: 1
uniqueItems: true
items:
type: string
enum:
- next
- human
- approve
- deny
description: The outcomes the endpoint may return. Any other answer sends the request to a person.
`approve` and `deny` must be listed explicitly. The editor defaults to `next` and
`human`.
include:
type: object
additionalProperties: false
description: What to send with the request.
required:
- agent_reasoning
- context
- previous_metadata
properties:
agent_reasoning:
type: boolean
description: Include the agent's own explanation of the call.
context:
type: boolean
description: Include the runtime metadata the adapter reported.
previous_metadata:
type: boolean
description: Include the metadata earlier blocks returned.
reason:
type: string
minLength: 1
description: The note recorded on decisions this block makes. The endpoint's own reason is kept as
evidence.
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: Where the request goes when the endpoint answers `human`, or when the call fails.
Defaults to the pipeline's default path.
StoredBranchBlock:
type: object
additionalProperties: false
description: A branch as saved in a revision, with its blocks as saved.
required:
- id
- snapshot_id
- name
- type
- enabled
- timeout
- max_attempts
- config
- config_version
- content_hash
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The block's key, unique within the pipeline including blocks inside branches.
snapshot_id:
type: string
format: uuid
description: The id of the saved copy.
name:
type: string
minLength: 1
description: The display name.
type:
type: string
const: branch
description: Always `branch`.
enabled:
type: boolean
const: true
description: Always `true`.
timeout:
type: string
example: 50ms
description: How long the condition may take.
max_attempts:
type: integer
const: 1
description: Always 1.
config:
$ref: "#/components/schemas/StoredBranchBlockConfig"
config_version:
type: integer
const: 1
description: The version of the config format. Always 1.
content_hash:
type: string
pattern: ^[0-9a-f]{64}$
description: A SHA-256 hash of the branch's own content. Each of its blocks has its own.
StoredBranchBlockConfig:
type: object
additionalProperties: false
description: A branch configuration as saved, with its blocks as saved.
required:
- when
- blocks
- reason
properties:
when:
$ref: "#/components/schemas/CELCondition"
blocks:
type: array
minItems: 1
description: The branch's blocks, in the order they run.
items:
$ref: "#/components/schemas/StoredRequestApprovalBlock"
reason:
type: string
minLength: 1
description: The note recorded when none of the branch's blocks decides.
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: Where the request goes when none of the branch's blocks decides.
StoredLLMJudgeBlock:
type: object
additionalProperties: false
description: A model review block as saved in a revision.
required:
- id
- snapshot_id
- name
- type
- enabled
- timeout
- max_attempts
- config
- config_version
- content_hash
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The block's key, unique within the pipeline including blocks inside branches.
snapshot_id:
type: string
format: uuid
description: The id of the saved copy.
name:
type: string
minLength: 1
description: The display name.
type:
type: string
const: llm_judge
description: Always `llm_judge`.
enabled:
type: boolean
const: true
description: Always `true`.
timeout:
type: string
example: 30s
description: How long one attempt may take.
max_attempts:
type: integer
minimum: 1
maximum: 3
description: How many times to try.
config:
$ref: "#/components/schemas/LLMJudgeBlockConfig"
config_version:
type: integer
const: 1
description: The version of the config format. Always 1.
content_hash:
type: string
pattern: ^[0-9a-f]{64}$
description: A SHA-256 hash of the block's content.
x-withhuman-edition: hosted
LLMJudgeBlockConfig:
type: object
additionalProperties: false
required:
- effort
- instructions
- include
- on_verdict
- automatic_decisions_require_confidence
- reason
description: 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.
properties:
model:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The key of the judge model to use, such as `gpt-5.6-terra`. Leave it out to use the
deployment's default model, which follows the deployment when its default changes. A key
the deployment does not offer is refused with `judge_model_unavailable`.
effort:
type: string
enum:
- low
- medium
- high
description: "How much reasoning the model spends: `low`, `medium`, or `high`."
instructions:
type: string
minLength: 1
maxLength: 8192
description: Your review policy, in plain language, up to 8,192 characters. It goes into the system
prompt after withHuman's fixed framing.
include:
type: object
additionalProperties: false
description: What to show the model.
required:
- agent_reasoning
- context
- previous_metadata
properties:
agent_reasoning:
type: boolean
description: Include the agent's own explanation of the call.
context:
type: boolean
description: Include the runtime metadata the adapter reported.
previous_metadata:
type: boolean
description: Include the metadata earlier blocks returned.
on_verdict:
type: object
additionalProperties: false
description: What the pipeline does with each verdict.
required:
- approve
- deny
- escalate
properties:
approve:
type: string
enum:
- approve
- next
- human
description: What an `approve` verdict does.
deny:
type: string
enum:
- deny
- next
- human
description: What a `deny` verdict does.
escalate:
type: string
enum:
- human
- next
description: What an `escalate` verdict does.
automatic_decisions_require_confidence:
type: string
enum:
- high
- medium
- low
description: The confidence the model must report before its verdict can decide on its own. Below
it, the verdict is treated as `escalate`.
reason:
type: string
minLength: 1
description: The note recorded on decisions this block makes. The model's reasoning is kept as
evidence.
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: Where the request goes when a verdict maps to `human`. Defaults to the pipeline's
default path.
RequestApprovalPipelinePreviewRequest:
type: object
additionalProperties: false
description: A pipeline document and a sample request to run it against.
required:
- blocks
- sample
properties:
blocks:
type: array
maxItems: 1000
description: The blocks, in the order they should run.
items:
$ref: "#/components/schemas/RequestApprovalBlockDefinition"
default_escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The default escalation path, as in a document. Reported in the result when the outcome
is `human`.
sample:
type: object
additionalProperties: false
description: The request to run the pipeline against.
required:
- request
- agent
properties:
request:
type: object
additionalProperties: false
description: The tool call.
required:
- tool
- arguments
properties:
tool:
type: string
minLength: 1
example: issue_refund
description: The tool the sample agent is calling.
server:
type: string
example: stripe
description: The MCP server that defines the tool, as an adapter would report it. Leave it out for a
built-in tool.
arguments:
type: object
additionalProperties: true
example:
amount_cents: 12000
description: The tool's arguments.
agent_reasoning:
type: string
description: The agent's own explanation of the call. Only blocks that include it see it.
context:
type: object
additionalProperties: true
description: Runtime metadata, as an adapter would report it. Only blocks that include it see it.
agent:
type: object
additionalProperties: false
description: The agent making the call.
required:
- slug
- name
properties:
slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The agent's slug.
name:
type: string
minLength: 1
description: A name for the sample agent.
previous_metadata:
type: object
description: Metadata as if earlier blocks had returned it, keyed by scope and then by block key.
Use it to carry the organization preview's metadata into an agent preview, so the
two previews together behave like one request.
properties:
organization:
type: object
additionalProperties:
type: object
additionalProperties: true
description: Metadata from the organization pipeline, by block key.
agent:
type: object
additionalProperties:
type: object
additionalProperties: true
description: Metadata from the agent's pipeline, by block key.
additionalProperties: false
RequestApprovalBlockDefinition:
description: One block of a pipeline document. The type picks the kind of block and the shape of its
config. These are the kinds every edition has; an edition may add kinds of its own with
their own config shapes (the hosted edition adds `llm_judge`), and a document naming a kind
this deployment lacks is refused as invalid.
oneOf:
- $ref: "#/components/schemas/AlwaysBlockDefinition"
- $ref: "#/components/schemas/CELBlockDefinition"
- $ref: "#/components/schemas/WebhookBlockDefinition"
- $ref: "#/components/schemas/BranchBlockDefinition"
- $ref: "#/components/schemas/LLMJudgeBlockDefinition"
discriminator:
propertyName: type
mapping:
always: "#/components/schemas/AlwaysBlockDefinition"
cel: "#/components/schemas/CELBlockDefinition"
webhook: "#/components/schemas/WebhookBlockDefinition"
branch: "#/components/schemas/BranchBlockDefinition"
llm_judge: "#/components/schemas/LLMJudgeBlockDefinition"
AlwaysBlockDefinition:
type: object
additionalProperties: false
description: A block that returns its configured outcome for every request that reaches it.
required:
- id
- name
- type
- enabled
- timeout
- max_attempts
- config
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: review-remaining-requests
description: A key for the block, unique within the pipeline including blocks inside branches.
name:
type: string
minLength: 1
example: Review remaining requests
description: A display name.
type:
type: string
const: always
description: Always `always`.
enabled:
type: boolean
const: true
description: Always `true`. Leave disabled blocks out of the document.
timeout:
type: string
example: 50ms
description: How long execution may take, as a duration. At most `1s`.
max_attempts:
type: integer
const: 1
description: Always 1.
config:
$ref: "#/components/schemas/AlwaysBlockConfig"
CELBlockDefinition:
type: object
additionalProperties: false
description: A block that tests the request against a condition.
required:
- id
- name
- type
- enabled
- timeout
- max_attempts
- config
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: A key for the block, unique within the pipeline including blocks inside branches.
name:
type: string
minLength: 1
description: A display name.
type:
type: string
const: cel
description: Always `cel`.
enabled:
type: boolean
const: true
description: Always `true`. Leave disabled blocks out of the document.
timeout:
type: string
example: 50ms
description: How long the condition may take, as a duration. At most `1s`.
max_attempts:
type: integer
const: 1
description: Always 1.
config:
$ref: "#/components/schemas/CELBlockConfig"
WebhookBlockDefinition:
type: object
additionalProperties: false
description: A block that asks a webhook endpoint of yours for an outcome.
required:
- id
- name
- type
- enabled
- timeout
- max_attempts
- config
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: A key for the block, unique within the pipeline including blocks inside branches.
name:
type: string
minLength: 1
description: A display name.
type:
type: string
const: webhook
description: Always `webhook`.
enabled:
type: boolean
const: true
description: Always `true`. Leave disabled blocks out of the document.
timeout:
type: string
example: 5s
description: How long one attempt may take, from connecting to the full response, as a duration. At
most `30s`.
max_attempts:
type: integer
minimum: 1
maximum: 3
description: How many times to try, from 1 to 3.
config:
$ref: "#/components/schemas/WebhookBlockConfig"
BranchBlockDefinition:
type: object
additionalProperties: false
description: 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.
required:
- id
- name
- type
- enabled
- timeout
- max_attempts
- config
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: refunds
description: A key for the block, unique within the pipeline including blocks inside branches.
name:
type: string
minLength: 1
example: Refunds
description: A display name.
type:
type: string
const: branch
description: Always `branch`.
enabled:
type: boolean
const: true
description: Always `true`. Leave disabled blocks out of the document.
timeout:
type: string
example: 50ms
description: How long the condition may take, as a duration. At most `1s`.
max_attempts:
type: integer
const: 1
description: Always 1.
config:
$ref: "#/components/schemas/BranchBlockConfig"
BranchBlockConfig:
type: object
additionalProperties: false
description: 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.
required:
- when
- blocks
- reason
properties:
when:
$ref: "#/components/schemas/CELCondition"
blocks:
type: array
minItems: 1
maxItems: 999
description: The branch's blocks, in the order they run. Any type, including further branches. They
count toward the pipeline's 1,000 blocks.
items:
$ref: "#/components/schemas/RequestApprovalBlockDefinition"
reason:
type: string
minLength: 1
description: The note recorded when none of the branch's blocks decides.
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: 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.
LLMJudgeBlockDefinition:
type: object
additionalProperties: false
description: A block that asks a model to review the request against your written policy.
required:
- id
- name
- type
- enabled
- timeout
- max_attempts
- config
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: A key for the block, unique within the pipeline including blocks inside branches.
name:
type: string
minLength: 1
description: A display name.
type:
type: string
const: llm_judge
description: Always `llm_judge`.
enabled:
type: boolean
const: true
description: Always `true`. Leave disabled blocks out of the document.
timeout:
type: string
example: 30s
description: How long one attempt may take, as a duration. At most `60s`.
max_attempts:
type: integer
minimum: 1
maximum: 3
description: How many times to try, from 1 to 3.
config:
$ref: "#/components/schemas/LLMJudgeBlockConfig"
x-withhuman-edition: hosted
RequestApprovalPipelinePreview:
type: object
additionalProperties: false
description: What the pipeline did with the sample.
required:
- outcome
- reason_code
- blocks
properties:
outcome:
type: string
enum:
- approve
- deny
- human
description: The final outcome.
reason_code:
type: string
enum:
- block_outcome
- block_error
- end_of_pipeline
- end_of_branch
description: "Why the run ended. `block_outcome`: a block decided. `block_error`: a block failed.
`end_of_pipeline`: the request passed every block. `end_of_branch`: the request entered
a branch and none of its blocks decided."
terminal_block_id:
type: string
description: "The block that ended the run: for `end_of_branch`, the branch. Absent when the request
passed every block."
blocks:
type: array
description: What each block the request reached did, in order. A branch appears when its condition
is evaluated, and once more with `end_of_branch` when none of its blocks decided.
items:
$ref: "#/components/schemas/RequestApprovalPipelinePreviewBlock"
escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: "The path a `human` outcome goes to, within this scope alone: the ending block's path,
or the document's default. Absent when the outcome is automatic or no path applies."
escalation_path_reason:
type: string
enum:
- block_escalation
- pipeline_default
- no_escalation
description: "Where the path came from. `block_escalation`: the ending block named it.
`pipeline_default`: the document's default. `no_escalation`: no path applies."
RequestApprovalPipelinePreviewBlock:
type: object
additionalProperties: false
description: What one block did in a preview.
required:
- block_id
- position
- outcome
- cost
- trace
properties:
block_id:
type: string
description: The block's key.
position:
type: integer
minimum: 0
description: The block's position in the pipeline, from 0, counting in document order with each
branch before the blocks it holds.
outcome:
type: string
enum:
- next
- approve
- deny
- human
- enter
description: What the block returned. A branch returns `enter` when its condition matched and `next`
when it did not.
reason:
type: string
description: The note the block recorded.
metadata:
type: object
additionalProperties: true
description: The metadata the block returned, if any.
cost:
type: integer
format: int64
minimum: 0
description: The cost the block recorded.
error_code:
type: string
description: "Why the block failed, if it did: `invalid_input`, `evaluation_error`,
`cost_limit_exceeded`, `resource_limit_exceeded`, `timeout`, `invalid_block_output`,
`internal_error`, `provider_error`, `endpoint_unreachable`, `endpoint_error`,
`endpoint_rejected`, `outcome_not_allowed`, `endpoint_unavailable`, or a code a kind the
edition adds reports (the hosted judge's `model_unavailable`)."
delivery:
type: object
additionalProperties: false
required:
- endpoint_key
- endpoint_host
- duration_ms
description: For webhook blocks. What was called and what came back. Never the bodies, never the
secret.
properties:
endpoint_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The endpoint that was called.
endpoint_revision:
type: integer
format: int64
minimum: 1
description: The revision that was delivered to. Absent when the endpoint had no active revision.
endpoint_host:
type: string
description: The host that was called.
http_status:
type: integer
description: The HTTP status the endpoint returned.
duration_ms:
type: integer
format: int64
description: How long the call took, in milliseconds.
reason:
type: string
description: The endpoint's own explanation, at most 1,024 characters.
trace:
type: array
description: How the condition was evaluated, node by node. For condition blocks and branches.
items:
type: object
additionalProperties: false
required:
- node_id
- matched
- error
properties:
node_id:
type: string
format: uuid
description: The condition's id.
matched:
type: boolean
description: Whether the condition held.
error:
type: boolean
description: Whether evaluating it failed.
end_of_branch:
type: boolean
description: Present and true on the step that closes a branch the request entered when none of its
blocks decided. The outcome is `human`.
RequestApprovalPipelineRevisionSummary:
type: object
additionalProperties: false
description: One pipeline revision, without its blocks.
required:
- id
- scope
- revision
- is_active
- block_count
- created_by_actor_id
- created_at
properties:
id:
type: string
format: uuid
scope:
$ref: "#/components/schemas/PipelineScope"
agent_slug:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The agent's slug. Present for the `agent` scope only.
revision:
type: integer
format: int64
minimum: 1
description: The revision number. Revisions count up from 1.
is_active:
type: boolean
description: Whether this is the revision in use.
block_count:
type: integer
minimum: 0
maximum: 1000
description: Every block in the revision, including blocks inside branches.
default_escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The escalation path for requests that reach a person without a block naming a path.
created_by_actor_id:
type: string
format: uuid
description: The membership or organization-key actor that created the revision.
created_at:
type: string
format: date-time
archived_at:
type: string
format: date-time
description: Present when the revision was archived with its agent. An archived revision can be read
but not activated until the agent is restored.
RequestApprovalPipelineDocument:
type: object
additionalProperties: false
description: The complete content of a new pipeline revision.
required:
- blocks
properties:
blocks:
type: array
maxItems: 1000
description: The blocks, in the order they should run. A branch holds its own blocks in
`config.blocks`, and the pipeline may hold at most 1,000 blocks counting those. An empty
list passes every request through to a person.
items:
$ref: "#/components/schemas/RequestApprovalBlockDefinition"
default_escalation_path:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: The escalation path for requests that reach a person without a block naming a path.
That includes requests that reach the end of the pipeline. The agent's default wins over
the organization's. With neither, any reviewer who can decide may take the request from
the queue. The path must exist when the revision is created, and be active when it is
activated.
EscalationPathSummary:
type: object
additionalProperties: false
description: One escalation path and a summary of its history.
required:
- path_key
- name
- latest_revision
- revision_count
- latest_created_at
properties:
path_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
name:
type: string
description: The name from the latest revision.
active_revision:
type: integer
format: int64
minimum: 1
description: The revision requests are routed with. Absent while no revision is active, which is
always the case for an archived path.
latest_revision:
type: integer
format: int64
minimum: 1
description: The newest revision, active or not.
revision_count:
type: integer
format: int64
minimum: 1
latest_created_at:
type: string
format: date-time
description: When the newest revision was created.
archived_at:
type: string
format: date-time
description: When the path was archived. Absent for a live path.
EscalationPathCreate:
type: object
additionalProperties: false
description: The first revision's document plus the key it creates.
required:
- path_key
- name
- working_hours
- nodes
properties:
path_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
name:
type: string
working_hours:
type: array
items:
$ref: "#/components/schemas/WorkingHoursSet"
nodes:
type: array
minItems: 1
items:
$ref: "#/components/schemas/EscalationPathNode"
repeat:
$ref: "#/components/schemas/EscalationPathRepeat"
WorkingHoursSet:
type: object
additionalProperties: false
description: A named set of weekly hours in one timezone.
required:
- id
- name
- timezone
- intervals
properties:
id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
example: uk-office
description: A key for the set, unique within the document.
name:
type: string
example: UK office hours
description: A display name.
timezone:
type: string
example: Europe/London
description: An IANA timezone name, such as `Europe/London`.
intervals:
type: array
minItems: 1
description: The open windows. At least one.
items:
$ref: "#/components/schemas/WeekdayInterval"
WeekdayInterval:
type: object
additionalProperties: false
description: One window that repeats every week.
required:
- weekdays
- start
- end
properties:
weekdays:
type: array
minItems: 1
description: The days the window applies to.
items:
type: string
enum:
- mon
- tue
- wed
- thu
- fri
- sat
- sun
start:
type: string
pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$
example: 09:00
description: When the window opens, as `HH:MM` in 24-hour time.
end:
type: string
pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$
example: 18:00
description: When the window closes, as `HH:MM` in 24-hour time.
EscalationPathNode:
type: object
additionalProperties: false
description: 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.
required:
- id
- type
properties:
id:
type: string
format: uuid
description: An id for the node, unique within the path.
type:
type: string
enum:
- level
- if_else
- defer
description: The kind of node.
targets:
type: array
description: "`level` only. The people and teams to notify."
items:
$ref: "#/components/schemas/EscalationTarget"
escalate_after:
type: string
example: 15m
description: "`level` only. How long to wait for a decision before moving to the next node. A
duration from `1m` to `168h`."
raise_urgency:
type: boolean
description: "`level` only. Raise the request to `interrupt` urgency before notifying."
conditions:
type: array
minItems: 1
maxItems: 10
description: "`if_else` only. Every condition must hold for `then` to run. From 1 to 10."
items:
$ref: "#/components/schemas/EscalationPathCondition"
then:
type: array
description: "`if_else` only. The nodes to run when every condition holds. An empty list moves on to
the next node."
items:
$ref: "#/components/schemas/EscalationPathNode"
else:
type: array
description: "`if_else` only. The nodes to run otherwise. An empty list moves on to the next node."
items:
$ref: "#/components/schemas/EscalationPathNode"
working_hours_id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: "`defer` only. The working-hours set to wait for."
standard_only:
type: boolean
description: "`defer` only. Let `interrupt` requests skip the wait."
EscalationTarget:
type: object
additionalProperties: false
description: 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.
required:
- type
properties:
type:
type: string
enum:
- user
- team
- broadcast
id:
type: string
format: uuid
description: Required for user and team, absent for broadcast.
EscalationPathCondition:
type: object
additionalProperties: false
description: One test in an if_else node. The type picks the kind of test, and only that kind's
fields may be set.
required:
- type
properties:
type:
type: string
enum:
- working_hours
- urgency
- request
description: 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.
working_hours_id:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
description: "`working_hours` only. The set to test."
active:
type: boolean
description: "`working_hours` only. `true` tests that the window is open, `false` that it is closed."
urgency:
type: string
enum:
- standard
- interrupt
description: "`urgency` only. The urgency the request must have."
when:
$ref: "#/components/schemas/CELCondition"
EscalationPathRepeat:
type: object
additionalProperties: false
description: Run the path again from the top when the nodes run out without a decision. Every
condition is evaluated again on each run.
required:
- times
- after
properties:
times:
type: integer
minimum: 1
maximum: 9
description: How many extra runs, from 1 to 9.
after:
type: string
example: 1h
description: How long to wait before each extra run. A duration from `1m` to `168h`.
EscalationPathRevision:
description: One escalation path revision with its full document.
allOf:
- $ref: "#/components/schemas/EscalationPathRevisionSummary"
- type: object
required:
- working_hours
- nodes
- document_version
- warnings
properties:
working_hours:
type: array
description: The named working-hours sets the nodes refer to.
items:
$ref: "#/components/schemas/WorkingHoursSet"
nodes:
type: array
description: The nodes, in order.
items:
$ref: "#/components/schemas/EscalationPathNode"
repeat:
$ref: "#/components/schemas/EscalationPathRepeat"
document_version:
type: integer
minimum: 1
description: The version of the document format.
warnings:
type: array
description: "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."
items:
$ref: "#/components/schemas/EscalationPathWarning"
EscalationPathRevisionSummary:
type: object
additionalProperties: false
description: One escalation path revision, without its document.
required:
- id
- path_key
- revision
- is_active
- name
- created_by_actor_id
- created_at
properties:
id:
type: string
format: uuid
path_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
revision:
type: integer
format: int64
minimum: 1
description: The revision number. Revisions count up from 1.
is_active:
type: boolean
description: Whether requests are routed with this revision.
name:
type: string
example: On-call engineers
description: The display name.
created_by_actor_id:
type: string
format: uuid
description: The membership or organization-key actor that created the revision.
created_at:
type: string
format: date-time
archived_at:
type: string
format: date-time
description: When the path was archived. Absent for a revision of a live path; an archived revision
can no longer be activated.
EscalationPathWarning:
type: object
description: One advisory finding about the document. The team fields are present only for findings
about a team target.
required:
- code
properties:
code:
type: string
enum:
- team_members_cannot_decide
- path_may_reach_nobody
description: "`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."
team_id:
type: string
format: uuid
team_name:
type: string
count:
type: integer
description: Members of the team who cannot decide requests.
member_count:
type: integer
description: Active members of the team.
EscalationPathDocument:
type: object
additionalProperties: false
description: The complete content of a new escalation path revision.
required:
- name
- working_hours
- nodes
properties:
name:
type: string
example: On-call engineers
description: A display name. Reviewers see it on the requests the path routes.
working_hours:
type: array
description: Named working-hours sets that `if_else` and `defer` nodes refer to by id. May be empty.
items:
$ref: "#/components/schemas/WorkingHoursSet"
nodes:
type: array
minItems: 1
description: The nodes, in order. At least one.
items:
$ref: "#/components/schemas/EscalationPathNode"
repeat:
$ref: "#/components/schemas/EscalationPathRepeat"
MyAPIKeysEnvelope:
type: object
additionalProperties: false
required:
- keys
- personal_api_keys_allowed
- scoping_enabled
- bearable_permissions
properties:
keys:
type: array
items:
$ref: "#/components/schemas/APIKey"
personal_api_keys_allowed:
type: boolean
description: The organization's switch.
scoping_enabled:
type: boolean
description: Whether a key may carry a permission list here. False in the open edition, where keys
inherit the member's role.
bearable_permissions:
type: array
items:
$ref: "#/components/schemas/APIKeyPermission"
APIKey:
type: object
additionalProperties: false
description: An API key without its secret. Personal prefixes start with `whk_`; organization
prefixes start with `who_`.
required:
- grants
- usable
- id
- prefix
- kind
- name
- permissions
- assurance_at_issue
- status
- created_at
- expires_at
- last_used_at
- revoked_at
properties:
grants:
type: array
items:
$ref: "#/components/schemas/Grant"
description: Current organization-key grants projected from RBAC permission policies. Empty for
personal keys.
effective_grants:
type: array
items:
$ref: "#/components/schemas/Grant"
description: On detail responses, currently allowed organization-key grants.
usable:
type: boolean
description: Whether the key is active and not blocked by organization policy or status.
blocked_reason:
type: string
enum:
- organization_inactive
- api_keys_disabled
id:
type: string
format: uuid
prefix:
type: string
example: whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31
kind:
type: string
enum:
- personal
- organization
description: Personal keys act as a member; organization keys act as themselves.
membership_id:
type: string
format: uuid
description: The member a personal key acts as.
created_by:
type: object
additionalProperties: false
description: Who minted it. Present on the organization-wide list.
required:
- membership_id
- display_name
- email
properties:
membership_id:
type: string
format: uuid
display_name:
type: string
email:
type: string
name:
type: string
permissions:
type:
- array
- "null"
items:
type: string
description: The narrowing list, or `null` when the key inherits everything its member holds. The
key's effective permissions are always this list intersected with the member's current
grants.
assurance_at_issue:
type: string
description: The assurance of the session that minted the key (`strong`, `single_factor`), presented
as the key's own when it decides.
oauth_client:
type: object
additionalProperties: false
description: The MCP client the key was minted for through the consent page. Absent for a key made
by hand.
required:
- id
- name
properties:
id:
type: string
format: uuid
name:
type: string
status:
type: string
enum:
- active
- expired
- revoked
created_at:
type: string
format: date-time
expires_at:
type:
- string
- "null"
format: date-time
description: "`null` when the key lives until revoked."
last_used_at:
type:
- string
- "null"
format: date-time
revoked_at:
type:
- string
- "null"
format: date-time
revocation_reason:
type: string
enum:
- manual
- membership_deprovisioned
CreateAPIKeyRequest:
type: object
additionalProperties: false
required:
- name
properties:
name:
type: string
minLength: 1
maxLength: 100
example: terraform
permissions:
type: array
items:
type: string
minItems: 1
description: Narrow the key to these permissions. Omit to inherit everything you hold. Each must be
a permission you hold; only in the hosted edition.
example:
- pipeline.read
- pipeline.write
- pipeline.activate
expires_at:
type: string
format: date-time
description: When the key stops working. Omit for a key that lives until revoked, unless the
deployment caps key lifetime.
IssuedAPIKeyEnvelope:
type: object
additionalProperties: false
required:
- key
- token
properties:
key:
$ref: "#/components/schemas/APIKey"
token:
type: string
description: "The raw key, shown once: `whk_<id>_<secret>`."
example: whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31_x5nH9v…
VerifiedAPIKeyEnvelope:
type: object
additionalProperties: false
required:
- key
- effective_permissions
properties:
effective_grants:
type: array
items:
$ref: "#/components/schemas/Grant"
key:
$ref: "#/components/schemas/APIKey"
effective_permissions:
type: array
items:
type: string
description: What the key may do right now, after narrowing and the member's current roles.
APIKeysEnvelope:
type: object
additionalProperties: false
required:
- keys
properties:
keys:
type: array
items:
$ref: "#/components/schemas/APIKey"
CreateOrganizationAPIKeyRequest:
type: object
additionalProperties: false
required:
- name
- permission_policies
properties:
name:
type: string
minLength: 1
maxLength: 100
permission_policies:
type: array
minItems: 1
maxItems: 20
items:
$ref: "#/components/schemas/PermissionPolicySpec"
description: 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.
expires_at:
type:
- string
- "null"
format: date-time
PermissionPolicySpec:
type: object
additionalProperties: false
required:
- scope_kind
- resource_ids
- permissions
properties:
scope_kind:
type: string
enum:
- organization
- team
- agent
resource_ids:
type: array
items:
type: string
maxItems: 200
uniqueItems: true
description: Empty for organization scope; otherwise the selected team IDs or agent slugs.
permissions:
type: array
items:
type: string
minItems: 1
uniqueItems: true
OrganizationAPIKeyCreated:
type: object
additionalProperties: false
description: An API key without its secret. Personal prefixes start with `whk_`; organization
prefixes start with `who_`.
required:
- secret_available
- grants
- usable
- id
- prefix
- kind
- name
- permissions
- assurance_at_issue
- status
- created_at
- expires_at
- last_used_at
- revoked_at
properties:
secret_available:
type: boolean
token:
type: string
writeOnly: false
description: Returned only in the first creation response.
grants:
type: array
items:
$ref: "#/components/schemas/Grant"
description: Current organization-key grants projected from RBAC permission policies. Empty for
personal keys.
effective_grants:
type: array
items:
$ref: "#/components/schemas/Grant"
description: On detail responses, currently allowed organization-key grants.
usable:
type: boolean
description: Whether the key is active and not blocked by organization policy or status.
blocked_reason:
type: string
enum:
- organization_inactive
- api_keys_disabled
id:
type: string
format: uuid
prefix:
type: string
example: whk_4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31
kind:
type: string
enum:
- personal
- organization
description: Personal keys act as a member; organization keys act as themselves.
membership_id:
type: string
format: uuid
description: The member a personal key acts as.
created_by:
type: object
additionalProperties: false
description: Who minted it. Present on the organization-wide list.
required:
- membership_id
- display_name
- email
properties:
membership_id:
type: string
format: uuid
display_name:
type: string
email:
type: string
name:
type: string
permissions:
type:
- array
- "null"
items:
type: string
description: The narrowing list, or `null` when the key inherits everything its member holds. The
key's effective permissions are always this list intersected with the member's current
grants.
assurance_at_issue:
type: string
description: The assurance of the session that minted the key (`strong`, `single_factor`), presented
as the key's own when it decides.
status:
type: string
enum:
- active
- expired
- revoked
created_at:
type: string
format: date-time
expires_at:
type:
- string
- "null"
format: date-time
description: "`null` when the key lives until revoked."
last_used_at:
type:
- string
- "null"
format: date-time
revoked_at:
type:
- string
- "null"
format: date-time
revocation_reason:
type: string
enum:
- manual
- membership_deprovisioned
Member:
type: object
description: A member as shown in the member list, with their name and email.
required:
- id
- display_name
- email
- status
- assignments
- admission_source
- directory_managed
- team_ids
- created_at
properties:
id:
type: string
format: uuid
description: The membership id. Other endpoints refer to a member by this id.
display_name:
type: string
example: Ada Lovelace
email:
type: string
example: [email protected]
status:
type: string
enum:
- active
- suspended
- deprovisioned
description: "`active` can sign in and act. `suspended` cannot until reactivated. `deprovisioned`
was removed and is kept for the record."
assignments:
type: array
description: The roles the member holds, directly and through their teams.
items:
$ref: "#/components/schemas/RoleAssignment"
admission_source:
type: string
enum:
- manual
- invite
- sso_jit
- directory
description: "How the person joined: added by hand, by accepting an invitation, on first SSO
sign-in, or by directory sync."
directory_managed:
type: boolean
description: Whether your directory owns this membership. Status changes here are refused.
team_ids:
type: array
description: The teams the member is on.
items:
type: string
format: uuid
created_at:
type: string
format: date-time
Membership:
type: object
description: A person's membership of the organization, as stored.
required:
- id
- user_id
- organization_id
- status
- admission_source
- directory_managed
- assignments
properties:
id:
type: string
format: uuid
description: The membership id. Other endpoints refer to a member by this id.
user_id:
type: string
format: uuid
description: The person's user id, shared across organizations.
organization_id:
type: string
format: uuid
status:
type: string
enum:
- active
- suspended
- deprovisioned
description: "`active` can sign in and act. `suspended` cannot until reactivated. `deprovisioned`
was removed and is kept for the record."
admission_source:
type: string
enum:
- manual
- invite
- sso_jit
- directory
description: "How the person joined: added by hand, by accepting an invitation, on first SSO
sign-in, or by directory sync."
directory_managed:
type: boolean
description: Whether your directory owns this membership. Status changes here are refused.
assignments:
type: array
description: The roles the member holds, directly and through their teams.
items:
$ref: "#/components/schemas/RoleAssignment"
team_ids:
type: array
description: The teams the member is on.
items:
type: string
format: uuid
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
PermissionDescriptor:
type: object
description: One permission a role can carry.
required:
- key
- area
- description
- holders
- resource
- dangerous
properties:
key:
type: string
example: request.decide
description: The permission key, shaped `resource.action`.
area:
type: string
example: request
description: The key's first segment, for grouping.
description:
type: string
example: Approve or deny requests routed to you
description: What the permission allows.
holders:
type: array
description: "Who can hold it: people, agents, or the gateway."
items:
type: string
enum:
- human
- agent
- gateway
- api_key
- organization_api_key
resource:
type: string
enum:
- organization
- team
- agent
- request
- scope
description: What kind of thing a check on this permission is about. A grant must cover that thing's
scope.
dangerous:
type: boolean
description: 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.
WebhookEndpointSummary:
type: object
additionalProperties: false
required:
- endpoint_key
- name
- url
- latest_revision
- revision_count
- latest_created_at
- secret_rotated_at
- uses
description: One webhook endpoint and a summary of its history.
properties:
endpoint_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
name:
type: string
description: The name from the latest revision.
url:
type: string
format: uri
description: The URL from the latest revision.
active_revision:
type: integer
format: int64
minimum: 1
description: The revision deliveries go to. Absent while the endpoint is archived or no revision has
been activated yet.
archived_at:
type: string
format: date-time
description: When the endpoint was archived. Absent for a live endpoint, including one whose
revisions are all drafts. Cleared when a revision is activated again.
latest_revision:
type: integer
format: int64
minimum: 1
description: The newest revision, active or not.
revision_count:
type: integer
format: int64
minimum: 1
latest_created_at:
type: string
format: date-time
description: When the newest revision was created.
secret_rotated_at:
type: string
format: date-time
description: When the signing secret was last created or rotated.
uses:
type: array
description: The active pipeline revisions that post to the endpoint. Empty when it can be archived.
items:
$ref: "#/components/schemas/PipelineUse"
PipelineUse:
type: object
required:
- pipeline_id
- scope
- revision
description: One place an active pipeline revision uses a shared object, an escalation path or a
webhook endpoint.
properties:
pipeline_id:
type: string
format: uuid
scope:
type: string
enum:
- organization
- agent
description: The pipeline's scope.
agent_slug:
type: string
description: The agent's slug. Present for the `agent` scope only.
revision:
type: integer
format: int64
description: The active revision number.
block_key:
type: string
description: The block that uses the object. Absent when the revision names an escalation path as
its default.
WebhookEndpointRevision:
type: object
additionalProperties: false
required:
- id
- endpoint_key
- revision
- is_active
- name
- url
- created_by_actor_id
- created_at
description: 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.
properties:
id:
type: string
format: uuid
endpoint_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
revision:
type: integer
format: int64
minimum: 1
description: The revision number. Revisions count up from 1.
is_active:
type: boolean
description: Whether deliveries go to this revision.
name:
type: string
example: Fraud check
description: The display name.
url:
type: string
format: uri
example: https://fraud.example.com/approvals
description: Where deliveries go.
created_by_actor_id:
type: string
format: uuid
description: The membership or organization-key actor that created the revision.
created_at:
type: string
format: date-time
signing_secret:
type: string
example: whsec_9f2c1b7e4d3a4f8b
description: 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.
WebhookEndpointDocument:
type: object
additionalProperties: false
description: The content of a new webhook endpoint revision.
required:
- name
- url
properties:
name:
type: string
minLength: 1
example: Fraud check
description: A display name. Pipeline authors see it when they pick an endpoint.
url:
type: string
format: uri
example: https://fraud.example.com/approvals
description: An absolute public HTTPS URL. It is checked against the egress policy, which refuses
private and internal addresses.
WebhookEndpointTest:
type: object
required:
- status_code
- duration_ms
description: What one test delivery produced. problem is absent when the endpoint answered with a
valid outcome.
properties:
status_code:
type: integer
description: The HTTP status the endpoint returned.
duration_ms:
type: integer
format: int64
description: How long the call took, in milliseconds.
outcome:
type: string
enum:
- next
- human
- approve
- deny
description: The outcome the endpoint answered with, when its answer was valid.
reason:
type: string
description: The reason the endpoint gave, if any.
problem:
type: string
enum:
- blocked
- response_too_large
- timeout
- unreachable
- status
- invalid_answer
description: "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."
message:
type: string
description: Details about the problem, in plain words.
WebhookEndpointSecret:
type: object
additionalProperties: false
description: A freshly rotated signing secret.
required:
- endpoint_key
- signing_secret
- secret_rotated_at
properties:
endpoint_key:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
signing_secret:
type: string
example: whsec_9f2c1b7e4d3a4f8b
description: The new secret. It appears only in this response. Starts with `whsec_`. Your receiver
uses it to verify the `WithHuman-Signature` header.
secret_rotated_at:
type: string
format: date-time
description: When the secret was rotated.
Team:
type: object
description: 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.
required:
- id
- name
- directory_managed
- member_count
- assignments
- escalation_policy
- created_at
- archived_at
properties:
id:
type: string
format: uuid
name:
type: string
example: Payments approvers
directory_managed:
type: boolean
description: Whether your directory owns this team. Such a team cannot be renamed or archived here.
member_count:
type: integer
description: How many members the team has, from every source.
assignments:
type: array
description: The roles the team holds. Every member inherits them.
items:
$ref: "#/components/schemas/RoleAssignment"
escalation_policy:
$ref: "#/components/schemas/TeamEscalationPolicyStatus"
archived_at:
type:
- string
- "null"
format: date-time
description: When the team was archived. Null for a live team.
created_at:
type: string
format: date-time
TeamEscalationPolicyStatus:
type: object
additionalProperties: false
description: 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.
required:
- active_revision
- latest_revision
properties:
active_revision:
type:
- integer
- "null"
format: int64
minimum: 1
latest_revision:
type:
- integer
- "null"
format: int64
minimum: 1
TeamMember:
type: object
description: One member of a team.
required:
- membership_id
- display_name
- email
- status
- sources
properties:
membership_id:
type: string
format: uuid
display_name:
type: string
example: Ada Lovelace
email:
type: string
example: [email protected]
status:
type: string
enum:
- active
- suspended
- deprovisioned
description: The member's status in the organization.
sources:
type: array
description: "How the person got on the team: added by hand, by your directory, or both."
items:
type: string
enum:
- manual
- directory
TeamEscalationPolicyRevision:
allOf:
- $ref: "#/components/schemas/TeamEscalationPolicyRevisionSummary"
- type: object
required:
- working_hours
- nodes
- document_version
- warnings
properties:
working_hours:
type: array
items:
$ref: "#/components/schemas/WorkingHoursSet"
nodes:
type: array
items:
$ref: "#/components/schemas/EscalationPathNode"
repeat:
$ref: "#/components/schemas/EscalationPathRepeat"
document_version:
type: integer
minimum: 1
warnings:
type: array
description: "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."
items:
$ref: "#/components/schemas/TeamEscalationPolicyWarning"
TeamEscalationPolicyRevisionSummary:
type: object
additionalProperties: false
required:
- id
- team_id
- revision
- is_active
- created_by_actor_id
- created_at
properties:
id:
type: string
format: uuid
team_id:
type: string
format: uuid
revision:
type: integer
format: int64
minimum: 1
is_active:
type: boolean
created_by_actor_id:
type: string
format: uuid
created_at:
type: string
format: date-time
TeamEscalationPolicyWarning:
type: object
required:
- code
- count
- member_count
properties:
code:
type: string
enum:
- team_members_cannot_decide
- target_not_member
description: 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.
membership_id:
type: string
format: uuid
description: The departed target. Present for target_not_member only.
display_name:
type: string
description: The departed target's name, while the roster still lists them under another status.
count:
type: integer
description: Members of the team who cannot decide requests; 0 for target_not_member.
member_count:
type: integer
description: Active members of the team; 0 for target_not_member.
TeamEscalationPolicyDocument:
type: object
additionalProperties: false
description: 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.
required:
- working_hours
- nodes
properties:
working_hours:
type: array
items:
$ref: "#/components/schemas/WorkingHoursSet"
nodes:
type: array
minItems: 1
items:
$ref: "#/components/schemas/EscalationPathNode"
repeat:
$ref: "#/components/schemas/EscalationPathRepeat"
Invitation:
type: object
description: An invitation to join the organization.
required:
- id
- organization_id
- email
- grants
- invited_by_membership_id
- status
- expires_at
- created_at
- updated_at
properties:
id:
type: string
format: uuid
organization_id:
type: string
format: uuid
email:
type: string
example: [email protected]
description: The address the invitation was sent to.
grants:
type: array
description: The roles the person receives when they accept.
items:
$ref: "#/components/schemas/InvitationGrant"
invited_by_membership_id:
type: string
format: uuid
description: The member who sent it.
status:
type: string
enum:
- pending
- accepted
- revoked
- expired
description: "`pending` can still be accepted. `accepted` and `revoked` are final. `expired` passed
its expiry without being accepted."
expires_at:
type: string
format: date-time
description: When the link stops working. Seven days from creation or the last resend.
accepted_membership_id:
type: string
format: uuid
description: The membership created on acceptance. Present for `accepted` only.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
InvitationGrant:
type: object
description: Permissions granted at one scope, with provenance.
required:
- scope_kind
- permissions
properties:
scope_kind:
type: string
enum:
- organization
- team
- agent
description: Where the role applies.
scope_id:
type: string
description: The team id or the agent slug. Absent for `organization`.
permissions:
type: array
items:
type: string
InvitationWithURL:
type: object
description: 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.
required:
- invitation
properties:
invitation:
$ref: "#/components/schemas/Invitation"
invite_url:
type: string
format: uri
description: 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.
PermissionPoliciesPage:
type: object
required:
- name
- policies
- resources
- permissions
- can_create
- is_owner
properties:
name:
type: string
policies:
type: array
items:
$ref: "#/components/schemas/PermissionPolicy"
resources:
type: array
items:
$ref: "#/components/schemas/PermissionResource"
permissions:
type: array
items:
$ref: "#/components/schemas/PermissionDescriptor"
can_create:
type: boolean
is_owner:
type: boolean
read_only_reason:
type: string
PermissionPolicy:
type: object
required:
- id
- revision
- scope_kind
- resource_ids
- permissions
- resources
- source
- can_edit
properties:
scope_kind:
type: string
enum:
- organization
- team
- agent
resource_ids:
type: array
items:
type: string
maxItems: 200
uniqueItems: true
description: Empty for organization scope; otherwise the selected team IDs or agent slugs.
permissions:
type: array
items:
type: string
minItems: 1
uniqueItems: true
id:
type: string
format: uuid
revision:
type: integer
format: int64
resources:
type: array
items:
$ref: "#/components/schemas/PermissionResource"
source:
type: string
enum:
- manual
- directory
via_team_id:
type: string
format: uuid
via_team_name:
type: string
can_edit:
type: boolean
read_only_reason:
type: string
PermissionResource:
type: object
required:
- id
- name
- kind
properties:
id:
type: string
name:
type: string
kind:
type: string
enum:
- organization
- team
- agent
PermissionPolicyInput:
type: object
additionalProperties: false
required:
- scope_kind
- resource_ids
- permissions
- revision
properties:
scope_kind:
type: string
enum:
- organization
- team
- agent
resource_ids:
type: array
items:
type: string
maxItems: 200
uniqueItems: true
description: Empty for organization scope; otherwise the selected team IDs or agent slugs.
permissions:
type: array
items:
type: string
minItems: 1
uniqueItems: true
revision:
type: integer
format: int64
minimum: 1
description: Required when updating an existing policy.
ToolCatalogReport:
type: object
additionalProperties: false
required:
- runtime
- discovery_context
- complete
- sources
properties:
runtime:
type: string
minLength: 1
maxLength: 128
discovery_context:
type: string
minLength: 1
maxLength: 256
description: Stable opaque identifier for this local project or runtime context
complete:
type: boolean
description: Whether all configured sources in this context were enumerated
sources:
type: array
maxItems: 200
items:
$ref: "#/components/schemas/ToolCatalogSourceReport"
ToolCatalogSourceReport:
type: object
additionalProperties: false
required:
- source
- complete
- tools
properties:
source:
type: string
enum:
- builtin
- mcp
server:
type: string
maxLength: 256
complete:
type: boolean
error:
type: string
maxLength: 1024
tools:
type: array
maxItems: 5000
items:
$ref: "#/components/schemas/ToolDefinition"
ToolDefinition:
type: object
additionalProperties: false
required:
- name
properties:
name:
type: string
minLength: 1
maxLength: 256
description: Exact tool name sent in approval requests
display_name:
type: string
maxLength: 512
description:
type: string
maxLength: 65536
input_schema:
type: object
additionalProperties: true
description: Original JSON Schema, at most 256 KiB. Absence means unknown.
annotations:
$ref: "#/components/schemas/OnboardingToolAnnotations"
OnboardingToolAnnotations:
type: object
properties:
title:
type: string
read_only_hint:
type: boolean
destructive_hint:
type: boolean
idempotent_hint:
type: boolean
open_world_hint:
type: boolean
ToolCatalogPage:
type: object
required:
- tools
- sources
- total
- next_offset
properties:
tools:
type: array
items:
$ref: "#/components/schemas/ToolCatalogEntry"
sources:
type: array
items:
$ref: "#/components/schemas/ToolCatalogSource"
total:
type: integer
minimum: 0
next_offset:
type:
- integer
- "null"
ToolCatalogEntry:
allOf:
- $ref: "#/components/schemas/ToolCatalogEntryDefinition"
- type: object
required:
- id
- agent_slug
- discovery_context
- runtime
- source
- available
- stale
- schema_fingerprint
- last_seen_at
- last_attempt_at
properties:
id:
type: string
format: uuid
agent_slug:
type: string
agent_instance_id:
type: string
format: uuid
instance_name:
type: string
discovery_context:
type: string
runtime:
type: string
source:
type: string
description: "`builtin`, `mcp`, or a source an edition adds (the hosted gateway's `gateway`)"
server:
type: string
available:
type: boolean
stale:
type: boolean
error:
type: string
schema_fingerprint:
type: string
last_seen_at:
type: string
format: date-time
last_attempt_at:
type: string
format: date-time
ToolCatalogEntryDefinition:
type: object
required:
- name
properties:
name:
type: string
display_name:
type: string
description:
type: string
input_schema:
type: object
additionalProperties: true
annotations:
$ref: "#/components/schemas/OnboardingToolAnnotations"
ToolCatalogSource:
type: object
required:
- id
- agent_slug
- runtime
- discovery_context
- source
- last_attempt_at
- last_success_at
properties:
id:
type: string
format: uuid
agent_slug:
type: string
agent_instance_id:
type: string
format: uuid
runtime:
type: string
discovery_context:
type: string
source:
type: string
description: "`builtin`, `mcp`, or a source an edition adds (the hosted gateway's `gateway`)"
server:
type: string
last_attempt_at:
type: string
format: date-time
last_success_at:
type:
- string
- "null"
format: date-time
error:
type: string
AuditEvent:
type: object
description: One entry in the audit log. Entries are never changed or deleted.
required:
- sequence
- id
- organization_id
- event_type
- actor_type
- subject_type
- subject_id
- data
- occurred_at
properties:
sequence:
type: integer
format: int64
description: The event's position in the organization's log. Later events have higher numbers.
id:
type: string
format: uuid
organization_id:
type: string
format: uuid
event_type:
type: string
description: What happened, as a dotted key such as `membership.suspended`.
actor_type:
type: string
description: "Who did it: `human`, `agent`, `system`, or `api_key`."
actor_id:
type: string
format: uuid
description: The person, agent, or key that acted. Absent for `system`.
actor_display_name:
type: string
description: The actor's name at the time.
subject_type:
type: string
description: What kind of thing the event is about, such as a request or a membership.
subject_id:
type: string
format: uuid
description: The thing the event is about.
data:
description: Details specific to the event type, as JSON.
occurred_at:
type: string
format: date-time
description: When it happened.
RequestTimeline:
type: object
additionalProperties: false
description: The request's story from its audit events. pipeline is absent while a suspended
pipeline is still waiting on a judge.
required:
- assessments
- claims
- escalations
- audit_event_count
properties:
pipeline:
$ref: "#/components/schemas/PipelineReview"
assessments:
type: array
items:
$ref: "#/components/schemas/JudgeAssessment"
claims:
type: array
items:
$ref: "#/components/schemas/ClaimEvent"
escalations:
type: array
description: What the escalation executor did for the request, oldest first. Empty for a default
queue request.
items:
$ref: "#/components/schemas/EscalationEvent"
audit_event_count:
type: integer
description: How many audit events the request has in total, for the link into the audit log.
PipelineReview:
type: object
additionalProperties: false
description: 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.
required:
- outcome
- reason_code
- blocks_evaluated
- completed_at
properties:
outcome:
type: string
enum:
- approve
- deny
- human
reason_code:
type: string
description: Why the pipeline stopped, for example block_outcome, block_timeout, retries_exhausted,
end_of_branch (the request entered a branch and none of its blocks decided),
no_active_revision.
blocks_evaluated:
type: integer
terminal_block_key:
type: string
terminal_block_name:
type: string
terminal_block_type:
type: string
enum:
- always
- cel
- llm_judge
- webhook
- branch
terminal_block_reason:
type: string
description: The sentence the pipeline author configured on the terminal block, written for the
reviewer.
endpoint_reason:
type: string
description: A webhook endpoint's own explanation of its answer, when the terminal block was a
webhook that gave one.
terminal_block_error_code:
type: string
description: Present when the terminal block failed instead of answering, for example
`evaluation_error` or `timeout`. A failed block always sends the request to a human, so
a reviewer should not read it as a match.
terminal_pipeline:
type: object
additionalProperties: false
description: The section of the effective pipeline that held the terminal block, at the revision
that ran. Enough to read that revision and find the block in it.
required:
- scope
- revision
properties:
scope:
type: string
enum:
- organization
- agent
agent_slug:
type: string
description: Present when the scope is `agent`.
revision:
type: integer
format: int64
completed_at:
type: string
format: date-time
JudgeAssessment:
type: object
additionalProperties: false
description: One judge block's verdict on the request. Evidence for the reviewer, never a decision.
required:
- block_key
- verdict
- confidence
- rationale
- downgraded_for_confidence
- evaluated_at
properties:
block_key:
type: string
block_name:
type: string
verdict:
type: string
enum:
- approve
- deny
- escalate
confidence:
type: string
enum:
- low
- medium
- high
rationale:
type: string
model_label:
type: string
description: The judge model's name when it judged the request.
provider:
type: string
model:
type: string
downgraded_for_confidence:
type: boolean
description: True when the confidence fell below the block's threshold and the verdict was treated
as escalate.
evaluated_at:
type: string
format: date-time
ClaimEvent:
type: object
additionalProperties: false
description: One moment in the request's claim history.
required:
- kind
- occurred_at
properties:
kind:
type: string
enum:
- claimed
- released
- lapsed
actor_display_name:
type: string
occurred_at:
type: string
format: date-time
EscalationEvent:
type: object
additionalProperties: false
description: 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.
required:
- kind
- occurred_at
properties:
kind:
type: string
enum:
- level_entered
- urgency_raised
- deferred
- repeated
- exhausted
team_id:
type: string
node_id:
type: string
level_ordinal:
type: integer
description: The level's position within its own document, one-based (level_entered only).
level_count:
type: integer
description: How many levels the document has (level_entered only).
urgency:
type: string
enum:
- standard
- interrupt
description: The urgency the level was entered at, or the urgency raised to.
until:
type: string
format: date-time
description: When a deferred walker resumes (deferred only).
iteration:
type: integer
description: Which repeat of the document is starting (repeated only).
occurred_at:
type: string
format: date-time
AgentToolAccess:
type: object
required:
- server_id
- server_slug
- mode
- tools
- revision
properties:
server_id:
type: string
format: uuid
server_slug:
type: string
mode:
type: string
enum:
- none
- selected
- all
tools:
type: array
items:
type: string
revision:
type: integer
format: int64
minimum: 1
AgentToolAccessInput:
type: object
additionalProperties: false
required:
- mode
- tools
- revision
properties:
mode:
type: string
enum:
- none
- selected
- all
tools:
type: array
maxItems: 1000
uniqueItems: true
description: Exact downstream tool names, meaningful only in selected mode. Empty for none or all;
no wildcard matching.
items:
type: string
minLength: 1
maxLength: 256
revision:
type: integer
format: int64
minimum: 0
description: Expected current version; zero for an unconfigured agent/server pair.
OrganizationAPIKeyOptions:
type: object
additionalProperties: false
required:
- organization_api_keys_allowed
- resources
- permissions
- max_ttl
properties:
organization_api_keys_allowed:
type: boolean
resources:
type: array
items:
$ref: "#/components/schemas/PermissionResource"
permissions:
type: array
items:
$ref: "#/components/schemas/PermissionDescriptor"
max_ttl:
type: string
description: Maximum lifetime as a duration. 0s means no cap.
securitySchemes:
bearerAuth:
type: http
scheme: bearer
cookieAuth:
type: apiKey
in: cookie
name: withhuman_session
apiKeyAuth:
type: http
scheme: bearer
bearerFormat: whk_ / who_
description: "A personal whk_ or organization who_ API key sent as Authorization: Bearer <token>.
Personal keys act as their member with live, optionally narrowed access. Organization keys
act as themselves with live RBAC permission policies. The x-withhuman-credential-kinds
extension lists the kinds supported by each operation. Revocation, expiry, organization
status, and the policy switch for that key kind are checked on every request."
responses:
Error:
description: Error response. A 403 from a permission check carries ForbiddenDetails in error.details.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
PreconditionFailed:
description: If-Match no longer equals the active revision
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
PreconditionRequired:
description: The required If-Match header was omitted
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
parameters:
IdempotencyKey:
name: Idempotency-Key
in: header
required: true
description: A key of your choosing that identifies this call, so a retry does not act twice. See
Idempotency in the API overview.
schema:
type: string
minLength: 1
example: 4f1c9a2e-7b3d-4e8f-a1c5-2d6b8e0f9a31
RequestID:
name: id
in: path
required: true
description: The request's id.
schema:
type: string
format: uuid
AgentSlug:
name: slug
in: path
required: true
description: "The agent's slug: the identity chosen when it was created, never changed and never reused."
schema:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
AgentInstanceID:
name: id
in: path
required: true
description: The instance's id.
schema:
type: string
format: uuid
ProvisionerID:
name: id
in: path
required: true
description: The provisioner's id.
schema:
type: string
format: uuid
CredentialID:
name: id
in: path
required: true
description: The credential's id.
schema:
type: string
format: uuid
PipelineRevision:
name: revision
in: path
required: true
description: The revision number. Revisions count up from 1.
schema:
type: integer
format: int64
minimum: 1
ActiveRevisionIfMatch:
name: If-Match
in: header
required: true
description: The revision you expect to be active, quoted, for example `"2"`. Take it from the
`ETag` of your last read. If the active revision changed in the meantime, the call fails
with 412 and nothing changes. Escalation paths and webhook endpoints accept `"0"` when no
revision is active. Pipelines always have an active revision.
schema:
type: string
pattern: ^"[0-9]+"$
example: '"2"'
AgentSlugScope:
name: agent_slug
in: path
required: true
description: The slug of the agent whose pipeline this is.
schema:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
PathKey:
name: path_key
in: path
required: true
description: The path's key. Lowercase letters, digits, dots, underscores and hyphens, up to 63
characters. You choose it when you create the first revision.
schema:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
APIKeyID:
name: id
in: path
required: true
description: The API key's id, the part after `whk_` or `who_` in the token.
schema:
type: string
format: uuid
MembershipID:
name: id
in: path
required: true
description: The membership's id.
schema:
type: string
format: uuid
WebhookEndpointKey:
name: endpoint_key
in: path
required: true
description: The endpoint's key. Lowercase letters, digits, dots, underscores and hyphens, up to 63
characters. You choose it when you create the first revision.
schema:
type: string
pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
TeamID:
name: id
in: path
required: true
description: The team's id.
schema:
type: string
format: uuid
InvitationID:
name: id
in: path
required: true
description: The invitation's id.
schema:
type: string
format: uuid
AuditEventID:
name: id
in: path
required: true
description: The audit event's id.
schema:
type: string
format: uuid
examples:
AAPPendingRequest:
summary: Pending review
value:
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
AAPApprovedRequest:
summary: Approved with a five-minute execution window
value:
id: 7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71
tool: issue_refund
arguments:
amount: 4900
reason: duplicate_charge
timeout: 30m
status: approved
created_at: 2026-09-17T12:00:00Z
deadline_at: 2026-09-17T12:30:00Z
decision:
status: approved
note: Refund the duplicate charge.
decided_at: 2026-09-17T12:02:00Z
expires_at: 2026-09-17T12:07:00Z
AAPCancelledRequest:
summary: Cancelled by the adapter
value:
id: 7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71
tool: issue_refund
arguments:
amount: 4900
reason: duplicate_charge
timeout: 30m
status: cancelled
created_at: 2026-09-17T12:00:00Z
deadline_at: 2026-09-17T12:30:00Z
decision:
status: cancelled
note: The agent stopped waiting.
decided_at: 2026-09-17T12:02:00Z