openapi: 3.1.0
info:
  title: OpenWOP v2 API
  version: 2.46.41
  summary: REST surface for declaring, executing, suspending, resuming, and observing multi-step workflows.
  description: 'The OpenWOP v2 REST API. Paths are unversioned and served at the bare origin; a client selects a version with
    the `OpenWOP-Version` header from the host''s `protocolVersions[]`. Conformance seams and test-mode operations are not
    in this document: they are in `api/seams-v2.yaml`. Every non-standard header is named `OpenWOP-<Name>`. Retry timing is
    carried only in `Retry-After`.


    Generated from `api/openapi.yaml` by `scripts/derive-v2-api.py`; do not edit.'
  contact:
    name: openwop spec working group
    url: https://openwop.dev/spec/v1/
  license:
    name: Apache-2.0
    identifier: Apache-2.0
externalDocs:
  description: OpenWOP specification (v1 corpus)
  url: https://openwop.dev/spec/v1/
servers:
- url: https://{host}
  description: An OpenWOP server.
  variables:
    host:
      default: api.example.com
      description: Replace with your server's hostname.
security:
- ApiKeyAuth: []
tags:
- name: discovery
  description: Public capability + spec discovery (no auth required).
- name: workflows
  description: Workflow definition manifest.
- name: runs
  description: Run lifecycle — create, read, stream, cancel, fork.
- name: agents
  description: Manifest-agent inventory. Read-only; gated on `capabilities.agents.manifestRuntime`. Dispatch uses the run
    surface (`WorkflowNode.agent` + `POST /runs`).
- name: tools
  description: 'Portable tool catalog: a read-only `ToolDescriptor` projection across tool sources (node-pack, workflow, mcp,
    connector, host-extension). Gated on `capabilities.toolCatalog`; scoped to the tools the caller is authorized to invoke.'
- name: hitl
  description: Human-in-the-loop interrupts and approvals.
- name: artifacts
  description: Run-produced artifacts.
- name: webhooks
  description: Subscribe to run events via outbound HTTP.
- name: triggers
  description: Durable trigger-bridge subscriptions. The external-event create surface (`POST /trigger-subscriptions`) is
    gated on `capabilities.triggerBridge.ingestion.registrationEndpoint`.
- name: audit
  description: Audit-log integrity verification (gated on the `openwop-audit-log-integrity` profile).
- name: prompts
  description: 'Prompt-template library: list, fetch, render, mutate. Gated on `capabilities.prompts.*`.'
- name: content
  description: 'Localized authored content: public delivery and tenant-scoped admin CRUD. Gated on `capabilities.content.supported`.'
- name: host
  description: Host-capability resources, such as the agent workspace file store (gated on `capabilities.workspace.*`).
- name: proposals
  description: Reviewable-learning proposals (RFC 0096, operations RFC 0249).
- name: connections
  description: 'Connection-pack provider registry: the definitions a host resolves against and the registrations it refused.'
