Documentation
DocsAPI reference

OpenAPI schema

Read or download the OpenAPI contract for the published withHuman API.

Updated Oct 26, 2026
openapi.yaml
openapi: 3.1.0
info:
  title: withHuman provider API
  version: 1.0.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 rules every approval request runs through, as an ordered list of blocks. A block
      can approve or deny a request, send it to a person, or pass it to the next block.
  - 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. Requires an `Idempotency-Key`
        header scoped to the provisioner: repeating the same submission with the same key within
        seven days answers `200` with the original instance and credential, without creating another
        instance, renewing the credential or verifying the receiver again. Reusing a key with
        different input answers `409` with code `idempotency_conflict`, a key whose attempt is still
        running answers `409` with code `idempotency_in_progress`, and replaying a key whose
        instance was deleted answers `409` with code `instance_deleted`. Pass `delivery` to use the
        Agent Approval Protocol's asynchronous mode: before the instance is created, withHuman sends
        a signed `webhook.verification` message to the receiver, which must answer `200` with the
        same `challenge` within ten seconds, or the call fails with `422` and code
        `webhook_verification_failed` and no instance is created (the same key may be retried). A
        malformed or non-public receiver URL answers `400` with code `invalid_webhook_url`.
        Afterwards, every request of the instance that becomes approved, denied, expired or
        cancelled sends one signed `request.resolved` notification to the receiver, retried with
        backoff for up to seven days until it answers `2xx`; notifications wait while the instance
        is disabled. The notification carries only IDs; read the decision with `GET
        /api/aap/v1/requests/{id}`. Both messages are signed with the Standard Webhooks scheme
        (`webhook-id`, `webhook-timestamp` and `webhook-signature` headers). Requires the
        deployment's secrets key; without it, delivery is refused with `501`."
      operationId: mintAgentInstance
      x-withhuman-permission: public
      tags:
        - Agent Approval Protocol
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              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.
                delivery:
                  $ref: "#/components/schemas/AgentInstanceDeliveryInput"
      responses:
        "200":
          description: The instance and credential an earlier submission with the same idempotency key created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AAPProvisionedInstance"
        "201":
          description: The new instance and its credential. Keep the credential's token; only a replay with
            the same provisioner token and idempotency key returns it again.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AAPProvisionedInstance"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        "501":
          $ref: "#/components/responses/Error"
  /api/aap/v1/instances/{id}:
    get:
      summary: Retrieve an agent instance
      description: Returns an instance of the provisioner token's agent with its receiver, if any. The
        signing secret and credentials are never returned. A deleted instance, or one of another
        agent, answers `404`.
      operationId: getProvisionedInstance
      x-withhuman-permission: public
      tags:
        - Agent Approval Protocol
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: The instance
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AAPInstance"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
    patch:
      summary: Update an agent instance
      description: "Renames an instance of the provisioner token's agent, replaces its receiver, or
        removes it. A `delivery` object replaces the whole configuration, signing secret included,
        and is verified first exactly as at creation: the old configuration and name stay in place
        until verification succeeds, and a failure answers `422` with code
        `webhook_verification_failed`. Queued notifications then go to the new receiver with the new
        secret, with unchanged IDs and bodies. `delivery: null` removes the receiver and ends
        outstanding deliveries and retries. Requires an `Idempotency-Key` header scoped to the
        provisioner and the instance: repeating the same submission answers the original response
        without applying it again. A deleted instance answers `404`."
      operationId: updateProvisionedInstance
      x-withhuman-permission: public
      tags:
        - Agent Approval Protocol
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              minProperties: 1
              properties:
                instance_name:
                  type: string
                  minLength: 1
                  example: ci-runner-07
                delivery:
                  description: Omit to keep the receiver, pass a complete configuration to replace it, or `null` to
                    remove it.
                  oneOf:
                    - $ref: "#/components/schemas/AgentInstanceDeliveryInput"
                    - type: "null"
      responses:
        "200":
          description: The updated instance
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AAPInstance"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "422":
          $ref: "#/components/responses/Error"
        "501":
          $ref: "#/components/responses/Error"
    delete:
      summary: Delete an agent instance
      description: "Retires an instance of the provisioner token's agent: its credentials are revoked, its
        receiver is removed with every outstanding delivery, and each of its pending requests is
        cancelled without a notification. Requests that already reached an outcome are unchanged.
        The instance stays listed in the product, disabled, and cannot be enabled again. Deleting it
        again answers `204`."
      operationId: deleteProvisionedInstance
      x-withhuman-permission: public
      tags:
        - Agent Approval Protocol
      security:
        - bearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: The instance is deleted
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
  /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/aap/v1/requests/{id}/consume:
    post:
      summary: Consume an approval
      operationId: consumeApprovalRequest
      x-withhuman-permission: request.create
      description: "Marks an approved request as used, right before the adapter runs the call. Each
        approval runs one call: only one consume succeeds, so a second attempt to run the same call
        is refused. Call it after checking `decision.expires_at`, and do not start the call until it
        succeeds. Only the instance that created the request may consume it; another instance gets a
        `403`. Requires an `Idempotency-Key` header that identifies this execution attempt:
        repeating the key returns the request as it is now, even after `expires_at`. The request
        takes no body; an empty body or `{}` is accepted. Answers `409` with code `already_consumed`
        when another key already consumed it, `not_approved` when the request is not `approved`, and
        `approval_expired` once `expires_at` has passed."
      tags:
        - Agent Approval Protocol
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/RequestID"
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
      responses:
        "200":
          description: The request, now consumed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AAPApprovalRequest"
              examples:
                consumed:
                  $ref: "#/components/examples/AAPConsumedRequest"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: The approval was already used (code `already_consumed`), the request is not approved
            (`not_approved`), or the approval expired (`approval_expired`)
  /api/aap/v1/requests/{id}/outcome:
    post:
      summary: Report an approved call's outcome
      operationId: reportApprovalOutcome
      x-withhuman-permission: request.create
      description: "Records whether the approved call succeeded, after the adapter consumed the approval
        and ran it. The outcome is for the record only: it never changes the request's status or
        decision. Report it once. Repeating the same body returns the same request; a different
        outcome answers `409` with code `outcome_conflict`. A request that was not consumed answers
        `409` with code `not_consumed`. Only the instance that created the request may report its
        outcome; another instance gets a `403`."
      tags:
        - Agent Approval Protocol
      security:
        - bearerAuth: []
      parameters:
        - $ref: "#/components/parameters/RequestID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReportApprovalOutcome"
      responses:
        "200":
          description: The request with the outcome
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AAPApprovalRequest"
              examples:
                reported:
                  $ref: "#/components/examples/AAPReportedRequest"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"
        "409":
          description: The approval was not consumed (code `not_consumed`), or a different outcome was already
            reported (`outcome_conflict`)
  /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.

        Pass `per_agent` to list every agent's recent history instead: at most that many of each
        agent's newest requests, automatic decisions included, newest first overall.
      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: per_agent
          in: query
          required: false
          description: Show each agent's newest requests instead of the queue, at most this many per agent,
            including the ones the pipeline decided. `limit` still caps the whole list, and the
            other filters apply to those newest requests.
          schema:
            type: integer
            minimum: 1
            maximum: 50
        - 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
                      - checklist
                      - 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."
                      checklist:
                        type:
                          - object
                          - "null"
                        description: What the organization has set up, however it was set up. Reported once the guided setup
                          no longer opens on its own, to a person with the permissions to run it;
                          `null` otherwise. The app offers the setup again while any item is
                          `false`.
                        required:
                          - agent_connected
                          - rules_written
                          - tested
                        properties:
                          agent_connected:
                            type: boolean
                            example: true
                            description: An active agent has an active instance holding an unrevoked, unexpired credential.
                          rules_written:
                            type: boolean
                            example: false
                            description: The entry pipeline can hold or deny a call instead of approving every one.
                          tested:
                            type: boolean
                            example: false
                            description: A person has decided one of the organization's requests.
                      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.
        Agents own no pipeline of their own: every request starts in the organization's entry
        pipeline, and rules for one agent are a branch or a delegate on `agent.slug` there.

        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.
                kind:
                  type: string
                  enum:
                    - service
                    - personal
                  default: service
                  description: Whose behalf the agent's instances act on. `service` for an agent the organization
                    runs, `personal` for one people run for themselves; see the Agent schema. It
                    cannot be changed later.
      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. Pipelines are
        the organization's, not the agent's, and are untouched."
      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`. 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: Update an instance
      operationId: updateAgentInstance
      x-withhuman-permission: agent.write
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Agents
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      description: >-
        Changes an instance's status or its display name, the two fields that can change after
        enrollment. The agent and its other instances are not affected.

        Disabling an instance keeps its credentials, but none of them can authenticate until it is
        enabled again. Renaming changes what the instance is called from now on: nothing addresses
        an instance by name after enrollment, so the running agent needs no change, and requests
        already made keep the name the instance had at the time.
      parameters:
        - $ref: "#/components/parameters/AgentInstanceID"
      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-server-01
                  description: A new display name, unique within the agent. Audited as `agent_instance.renamed`.
      responses:
        "200":
          description: The updated instance
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentInstance"
        "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 instance
        "409":
          $ref: "#/components/responses/Error"
          description: The instance's agent is archived (`agent_archived`), or another instance of the agent
            already has the name (`agent_instance_name_taken`)
  /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.
                delivery:
                  $ref: "#/components/schemas/AgentInstanceRegistrationDelivery"
      responses:
        "201":
          description: The new instance and its credential. The token appears only in this response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentInstanceCredential"
        "400":
          $ref: "#/components/responses/Error"
          description: An organization key tried to register an instance of a personal agent, which belongs to
            a person (`agent_owner_required`), or the receiver URL is not allowed
            (`invalid_webhook_url`)
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot issue credentials
        "404":
          $ref: "#/components/responses/Error"
          description: No such agent, or no registration you started for it
        "409":
          $ref: "#/components/responses/Error"
          description: The agent is archived (`agent_archived`)
        "410":
          $ref: "#/components/responses/Error"
          description: The registration expired or was used (`instance_registration_expired`). Start a new one.
        "422":
          $ref: "#/components/responses/Error"
          description: The receiver did not answer the verification (`webhook_verification_failed`). The
            registration is kept, so you can fix the receiver and try again.
        "501":
          $ref: "#/components/responses/Error"
          description: This server has no secrets key, so it cannot deliver webhooks (`webhook_unconfigured`)
  /api/v1/agents/{slug}/instance_registrations:
    post:
      summary: Start a webhook registration
      description: >-
        Starts registering an instance that is notified when its requests are decided, so the agent
        can pause instead of polling. The response holds a registration ID and a signing secret. The
        secret appears only in this response.

        Configure your receiver with both, then register the instance with `delivery` naming this
        registration and the receiver URL. The receiver must answer a signed `webhook.verification`
        challenge before the instance is created. The registration expires after one hour.
      operationId: createAgentInstanceRegistration
      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 registration. The signing secret appears only in this response.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentInstanceRegistration"
        "400":
          $ref: "#/components/responses/Error"
          description: An organization key tried to register an instance of a personal agent, which belongs to
            a person (`agent_owner_required`)
        "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`)
        "501":
          $ref: "#/components/responses/Error"
          description: This server has no secrets key, so it cannot deliver webhooks (`webhook_unconfigured`)
  /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"
        "400":
          $ref: "#/components/responses/Error"
          description: The agent is personal, and personal agents have no provisioner tokens (`personal_agent`)
        "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 entry pipeline, where every request starts,
        first, then every pipeline a delegate block can hand requests to. Each entry carries the
        key, the name, the active revision number and a summary of its history.

        A pipeline always has an active revision. An empty revision passes every request that enters
        the pipeline through to a person. An archived pipeline has no active revision, carries
        `archived_at`, and is left out unless `status` says otherwise. The list is narrowed to the
        pipelines you may read.
      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
    post:
      summary: Create a pipeline
      description: >-
        Creates a pipeline that a delegate block in another pipeline can hand requests to, with its
        first revision active at once. You choose the key; it is taken for good, archived pipelines
        included, and can never be `entry`, which every organization already has.

        Creating a pipeline is an organization-wide act: it needs `pipeline.write` at the
        organization. Grant a team `pipeline.read`, `pipeline.write` and `pipeline.activate` at
        `pipeline:<key>` and they can edit the new pipeline and nothing else.
      operationId: createRequestApprovalPipeline
      x-withhuman-permission: pipeline.write
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Approval pipelines
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RequestApprovalPipelineCreate"
      responses:
        "201":
          description: The new pipeline's first revision, active
          headers:
            ETag:
              description: The active revision number, quoted
              schema:
                type: string
                example: '"1"'
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RequestApprovalPipelineRevision"
        "400":
          $ref: "#/components/responses/Error"
          description: The key, the name, or the document is invalid, or a delegate names a pipeline that does
            not exist (`request_approval_pipeline_not_found`)
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot create pipelines
        "409":
          $ref: "#/components/responses/Error"
          description: The key was ever used (`request_approval_pipeline_exists`), or is `entry`
            (`request_approval_pipeline_entry`)
  /api/v1/request-approval-pipelines/{pipeline_key}:
    get:
      summary: Retrieve a pipeline's active revision
      description: >-
        Returns the pipeline's active revision, with its blocks in the order they run. `entry` is
        the pipeline every request starts in; any other pipeline runs for the requests a delegate
        block hands to it.

        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/PipelineKey"
      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 this pipeline
        "404":
          $ref: "#/components/responses/Error"
          description: No pipeline with this key exists, or it is archived
    delete:
      summary: Archive a pipeline
      description: >-
        Archives the pipeline: every revision is stamped, none is active, and its key stays taken.
        Its revisions remain readable with `status=archived` or `all`.

        The entry pipeline cannot be archived, and neither can a pipeline that a delegate block in
        an active revision still hands requests to; remove those blocks first. `GET .../uses` lists
        them.
      operationId: archiveRequestApprovalPipeline
      x-withhuman-permission: pipeline.activate
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Approval pipelines
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PipelineKey"
      responses:
        "204":
          description: The pipeline is archived
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot archive this pipeline
        "404":
          $ref: "#/components/responses/Error"
          description: No pipeline with this key exists
        "409":
          $ref: "#/components/responses/Error"
          description: The pipeline is the entry pipeline (`request_approval_pipeline_entry`), is already
            archived (`request_approval_pipeline_archived`), or an active revision delegates to it
            (`request_approval_pipeline_in_use`, with the uses in `error.details`)
  /api/v1/request-approval-pipelines/{pipeline_key}/uses:
    get:
      summary: List where a pipeline is delegated to
      description: Returns the active pipeline revisions whose delegate blocks hand requests to this
        pipeline. The same set refuses archiving it.
      operationId: listRequestApprovalPipelineUses
      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/PipelineKey"
      responses:
        "200":
          description: The uses
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                  - uses
                properties:
                  uses:
                    type: array
                    items:
                      $ref: "#/components/schemas/PipelineUse"
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot read this pipeline
  /api/v1/request-approval-pipelines/{pipeline_key}/preview:
    post:
      summary: Preview a 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 document stands in for this pipeline. Delegate blocks expand to their target pipelines'
        current active revisions, so the preview walks what a request would; a skipped delegate
        calls nothing. The sample names an agent by slug and name; any valid slug will do, and
        conditions read it as `agent.slug`.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PipelineKey"
      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, a delegate names a pipeline with no active
            revision (`request_approval_pipeline_not_found`), or the delegation would loop
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot edit this pipeline
  /api/v1/request-approval-pipelines/{pipeline_key}/revisions:
    get:
      summary: List pipeline revisions
      description: >-
        Returns the 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: 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/PipelineKey"
        - 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 this pipeline
    post:
      summary: Create a 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 the pipeline from a complete document. The document is
        validated and compiled first, and every escalation path, webhook endpoint and pipeline it
        names must exist. The revision is created as a draft, which can be changed in place until it
        is activated. Activate it to put it into use. Omit `name` to keep the active revision's.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PipelineKey"
      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, or a delegate names a pipeline that does not exist
            (`request_approval_pipeline_not_found`) or this pipeline itself
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot edit this pipeline
        "404":
          $ref: "#/components/responses/Error"
          description: No pipeline with this key exists
        "409":
          $ref: "#/components/responses/Error"
          description: The pipeline is archived (`request_approval_pipeline_archived`)
  /api/v1/request-approval-pipelines/{pipeline_key}/revisions/{revision}:
    get:
      summary: Retrieve a pipeline revision
      description: Returns one revision of the 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/PipelineKey"
        - $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 this pipeline
        "404":
          $ref: "#/components/responses/Error"
          description: No such revision
    put:
      summary: Update a draft pipeline revision
      operationId: updateRequestApprovalPipelineRevision
      x-withhuman-permission: pipeline.write
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Approval pipelines
      description: Replaces the content of a draft revision, one that has never been active, with a
        complete document. The revision keeps its number. The document is checked exactly as a new
        revision's. Omit `name` to keep the draft's. Once a revision has been active it can no
        longer change.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PipelineKey"
        - $ref: "#/components/parameters/PipelineRevision"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RequestApprovalPipelineDocument"
      responses:
        "200":
          description: The updated draft
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RequestApprovalPipelineRevision"
        "400":
          $ref: "#/components/responses/Error"
          description: The document is invalid, or a delegate names a pipeline that does not exist
            (`request_approval_pipeline_not_found`) or this pipeline itself
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot edit this pipeline
        "404":
          $ref: "#/components/responses/Error"
          description: No such revision
        "409":
          $ref: "#/components/responses/Error"
          description: The revision has been active (`revision_not_draft`), or the pipeline is archived
            (`request_approval_pipeline_archived`)
  /api/v1/request-approval-pipelines/{pipeline_key}/revisions/{revision}/references:
    get:
      summary: Report what a pipeline revision depends on
      operationId: getRequestApprovalPipelineRevisionReferences
      x-withhuman-permission: pipeline.read
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Approval pipelines
      description: Reports the escalation paths, webhook endpoints and pipelines the revision names with
        their current state, and any block the deployment or plan cannot run, so a rollback can be
        explained before it is attempted. The activation itself re-checks inside its transaction.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PipelineKey"
        - $ref: "#/components/parameters/PipelineRevision"
      responses:
        "200":
          description: Live state of every dependency
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PipelineReferences"
        "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/{pipeline_key}/revisions/{revision}/activate:
    post:
      summary: Activate a 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, every webhook endpoint its blocks post to, and
        every pipeline its delegate blocks hand requests to must have an active revision, and the
        delegation graph must stay a tree: no pipeline may reach itself again, no chain may be
        deeper than 8 delegations, and the flattened program from the entry pipeline may hold at
        most 10,000 blocks. Otherwise the call fails with 409 and nothing changes.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PipelineKey"
        - $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 this pipeline
        "404":
          $ref: "#/components/responses/Error"
          description: No such revision
        "409":
          $ref: "#/components/responses/Error"
          description: A reference has no active revision (`escalation_path_inactive`,
            `webhook_endpoint_inactive`, or `request_approval_pipeline_inactive`, with the keys in
            `error.details`), the delegation graph would loop
            (`request_approval_pipeline_delegation_cycle`, the keys on the cycle in
            `error.details.pipelines`), run too deep
            (`request_approval_pipeline_delegation_too_deep`) or too large
            (`request_approval_pipeline_too_large`), or the pipeline is archived
            (`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 as a draft, which can be changed in place
        until it is activated. 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 as a draft, which can be changed in place until it is activated. 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
    put:
      summary: Update a draft escalation path revision
      operationId: updateEscalationPathRevision
      x-withhuman-permission: escalation_path.write
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Escalation paths
      description: Replaces the name and document of a draft revision, one that has never been active. The
        revision keeps its number. The document is checked exactly as a new revision's. Once a
        revision has been active it can no longer change.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/PathKey"
        - $ref: "#/components/parameters/PipelineRevision"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EscalationPathDocument"
      responses:
        "200":
          description: The updated draft
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EscalationPathRevision"
        "400":
          $ref: "#/components/responses/Error"
          description: The document is invalid or names a target that 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 revision
        "409":
          $ref: "#/components/responses/Error"
          description: The revision has been active (`revision_not_draft`), or the path is archived
            (`escalation_path_archived`)
  /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, 403 authentication_stale or authentication_weak when the session must sign in
        again or with MFA first.
      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/roles:
    get:
      summary: List predefined roles
      operationId: listRoles
      x-withhuman-permission: self
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
      tags:
        - Members
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      description: The predefined roles a person can be invited with or given, owner first, with the
        permissions each holds. They are the same in every organization.
      responses:
        "200":
          description: The predefined roles
          content:
            application/json:
              schema:
                type: object
                required:
                  - roles
                properties:
                  roles:
                    type: array
                    items:
                      $ref: "#/components/schemas/Role"
        "401":
          $ref: "#/components/responses/Error"
          description: You are not signed in
  /api/v1/members/{id}/role:
    put:
      summary: Give a member a predefined role
      operationId: setMemberRole
      x-withhuman-permission: permission_policy.write
      x-withhuman-credential-kinds:
        - session
      tags:
        - Members
      security:
        - cookieAuth: []
      description: >-
        Gives the member one predefined role everywhere in the organization, replacing every
        permission they hold directly, custom permission policies included. What they inherit from
        teams and what your directory manages stay.

        You cannot change your own role. Only an owner may grant the owner role or change a member
        who holds it, the organization must keep at least one active owner, and a role holding a
        sensitive permission needs a fresh sign-in.
      parameters:
        - $ref: "#/components/parameters/MembershipID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
                - role
              properties:
                role:
                  $ref: "#/components/schemas/RoleKey"
      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: The role is unknown
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot change permissions, this is your own membership, the role or the member is
            an owner and you are not, or the role needs a fresher sign-in
        "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
  /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 `PUT /api/v1/members/{id}/role` or, where
        the edition offers custom permissions, 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
    put:
      summary: Update a draft webhook endpoint revision
      operationId: updateWebhookEndpointRevision
      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 name and URL of a draft revision, one that has never been active. The
        revision keeps its number and the endpoint keeps its signing secret, which is never
        included. Once a revision has been active it can no longer change.
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointKey"
        - $ref: "#/components/parameters/PipelineRevision"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointDocument"
      responses:
        "200":
          description: The updated draft
          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
        "404":
          $ref: "#/components/responses/Error"
          description: No such revision
        "409":
          $ref: "#/components/responses/Error"
          description: The revision has been active (`revision_not_draft`)
        "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}/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 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: The new revision, a draft
          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 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"
    put:
      summary: Update a draft team escalation policy revision
      operationId: updateTeamEscalationPolicyRevision
      x-withhuman-permission: team.escalation.write
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Teams
      description: Replaces the document of a draft revision, one that has never been active. The revision
        keeps its number. The document is checked exactly as a new revision's. Once a revision has
        been active it can no longer change.
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - $ref: "#/components/parameters/TeamID"
        - $ref: "#/components/parameters/PipelineRevision"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TeamEscalationPolicyDocument"
      responses:
        "200":
          description: The updated draft
          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 revision has been active (`revision_not_draft`), or the team is archived
  /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 a predefined role, or, where the edition offers custom permissions
        (`custom_permissions_enabled` in the runtime config), with explicit permission policies.
        Send exactly one of `role` and `permission_policies`. Only an owner may invite an owner,
        every permission must be held by the inviter at the selected scope or wider, and a role or
        policy holding a sensitive permission needs a fresh sign-in.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
              properties:
                email:
                  type: string
                  format: email
                  description: Where to send the invitation. The person must accept with this address.
                role:
                  $ref: "#/components/schemas/RoleKey"
                  description: The predefined role the person receives everywhere in the organization.
                permission_policies:
                  type: array
                  minItems: 1
                  items:
                    $ref: "#/components/schemas/PermissionPolicySpec"
                  description: Custom permissions instead of a role. Refused with 403 `custom_permissions_unavailable`
                    where the edition offers predefined roles alone.
              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"
        "400":
          $ref: "#/components/responses/Error"
          description: Neither or both of `role` and `permission_policies` were sent, or the role is unknown
        "403":
          $ref: "#/components/responses/Error"
          description: You cannot invite people, a grant exceeds what you hold or needs a fresher sign-in, or
            custom permissions are not available in this edition (`custom_permissions_unavailable`)
        "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
      description: Refused with 403 `custom_permissions_unavailable` where the edition offers predefined
        roles alone (`custom_permissions_enabled` false).
      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
      description: Refused with 403 `custom_permissions_unavailable` where the edition offers predefined
        roles alone (`custom_permissions_enabled` false).
      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
      description: Refused with 403 `custom_permissions_unavailable` where the edition offers predefined
        roles alone (`custom_permissions_enabled` false).
      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
      description: Refused with 403 `custom_permissions_unavailable` where the edition offers predefined
        roles alone (`custom_permissions_enabled` false).
      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
      description: Refused with 403 `custom_permissions_unavailable` where the edition offers predefined
        roles alone (`custom_permissions_enabled` false).
      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
      description: Refused with 403 `custom_permissions_unavailable` where the edition offers predefined
        roles alone (`custom_permissions_enabled` false).
      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/tool_definitions:
    get:
      tags:
        - Agents
      summary: List distinct tool definitions
      operationId: listToolDefinitions
      x-withhuman-permission: agent.read
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      description: "Returns the catalogue within the caller's agent scope in one response, folded to
        distinct definitions: a tool name on a server with one schema and description appears once,
        listing every runtime and instance that offers it, however many agents see it. Also returns
        every server a catalogue source names, including sources where discovery found no tools. The
        edition's own tool source is refreshed as for the catalogue listing. Useful for suggesting
        tools and argument fields while writing pipeline conditions; the information is advisory and
        never changes approval decisions."
      responses:
        "200":
          description: Distinct definitions and the servers the catalogue names
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ToolDefinitionList"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $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
        - $ref: "#/components/parameters/AuditQuery"
        - $ref: "#/components/parameters/AuditEventType"
        - $ref: "#/components/parameters/AuditActorType"
        - $ref: "#/components/parameters/AuditActorID"
        - $ref: "#/components/parameters/AuditSubjectType"
        - $ref: "#/components/parameters/AuditSubjectID"
        - $ref: "#/components/parameters/AuditOutcome"
        - $ref: "#/components/parameters/AuditFromAt"
        - $ref: "#/components/parameters/AuditToAt"
        - 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/stream:
    get:
      summary: Stream new audit events
      description: >-
        Server-sent events carrying the organization's audit events as they are recorded. Takes the
        same filters as the listing.

        Each `audit` event's data is `{"events": [...]}`, oldest first, and its `id` is the highest
        `sequence` sent so far. A comment line keeps the connection open while nothing new arrives.
        Pass the `sequence` of the newest event you already have as `after_sequence`; a reconnecting
        client's `Last-Event-ID` header resumes from its last frame.
      operationId: streamAuditEvents
      x-withhuman-permission: audit.read
      x-withhuman-credential-kinds:
        - session
        - personal_api_key
        - organization_api_key
      tags:
        - Audit
      security:
        - cookieAuth: []
        - apiKeyAuth: []
      parameters:
        - name: after_sequence
          in: query
          description: Only events recorded after the event with this `sequence`. Leave it out to receive
            every matching event from the start of the log.
          schema:
            type: integer
            format: int64
            minimum: 0
        - $ref: "#/components/parameters/AuditQuery"
        - $ref: "#/components/parameters/AuditEventType"
        - $ref: "#/components/parameters/AuditActorType"
        - $ref: "#/components/parameters/AuditActorID"
        - $ref: "#/components/parameters/AuditSubjectType"
        - $ref: "#/components/parameters/AuditSubjectID"
        - $ref: "#/components/parameters/AuditOutcome"
        - $ref: "#/components/parameters/AuditFromAt"
        - $ref: "#/components/parameters/AuditToAt"
        - name: Last-Event-ID
          in: header
          description: Sent by a reconnecting client. Resumes after this `sequence` when it is later than
            `after_sequence`.
          schema:
            type: string
      responses:
        "200":
          description: Event stream
          content:
            text/event-stream:
              schema:
                type: string
        "400":
          $ref: "#/components/responses/Error"
          description: A filter or `after_sequence` is malformed
        "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, and the scopes the agent may call
        them with: any granted scope works with any granted tool. Every scope must be one of the
        server's current scopes. A member-held scope can be granted to a personal agent only (400
        otherwise); its instances call with each owner's own connection. 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
        - kind
        - 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.
        kind:
          type: string
          enum:
            - service
            - personal
          description: "Whose behalf the agent's instances act on, chosen once. A `service` agent is one the
            organization runs, such as a support bot: its instances share one identity,
            administrators enroll them, and provisioner tokens mint them. A `personal` agent is one
            people run for themselves, such as a coding assistant: every instance belongs to the
            member who enrolled it, any active member may connect their own, and the instances are
            disabled when that member is removed. Personal agents have no provisioner tokens."
        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.
        membership_id:
          type: string
          format: uuid
          description: The member this instance belongs to and acts for. Present exactly when the agent is
            `personal`.
        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.
        delivery:
          $ref: "#/components/schemas/AgentInstanceDelivery"
    AgentInstanceDelivery:
      type: object
      description: The verified receiver of an instance using asynchronous mode, returned when the
        instance is created. The signing secret is never returned.
      required:
        - type
        - url
        - registration_id
      properties:
        type:
          type: string
          enum:
            - webhook
        url:
          type: string
          format: uri
          example: https://harness.example.com/aap/events
        registration_id:
          type: string
          format: uuid
    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.
    AgentInstanceDeliveryInput:
      type: object
      description: "A webhook receiver for asynchronous mode. Install the registration ID and signing
        secret on the receiver before calling: the verification message names the registration so
        the receiver can pick the secret before it knows the instance ID."
      required:
        - type
        - url
        - registration_id
        - signing_secret
      additionalProperties: false
      properties:
        type:
          type: string
          enum:
            - webhook
          description: The only delivery type offered. Another value answers `400` with code
            `unsupported_delivery_type`.
        url:
          type: string
          format: uri
          example: https://harness.example.com/aap/events
          description: The exact HTTPS receiver URL. Private, loopback and other non-public addresses are
            refused, and redirects are not followed.
        registration_id:
          type: string
          format: uuid
          description: A UUID you generate for this configuration and associate with the secret on the
            receiver. It is echoed in the verification message.
        signing_secret:
          type: string
          writeOnly: true
          pattern: ^whsec_[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$
          description: "`whsec_` followed by 32 random bytes in padded base64. Use a separate secret for each
            instance. It is never returned."
    AAPProvisionedInstance:
      example:
        instance:
          id: b29a43a7-1949-4b9f-9d61-b7ae66f85d31
          name: ci-runner-07
        credential:
          token: whc_live_3f6b9c1d0e7a4b2c
          expires_at: 2026-09-18T12:00:00Z
      type: object
      additionalProperties: false
      required:
        - instance
        - credential
      description: A provisioned instance and its credential.
      properties:
        instance:
          $ref: "#/components/schemas/AAPInstance"
        credential:
          type: object
          additionalProperties: false
          required:
            - token
          properties:
            token:
              type: string
              minLength: 1
              description: The instance credential. Only a replay with the same provisioner token and idempotency
                key returns it again.
            expires_at:
              type: string
              format: date-time
              description: When the credential stops working.
    AAPInstance:
      example:
        id: b29a43a7-1949-4b9f-9d61-b7ae66f85d31
        name: ci-runner-07
        delivery:
          type: webhook
          url: https://harness.example.com/aap/events
          registration_id: 8246931c-4ce0-4c15-a94a-e9c078ad06e2
      type: object
      additionalProperties: false
      required:
        - id
        - name
      description: An instance as the Agent Approval Protocol returns it to a provisioner. Credentials and
        the signing secret are never included.
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          minLength: 1
          description: Unique within the agent.
        delivery:
          $ref: "#/components/schemas/AgentInstanceDelivery"
          description: Absent when the instance has no receiver.
    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
            - pipeline
            - request
          description: What kind of thing the check was about.
        resource_id:
          type: string
          description: The team, agent slug, pipeline key, 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"
        consumption:
          $ref: "#/components/schemas/AAPConsumption"
      oneOf:
        - properties:
            status:
              const: pending
            decision: false
            consumption: false
        - required:
            - decision
          properties:
            status:
              const: approved
            decision:
              properties:
                status:
                  const: approved
        - required:
            - decision
          properties:
            status:
              const: denied
            decision:
              properties:
                status:
                  const: denied
            consumption: false
        - required:
            - decision
          properties:
            status:
              const: expired
            decision:
              properties:
                status:
                  const: expired
            consumption: false
        - required:
            - decision
          properties:
            status:
              const: cancelled
            decision:
              properties:
                status:
                  const: cancelled
            consumption: false
    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. An approved call must start before expires_at: 30 minutes after
        decided_at when the instance had webhook delivery at decision time, five minutes otherwise.
        Reads and retries never extend expires_at."
      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
    AAPConsumption:
      example:
        consumed_at: 2026-09-17T12:02:04Z
        outcome:
          status: succeeded
          note: Refund issued
          reported_at: 2026-09-17T12:02:06Z
      type: object
      additionalProperties: false
      required:
        - consumed_at
      description: The creating instance used the approval. Present only once an approved request was
        consumed. An adapter that finds a consumption it did not make treats the approval as used.
      properties:
        consumed_at:
          type: string
          format: date-time
        outcome:
          $ref: "#/components/schemas/AAPOutcome"
    AAPOutcome:
      example:
        status: succeeded
        note: Refund issued
        reported_at: 2026-09-17T12:02:06Z
      type: object
      additionalProperties: false
      required:
        - status
        - reported_at
      description: How the approved call went, as the adapter reported it. For the record only.
      properties:
        status:
          type: string
          enum:
            - succeeded
            - failed
        note:
          type: string
          minLength: 1
          maxLength: 1000
        reported_at:
          type: string
          format: date-time
    ReportApprovalOutcome:
      example:
        status: succeeded
        note: Refund issued
      type: object
      additionalProperties: false
      required:
        - status
      properties:
        status:
          type: string
          enum:
            - succeeded
            - failed
          description: Whether the approved call succeeded.
        note:
          type: string
          minLength: 1
          maxLength: 1000
          description: A short note about the result, such as an error message or a reference.
    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"
            requester_display_name:
              type: string
              example: Chris
              description: The name of the member a personal instance acted for. Absent for a service agent's
                request.
            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"
            summary:
              description: A one-line description of the request written for reviewers, on editions that write
                them, for requests that reached a person. Absent until one is written. A reading aid
                only, like the presentation.
              $ref: "#/components/schemas/RequestSummary"
    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.
        requester_membership_id:
          type: string
          format: uuid
          description: "The member the instance acts for, when the agent is personal: the owner of the
            instance at the time the request was made. Absent for a service agent's request, and
            absent from the response that creates the request."
        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"
        consumption:
          description: Present once the agent used the approval to run the call, with the call's outcome if
            the agent reported it.
          $ref: "#/components/schemas/AAPConsumption"
    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
    RequestSummary:
      type: object
      required:
        - text
        - generated_at
      properties:
        text:
          type: string
          maxLength: 140
          example: Refund $49.00 on charge ch_3N1x for a faulty bag
        generated_at:
          type: string
          format: date-time
          example: 2026-09-08T12:00:03Z
    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
            - pipeline
        scope_id:
          type: string
          description: The team id, agent slug, or pipeline key 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
        built_in_role:
          $ref: "#/components/schemas/RoleKey"
          description: The predefined role this assignment grants. Absent for a custom permission policy.
        permission_policy_id:
          type: string
          format: uuid
    RoleKey:
      type: string
      enum:
        - owner
        - admin
        - reviewer
        - auditor
      description: A predefined role. `owner` is full control and only an owner may grant it; `admin`
        manages agents, pipelines, paths, members and teams; `reviewer` decides the requests routed
        to them; `auditor` reads everything without changing anything.
    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. agent_owner sends it to
        the person who owns the agent instance that made it; escalated pins an escalation path
        revision and the people it reaches for this request; default_queue means any authorized
        reviewer.
      required:
        - outcome
        - reason
        - urgency
        - targets
        - added_reviewers
        - team_policies
        - escalation
        - created_at
      properties:
        outcome:
          type: string
          enum:
            - agent_owner
            - escalated
            - default_queue
          example: escalated
          description: "`agent_owner` when the agent's owner took the request; the path fields then describe
            the fallback pinned beside it. `escalated` when an escalation path took the request.
            `default_queue` when no path applied and every reviewer who can decide was asked."
        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 route. `pipeline_default`: the pipeline's default route took it.
            `no_escalation`: the pipeline named no path. `path_inactive`: the named path had no
            active revision. `evaluation_error`: the path could not be resolved. The last three mean
            no path applied."
        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, or for `agent_owner` the fallback pinned beside the
            owner. Absent for `default_queue`.
        escalation_path_name:
          type: string
          example: On-call engineers
          description: The name of the path revision that took the request, or the fallback revision for
            `agent_owner`. Absent for `default_queue`.
        escalation_path_revision_id:
          type: string
          format: uuid
          description: The path revision that was active at the time. Absent for `default_queue`, and for
            `agent_owner` when the fallback path had no active revision.
        pipeline_revision_id:
          type: string
          format: uuid
          description: The pipeline revision that routed the request. Absent for `default_queue`.
        block_id:
          type: string
          format: uuid
          description: The pipeline block that named the path. Present for `block_escalation` only.
        agent_owner:
          $ref: "#/components/schemas/ApprovalRoutingTarget"
          description: The person the request was sent to as the owner of the agent instance that made it.
            Present for `agent_owner` only.
        agent_owner_fallback:
          type: string
          enum:
            - no_agent_owner
            - agent_owner_inactive
            - agent_owner_cannot_decide
          description: "Why a route that asks the agent's owner went to its fallback path when the request
            arrived. `no_agent_owner`: a service agent made it. `agent_owner_inactive`: the owner
            was suspended or removed. `agent_owner_cannot_decide`: the owner may not decide
            requests."
        agent_owner_handed_off_at:
          type: string
          format: date-time
          description: When the agent's owner was suspended or removed and the fallback path took over the
            request. Present for `agent_owner` only.
        targets:
          type: array
          description: "Who may claim or decide the request. For `agent_owner`, the owner alone until a
            handoff. Otherwise the people and teams the pinned path reaches for this request, in
            path order: the levels whose request conditions hold for it, plus both branches of any
            working-hours or urgency test, at every level regardless of timing. Only these people,
            and `added_reviewers`, may claim or decide. Empty for `default_queue`. Empty for
            `escalated` means the path reaches nobody for this request and only a break-glass
            reviewer may decide it."
          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:
            owner_display_name:
              type: string
              description: The name of the member a personal instance belongs to. Absent for a service agent's
                instance.
            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.
    AgentInstanceRegistrationDelivery:
      type: object
      description: The receiver for a registration you started. It is verified before the instance is created.
      required:
        - registration_id
        - url
      properties:
        registration_id:
          type: string
          format: uuid
          description: The registration's ID.
        url:
          type: string
          format: uri
          example: https://harness.example.com/aap/events
          description: The exact HTTPS receiver URL. Private, loopback and other non-public addresses are
            refused, and redirects are not followed.
    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"
    AgentInstanceRegistration:
      type: object
      description: A pending webhook registration. Configure your receiver with the registration ID and
        signing secret.
      required:
        - registration_id
        - signing_secret
        - expires_at
      properties:
        registration_id:
          type: string
          format: uuid
          description: The ID the verification message names. Your receiver uses it to choose the signing
            secret.
        signing_secret:
          type: string
          pattern: ^whsec_[A-Za-z0-9+/]{42}[AEIMQUYcgkosw048]=$
          description: "The receiver's signing secret: `whsec_` followed by 32 random bytes in padded base64.
            It is never returned again."
        expires_at:
          type: string
          format: date-time
          description: When the registration can no longer be completed.
    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
          description: A registered client's ID, or the https URL of the client's metadata document
            (draft-ietf-oauth-client-id-metadata-document).
        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:
        - pipeline_key
        - name
        - latest_revision
        - revision_count
        - latest_created_at
        - referenced_by
      properties:
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: The pipeline's key. `entry` is the pipeline every request starts in.
        name:
          type: string
          description: "The pipeline's display name: the active revision's, or the latest revision's for an
            archived pipeline."
        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: readable, never active again, its key taken
            for good."
        referenced_by:
          type: array
          description: Every delegate block in an active pipeline revision that hands requests to this
            pipeline, the entry pipeline first, then by pipeline key and block position. Empty when
            no request can reach it.
          items:
            $ref: "#/components/schemas/PipelineDelegation"
    PipelineDelegation:
      type: object
      additionalProperties: false
      description: One delegate block in an active pipeline revision.
      required:
        - pipeline_key
        - pipeline_name
        - revision
        - block_key
        - block_name
      properties:
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: The key of the pipeline holding the delegate block.
        pipeline_name:
          type: string
          description: That pipeline's name as of its active revision.
        revision:
          type: integer
          format: int64
          minimum: 1
          description: The active revision holding the block.
        block_key:
          type: string
          description: The delegate block's key, unique within the revision.
        block_name:
          type: string
          description: The delegate block's name.
    RequestApprovalPipelineCreate:
      type: object
      additionalProperties: false
      description: A new pipeline and the content of its first revision.
      required:
        - pipeline_key
        - name
      properties:
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          example: payments
          description: The pipeline's key. Taken for good once used, archived pipelines included; never `entry`.
        name:
          type: string
          minLength: 1
          maxLength: 120
          example: Payments
          description: The display name.
        blocks:
          type: array
          maxItems: 1000
          description: The first revision's blocks, in the order they should run. Omit for a pipeline that
            passes every request that enters it through to a person until it is edited.
          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 from this pipeline without a block
            naming a path, as in a document.
        default_ask_agent_owner:
          type: boolean
          description: "Send those requests to the person who owns the agent instance that made them instead.
            `default_escalation_path`, if set, is then the fallback for a request whose agent has no
            owner able to decide it (a service agent, or an owner who was deactivated); without it
            the fallback is the enclosing pipeline's default, else any authorized reviewer. The
            owner is the only reviewer: the request never moves to the path because the owner has
            not answered."
    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 (`outcome`, `branch`, `delegate`, `webhook`);
        "if this, then that" is a branch whose blocks end in an outcome block; 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/OutcomeBlockDefinition"
        - $ref: "#/components/schemas/WebhookBlockDefinition"
        - $ref: "#/components/schemas/BranchBlockDefinition"
        - $ref: "#/components/schemas/DelegateBlockDefinition"
        - $ref: "#/components/schemas/LLMJudgeBlockDefinition"
      discriminator:
        propertyName: type
        mapping:
          outcome: "#/components/schemas/OutcomeBlockDefinition"
          webhook: "#/components/schemas/WebhookBlockDefinition"
          branch: "#/components/schemas/BranchBlockDefinition"
          delegate: "#/components/schemas/DelegateBlockDefinition"
          llm_judge: "#/components/schemas/LLMJudgeBlockDefinition"
    OutcomeBlockDefinition:
      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: outcome
          description: Always `outcome`.
        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/OutcomeBlockConfig"
    OutcomeBlockConfig:
      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.
        ask_agent_owner:
          type: boolean
          description: Send the request to the person who owns the agent instance that made it, instead of
            `escalation_path`. The path, if set, is then the fallback when the request has no owner
            able to decide it; without one the fallback is the pipeline default.
    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"
    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.
        ask_agent_owner:
          type: boolean
          description: Send the request to the person who owns the agent instance that made it, instead of
            `escalation_path`. The path, if set, is then the fallback when the request has no owner
            able to decide it; without one the fallback is the pipeline default.
    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. "If this, then that" is a branch whose blocks end in an
        outcome block; such a branch never runs out, so it needs no reason or path.
      required:
        - when
        - blocks
      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
          description: The note recorded when none of the branch's blocks decides; empty when the branch has
            none.
        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.
        ask_agent_owner:
          type: boolean
          description: Send the request to the person who owns the agent instance that made it, instead of
            `escalation_path`. The path, if set, is then the fallback when the request has no owner
            able to decide it; without one the fallback is the pipeline default.
    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"
    DelegateBlockDefinition:
      type: object
      additionalProperties: false
      description: A block that hands the requests its condition matches to another pipeline, named by
        key, and is skipped by every other request. A request that enters the delegated pipeline is
        decided inside it, or goes to a person under that pipeline's own default escalation path
        (else the defaults of the pipelines that delegated into it) if none of its blocks decides;
        nothing after the delegate runs for that request. The condition belongs to the pipeline
        holding the block; everything after a match belongs to the target pipeline's owners.
      required:
        - id
        - name
        - type
        - enabled
        - timeout
        - max_attempts
        - config
      properties:
        id:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          example: to-payments
          description: A key for the block, unique within the pipeline including blocks inside branches.
        name:
          type: string
          minLength: 1
          example: Payments team
          description: A display name.
        type:
          type: string
          const: delegate
          description: Always `delegate`.
        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/DelegateBlockConfig"
    DelegateBlockConfig:
      type: object
      additionalProperties: false
      description: When the condition matches, the request continues in the named pipeline's active
        revision and is settled there. When it does not match, the delegate is skipped and the
        request passes to the next block. A condition that cannot be evaluated sends the request to
        a person on the default path of the pipeline holding the block. The named pipeline must
        exist when the revision is created and be active when it is activated; it can never be
        `entry`, and the activation refuses a delegation that would loop.
      required:
        - when
        - pipeline_key
      properties:
        when:
          $ref: "#/components/schemas/CELCondition"
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          example: payments
          description: The pipeline that takes matching requests.
    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
    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.
    RequestApprovalPipelineRevision:
      type: object
      additionalProperties: false
      description: One pipeline revision with its blocks in the order they run.
      required:
        - id
        - pipeline_key
        - name
        - revision
        - is_active
        - block_count
        - created_by_actor_id
        - created_at
        - blocks
      properties:
        id:
          type: string
          format: uuid
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: The pipeline's key.
        name:
          type: string
          description: The pipeline's display name as of this revision.
        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 from this pipeline without a block
            naming a path.
        default_ask_agent_owner:
          type: boolean
          description: Send requests that reach a person without a block naming a path to the person who owns
            the agent instance that made them, instead of `default_escalation_path`. The path, if
            any, is then the fallback for a request whose agent has no owner able to decide it;
            without one the fallback is the enclosing pipeline's default, else any authorized
            reviewer.
        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
        activated_at:
          type: string
          format: date-time
          description: When the revision was first active. Absent for a draft, which can still be changed.
        archived_at:
          type: string
          format: date-time
          description: Present when the pipeline was archived.
        blocks:
          type: array
          maxItems: 1000
          description: The blocks, in the order they run. A branch holds its own blocks in `config.blocks`; a
            delegate names the pipeline it hands matching requests to in `config.pipeline_key`.
          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/StoredOutcomeBlock"
        - $ref: "#/components/schemas/StoredWebhookBlock"
        - $ref: "#/components/schemas/StoredBranchBlock"
        - $ref: "#/components/schemas/StoredDelegateBlock"
        - $ref: "#/components/schemas/StoredLLMJudgeBlock"
      discriminator:
        propertyName: type
        mapping:
          outcome: "#/components/schemas/StoredOutcomeBlock"
          webhook: "#/components/schemas/StoredWebhookBlock"
          branch: "#/components/schemas/StoredBranchBlock"
          llm_judge: "#/components/schemas/StoredLLMJudgeBlock"
    StoredOutcomeBlock:
      type: object
      additionalProperties: false
      description: An outcome 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: outcome
          description: Always `outcome`.
        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/OutcomeBlockConfig"
        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.
    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.
    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
          description: The note recorded when none of the branch's blocks decides; empty when the branch has
            none.
        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.
        ask_agent_owner:
          type: boolean
          description: Send the request to the person who owns the agent instance that made it, instead of
            `escalation_path`. The path, if set, is then the fallback when the request has no owner
            able to decide it; without one the fallback is the pipeline default.
    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
    StoredDelegateBlock:
      type: object
      additionalProperties: false
      description: A delegate 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: delegate
          description: Always `delegate`.
        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/DelegateBlockConfig"
        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.
    PipelineUse:
      type: object
      required:
        - pipeline_id
        - pipeline_key
        - revision
      description: One place an active pipeline revision uses a shared object, an escalation path, a
        webhook endpoint, or another pipeline.
      properties:
        pipeline_id:
          type: string
          format: uuid
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: The pipeline whose revision uses the object.
        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.
    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`.
        default_ask_agent_owner:
          type: boolean
          description: Whether the default asks the agent's owner first, as in a document.
        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 pipeline key and then by block
                key, exactly as conditions read it under
                `/previous_metadata/<pipeline_key>/<block_key>`.
              additionalProperties:
                type: object
                additionalProperties:
                  type: object
                  additionalProperties: true
                description: Metadata from one pipeline, by block key.
    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
            - block_timeout
            - retries_exhausted
            - invalid_block_output
            - end_of_pipeline
            - end_of_branch
          description: "Why the run ended. `block_outcome`: a block decided. `block_error`, `block_timeout`,
            `retries_exhausted`, `invalid_block_output`: a block failed. `end_of_pipeline`: the
            request passed every block. `end_of_branch`: the request entered a branch or a delegated
            pipeline and none of its blocks decided."
        terminal_block_id:
          type: string
          description: "The block that ended the run: for `end_of_branch`, the branch or the delegate. Absent
            when the request passed every block."
        blocks:
          type: array
          description: What each block the request reached did, in order, each with the pipeline it belongs
            to. A branch or delegate appears when its condition is evaluated, and once more with
            `end_of_branch` when none of the blocks inside decided.
          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: the ending block's path, else the default of the
            pipeline the request was in, else the defaults of the pipelines that delegated into it.
            Absent when the outcome is automatic or no path applies."
        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`: a pipeline's default. `no_escalation`: no path applies."
        escalation_path_pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: The pipeline whose block or default supplied the path. Absent with `no_escalation`.
        ask_agent_owner:
          type: boolean
          description: Present when the route asks the agent's owner, with `escalation_path` as the fallback.
            A preview has no real agent instance, so it names the route, never a person.
    RequestApprovalPipelinePreviewBlock:
      type: object
      additionalProperties: false
      description: What one block did in a preview.
      required:
        - block_id
        - pipeline_key
        - position
        - outcome
        - cost
        - trace
      properties:
        block_id:
          type: string
          description: The block's key.
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: "The pipeline the block belongs to: the previewed document's key, or a delegated
            pipeline's."
        position:
          type: integer
          minimum: 0
          description: The block's position in the effective pipeline, from 0, counting in document order with
            each branch or delegate before the blocks it holds.
        outcome:
          type: string
          enum:
            - next
            - approve
            - deny
            - human
            - enter
          description: What the block returned. A branch or delegate 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`, `input_too_large`,
            `evaluation_error`, `cost_limit_exceeded`, `resource_limit_exceeded`, `timeout`,
            `invalid_block_output`, `internal_error`, `provider_error`, `endpoint_unreachable`,
            `endpoint_error`, `endpoint_rejected`, `outcome_not_allowed`, `endpoint_unavailable`, or
            a code a kind the edition adds reports (the hosted judge's `model_unavailable`)."
        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
        - pipeline_key
        - name
        - revision
        - is_active
        - block_count
        - created_by_actor_id
        - created_at
      properties:
        id:
          type: string
          format: uuid
        pipeline_key:
          type: string
          pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
          description: The pipeline's key.
        name:
          type: string
          description: The pipeline's display name as of this revision.
        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 from this pipeline without a block
            naming a path.
        default_ask_agent_owner:
          type: boolean
          description: Send requests that reach a person without a block naming a path to the person who owns
            the agent instance that made them, instead of `default_escalation_path`. The path, if
            any, is then the fallback for a request whose agent has no owner able to decide it;
            without one the fallback is the enclosing pipeline's default, else any authorized
            reviewer.
        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
        activated_at:
          type: string
          format: date-time
          description: When the revision was first active. Absent for a draft, which can still be changed.
        archived_at:
          type: string
          format: date-time
          description: Present when the pipeline was archived. An archived revision can be read but never
            activated.
    RequestApprovalPipelineDocument:
      type: object
      additionalProperties: false
      description: The complete content of a new pipeline revision.
      required:
        - blocks
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
          description: A new display name for the pipeline. Omit to keep the active revision's.
        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 that enters the pipeline 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 from this pipeline without a block
            naming a path. That includes requests that run out of it. A delegated pipeline without
            one inherits the defaults of the pipelines that delegated into it, nearest first; with
            none anywhere, any reviewer who can decide may take the request from the queue. The path
            must exist when the revision is created, and be active when it is activated.
        default_ask_agent_owner:
          type: boolean
          description: "Send those requests to the person who owns the agent instance that made them instead.
            `default_escalation_path`, if set, is then the fallback for a request whose agent has no
            owner able to decide it (a service agent, or an owner who was deactivated); without it
            the fallback is the enclosing pipeline's default, else any authorized reviewer. The
            owner is the only reviewer: the request never moves to the path because the owner has
            not answered."
    PipelineReferences:
      type: object
      required:
        - escalation_paths
        - webhook_endpoints
        - pipelines
        - blocks
        - activatable
      description: What one pipeline revision depends on, and whether each dependency is in place right
        now. activatable is false while any entry would refuse activation.
      properties:
        pipelines:
          type: array
          description: The pipelines the revision's delegate blocks hand requests to.
          items:
            type: object
            required:
              - pipeline_key
              - block_keys
              - exists
              - active
            properties:
              pipeline_key:
                type: string
                pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
              name:
                type: string
                description: The active revision's name. Absent while none is active.
              block_keys:
                type: array
                items:
                  type: string
                description: The delegate blocks that name the pipeline.
              exists:
                type: boolean
                description: Whether the pipeline has a live revision (neither never created nor archived).
              active:
                type: boolean
                description: Whether a revision of the pipeline is active.
        escalation_paths:
          type: array
          description: The escalation paths the revision names.
          items:
            type: object
            required:
              - path_key
              - block_keys
              - default
              - exists
              - active
            properties:
              path_key:
                type: string
              block_keys:
                type: array
                items:
                  type: string
                description: The blocks whose `human` outcome names the path.
              default:
                type: boolean
                description: Whether the revision names the path as its default.
              exists:
                type: boolean
                description: Whether at least one revision of the path exists.
              active:
                type: boolean
                description: Whether a revision of the path is active.
        webhook_endpoints:
          type: array
          description: The webhook endpoints the revision posts to.
          items:
            type: object
            required:
              - endpoint_key
              - block_keys
              - exists
              - active
            properties:
              endpoint_key:
                type: string
                pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
              name:
                type: string
                description: The active revision's name. Absent while none is active.
              block_keys:
                type: array
                items:
                  type: string
                description: The webhook blocks that post to the endpoint.
              exists:
                type: boolean
                description: Whether at least one revision of the endpoint exists.
              active:
                type: boolean
                description: Whether a revision of the endpoint is active.
        blocks:
          type: array
          description: Blocks the deployment or the plan cannot run right now.
          items:
            type: object
            required:
              - block_key
              - type
              - problem
            properties:
              block_key:
                type: string
              type:
                type: string
                description: The kind of block.
              problem:
                type: string
                description: "Why it cannot run. `plan`: the plan does not include it. `webhook_unconfigured`: the
                  deployment has no secrets key. A kind the edition adds reports its own codes (the
                  hosted judge's `llm_judge_unconfigured` and `judge_model_unavailable`)."
        activatable:
          type: boolean
          description: Whether activating the revision would succeed right now.
    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
        activated_at:
          type: string
          format: date-time
          description: When the revision was first active. Absent for a draft, which can still be changed.
        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
    Role:
      type: object
      required:
        - key
        - name
        - description
        - permissions
        - dangerous
      properties:
        key:
          $ref: "#/components/schemas/RoleKey"
        name:
          type: string
          example: Reviewer
        description:
          type: string
        permissions:
          type: array
          items:
            type: string
          description: Every permission the role holds, including the ones this edition registers.
        dangerous:
          type: boolean
          description: The role holds a sensitive permission, so granting it needs a fresh sign-in.
    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"
    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
        activated_at:
          type: string
          format: date-time
          description: When the revision was first active. Absent for a draft, which can still be changed.
        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
        activated_at:
          type: string
          format: date-time
          description: When the revision was first active. Absent for a draft, which can still be changed.
    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:
        role:
          $ref: "#/components/schemas/RoleKey"
          description: The predefined role the grant makes. Absent for custom permissions.
        role_name:
          type: string
          description: That role's name.
        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
        - available
        - 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
        instance_name:
          type: string
          description: The instance that reported the source, when an instance did
        available:
          type: boolean
          description: "Whether the source's owner can serve now: the reporting instance is active with a live
            credential, or the edition reports the external source live"
        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
          description: Why the last discovery failed, as the reporting instance or the edition described it.
            Empty after a successful discovery
    ToolDefinitionList:
      type: object
      required:
        - tool_definitions
        - servers
      properties:
        tool_definitions:
          type: array
          items:
            $ref: "#/components/schemas/CatalogToolDefinition"
        servers:
          type: array
          items:
            type: string
    CatalogToolDefinition:
      type: object
      required:
        - name
        - uses
      properties:
        name:
          type: string
          description: Exact tool name sent in approval requests
        server:
          type: string
        description:
          type: string
        input_schema:
          type: object
          additionalProperties: true
        uses:
          type: array
          items:
            $ref: "#/components/schemas/ToolDefinitionUse"
    ToolDefinitionUse:
      type: object
      required:
        - runtime
        - available
        - stale
      properties:
        runtime:
          type: string
          description: The runtime that offers the definition, or the source an edition adds (the hosted
            gateway's `gateway`)
        instance_name:
          type: string
          description: The instance that reported the definition, when an instance did
        available:
          type: boolean
        stale:
          type: boolean
    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
        - digests
        - 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"
        digests:
          type: array
          description: Prompts for the request that a notification window held and sent when it closed, oldest
            first, one per person and contact kind.
          items:
            $ref: "#/components/schemas/DigestEvent"
        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
            - delegate
        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 pipeline that held the terminal block (the entry pipeline, or one a delegate handed
            the request to), at the revision pinned for the request. Enough to read that revision
            and find the block in it.
          required:
            - pipeline_key
            - revision
          properties:
            pipeline_key:
              type: string
              pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
            name:
              type: string
              description: The pipeline's name when the request ran.
            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
    DigestEvent:
      type: object
      additionalProperties: false
      description: A prompt a notification window held and sent when it closed, alone (request_count 1) or
        in a digest naming request_count requests.
      required:
        - kind
        - request_count
        - window_opened_at
        - occurred_at
      properties:
        membership_id:
          type: string
        display_name:
          type: string
          description: The person's name when the window closed.
        kind:
          type: string
          description: The contact kind the window belongs to, such as email, slack, discord, sms, whatsapp or
            push.
        request_count:
          type: integer
          description: How many requests the message named.
        window_opened_at:
          type: string
          format: date-time
        occurred_at:
          type: string
          format: date-time
    AgentToolAccess:
      type: object
      required:
        - server_id
        - server_slug
        - mode
        - tools
        - scopes
        - 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
        scopes:
          type: array
          items:
            type: string
        revision:
          type: integer
          format: int64
          minimum: 1
    AgentToolAccessInput:
      type: object
      additionalProperties: false
      required:
        - mode
        - tools
        - scopes
        - 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
        scopes:
          type: array
          maxItems: 20
          uniqueItems: true
          description: The scopes the agent may call with; any granted scope works with any granted tool.
            Empty exactly when mode is none.
          items:
            type: string
            pattern: ^[a-z0-9][a-z0-9_]{0,62}$
        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
        - included_in_plan
        - resources
        - permissions
        - max_ttl
      properties:
        organization_api_keys_allowed:
          type: boolean
        included_in_plan:
          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."
  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
    PipelineKey:
      name: pipeline_key
      in: path
      required: true
      description: The pipeline's key. `entry` is the pipeline every request starts in; every other key
        was chosen when the pipeline was created. Lowercase letters, digits, dots, underscores and
        hyphens, up to 63 characters.
      schema:
        type: string
        pattern: ^[a-z0-9][a-z0-9._-]{0,62}$
    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"'
    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
    AuditQuery:
      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
    AuditEventType:
      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
    AuditActorType:
      name: actor_type
      in: query
      description: Only events by this kind of actor.
      schema:
        type: string
        enum:
          - human
          - agent
          - system
          - api_key
    AuditActorID:
      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
    AuditSubjectType:
      name: subject_type
      in: query
      description: Only events about this kind of thing, such as a request or a membership.
      schema:
        type: string
    AuditSubjectID:
      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
    AuditOutcome:
      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
    AuditFromAt:
      name: from_at
      in: query
      description: Only events at or after this time.
      schema:
        type: string
        format: date-time
    AuditToAt:
      name: to_at
      in: query
      description: Only events before this time.
      schema:
        type: string
        format: date-time
    AuditEventID:
      name: id
      in: path
      required: true
      description: The audit event's id.
      schema:
        type: string
        format: uuid
  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"
  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 the window to start the call
      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
    AAPConsumedRequest:
      summary: Approved and consumed
      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
        consumption:
          consumed_at: 2026-09-17T12:02:04Z
    AAPReportedRequest:
      summary: Consumed with its outcome
      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
        consumption:
          consumed_at: 2026-09-17T12:02:04Z
          outcome:
            status: succeeded
            note: Refund issued
            reported_at: 2026-09-17T12:02:06Z