Documentation
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