paths:
  /.well-known/openwop:
    get:
      tags:
      - discovery
      summary: Capability declaration handshake.
      operationId: getCapabilities
      security: []
      parameters:
      - name: If-None-Match
        in: header
        required: false
        schema:
          type: string
        description: Conditional request on the discovery document (capabilities.md §1) and the run snapshot (runs.md §Snapshot).
          A value that matches the current `ETag` (runs.md §Caching and encoding) MUST yield `304 Not Modified` with no body.
          The 304 carries `OpenWOP-Version` like every response (versioning.md §1.4).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Capabilities object — see `capabilities.md`.
          headers:
            Cache-Control:
              schema:
                type: string
              example: public, max-age=300
            ETag:
              schema:
                type: string
              description: Validator for the discovery document (capabilities.md §1). A v2 host MUST send it and MUST honor
                `If-None-Match` with `304`. A host that changes semantics without changing the bytes is non-conformant.
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Capabilities'
        '304':
          description: '`If-None-Match` matched the current `ETag`; the body is unchanged.'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '503':
          description: Server unable to compute capabilities (transient).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /openapi.json:
    get:
      tags:
      - discovery
      summary: Self-describing OpenAPI 3.1 spec.
      operationId: getOpenApiSpec
      security: []
      responses:
        '200':
          description: This document.
          content:
            application/json:
              schema:
                type: object
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '503':
          description: Server unable to serve spec (transient).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /workflows/{workflowId}:
    get:
      tags:
      - workflows
      summary: Read a workflow definition.
      operationId: getWorkflow
      security:
      - ApiKeyAuth:
        - manifest:read
      - OAuth2:
        - manifest:read
      - OpenIdConnect:
        - manifest:read
      parameters:
      - $ref: '#/components/parameters/WorkflowId'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Workflow definition.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/workflow-definition.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /runs:
    post:
      tags:
      - runs
      summary: Create a new run.
      operationId: createRun
      security:
      - ApiKeyAuth:
        - runs:create
      - OAuth2:
        - runs:create
      - OpenIdConnect:
        - runs:create
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - in: header
        name: OpenWOP-Dedup
        schema:
          type: string
          enum:
          - enforce
        description: When set, the host's cross-host claim system rejects a duplicate `(tenantId, scopeId)` pair with `409
          Conflict`.
      - in: header
        name: OpenWOP-Force-Engine-Version
        schema:
          type: integer
          minimum: 0
        description: Test keys only. The server emits this run's events as if it ran the given engine version, which must
          be within `Capabilities.testing.forceEngineVersionRange`. Servers MUST reject it on production API keys with `403
          force_engine_version_forbidden`.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              unevaluatedProperties: false
              allOf:
              - type: object
                properties:
                  workflowId:
                    type: string
                    minLength: 1
                  inputs:
                    type: object
                    description: Workflow inputs (consumed by triggers/nodes).
                  residency:
                    $ref: ../../schemas/v2/residency.schema.json
                  tenantId:
                    type: string
                    description: Tenant scope. A host typically derives it from the API key.
                  scopeId:
                    type: string
                    description: Opaque correlation ID for `OpenWOP-Dedup`.
                  callbackUrl:
                    type: string
                    format: uri
                    description: Signed-token HITL callback URL (see `interrupt.md`).
                  mode:
                    type: string
                    enum:
                    - eval
                    description: 'Set to `eval` to run an eval-suite projection instead of a workflow run: the host runs the
                      `evalSuiteRef` against `agentId`, emits the content-free `eval.*` family, and terminates with an `EvalSummary`
                      readable at `GET /runs/{runId}/eval-summary`. Gated on `capabilities.agents.evalSuite.supported`; a
                      host that omits it rejects `mode: "eval"` with `422 capability_not_provided`. Omit for a normal workflow
                      run.'
                  evalSuiteRef:
                    type: string
                    minLength: 1
                    description: URI of the `AgentEvalSuite` to run. Required when mode is `eval`.
                  agentId:
                    type: string
                    minLength: 1
                    description: The manifest agent the eval suite targets. Required when mode is `eval`.
                  runSecrets:
                    type: array
                    minItems: 1
                    description: Run-supplied secrets. Accepted only by a host advertising `capabilities.secrets.runSecrets`;
                      a client MUST NOT send it to any other host. A `run:` ref resolves only within the run that supplied
                      it, and no value is ever echoed, logged or folded into a persisted digest (the idempotency request digest
                      is computed without it).
                    items:
                      type: object
                      unevaluatedProperties: false
                      required:
                      - ref
                      - value
                      properties:
                        ref:
                          type: string
                          pattern: ^run:[A-Za-z0-9_.-]{1,64}$
                        value:
                          type: string
                          minLength: 16
                          maxLength: 4096
                          writeOnly: true
                  evalModes:
                    type: array
                    minItems: 1
                    uniqueItems: true
                    items:
                      type: string
                      enum:
                      - golden
                      - rubric
                      - adversarial
                      - regression
                      - live-shadow
                    description: 'RFC 0251 — the RFC 0081 §D modes this eval run exercises: a subset of the suite''s and the
                      host''s `modes` (absent ⇒ the host''s default). Only with `mode` `eval`, and only to a host advertising
                      `agents.evalSuite.baselineSelection`, whose description holds the refusal rules.'
                  baselineRunId:
                    $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
                    description: RFC 0251 — the prior eval run a `regression` run is compared against; requires `regression`
                      in `evalModes`. Only to a host advertising `agents.evalSuite.baselineSelection`. An unusable baseline
                      is refused `400 validation_error`, identically whatever the reason.
                  fixtureRuns:
                    type: object
                    minProperties: 1
                    propertyNames:
                      type: string
                      minLength: 1
                    additionalProperties:
                      $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
                    description: RFC 0252 — maps a suite `taskId` to a prior run whose recorded tool calls become that task's
                      fixtures, served under `onMiss` `fail`. Only with `mode` `eval`, and only to a host advertising `agents.evalSuite.recordedFixtures`,
                      whose description holds the refusal rules. An unusable source run is refused `400 validation_error`,
                      identically whatever the reason.
                if:
                  properties:
                    mode:
                      const: eval
                  required:
                  - mode
                then:
                  required:
                  - evalSuiteRef
                  - agentId
                else:
                  required:
                  - workflowId
              - $ref: ../../schemas/v2/run-options.schema.json
      responses:
        '201':
          description: Run accepted.
          headers:
            OpenWOP-Idempotent-Replay:
              schema:
                type: boolean
              description: Set when the response was served from the idempotency cache.
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            application/json:
              schema:
                type: object
                required:
                - runId
                - status
                - eventsUrl
                properties:
                  runId:
                    type: string
                  status:
                    type: string
                    enum:
                    - pending
                    - running
                    - waiting-approval
                    - waiting-input
                    - waiting-external
                  eventsUrl:
                    type: string
                    format: uri
                  statusUrl:
                    type: string
                    format: uri
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: An `OpenWOP-Dedup` collision, or a concurrent `Idempotency-Key` collision.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds until the active claim is stale-eligible.
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RunClaimConflict'
        '429':
          $ref: '#/components/responses/RateLimited'
    get:
      tags:
      - runs
      operationId: listRuns
      summary: List the caller's runs, newest first
      description: 'Lists only runs whose tenant segment is the caller''s; every `runId` is tenant-bound (identity.md §5).
        The list is cursor-paginated. Gated on the `runList` family: a host that does not advertise it answers `404 not_found`.


        `limit` is honoured up to `runList.maxPageSize`. `cursor` is opaque; a cursor the host did not mint is `400 validation_error`.
        `workflowId` and `status` are exact-match filters when `runList.filters` names them; an unadvertised filter is ignored.'
      parameters:
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page size; clamped to `runList.maxPageSize`.
      - name: cursor
        in: query
        required: false
        schema:
          type: string
          minLength: 1
          maxLength: 2048
        description: Opaque cursor from a previous page's `nextCursor`.
      - name: workflowId
        in: query
        required: false
        schema:
          $ref: ../../schemas/v2/ids.schema.json#/$defs/workflowId
        description: Exact-match filter, when advertised in `runList.filters`.
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - pending
          - running
          - paused
          - waiting-approval
          - waiting-input
          - waiting-external
          - completed
          - failed
          - cancelling
          - cancelled
        description: Exact-match filter, when advertised in `runList.filters`.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: One page of the caller's runs.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/run-list-response.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
  /runs/{runId}:
    get:
      tags:
      - runs
      summary: Read run state (cached projection).
      operationId: getRun
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - in: header
        name: If-None-Match
        required: false
        schema:
          type: string
        description: Conditional request on the discovery document (capabilities.md §1) and the run snapshot (runs.md §Snapshot).
          A value that matches the current `ETag` (runs.md §Caching and encoding) MUST yield `304 Not Modified` with no body.
          The 304 carries `OpenWOP-Version` like every response (versioning.md §1.4).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Projected run state.
          headers:
            ETag:
              schema:
                type: string
              description: Strong validator derived from the run's latest persisted event-log sequence number. It changes
                on every observable state transition and is stable while none occurs.
            Content-Encoding:
              required: false
              schema:
                type: string
                enum:
                - gzip
                - br
                - zstd
              description: 'Present only when the host negotiated compression from `Accept-Encoding`; pairs with `Vary: Accept-Encoding`.
                The decoded body is byte-identical to the identity body.'
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RunSnapshot'
        '304':
          description: 'Not Modified: `If-None-Match` matched the current `ETag`. Empty body.'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /runs/{runId}/events:
    get:
      tags:
      - runs
      summary: SSE stream of run events.
      operationId: streamRunEvents
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - in: query
        name: streamMode
        schema:
          type: string
          pattern: ^(values|(updates|messages|debug)(,(updates|messages|debug))*)$
          default: updates
        description: A stream mode from the closed set, or a comma-separated combination of them. `values` never combines.
      - in: query
        name: bufferMs
        schema:
          type: integer
          minimum: 0
          maximum: 5000
        description: 'Optional batching hint. When set, the server accumulates events for up to N ms, or until a forced-flush
          trigger fires (terminal events, suspensions, connection close), and emits a single SSE event with `event: batch`
          and `data:` as a JSON array of `RunEventDoc`. Range 0..5000; `0` means no buffering. See `stream-modes.md` §Aggregation
          hint.'
      - in: query
        name: resumable
        schema:
          type: boolean
          default: false
        description: RFC 0247. `true` says this consumer resumes with `Last-Event-ID` when the stream closes without the terminal
          event, so a host advertising `limits.maxStreamAgeSec` MAY end it early (a bounded close, never terminal). Absent
          or `false`, the host MUST hold a live stream open (events.md §Resuming with `Last-Event-ID`). A consumer that sends
          it MUST resume with `resumable=true` and `Last-Event-ID`.
      - in: header
        name: Last-Event-ID
        schema:
          type: string
        description: Resume from sequence after this ID.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: SSE stream. Auto-closes on terminal event. Keep-alive comments every 30s.
          content:
            text/event-stream:
              schema:
                type: string
                description: SSE events. Each event has `id:`, `event:`, `data:` per RFC 8895.
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: Unsupported `streamMode`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnsupportedStreamMode'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /runs/{runId}/events/poll:
    get:
      tags:
      - runs
      summary: Long-poll fallback for non-SSE clients.
      operationId: pollRunEvents
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - in: query
        name: afterSequence
        schema:
          type: integer
          minimum: 0
        description: Return events with `sequence > afterSequence`. Omitted, the poll starts from the first event (sequence
          0).
      - in: query
        name: timeout
        schema:
          type: integer
          minimum: 1
          maximum: 60
          default: 30
        description: Seconds to wait for new events. Max 60.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Events after `afterSequence`.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                - runId
                - events
                - lastSequence
                - status
                - isTerminal
                properties:
                  runId:
                    $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
                  events:
                    type: array
                    items:
                      $ref: ../../schemas/v2/run-event.schema.json
                  lastSequence:
                    type: integer
                    minimum: -1
                    description: The highest sequence in the log at the time of the response; -1 when the log is empty.
                  status:
                    type: string
                  isTerminal:
                    type: boolean
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /runs/{runId}/cancel:
    post:
      tags:
      - runs
      summary: Cancel an in-flight run.
      operationId: cancelRun
      security:
      - ApiKeyAuth:
        - runs:cancel
      - OAuth2:
        - runs:cancel
      - OpenIdConnect:
        - runs:cancel
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
      responses:
        '200':
          description: Run cancellation accepted (cascade may be async).
          content:
            application/json:
              schema:
                type: object
                properties:
                  runId:
                    type: string
                  status:
                    type: string
                    enum:
                    - cancelled
                    - cancelling
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /runs/{runId}/annotations:
    post:
      tags:
      - runs
      summary: Record a non-blocking quality annotation on a run.
      operationId: createAnnotation
      security:
      - ApiKeyAuth:
        - runs:annotate
      - OAuth2:
        - runs:annotate
      - OpenIdConnect:
        - runs:annotate
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/annotation-create.schema.json
      responses:
        '201':
          description: Annotation recorded. Returns the persisted annotation.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/annotation.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    get:
      tags:
      - runs
      summary: List the annotations recorded on a run.
      operationId: listAnnotations
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Annotations for the run (tenant-scoped).
          content:
            application/json:
              schema:
                type: object
                required:
                - annotations
                properties:
                  annotations:
                    type: array
                    items:
                      $ref: ../../schemas/v2/annotation.schema.json
                additionalProperties: false
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /trigger-subscriptions:
    post:
      tags:
      - triggers
      summary: Register an external-event trigger subscription.
      description: Creates an external-event (`webhook`/`email`/`form`) TriggerSubscription bound to a workflow, with a dedup
        config and a source-authenticity verification policy. The `workflowId` MUST resolve under the caller's owner triple.
        The response carries the created TriggerSubscription plus a source-specific `binding`; the binding secret or URL is
        returned once and cannot be re-fetched in cleartext.
      operationId: createTriggerSubscription
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/trigger-subscription-registration.schema.json
      responses:
        '201':
          description: Subscription created. Returns the TriggerSubscription + a source-specific binding.
          content:
            application/json:
              schema:
                type: object
                required:
                - subscription
                - binding
                additionalProperties: false
                properties:
                  subscription:
                    $ref: ../../schemas/v2/trigger-subscription.schema.json
                  binding:
                    type: object
                    description: 'Source-specific binding the caller needs to wire the external source. For `webhook`: `{
                      ingestUrl, secretFingerprint }`; for `email`: `{ ingestAddress }`; for `form`: `{ ingestUrl }`. The
                      secret is returned once, at creation (SR-1).'
                    additionalProperties: true
                    properties:
                      ingestUrl:
                        type: string
                        format: uri
                      ingestAddress:
                        type: string
                      secretFingerprint:
                        type: string
                        maxLength: 32
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /trigger-subscriptions/{subscriptionId}/dead-letters:
    get:
      tags:
      - triggers
      summary: List a trigger subscription's dead-lettered deliveries.
      description: 'Lists the subscription''s dead-lettered deliveries, newest first, for `triggerBridge.deadLetter.retentionDays`.
        Each record carries the fields of the dead-lettered `trigger.delivery-attempted` payload and, when the dead-lettering
        changed the subscription''s state, the `trigger.subscription-state-changed` payload. A delivery refused by a `required`
        verification check appears with `reason: "verification_failed"` and no `stateChange`. A record MUST NOT carry the
        inbound body, the inbound headers, the signature, the signing secret, or any credential. Gated on `triggerBridge.deadLetter`:
        a host that does not advertise it answers `404 not_found`. A subscription id whose tenant segment is not the caller''s
        is refused `403 id_tenant_mismatch` (`identity.md` §5); an id the host never minted answers `404 not_found`.'
      operationId: listTriggerDeadLetters
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
      parameters:
      - name: subscriptionId
        in: path
        required: true
        schema:
          $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
        description: 'The trigger subscription, tenant-bound (`identity.md` §5) and carried as one path segment: `~`-projected
          or percent-encoded.'
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page size; clamped to `triggerBridge.deadLetter.maxPageSize`.
      - name: cursor
        in: query
        required: false
        schema:
          type: string
          minLength: 1
          maxLength: 2048
        description: Opaque cursor from a previous page's `nextCursor`. A cursor minted for another subscription MUST be refused
          `400 validation_error`.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: One page of dead-lettered deliveries, newest first.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/trigger-dead-letter-page.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /content/pages/{slug}:
    get:
      tags:
      - content
      security: []
      summary: Deliver a published content page resolved for the negotiated locale.
      description: 'Returns the published page `slug` for the locale negotiated from `Accept-Language`, with its sections
        already merged, in render order. Locale fallback follows i18n.md: q-value order, then language family, then `content.baseLocale`.
        Sections are merged field by field (`localized-content.md` §C).


        The response sets `Content-Language` to the locale used, `Vary: Accept-Language, Accept-Encoding`, and `Cache-Control:
        public, max-age=300, stale-while-revalidate=3600`. Only `status: "published"` content is served.


        The tenant is host-resolved (`localized-content.md` §F): from the credential when authenticated, host-defined (for
        example by domain) when anonymous. A `slug` absent for the resolved tenant returns the same `404` as a nonexistent
        slug, so tenants cannot be enumerated.'
      operationId: getContentPage
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
          pattern: ^[a-z][a-z0-9-]*$
        description: The page slug.
      - name: Accept-Language
        in: header
        required: false
        schema:
          type: string
        description: BCP-47 preference list; authoritative for locale selection (i18n.md). A malformed value MUST NOT produce
          a 400.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The resolved, published page for the negotiated locale.
          headers:
            Content-Language:
              schema:
                type: string
              description: The BCP-47 locale actually used (equals the response `locale`).
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/localized-content-page-response.schema.json
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - content
      summary: Delete a content page (admin).
      description: 'Deletes a page with its sections and every locale overlay: the `DELETE …/content/pages/{pageId}` admin
        operation of `localized-content.md` §D. Tenant-scoped; requires `content.write`.


        The path segment is the page''s `pageId`, not its slug. The operation shares this path item with public delivery,
        which addresses the same segment by `slug`, and OpenAPI 3.1 forbids two templates that differ only in parameter name,
        so the parameter keeps delivery''s name. A `pageId` absent for the caller''s tenant returns the same `404` as a nonexistent
        one (`localized-content.md` §F), so tenants cannot be enumerated.'
      operationId: deleteContentPage
      security:
      - ApiKeyAuth:
        - content:write
      - OAuth2:
        - content:write
      - OpenIdConnect:
        - content:write
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
          minLength: 1
        description: For this operation, the page's `pageId`, not its slug; the operation description explains the shared
          name.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '204':
          description: The page, its sections and its locale overlays were deleted.
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /content/pages:
    get:
      tags:
      - content
      summary: List content pages for the caller's tenant (admin).
      description: Tenant-scoped admin listing of pages, draft and published. Requires a principal with `content.read` scope.
      operationId: listContentPages
      security:
      - ApiKeyAuth:
        - content:read
      - OAuth2:
        - content:read
      - OpenIdConnect:
        - content:read
      responses:
        '200':
          description: The caller-tenant's pages.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: ../../schemas/v2/localized-content-page.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
    post:
      tags:
      - content
      summary: Create a content page (admin).
      description: Tenant-scoped page creation. Requires `content.write` scope.
      operationId: createContentPage
      security:
      - ApiKeyAuth:
        - content:write
      - OAuth2:
        - content:write
      - OpenIdConnect:
        - content:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/localized-content-page.schema.json
      responses:
        '201':
          description: The created page.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/localized-content-page.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: Invalid request (for example `baseLocale` in `supportedLocales`, or a malformed locale or slug).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /content/pages/{pageId}/sections/{sectionId}:
    put:
      tags:
      - content
      summary: Upsert a section's base or per-locale fields (admin).
      description: Locale-targeted write. `locale == content.baseLocale` upserts the section's base `data`; any other locale
        (case-canonical BCP 47 subset) upserts `localizations[locale]`. Tenant-scoped; requires `content.write`.
      operationId: putContentSection
      security:
      - ApiKeyAuth:
        - content:write
      - OAuth2:
        - content:write
      - OpenIdConnect:
        - content:write
      parameters:
      - name: pageId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
      - name: sectionId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
              - locale
              - data
              properties:
                locale:
                  type: string
                  pattern: ^[a-z]{2,3}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$
                  description: Target locale. `baseLocale` upserts `data`; any other locale upserts `localizations[locale]`.
                data:
                  type: object
                  additionalProperties: true
                  description: The field overlay for the target locale.
      responses:
        '200':
          description: The updated section record.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/localized-content-section.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: Invalid request (for example `baseLocale` in `supportedLocales`, or a malformed locale or slug).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /content/settings:
    get:
      tags:
      - content
      summary: Read the tenant's content language settings (admin).
      description: Tenant-scoped read of `{ baseLocale, supportedLocales, autoTranslateOnPublish }`. Requires `content.read`.
      operationId: getContentSettings
      security:
      - ApiKeyAuth:
        - content:read
      - OAuth2:
        - content:read
      - OpenIdConnect:
        - content:read
      responses:
        '200':
          description: The tenant's language settings.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/localized-content-language-settings.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
    put:
      tags:
      - content
      summary: Update the tenant's content language settings (admin).
      description: Tenant-scoped settings update. The invariant `baseLocale ∉ supportedLocales` MUST hold, else `400`. Requires
        `content.write`.
      operationId: putContentSettings
      security:
      - ApiKeyAuth:
        - content:write
      - OAuth2:
        - content:write
      - OpenIdConnect:
        - content:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/localized-content-language-settings.schema.json
      responses:
        '200':
          description: The updated language settings.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/localized-content-language-settings.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: Invalid request (for example `baseLocale` in `supportedLocales`, or a malformed locale or slug).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /runs:bulk-cancel:
    post:
      tags:
      - runs
      summary: Cancel a set of in-flight runs in a single request.
      description: 'Accepts a non-empty array of runIds and cancels each one independently. Returns `200` with a per-id results
        array even when some cancellations fail: the operation succeeds when the request reached the host, whatever the per-id
        outcomes. Hosts enforce a host-defined cap on the array length (RECOMMENDED 100); an over-cap request returns `400
        validation_error`.'
      operationId: bulkCancelRuns
      security:
      - ApiKeyAuth:
        - runs:cancel
      - OAuth2:
        - runs:cancel
      - OpenIdConnect:
        - runs:cancel
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - runIds
              properties:
                runIds:
                  type: array
                  minItems: 1
                  maxItems: 100
                  items:
                    $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
                reason:
                  type: string
                  maxLength: 512
              additionalProperties: false
      responses:
        '200':
          description: Per-id cancel results.
          content:
            application/json:
              schema:
                type: object
                required:
                - results
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      required:
                      - runId
                      - ok
                      properties:
                        runId:
                          type: string
                          minLength: 1
                        ok:
                          type: boolean
                        status:
                          type: string
                          enum:
                          - cancelled
                          - cancelling
                        error:
                          $ref: ../../schemas/v2/error-envelope.schema.json
                      additionalProperties: false
                additionalProperties: false
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
  /runs/{runId}:fork:
    post:
      tags:
      - runs
      summary: Fork the run for replay or branch (see `replay.md`).
      operationId: forkRun
      security:
      - ApiKeyAuth:
        - runs:create
        - runs:read
      - OAuth2:
        - runs:create
        - runs:read
      - OpenIdConnect:
        - runs:create
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - mode
              properties:
                fromSeq:
                  type: integer
                  minimum: 0
                  description: 'Inclusive: events `< fromSeq` are fixed history and events `>= fromSeq` are re-executed. Required
                    for `branch` (the branch point). Optional for `replay`, where it defaults to `0`, full re-execution from
                    the start of the source run (`replay.md` §"Replay-mode defaults").'
                mode:
                  type: string
                  enum:
                  - replay
                  - branch
                runOptionsOverlay:
                  type: object
                  description: '`branch` mode only: caller-supplied `RunOptions` to overlay.'
              additionalProperties: false
      responses:
        '201':
          description: Fork accepted, new run started.
          content:
            application/json:
              schema:
                type: object
                required:
                - runId
                - sourceRunId
                - mode
                - status
                - eventsUrl
                properties:
                  runId:
                    type: string
                  sourceRunId:
                    type: string
                  fromSeq:
                    type: integer
                  mode:
                    type: string
                    enum:
                    - replay
                    - branch
                  status:
                    type: string
                  eventsUrl:
                    type: string
                    format: uri
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: Invalid `fromSeq`, `replay` with a non-empty `runOptionsOverlay`, and similar.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          description: '`fromSeq` names a sequence number that does not exist in the source run''s event log.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}/ancestry:
    get:
      tags:
      - runs
      summary: Read a run's parent in the cross-host composition chain
      description: 'Returns the run''s immediate parent in the cross-host composition chain. Gated on `capabilities.multiAgent.executionModel.crossHostCausation.ancestryEndpointSupported:
        true`; a host that does not advertise it returns 404 not_found. Clients walk the full chain one hop at a time by following
        `parent.wellKnownUrl` in each response.'
      operationId: getRunAncestry
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: 'The run''s immediate parent (`parent: null` for a top-level run).'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/run-ancestry-response.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: 'The run does not exist, or the host does not advertise `crossHostCausation.ancestryEndpointSupported:
            true` and treats the endpoint as absent. Clients can tell the two apart from the host''s discovery document.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /agents:
    get:
      tags:
      - agents
      summary: List installed manifest agents
      description: 'Lists the manifest agents installed in this host''s AgentRegistry. Gated on `capabilities.agents.manifestRuntime.supported:
        true`; a host that does not advertise it returns 404. The projection is read-only and never carries the system-prompt
        body, resolved handoff schemas, or credential material (SR-1). There is no dispatch endpoint: a manifest agent is
        invoked as a run whose node pins it with `WorkflowNode.agent` + `POST /runs`.


        The result is scoped to the authenticated principal''s owner triple. When `capabilities.agents.manifestRuntime.installScope`
        is `''tenant''`, only the agents available to the caller''s tenant and workspace are returned; an agent another workspace
        installed is absent, never disclosed. When it is `''host''` (the default), the inventory is host-global. A `''tenant''`-scoped
        host MUST reject unauthenticated or unscoped requests under its standard auth contract rather than fall back to a
        global list.'
      operationId: listAgents
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      responses:
        '200':
          description: Installed manifest agents, sorted by `agentId`. When the host advertises `a2a.agentCards`, an entry
            the host routes a workflow to carries `a2aTenant`, the opaque A2A `tenant` value of that agent's card (interop.md
            §Per-agent cards).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-inventory-response.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Host does not advertise `capabilities.agents.manifestRuntime` and treats the endpoint as absent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /agents/{agentId}:
    get:
      tags:
      - agents
      summary: Read one installed manifest agent
      description: 'Returns one installed manifest agent''s inventory entry, or 404 when no such agent is installed or the
        host does not advertise `capabilities.agents.manifestRuntime`. The lookup is resolved within the authenticated principal''s
        owner triple: on an `installScope: ''tenant''` host, an agent the caller''s workspace has not approved returns the
        same 404 as "not installed", so another tenant''s inventory is never disclosed.'
      operationId: getAgent
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - in: path
        name: agentId
        required: true
        schema:
          type: string
          pattern: ^(?!roster$|org-chart$).+$
        description: The manifest agentId. MUST NOT be the reserved literals `roster` or `org-chart`, which name the sibling
          collection routes (`/agents/roster`, `/agents/org-chart`), so `/agents/{agentId}` stays unambiguous.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The agent's inventory entry. When the host advertises `a2a.agentCards`, an entry the host routes a
            workflow to carries `a2aTenant`, the opaque A2A `tenant` value of that agent's card (interop.md §Per-agent cards).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-inventory-response.schema.json#/$defs/AgentInventoryEntry
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such agent, or the host doesn't advertise the capability.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /agents/{agentId}/deployments:
    get:
      tags:
      - agents
      summary: List an agent's deployment records
      description: 'Lists the deployment records, one per `(agentId, version)`, for a manifest agent: the lifecycle `state`,
        the named `channels`, the canary share, the rollback pointer, and the last-transition provenance. Read-only and content-free
        of any manifest body or credential (SR-1). Gated on `capabilities.agents.deployment.supported: true`; a host that
        does not advertise it returns 404. Tenant-scoped to the caller''s owner triple when `installScope: ''tenant''`.'
      operationId: listAgentDeployments
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - in: path
        name: agentId
        required: true
        schema:
          type: string
          pattern: ^(?!roster$|org-chart$).+$
        description: The manifest agentId. MUST NOT be the reserved literals `roster` or `org-chart`, which name the sibling
          collection routes (`/agents/roster/{rosterId}`, `/agents/org-chart/{departmentId}`), so `/agents/{agentId}/deployments`
          stays unambiguous.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The agent's deployment records (version-sorted).
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: ../../schemas/v2/agent-deployment.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such agent, or the host doesn't advertise `capabilities.agents.deployment`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
    post:
      tags:
      - agents
      summary: Request a deployment state transition
      description: 'Requests a transition: promote, pause, deprecate, rollback, or adjust-canary. The host MUST authorize
        fail-closed against the `deploy:*` scope (an absent or unseeded role denies), run any configured approvalGate, and,
        when the gate carries `requiredEval`, verify that the referenced eval run is terminal and `EvalSummary.passed` before
        emitting `deployment.promoted`. On success it returns the updated deployment record and emits the matching content-free
        `deployment.*` event.'
      operationId: transitionAgentDeployment
      security:
      - ApiKeyAuth:
        - deploy:*
      - OAuth2:
        - deploy:*
      - OpenIdConnect:
        - deploy:*
      parameters:
      - in: path
        name: agentId
        required: true
        schema:
          type: string
        description: The manifest agentId.
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/agent-deployment-transition.schema.json
      responses:
        '200':
          description: The deployment record after the applied transition. Its `transitionRunId` names the deployment-management
            run that carries the transition's `deployment.*` event.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-deployment.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: '`validation_error`: the body is malformed, or names a state or `canaryPercent` the host does not advertise
            (checked before the state machine).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          description: 'Fail-closed authorization denial: the principal lacks `deploy:*` (RFC 0049). The challenge names `scope="deploy:*"`.
            No management run is created.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          description: No such agent, or the host doesn't advertise `capabilities.agents.deployment`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '422':
          description: '`eval_gate_unmet`: a `requiredEval` gate''s referenced eval run is not terminal and passed.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '202':
          description: 'The transition is gated (RFC 0051): the management run is suspended on its approval interrupt. The
            record is returned unchanged except for `transitionRunId`, which names that run.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-deployment.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '409':
          description: '`deployment_state_conflict`: the record''s current state does not allow the transition (an illegal
            edge, a `promote` that skips a step, or a rollback with no prior active version).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /agents/roster:
    get:
      tags:
      - agents
      summary: List the standing agent roster
      description: 'Lists the standing agent roster visible to the caller: named "digital-twin employee" instances and their
        workflow portfolios. Gated on `capabilities.agents.roster.supported: true`; a host that does not advertise it returns
        404. Tenant-scoped: on an `installScope: ''tenant''` host only the caller''s owner-triple entries are returned. Read-only;
        content-free (SR-1).'
      operationId: listAgentRoster
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      responses:
        '200':
          description: The caller's standing roster (rosterId-sorted).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-roster-response.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Host does not advertise `capabilities.agents.roster`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /agents/roster/{rosterId}:
    get:
      tags:
      - agents
      summary: Read one standing roster entry
      description: 'Returns one standing roster entry, or 404 when no such entry exists, the host does not advertise `capabilities.agents.roster`,
        or (on an `installScope: ''tenant''` host) the entry is outside the caller''s owner triple. A cross-tenant entry returns
        the same 404 as "not found", so another tenant''s roster is never disclosed.'
      operationId: getAgentRosterEntry
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - in: path
        name: rosterId
        required: true
        schema:
          type: string
        description: The standing instance id (a `host:<id>` AgentRef agentId).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The roster entry.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-roster-entry.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such entry, cross-tenant, or capability unadvertised.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /agents/org-chart:
    get:
      tags:
      - agents
      summary: Read the caller's agent org chart
      description: 'Returns the caller''s agent org chart: departments, roles, and `reportsTo` edges over roster members.
        Gated on `capabilities.agents.orgChart.supported: true`; a host that does not advertise it returns 404. Tenant-scoped.
        The chart is descriptive only: an org edge confers no authority (`org-position-no-authority-escalation`).'
      operationId: getAgentOrgChart
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      responses:
        '200':
          description: The caller's org-chart.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/agent-org-chart.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Host does not advertise `capabilities.agents.orgChart`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /agents/org-chart/{departmentId}:
    get:
      tags:
      - agents
      summary: Read one department's subtree and responsibility roll-up
      description: Returns one department's subtree and responsibility roll-up (the union of its members' portfolios). `?recursive=false`
        narrows the roll-up to direct members without changing the response shape. Returns 404 when the department is unknown
        or cross-tenant, or the host does not advertise `capabilities.agents.orgChart`. The roll-up grants nothing.
      operationId: getAgentOrgChartDepartment
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - in: path
        name: departmentId
        required: true
        schema:
          type: string
        description: The department id to root the subtree and roll-up at.
      - in: query
        name: recursive
        required: false
        schema:
          type: boolean
          default: true
        description: When `false`, the roll-up scopes to direct members only.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The department subtree + responsibility roll-up.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/org-chart-responsibility-view.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Unknown/cross-tenant department, or capability unadvertised.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /tools:
    get:
      tags:
      - tools
      summary: List the tools the caller may invoke
      description: 'Lists the portable `ToolDescriptor`s visible to the caller across every tool source (node-pack, workflow,
        mcp, connector, host-extension). Gated on `capabilities.toolCatalog.supported: true`; a host that does not advertise
        it returns 404. The projection is authorization-scoped: a principal sees only the tools it may invoke, and a second
        principal MUST NOT see another principal''s tools. Read-only; no descriptor carries credential material (SR-1).'
      operationId: listTools
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - name: view
        in: query
        required: false
        schema:
          type: string
          enum:
          - standard
          - compact
          default: standard
        description: '`compact` returns the `{ tools: CompactToolDescriptor[] }` projection (heavy descriptor fields omitted,
          `inputSchema` bounded to the compact structural subset) when the host advertises `capabilities.toolCatalog.compactView:
          true`. Defaults to `standard`. A host that does not advertise `compactView` treats `view=compact` like any unknown
          query parameter and returns the standard view.'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: 'The caller''s authorized tool catalog (standard array, or the compact `{ tools: [] }` projection when
            `view=compact`).'
          content:
            application/json:
              schema:
                oneOf:
                - type: array
                  items:
                    $ref: ../../schemas/v2/tool-descriptor.schema.json
                - type: object
                  required:
                  - tools
                  properties:
                    tools:
                      type: array
                      items:
                        $ref: ../../schemas/v2/compact-tool-descriptor.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Host does not advertise `capabilities.toolCatalog`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /tools/{toolId}:
    get:
      tags:
      - tools
      summary: Read one tool descriptor
      description: Returns one `ToolDescriptor` by its stable `toolId`, or 404 when no such tool exists, the caller is not
        authorized for it, or the host does not advertise `capabilities.toolCatalog`. A tool the caller may not see returns
        the same 404 as "not found".
      operationId: getTool
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - in: path
        name: toolId
        required: true
        schema:
          type: string
        description: The stable tool id (`ToolDescriptor.toolId`).
      - name: view
        in: query
        required: false
        schema:
          type: string
          enum:
          - standard
          - compact
          default: standard
        description: '`compact` returns one `CompactToolDescriptor` (heavy fields omitted, `inputSchema` bounded to the compact
          structural subset) when the host advertises `capabilities.toolCatalog.compactView: true`. Defaults to `standard`.
          A host that does not advertise `compactView` treats `view=compact` like any unknown query parameter.'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The tool descriptor (standard, or one `CompactToolDescriptor` when `view=compact`).
          content:
            application/json:
              schema:
                oneOf:
                - $ref: ../../schemas/v2/tool-descriptor.schema.json
                - $ref: ../../schemas/v2/compact-tool-descriptor.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such tool, unauthorized, or capability unadvertised.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}/eval-summary:
    get:
      tags:
      - runs
      summary: Read the scorecard of a terminal eval run
      description: 'Returns the `EvalSummary` scorecard for a terminal eval run (one started with `mode: "eval"`): aggregate
        and per-task scores, cost, latency, schema validity, redaction-safe safety findings, the suite provenance, and, in
        regression mode, the score delta against a baseline. It carries no task output, rubric prose, or credentials (SR-1;
        `eval-summary-no-content-leak`). Gated on `capabilities.agents.evalSuite.supported: true`; a host that does not advertise
        it returns 404. Returns 409 while the run is not yet terminal.'
      operationId: getEvalSummary
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The eval run's scorecard.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/eval-summary.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such run, the run is not an eval run, or the host does not advertise `capabilities.agents.evalSuite`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '409':
          description: The eval run is still running; the summary is not yet final.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}:diff:
    get:
      tags:
      - runs
      summary: Diff two runs
      description: 'Returns a deterministic, replay-aware structured diff of two runs, typically a run and its fork: `divergedAtSeq`,
        ordered `eventDiffs[]`, and `stateDiff`. The diff is a pure function of the two event logs (see the `replay.md` determinism
        contract). Requires `runs:read` on both runs. A host that does not implement it returns 404.'
      operationId: diffRun
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - name: against
        in: query
        required: true
        description: The other run id to diff `{runId}` against (the `b` run).
        schema:
          type: string
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: 'Structured diff of the two runs. `divergedAtSeq: null` and an empty `eventDiffs` when the logs are
            identical.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/run-diff-response.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: Missing or malformed `against` query parameter.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          description: The caller lacks `runs:read` on `{runId}` or on `against` (`forbidden`); composes with cross-workspace
            isolation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          description: Either run does not exist, or the host does not implement the diff endpoint and treats the path as
            absent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}:pause:
    post:
      tags:
      - runs
      summary: Administratively pause an in-flight run.
      description: An operator-driven pause, distinct from cancel (terminal) and from a HITL suspend (workflow-driven). Emits
        a `run.paused` event when the pause takes effect. The run leaves `paused` only through `:resume` or `:cancel`.
      operationId: pauseRun
      security:
      - ApiKeyAuth:
        - runs:cancel
      - OAuth2:
        - runs:cancel
      - OpenIdConnect:
        - runs:cancel
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Free-form rationale, persisted on the `run.paused` event payload.
                drainPolicy:
                  type: string
                  enum:
                  - immediate
                  - drain-current-node
                  default: drain-current-node
                  description: '`immediate` snapshots between events; `drain-current-node` lets the running node reach a terminal
                    state before the run transitions to `paused`.'
              additionalProperties: false
      responses:
        '202':
          description: Pause requested; the transition emits `run.paused` when complete.
          content:
            application/json:
              schema:
                type: object
                required:
                - runId
                - status
                properties:
                  runId:
                    type: string
                  status:
                    type: string
                    enum:
                    - paused
                  pausedAt:
                    type: string
                    format: date-time
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Run is already paused, terminal, or in a state that cannot be paused.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}:resume:
    post:
      tags:
      - runs
      summary: Resume a paused run.
      description: 'Reverses a prior `:pause`: the run transitions from `paused` to `running` and emits `run.resumed`.'
      operationId: resumeRun
      security:
      - ApiKeyAuth:
        - runs:cancel
      - OAuth2:
        - runs:cancel
      - OpenIdConnect:
        - runs:cancel
      parameters:
      - $ref: '#/components/parameters/RunId'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
              additionalProperties: false
      responses:
        '202':
          description: Resume requested.
          content:
            application/json:
              schema:
                type: object
                required:
                - runId
                - status
                properties:
                  runId:
                    type: string
                  status:
                    type: string
                    enum:
                    - running
                  resumedAt:
                    type: string
                    format: date-time
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Run is not currently paused.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}/interrupts/{nodeId}:
    post:
      tags:
      - hitl
      summary: Resolve an interrupt via the run-scoped surface.
      operationId: resolveInterruptByRun
      security:
      - ApiKeyAuth:
        - approvals:respond
      - OAuth2:
        - approvals:respond
      - OpenIdConnect:
        - approvals:respond
      parameters:
      - $ref: '#/components/parameters/RunId'
      - in: path
        name: nodeId
        required: true
        schema:
          type: string
          minLength: 1
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - resumeValue
              properties:
                resumeValue:
                  description: Validated against the interrupt's `resumeSchema` if declared.
              additionalProperties: false
      responses:
        '200':
          description: Interrupt resolved; executor unblocks.
          content:
            application/json:
              schema:
                type: object
                properties:
                  runId:
                    type: string
                  nodeId:
                    type: string
                  status:
                    type: string
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Interrupt not found or already resolved.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '409':
          description: Concurrent resolve; only one wins.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '422':
          description: The run was cancelled while the interrupt was pending.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /interrupts/{token}:
    parameters:
    - in: path
      name: token
      required: true
      schema:
        type: string
      description: HMAC-signed token issued by the server at suspension time. Format `base64url(payload).hmac_sha256(secret,
        payload)`.
    get:
      tags:
      - hitl
      summary: Inspect an interrupt without resolving (signed-token surface).
      operationId: inspectInterruptByToken
      security: []
      responses:
        '200':
          description: Interrupt details.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/suspend-request.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '410':
          description: Token expired.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
    post:
      tags:
      - hitl
      summary: Resolve an interrupt via signed token (asynchronous callback).
      operationId: resolveInterruptByToken
      security: []
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - resumeValue
              properties:
                resumeValue: {}
              additionalProperties: false
      responses:
        '200':
          description: Resolution accepted.
          content:
            application/json:
              schema:
                type: object
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '410':
          description: Token expired.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /runs/{runId}/artifacts/{artifactId}:
    get:
      tags:
      - artifacts
      summary: Read a run-produced artifact.
      operationId: getArtifact
      security:
      - ApiKeyAuth:
        - artifacts:read
      - OAuth2:
        - artifacts:read
      - OpenIdConnect:
        - artifacts:read
      parameters:
      - $ref: '#/components/parameters/RunId'
      - in: path
        name: artifactId
        required: true
        schema:
          type: string
          minLength: 1
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Artifact payload. Under `application/json` the shape is implementation-defined. Under `application/a2a+json`
            (negotiated by `Accept`; a host SHOULD offer it) it is an A2A `Artifact` whose `artifactId` equals the path segment,
            and a `url` Part in it MUST NOT resolve beyond the caller's `artifacts:read` authorization.
          content:
            application/json:
              schema:
                type: object
                description: Implementation-defined artifact shape.
            application/a2a+json:
              schema:
                $ref: ../../schemas/v2/artifact.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /webhooks:
    post:
      tags:
      - webhooks
      summary: Register a webhook subscription.
      operationId: registerWebhook
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - url
              - events
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                  description: Event types to subscribe to (see `run-event.schema.json` enum).
                secret:
                  type: string
                  description: The host signs payloads with this secret using HMAC-SHA256. REQUIRED in the Standard Webhooks
                    form `whsec_<base64>` (24–64 decoded bytes) when `signatureAlgorithms` lists `standard-webhooks-1`. Never
                    echoed in any response. When omitted, the host generates one and returns it once in the `201` (RFC 0221).
                tags:
                  type: array
                  items:
                    type: string
                  description: Filter to runs carrying these tags (see `run-options.md`).
                signatureAlgorithms:
                  type: array
                  minItems: 1
                  uniqueItems: true
                  contains:
                    const: v1
                  items:
                    type: string
                    enum:
                    - v1
                    - standard-webhooks-1
                  description: 'The per-subscription signature opt-in. Absent means `["v1"]`. When present it MUST contain
                    `v1`, MUST NOT repeat a value, and every value MUST be listed in the host''s `webhooks.signatureAlgorithms`;
                    otherwise `400 validation_error`.


                    Listing `standard-webhooks-1` opts the subscription into the Standard Webhooks 1.0.0 companion scheme,
                    which runs endpoint verification before the `201` (`400 webhook_endpoint_unverified` when the endpoint
                    does not echo the challenge).'
              additionalProperties: false
      responses:
        '201':
          description: Webhook registered.
          content:
            application/json:
              schema:
                type: object
                required:
                - webhookId
                properties:
                  webhookId:
                    $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
                  signatureAlgorithms:
                    type: array
                    items:
                      type: string
                    description: The list the dispatcher will apply; present whenever the request carried `signatureAlgorithms`.
                  secret:
                    type: string
                    minLength: 1
                    description: 'Present only when the request omitted `secret`: the secret the host generated, returned
                      this once. A supplied secret is never echoed.'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: '`validation_error` (malformed body, or an invalid `signatureAlgorithms` or `secret` opt-in), `webhook_url_rejected`
            (egress), or `webhook_endpoint_unverified` (an opted-in endpoint did not echo the verification challenge; no subscription
            is persisted).'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
  /webhooks/{webhookId}:
    delete:
      tags:
      - webhooks
      summary: Unregister a webhook.
      operationId: unregisterWebhook
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
      parameters:
      - in: path
        name: webhookId
        required: true
        schema:
          $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
        description: 'Tenant-bound `<tenantId>/<opaque>` (`identity.md` §5), carried as one path segment: `~`-projected or
          percent-encoded.'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '204':
          description: Unregistered.
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /webhooks/{webhookId}/rotate-secret:
    post:
      tags:
      - webhooks
      operationId: rotateWebhookSecret
      summary: Rotate a Standard Webhooks subscription's secret with an overlap
      description: 'Rotates the secret of a subscription that opted into `standard-webhooks-1`. Gated on `webhooks.secretRotation`:
        a host that does not advertise it MUST answer `404 not_found`. Any other subscription gets `400 validation_error`,
        because its single `v1` signature cannot overlap. Tenant checks are those of `unregisterWebhook`: `403 id_tenant_mismatch`
        when the id''s tenant segment is not the caller''s, checked before the lookup (`identity.md` §5); `404` when unknown.


        For `overlapSeconds` after `rotatedAt`, `webhook-signature` carries one entry under the new secret and one under the
        previous one, and `OpenWOP-Signature` stays on the previous secret. At `previousSecretExpiresAt` only the new secret
        signs. A second rotation inside an overlap retires the oldest secret immediately. Rotation does not re-verify the
        endpoint. The response carries no secret.'
      parameters:
      - name: webhookId
        in: path
        required: true
        schema:
          $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
        description: 'Tenant-bound `<tenantId>/<opaque>` (`identity.md` §5), carried as one path segment: `~`-projected or
          percent-encoded.'
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - secret
              additionalProperties: false
              properties:
                secret:
                  type: string
                  pattern: ^whsec_[A-Za-z0-9+/]+={0,2}$
                  description: 'The new secret: `whsec_<base64>`, decoding to 24–64 bytes.'
      responses:
        '200':
          description: Rotated. No secret is returned.
          content:
            application/json:
              schema:
                type: object
                required:
                - rotatedAt
                - previousSecretExpiresAt
                additionalProperties: false
                properties:
                  rotatedAt:
                    type: string
                    format: date-time
                  previousSecretExpiresAt:
                    type: string
                    format: date-time
                    description: '`rotatedAt + overlapSeconds`.'
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
  /audit/verify:
    get:
      tags:
      - audit
      summary: Verify the audit-log hash chain over [fromSeq, toSeq].
      description: Re-walks the audit-log entries in the requested range, recomputes each entry's `prevHash` from the canonical
        RFC 8785 JCS serialization of the prior entry, verifies signed checkpoints against the host's advertised `auditLogIntegrity.checkpointPublicKey`,
        and returns `chainValid` with any anomalies. Hosts MUST require the `audit:read` scope. Served by a host that advertises
        the `auditLogIntegrity` family, which MUST list every checkpoint whose `atSequence` is in the range, ascending; a
        host that does not advertise it MAY omit this endpoint entirely, and clients SHOULD check `/.well-known/openwop` first.
        See `spec/v2/core/security-defaults.md` §Audit-log integrity.
      operationId: verifyAuditLog
      security:
      - ApiKeyAuth:
        - audit:read
      - OAuth2:
        - audit:read
      - OpenIdConnect:
        - audit:read
      parameters:
      - in: query
        name: fromSeq
        required: true
        schema:
          type: integer
          minimum: 0
        description: First audit-log sequence to include (inclusive).
      - in: query
        name: toSeq
        required: true
        schema:
          type: integer
          minimum: 0
        description: Last audit-log sequence to include (inclusive). MUST be >= fromSeq.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Verification result.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/audit-verify-result.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Host does not advertise the audit-log-integrity profile.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
  /prompts:
    get:
      tags:
      - prompts
      summary: List prompt templates available to the caller.
      operationId: listPromptTemplates
      security:
      - ApiKeyAuth:
        - prompts:read
      - OAuth2:
        - prompts:read
      - OpenIdConnect:
        - prompts:read
      parameters:
      - in: query
        name: kind
        schema:
          type: string
          enum:
          - system
          - user
          - few-shot
          - schema-hint
        description: Filter by `PromptTemplate.kind`.
      - in: query
        name: tag
        schema:
          type: string
        description: Filter to templates whose `tags[]` contains this exact tag. Hosts MAY accept the parameter more than
          once; repeated values combine with AND (every named tag must be present).
      - in: query
        name: modelClass
        schema:
          type: string
        description: Filter to templates whose `modelHints.modelClass` matches.
      - in: query
        name: source
        schema:
          type: string
          enum:
          - host
          - pack
          - user
        description: Filter by `meta.source` provenance.
      - in: query
        name: cursor
        schema:
          type: string
        description: Opaque pagination cursor.
      - in: query
        name: limit
        schema:
          type: integer
          minimum: 1
          maximum: 200
          default: 50
        description: Maximum entries per page.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: Paginated list of templates.
          content:
            application/json:
              schema:
                type: object
                required:
                - items
                properties:
                  items:
                    type: array
                    items:
                      $ref: ../../schemas/v2/prompt-template.schema.json
                  nextCursor:
                    type: string
                    description: Opaque cursor; absent on the final page.
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - prompts
      summary: Create a new prompt template (mutable libraries only).
      operationId: createPromptTemplate
      security:
      - ApiKeyAuth:
        - prompts:write
      - OAuth2:
        - prompts:write
      - OpenIdConnect:
        - prompts:write
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/prompt-template.schema.json
      responses:
        '201':
          description: Template created. `Location` header carries the canonical URI.
          headers:
            Location:
              schema:
                type: string
              description: Canonical URI of the new template.
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: A template with this `(templateId, version)` pair already exists.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          $ref: '#/components/responses/NotFound'
  /prompts/{templateId}:
    parameters:
    - in: path
      name: templateId
      required: true
      schema:
        type: string
        pattern: ^[a-z0-9][a-z0-9._-]{0,127}$
      description: '`PromptTemplate.templateId`.'
    - in: query
      name: version
      schema:
        type: string
        pattern: ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$
      description: Pin to a specific SemVer version; latest when omitted.
    - in: query
      name: libraryId
      schema:
        type: string
        pattern: ^[a-z0-9][a-z0-9._-]{0,127}$
      description: 'Disambiguates when several installed packs ship the same templateId. Hosts MUST answer `validation_error`
        with `details.field: libraryId` when the reference is ambiguous and libraryId is omitted.'
    get:
      tags:
      - prompts
      summary: Fetch a single prompt template.
      operationId: getPromptTemplate
      security:
      - ApiKeyAuth:
        - prompts:read
      - OAuth2:
        - prompts:read
      - OpenIdConnect:
        - prompts:read
      responses:
        '200':
          description: The PromptTemplate.
          headers:
            ETag:
              schema:
                type: string
              description: SHA-256 of the canonical body.
            Cache-Control:
              schema:
                type: string
              description: Honors immutable semantics when version was pinned.
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/prompt-template.schema.json
        '304':
          description: Conditional revalidation succeeded.
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: '`validation_error` with `details.field: libraryId` when `libraryId` disambiguation is required.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such template (or version).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
    put:
      tags:
      - prompts
      summary: Replace a prompt template (mutable libraries; user-source only).
      operationId: updatePromptTemplate
      security:
      - ApiKeyAuth:
        - prompts:write
      - OAuth2:
        - prompts:write
      - OpenIdConnect:
        - prompts:write
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/prompt-template.schema.json
      responses:
        '200':
          description: Template updated.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/prompt-template.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          description: Template is pack-sourced or host-built-in (read-only).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          description: No such template.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '409':
          description: Submitted version does not exceed stored version (SemVer).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
    delete:
      tags:
      - prompts
      summary: Delete a prompt template (mutable libraries; user-source only).
      operationId: deletePromptTemplate
      security:
      - ApiKeyAuth:
        - prompts:write
      - OAuth2:
        - prompts:write
      - OpenIdConnect:
        - prompts:write
      responses:
        '204':
          description: Template deleted.
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          description: Template is pack-sourced or host-built-in (read-only).
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          description: No such template.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /prompts:render:
    post:
      tags:
      - prompts
      summary: Render a prompt template with supplied variable bindings.
      description: Returns the composed body, its sha256 hash, and per-variable hashes. Does not dispatch an LLM call. The
        response's `hash` MUST equal the `hash` a matching `prompt.composed` event would carry at dispatch time for the same
        `(ref, variables, contentTrust)` inputs, so rendering is deterministic and replay-safe. Secret-source variable values
        MUST be supplied as `[REDACTED:<credentialRef>]` markers; the host resolves the plaintext internally and never echoes
        it in the `composed` response field (SR-1).
      operationId: renderPromptTemplate
      security:
      - ApiKeyAuth:
        - prompts:read
      - OAuth2:
        - prompts:read
      - OpenIdConnect:
        - prompts:read
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - ref
              - variables
              properties:
                ref:
                  $ref: ../../schemas/v2/prompt-ref.schema.json
                variables:
                  type: object
                  description: Variable bindings keyed by `PromptVariable.name`. Secret-source bindings carry `[REDACTED:<credentialRef>]`
                    markers; the host resolves the real value internally.
                  additionalProperties: true
                contentTrust:
                  type: string
                  enum:
                  - trusted
                  - untrusted
                  description: Aggregate trust marker for the supplied bindings, propagated through composition.
      responses:
        '200':
          description: Composed result.
          content:
            application/json:
              schema:
                type: object
                required:
                - hash
                - refs
                - variableHashes
                properties:
                  composed:
                    type: string
                    description: Full composed body. Present only when observability is `full`.
                  hash:
                    type: string
                    pattern: ^sha256:[0-9a-f]{64}$
                  refs:
                    type: array
                    items:
                      type: string
                  variableHashes:
                    type: object
                    additionalProperties:
                      type: string
                      pattern: ^sha256:[0-9a-f]{64}$
                  contentTrust:
                    type: string
                    enum:
                    - trusted
                    - untrusted
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          description: '`validation_error`: a required variable is missing, a bound type differs from its declared type, or
            the PromptRef is malformed.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Referenced template does not exist.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
  /host/effect-seams:
    get:
      tags:
      - host
      operationId: getEffectSeamManifest
      summary: Read the host's effect-seam manifest
      description: Lists every outbound effect seam that replay suppression covers. A seam omitted here is invisible to the
        conformance suite; an audit of the host's seams is the control.
      responses:
        '200':
          description: The manifest.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/effect-seam-manifest.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
  /runs/{runId}/compensation:
    parameters:
    - $ref: '#/components/parameters/RunId'
    get:
      tags:
      - runs
      operationId: getRunCompensation
      summary: Read a run's compensation plan and attempts
      description: The run's compensation plan and every attempt, as a read projection.
      responses:
        '200':
          description: The projection.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/compensation-projection.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          $ref: '#/components/responses/NotFound'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
  /runs/{runId}/effects:
    parameters:
    - $ref: '#/components/parameters/RunId'
    get:
      tags:
      - runs
      operationId: getRunEffects
      summary: Read a run's Layer-2 effect ledger
      description: Effect records keyed by business identity; the witness for the Layer-2 effect-identity obligation.
      responses:
        '200':
          description: The ledger projection.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/effect-ledger-projection.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '404':
          $ref: '#/components/responses/NotFound'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
  /webhooks/{webhookId}/dead-letters:
    get:
      tags:
      - webhooks
      operationId: listWebhookDeadLetters
      summary: List a subscription's dead-lettered deliveries
      description: 'Lists deliveries that were dead-lettered, making `webhooks.md` §Durability observable. A delivery whose
        retries are exhausted, or a `payload_unprojectable` delivery dead-lettered on its first attempt, appears here until
        `retentionDays` elapse. Gated on the `webhooks.deadLetter` facet: a host that does not advertise it answers `404 not_found`.


        The record is content-free: it names a delivery and never carries the delivered bytes, the request headers, or the
        subscription secret.'
      parameters:
      - name: webhookId
        in: path
        required: true
        schema:
          $ref: ../../schemas/v2/ids.schema.json#/$defs/subscriptionId
        description: 'The subscription, tenant-bound (`identity.md` §5) and carried as one path segment: `~`-projected or
          percent-encoded.'
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
        description: Page size; clamped to `webhooks.deadLetter.maxPageSize`.
      - name: cursor
        in: query
        required: false
        schema:
          type: string
          minLength: 1
          maxLength: 2048
        description: Opaque cursor from a previous page's `nextCursor`. A cursor minted for another subscription MUST be refused
          `400 validation_error`.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: One page of dead-lettered deliveries, newest first.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/webhook-dead-letter-page.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
  /host/ui-plugins/{packName}/{pluginId}/frame:
    get:
      tags:
      - host
      operationId: getUiPluginFrame
      summary: The frame document a front-end plugin runs in
      description: 'The HTML document the host mounts the plugin in, and the only way it mounts it. With `uiPlugins.isolation`
        `cross-origin-iframe`, its `Content-Security-Policy` MUST carry a `sandbox` directive admitting `allow-scripts` and
        never `allow-same-origin`, and MUST set `default-src ''none''` with no `connect-src` source beyond the plugin''s declared
        `connectSrc` (`packs.md` §Front-end plugin packs). Gated on `uiPlugins.served`: a host that does not advertise it,
        or a plugin that is not installed, answers `404 not_found`.'
      parameters:
      - name: packName
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 214
        description: The installed frontend-plugin pack.
      - name: pluginId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 128
        description: The plugin's `uiPlugins[].id` in that pack.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The frame document.
          headers:
            Content-Security-Policy:
              schema:
                type: string
              description: The plugin frame's isolation and egress policy.
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
          content:
            text/html:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
      - ApiKeyAuth:
        - manifest:read
      - OAuth2:
        - manifest:read
      - OpenIdConnect:
        - manifest:read
  /host/ui-plugins/{packName}/{pluginId}/rpc:
    post:
      tags:
      - host
      operationId: dispatchUiPluginRequest
      summary: Dispatch one ui-plugin/1 request as the named plugin
      description: 'Runs one `ui-plugin/1` request through the dispatcher the plugin boundary uses, as the caller and as the
        named plugin. A method outside the plugin''s declared `hostApi` or the advertised `uiPlugins.hostApi` MUST return
        `ok: false` with `method_not_allowed` and MUST NOT execute. A refused call is `ok: false` inside a `200`, never an
        HTTP error. A response MUST NOT carry credential material. Gated on `uiPlugins.served`.'
      parameters:
      - name: packName
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 214
        description: The installed frontend-plugin pack.
      - name: pluginId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 128
        description: The plugin's `uiPlugins[].id` in that pack.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
              - message
              properties:
                message:
                  $ref: ../../schemas/v2/ui-plugin-message.schema.json#/$defs/request
      responses:
        '200':
          description: The `ui-plugin/1` response.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/ui-plugin-message.schema.json#/$defs/response
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
      - ApiKeyAuth:
        - artifacts:read
      - OAuth2:
        - artifacts:read
      - OpenIdConnect:
        - artifacts:read
  /connection-providers:
    get:
      tags:
      - connections
      operationId: listConnectionProviders
      summary: List the host's connection providers and refused pack registrations
      description: 'Every provider definition the host resolves against, built-ins included, each bare id once, and the pack
        registrations it refused under `connection-packs.md` §Provider identity. Host-global: no tenant data. Gated on `connections.providerRead`:
        a host that does not advertise it answers `404 not_found`.'
      responses:
        '200':
          description: The registry.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/connection-provider-registry.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      parameters:
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      security:
      - ApiKeyAuth:
        - manifest:read
      - OAuth2:
        - manifest:read
      - OpenIdConnect:
        - manifest:read
  /connection-providers/{providerId}:
    get:
      tags:
      - connections
      operationId: resolveConnectionProvider
      summary: Resolve one connection provider reference
      description: 'Resolves a bare provider id, or with `pack` the qualified form `<packName>#<providerId>`, to the one definition
        it names. A bare id resolves only when exactly one definition exists; with `pack` it resolves only to that pack''s
        definition. Not resolvable: `404` `connection_provider_unresolved`. Gated on `connections.providerRead`.'
      parameters:
      - name: providerId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 128
        description: The bare provider id.
      - name: pack
        in: query
        required: false
        schema:
          type: string
          minLength: 1
          maxLength: 214
        description: 'A pack name: the qualified form, carried as a query parameter so no `#` travels in the path.'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '200':
          description: The resolved definition.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/connection-provider-registry.schema.json#/$defs/provider
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
      - ApiKeyAuth:
        - manifest:read
      - OAuth2:
        - manifest:read
      - OpenIdConnect:
        - manifest:read
  /host/events:
    get:
      tags:
      - host
      operationId: streamHostEvents
      summary: Stream host-scoped events (`heartbeat.*` and host events) as SSE
      description: The default `hostEvents` address; a host MAY declare another under `heartbeat.deliveryChannel`. Every message
        is delivered only within the caller's tenant (RFC 0236 §D). Carries no run data.
      responses:
        '200':
          description: '`text/event-stream` of `hostEvents` messages.'
          content:
            text/event-stream:
              schema:
                type: string
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '409':
          description: '`host_event_resync_required`: the `replay=required` cursor cannot be resumed (RFC 0250).'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      parameters:
      - in: query
        name: resumable
        schema:
          type: boolean
          default: false
        description: RFC 0247. `true` says this consumer resumes with `Last-Event-ID` when the stream closes without the terminal
          event, so a host advertising `limits.maxStreamAgeSec` MAY end it early (a bounded close, never terminal). Absent
          or `false`, the host MUST hold a live stream open (events.md §Resuming with `Last-Event-ID`). A consumer that sends
          it MUST resume with `resumable=true` and `Last-Event-ID`.
      - name: replay
        in: query
        required: false
        schema:
          type: string
          enum:
          - required
        description: RFC 0250. Send only when `hostEvents.replay` is advertised, with `Last-Event-ID` naming a durable event.
          The host replays every visible durable event after it, in commit order, then continues live; a cursor it cannot
          resume is refused `409 host_event_resync_required` before any SSE byte, identically in every case.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      security:
      - ApiKeyAuth:
        - runs:read
      - OAuth2:
        - runs:read
      - OpenIdConnect:
        - runs:read
  /host/events/test:
    post:
      tags:
      - host
      operationId: emitTestHostEvent
      summary: Emit one test host event to the caller's tenant
      description: 'Bound on a host listing `host-test.durable-triggered` or `host-test.ephemeral-triggered` in `hostEvents.types[]`,
        served for each listed class and otherwise answered `404`. Emits one event of the requested class''s reserved type,
        with an empty payload, through the ordinary host-event path: envelope, `/host/events`, `{ hostEvent }` webhooks, delivery
        class and tenant gate. It belongs to the caller''s tenant and to `workspaceId` when given, and MUST NOT reach another.
        A `delivery` whose type is not listed is `400 validation_error`; another tenant''s `workspaceId` is `403 id_tenant_mismatch`,
        an unknown one `404`. A host SHOULD rate-limit it more tightly than reads.'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
              - delivery
              properties:
                delivery:
                  type: string
                  enum:
                  - durable
                  - ephemeral
                workspaceId:
                  $ref: ../../schemas/v2/ids.schema.json#/$defs/workspaceId
      responses:
        '202':
          description: Emitted; `eventId` is the envelope's.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                - eventId
                - type
                properties:
                  eventId:
                    $ref: ../../schemas/v2/ids.schema.json#/$defs/eventId
                  type:
                    type: string
                    enum:
                    - host-test.durable-triggered
                    - host-test.ephemeral-triggered
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
      - ApiKeyAuth:
        - webhooks:manage
      - OAuth2:
        - webhooks:manage
      - OpenIdConnect:
        - webhooks:manage
  /proposals:
    get:
      tags:
      - proposals
      operationId: listProposals
      summary: List the caller's proposals
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Scoped to the caller''s
        tenant and workspace.'
      parameters:
      - name: state
        in: query
        required: false
        schema:
          type: string
          enum:
          - draft
          - revised
          - applied
          - rejected
          - archived
      - name: kind
        in: query
        required: false
        schema:
          type: string
          enum:
          - agent-pack
          - workflow-chain-pack
          - prompt-template
          - automation
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
      - name: cursor
        in: query
        required: false
        schema:
          type: string
          minLength: 1
          maxLength: 2048
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: One page of proposals.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal-list-response.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
      security:
      - ApiKeyAuth:
        - proposals:read
      - OAuth2:
        - proposals:read
      - OpenIdConnect:
        - proposals:read
    post:
      tags:
      - proposals
      operationId: submitProposal
      summary: Submit a client-authored draft
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Served only when `agents.proposals.clientDrafts`
        is `true`; otherwise `404 not_found`. The draft is held to every RFC 0096 rule, inertness first, with `provenance.sourceRunIds:
        []`. A `kind` outside `artifactKinds` is `400 validation_error` with `details.field: kind`.'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/proposal-submit.schema.json
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '201':
          description: 'The draft, in `state: draft`.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
      security:
      - ApiKeyAuth:
        - proposals:write
      - OAuth2:
        - proposals:write
      - OpenIdConnect:
        - proposals:write
  /proposals/{proposalId}:
    get:
      tags:
      - proposals
      operationId: getProposal
      summary: Read one proposal, including its inert artifact
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Carries `artifactDigest`,
        the value `applyProposal` binds to.'
      parameters:
      - name: proposalId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 256
        description: The proposal. A proposal another tenant owns MUST be answered exactly as an unknown id (`404`).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: The proposal.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      security:
      - ApiKeyAuth:
        - proposals:read
      - OAuth2:
        - proposals:read
      - OpenIdConnect:
        - proposals:read
    patch:
      tags:
      - proposals
      operationId: reviseProposal
      summary: Revise a draft
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Sets `state: revised`
        and MUST NOT activate. A terminal proposal (`applied`, `rejected`, `archived`) is refused `409 proposal_state_conflict`;
        a lost concurrent update `409 proposal_stale`.'
      parameters:
      - name: proposalId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 256
        description: The proposal. A proposal another tenant owns MUST be answered exactly as an unknown id (`404`).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/proposal-revise.schema.json
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: The revised proposal.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '409':
          description: '`proposal_state_conflict` (terminal) or `proposal_stale` (lost update).'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      security:
      - ApiKeyAuth:
        - proposals:write
      - OAuth2:
        - proposals:write
      - OpenIdConnect:
        - proposals:write
    delete:
      tags:
      - proposals
      operationId: archiveProposal
      summary: Archive a proposal (soft)
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Sets `state: archived`,
        from `draft`, `revised` or `rejected`. On `applied` or `archived` it is refused `409 proposal_state_conflict`: archiving
        uninstalls nothing, so an applied proposal is never relabelled; remove an installed artifact through its own store.'
      parameters:
      - name: proposalId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 256
        description: The proposal. A proposal another tenant owns MUST be answered exactly as an unknown id (`404`).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: The archived proposal.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '409':
          description: '`proposal_state_conflict`: applied or already archived.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      security:
      - ApiKeyAuth:
        - proposals:write
      - OAuth2:
        - proposals:write
      - OpenIdConnect:
        - proposals:write
  /proposals/{proposalId}:apply:
    post:
      tags:
      - proposals
      operationId: applyProposal
      summary: Activate a proposal and install its artifact
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Routes through `agents.proposals.activation`.
        Under `direct-rbac` the caller MUST hold `proposals:apply`; without it the host answers `403` with `error="insufficient_scope"`,
        installs nothing and leaves `state` unchanged. Under `approval-gate` the host answers `202 {runId}` for a gate run
        that suspends on an `approval` interrupt (`data.artifactType: "proposal"`), and installs only on `accept` (`interrupt.md`
        §Approval).


        The optional body binds the apply to the reviewed bytes: a sent `artifactDigest` that differs from the proposal''s
        MUST be refused `409 proposal_stale`, installing nothing; a host MAY require it (`400`, `details.field: artifactDigest`).
        On success `state` is `applied` and `activation.installedArtifact` names the v2 read that serves the installed artifact
        in the applying tenant; its body minus the kind''s host-owned fields MUST be RFC 8785 (JCS) equal to the last-persisted
        `artifact`. A store that refuses the install is `409 proposal_install_conflict` (`details.reason`); a terminal proposal
        `409 proposal_state_conflict`.'
      parameters:
      - name: proposalId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 256
        description: The proposal. A proposal another tenant owns MUST be answered exactly as an unknown id (`404`).
      - $ref: '#/components/parameters/IdempotencyKey'
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: ../../schemas/v2/proposal-apply.schema.json
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: Applied; `activation.installedArtifact` is populated.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '202':
          description: An approval-gate run was opened.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                required:
                - runId
                properties:
                  runId:
                    $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '400':
          $ref: '#/components/responses/ValidationError'
        '409':
          description: '`proposal_stale`, `proposal_state_conflict` or `proposal_install_conflict`.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      security:
      - ApiKeyAuth:
        - proposals:apply
      - OAuth2:
        - proposals:apply
      - OpenIdConnect:
        - proposals:apply
  /proposals/{proposalId}:reject:
    post:
      tags:
      - proposals
      operationId: rejectProposal
      summary: Reject a proposal
      description: 'Gated on `agents.proposals`: a host that does not advertise it answers `404 not_found` (`errors.md` §Unadvertised
        operations). A proposal another tenant owns is answered exactly as an id that does not exist. Sets `state: rejected`.
        A terminal proposal is refused `409 proposal_state_conflict`.'
      parameters:
      - name: proposalId
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 256
        description: The proposal. A proposal another tenant owns MUST be answered exactly as an unknown id (`404`).
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                reason:
                  type: string
                  maxLength: 2048
                  description: SR-1 redaction-safe.
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: The rejected proposal.
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/proposal.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
        '409':
          description: '`proposal_state_conflict`.'
          content:
            application/json:
              schema:
                $ref: ../../schemas/v2/error-envelope.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      security:
      - ApiKeyAuth:
        - proposals:write
      - OAuth2:
        - proposals:write
      - OpenIdConnect:
        - proposals:write
  /installed-packs/{packName}:
    get:
      tags:
      - proposals
      operationId: getInstalledPack
      summary: Read a pack installed in the caller's tenant
      description: 'Advertised when `agents.proposals.readback` contains `workflow-chain-pack` or `agent-pack`; a host that
        does not advertise it answers `404 not_found` (`errors.md` §Unadvertised operations). Evaluated in the caller''s tenant:
        a pack installed in another tenant MUST be answered exactly as a name that is not installed (`404`). A host MUST serve
        every pack `applyProposal` installed in the tenant, at the version currently installed; it MAY also serve packs it
        installed from a registry or ships itself.


        The body is the bare installed `pack.json`, validating against `workflow-chain-pack-manifest` when `kind` is `"workflow-chain"`,
        else `node-pack-manifest`. It carries no install metadata, and a host MUST NOT add a vendor-hatch property the installed
        artifact did not carry. For a proposal-installed pack it MUST be RFC 8785 (JCS) equal to the applied proposal''s `artifact`,
        with nothing removed from either side.'
      parameters:
      - name: packName
        in: path
        required: true
        schema:
          type: string
          minLength: 1
          maxLength: 256
          pattern: ^(core|vendor|community|private)\.[a-z][a-z0-9_-]*(\.[a-z][a-zA-Z0-9_-]*)+$
        description: The manifest `name`.
      - $ref: '#/components/parameters/OpenWOPVersion'
      - $ref: '#/components/parameters/OpenWOPClientVersion'
      responses:
        '401':
          $ref: '#/components/responses/Unauthenticated'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '200':
          description: The installed pack.json.
          content:
            application/json:
              schema:
                oneOf:
                - $ref: ../../schemas/v2/workflow-chain-pack-manifest.schema.json
                - $ref: ../../schemas/v2/node-pack-manifest.schema.json
          headers:
            OpenWOP-Version:
              $ref: '#/components/headers/OpenWOPVersion'
      security:
      - ApiKeyAuth:
        - manifest:read
      - OAuth2:
        - manifest:read
      - OpenIdConnect:
        - manifest:read
components:
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: OpenWOP API key. The format is implementation-defined; the reference implementation uses `hk_`/`hk_test_`
        prefixes. Each key carries one or more scopes from the canonical vocabulary (`manifest:read`, `runs:create`, `runs:read`,
        `runs:cancel`, `artifacts:read`, `webhooks:manage`, `approvals:respond`, `audit:read`). Scope matching is in `spec/v2/core/identity.md`
        §2.1.
    OAuth2:
      type: oauth2
      description: 'The OAuth 2.0 lane (`auth.lanes[].lane: "oauth2"`). Discovery is RFC 9728 Protected Resource Metadata
        at the path-inserted well-known URI (`identity.md` §2.5). The URLs are placeholders.'
      flows:
        clientCredentials:
          tokenUrl: https://issuer.example/oauth2/token
          scopes:
            manifest:read: Read workflow / canvas-type / endpoint manifests
            runs:create: Create new runs
            runs:read: Read run state and event stream
            runs:cancel: Cancel an in-flight run
            runs:annotate: Record a quality annotation on a run
            artifacts:read: Read artifacts produced by runs
            webhooks:manage: Register/unregister webhook subscriptions
            approvals:respond: Respond to HITL approval gates
            packs:publish: Publish node-pack versions
            packs:read: Fetch a published node-pack tarball or signature
            packs:yank: Yank a node-pack version
            packs:yank-revert: Reinstate a yanked node-pack version
            audit:read: Verify audit-log integrity
            workspace:read: Read agent-workspace files
            workspace:write: Write agent-workspace files
            prompts:read: Read and render prompt templates
            prompts:write: Create, update and delete prompt templates
            content:read: Read authored content pages and locale settings
            content:write: Create, publish and translate authored content pages
            deploy:*: Drive an agent deployment state transition
            proposals:read: Read proposals (RFC 0249).
            proposals:write: Submit, revise, reject and archive proposals (RFC 0249).
            proposals:apply: Activate a proposal and install its artifact (RFC 0249).
        authorizationCode:
          authorizationUrl: https://issuer.example/oauth2/authorize
          tokenUrl: https://issuer.example/oauth2/token
          scopes:
            manifest:read: Read workflow / canvas-type / endpoint manifests
            runs:create: Create new runs
            runs:read: Read run state and event stream
            runs:cancel: Cancel an in-flight run
            runs:annotate: Record a quality annotation on a run
            artifacts:read: Read artifacts produced by runs
            webhooks:manage: Register/unregister webhook subscriptions
            approvals:respond: Respond to HITL approval gates
            packs:publish: Publish node-pack versions
            packs:read: Fetch a published node-pack tarball or signature
            packs:yank: Yank a node-pack version
            packs:yank-revert: Reinstate a yanked node-pack version
            audit:read: Verify audit-log integrity
            workspace:read: Read agent-workspace files
            workspace:write: Write agent-workspace files
            prompts:read: Read and render prompt templates
            prompts:write: Create, update and delete prompt templates
            content:read: Read authored content pages and locale settings
            content:write: Create, publish and translate authored content pages
            deploy:*: Drive an agent deployment state transition
            proposals:read: Read proposals (RFC 0249).
            proposals:write: Submit, revise, reject and archive proposals (RFC 0249).
            proposals:apply: Activate a proposal and install its artifact (RFC 0249).
    OpenIdConnect:
      type: openIdConnect
      openIdConnectUrl: https://issuer.example/.well-known/openid-configuration
      description: 'The OpenID Connect lane (`auth.lanes[].lane: "oidc"`). An ID token MAY be the bearer only when its `aud`
        equals the host''s configured audience (`identity.md` §2.1); any other `aud` is `audience_mismatch`. The URL is a
        placeholder.'
    MutualTLS:
      type: mutualTLS
      description: 'The mTLS lane (`auth.lanes[].lane: "mtls"`). mTLS is required *in addition to* a bearer, never instead
        of one, so it appears in no canonical requirement below. A host that requires it adds `MutualTLS: []` inside each
        requirement object (AND).'
  parameters:
    WorkflowId:
      in: path
      name: workflowId
      required: true
      schema:
        $ref: ../../schemas/v2/ids.schema.json#/$defs/workflowId
    RunId:
      in: path
      name: runId
      required: true
      schema:
        $ref: ../../schemas/v2/ids.schema.json#/$defs/runId
      description: Tenant-bound `<tenantId>/<opaque>` (`identity.md` §5). The `/` is part of the identifier and MUST be percent-encoded
        as `%2F` in the path segment.
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      required: false
      schema:
        type: string
        maxLength: 255
      description: 'Per-mutation idempotency token (`idempotency.md` Layer 1). The server caches `(tenantId, endpoint, key)`
        → response for ≥24h. A duplicate request returns the cached response with `OpenWOP-Idempotent-Replay: true`.'
    OpenWOPVersion:
      name: OpenWOP-Version
      in: header
      required: false
      schema:
        type: string
        pattern: ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$
      description: Selects one of the host's listed major.minor versions. Absent, `/.well-known/openwop` uses `preferredVersion`
        and every other unversioned path is v2; an unlisted value is 406 protocol_version_unsupported.
    OpenWOPClientVersion:
      name: OpenWOP-Client-Version
      in: header
      required: false
      schema:
        type: string
      description: The protocol version the client implements (versioning.md §1.5). Compared with `minClientVersion` on major.minor.
        A malformed value is treated as absent and MUST NOT produce a 400. Never selects a contract.
  responses:
    Unauthenticated:
      description: Missing or invalid credential.
      headers:
        WWW-Authenticate:
          description: A `Bearer` challenge. On a host with an oauth2 or oidc lane it carries `resource_metadata` and `error="invalid_token"`.
            Never sent on a non-disclosure 404.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Credential valid but lacks required scope or fails resource binding.
      headers:
        WWW-Authenticate:
          description: On a scope refusal, `error="insufficient_scope"` with every required `scope`. Absent on a resource-binding
            403.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource doesn't exist or caller can't see it (do not leak existence).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: Request body or parameters malformed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Too many requests.
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      $ref: ../../schemas/v2/error-envelope.schema.json
    Capabilities:
      $ref: ../../schemas/v2/capabilities.schema.json
    RunSnapshot:
      $ref: ../../schemas/v2/run-snapshot.schema.json
    RunClaimConflict:
      $ref: ../../schemas/v2/error-envelope.schema.json
      description: '`RunClaimConflict` is the canonical error envelope; `error` carries the code.'
    UnsupportedStreamMode:
      $ref: ../../schemas/v2/error-envelope.schema.json
      description: '`UnsupportedStreamMode` is the canonical error envelope; `error` carries the code.'
  headers:
    OpenWOPVersion:
      schema:
        type: string
      description: The contract that produced this response. It MUST equal the one used.
      required: true
