# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/playbooks.yaml (runtime 0.93.0)
#  + policy: openapi/mirror-policies/playbooks.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — Playbooks API
  version: 1.0.0
  description: >-
    Playbooks: versioned templates of nodes whose inputs and outputs a case fills per subject,
    recomputing only what a changed value reaches, with confirmations, exports and explicit
    upgrades. A fully runtime-backed module — this spec is generated verbatim from the runtime's
    own, re-rooted under /v1/projects/{project_id}. The project in the path is authorized by
    naturali and enforced upstream by the project's scoped credential.


    This module mirrors the upstream runtime verbatim (tier A, #304): paths are the runtime's own
    re-rooted under /v1/projects/{project_id}, and every field, method, status code and error shape
    passes through unchanged. Errors raised by the runtime arrive in its envelope; errors raised by
    naturali itself (authentication, project resolution, an unreachable runtime) use naturali's.
  contact:
    name: naturali.ai
    url: https://naturali.ai
servers:
  - url: "{baseUrl}"
    description: Host of your naturali.ai deployment; every path carries the /v1 prefix.
    variables:
      baseUrl:
        description: Base host URL.
        default: https://api.naturali.ai
tags:
  - name: Playbooks
    description: Manage playbooks and the cases filled against them
security:
  - bearerAuth: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/playbooks:
    get:
      tags:
        - Playbooks
      summary: List playbooks
      description: Returns the playbooks defined in a project, newest first.
      operationId: listPlaybooks
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of playbooks
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Playbook"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
    post:
      tags:
        - Playbooks
      summary: Create a playbook
      description: >-
        Creates a playbook at version 1. The definition is refused with `400
        PLAYBOOK_VALIDATION_FAILED` when it holds a duplicate node or input, an input reading a
        missing node or field, an `execute` node without an executor or another kind with one,
        transformations on a non-final node, or a cycle; `meta.problems` lists every one. A
        duplicate `name` in the project is `409 NAME_CONFLICT`.


        The caller becomes the version's author, whom its executors run as, so it must be allowed to
        start every executor the definition names (`403 PLAYBOOK_EXECUTOR_FORBIDDEN`). A definition
        with executors written with a trigger or OAuth token is `409 PLAYBOOK_AUTHOR_UNAVAILABLE`:
        those carry their boundary in the token, so no run can act as them.
      operationId: createPlaybook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlaybookRequest"
      responses:
        "201":
          description: Playbook created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Playbook"
        "400":
          description: Bad request — a malformed or invalid definition
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "409":
          description: A playbook with this name already exists in the project
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbooks/validate:
    post:
      tags:
        - Playbooks
      summary: Validate a playbook definition
      description: >-
        Answers what a create or update would be told about a definition, without writing: `valid`,
        and every graph problem it holds. A definition whose shape cannot be read is `400
        VALIDATION_FAILED`, as on a write.


        With `project_id`, the upstreams are also read against that project's playbooks, as a write
        there would read them, and the caller needs both `playbooks:ValidatePlaybook` and
        `playbooks:ListPlaybooks` on the project. Without it, only the definition's own graph is
        checked.
      operationId: validatePlaybook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ValidatePlaybookRequest"
      responses:
        "200":
          description: Validation result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookValidationResult"
        "400":
          description: Bad request — the definition's shape cannot be read, or `playbook_id` is given without
            `project_id`
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: "`playbook_id` names no playbook of the project"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbooks/{playbook_id}:
    get:
      tags:
        - Playbooks
      summary: Get a playbook
      description: Returns a playbook with its current definition and version.
      operationId: getPlaybook
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
      responses:
        "200":
          description: The playbook
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Playbook"
        "401":
          description: Unauthorized
        "404":
          description: Playbook not found
    patch:
      tags:
        - Playbooks
      summary: Update a playbook
      description: >-
        Updates any of `name`, `description` and `definition`. Only a change to `definition`
        archives a new version; a rename or a rewrite of the definition the playbook already holds
        leaves `version` where it is. A case keeps the version it was created on.


        A new version is authored by the caller, checked as on create: `403
        PLAYBOOK_EXECUTOR_FORBIDDEN` for an executor it may not start, `409
        PLAYBOOK_AUTHOR_UNAVAILABLE` for a trigger or OAuth token.
      operationId: updatePlaybook
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
        - $ref: "#/components/parameters/IfMatchVersion"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePlaybookRequest"
      responses:
        "200":
          description: Playbook updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Playbook"
        "400":
          description: Bad request — a malformed or invalid definition
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Playbook not found
        "409":
          $ref: "#/components/responses/VersionConflict"
    delete:
      tags:
        - Playbooks
      summary: Delete a playbook
      description: Deletes a playbook and its version archive. A playbook with cases is refused with `409
        PLAYBOOK_HAS_CASES`, and one another playbook's current definition declares as an upstream
        with `409 PLAYBOOK_HAS_DEPENDENTS`.
      operationId: deletePlaybook
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
      responses:
        "204":
          description: Playbook deleted
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Playbook not found
        "409":
          description: The playbook has cases, or another playbook declares it as an upstream
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbooks/{playbook_id}/versions:
    get:
      tags:
        - Playbooks
      summary: List a playbook's versions
      description: Returns the playbook's archived definitions, newest first. A version is written on
        create and on every write that changes `definition`.
      operationId: listPlaybookVersions
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of playbook versions, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookVersion"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "404":
          description: Playbook not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbooks/{playbook_id}/versions/{version}:
    get:
      tags:
        - Playbooks
      summary: Fetch an archived playbook version
      description: "Returns the definition a version held: what a case pinned to it is evaluated against."
      operationId: getPlaybookVersion
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
        - $ref: "#/components/parameters/Version"
      responses:
        "200":
          description: Archived playbook version
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookVersion"
        "400":
          description: Bad request — version is not a positive integer
        "401":
          description: Unauthorized
        "404":
          description: Not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbooks/{playbook_id}/versions/{version}/restore:
    post:
      tags:
        - Playbooks
      summary: Restore an archived playbook version
      description: Writes an archived definition back as the playbook's live one, which archives it again
        as a new version rather than rewinding the counter. Restoring the definition the playbook
        already holds is a no-op. The caller authors the new version, checked as on create.
      operationId: restorePlaybookVersion
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
        - $ref: "#/components/parameters/Version"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RestorePlaybookVersionRequest"
      responses:
        "200":
          description: The playbook, at its new version
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Playbook"
        "400":
          description: Bad request — version is not a positive integer
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Not found
        "409":
          $ref: "#/components/responses/VersionConflict"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbooks/{playbook_id}/cases:
    post:
      tags:
        - Playbooks
      summary: Create a case
      description: Creates a case for one subject on the playbook's current version. The case stays on
        that version however the playbook changes later. A playbook that declares `upstreams` needs
        each bound to a case of the declared playbook, in the same project, of the same `actor_id`,
        that the caller may read (`playbooks:GetPlaybookCase`); anything else is `400
        PLAYBOOK_CASE_UPSTREAM_INVALID`. The binding is fixed for the life of the case.
      operationId: createPlaybookCase
      x-naturali-resource:
        kind: playbook
        from: playbook_id
      parameters:
        - $ref: "#/components/parameters/PlaybookId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePlaybookCaseRequest"
      responses:
        "201":
          description: Case created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "400":
          description: Bad request — subject is missing, or an upstream is unbound or bound to a case it may
            not be
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Playbook not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases:
    get:
      tags:
        - Playbooks
      summary: List playbook cases
      description: Returns a project's cases, newest first, without node states; read one case for those.
      operationId: listPlaybookCases
      parameters:
        - name: playbook_id
          in: query
          description: Only the cases of this playbook
          schema:
            type: string
            example: pbk_V1StGXR8Z5jdHi6B
        - name: actor_id
          in: query
          required: false
          description: Only the cases that belong to this actor. Pin it in a tool's `preset_parameters` as
            `{{context:actor_id}}` so an agent lists the cases of the end user it serves only.
          schema:
            type: string
            example: actor_V1StGXR8Z5jdHi6B
        - name: subject
          in: query
          required: false
          description: Only the cases whose `subject` is exactly this
          schema:
            type: string
            example: ACME Ltda (CNPJ 12.345.678/0001-90)
        - name: automation_status
          in: query
          required: false
          description: Only the cases in these automation statuses. Repeat the parameter to OR values. A value
            outside the enum, empty string included, is a `400`.
          schema:
            type: array
            items:
              $ref: "#/components/schemas/PlaybookAutomationStatus"
          style: form
          explode: true
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of cases
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookCaseSummary"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}:
    get:
      tags:
        - Playbooks
      summary: Get a case
      description: Returns a case with every node's state, computed from its entries on each read and
        never stored.
      operationId: getPlaybookCase
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
      responses:
        "200":
          description: The case
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "401":
          description: Unauthorized
        "404":
          description: Case not found
    delete:
      tags:
        - Playbooks
      summary: Delete a case
      description: Deletes a case and every entry written to it. A case other cases are bound to as their
        upstream is refused with `409 PLAYBOOK_CASE_HAS_DEPENDENTS`.
      operationId: deletePlaybookCase
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
      responses:
        "204":
          description: Case deleted
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Case not found
        "409":
          description: Other cases are bound to this case as their upstream
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/inputs:
    post:
      tags:
        - Playbooks
      summary: Write a case's inputs
      description: "Writes a batch of manual inputs, all or none. Each write names the revision it read in
        `expected_revision`, `null` for a field still unset; when any field has moved on the whole
        batch is refused with `409 PLAYBOOK_INPUT_CONFLICT` and `meta.conflicts` names each field.
        An input the case's version does not declare, one that reads another node, or a value its
        schema refuses is `400 PLAYBOOK_INPUT_INVALID`. A value equal to the field's current one
        writes no revision. Once written, every node the write makes runnable is queued to run,
        unless the case is paused. With `wait: true` it answers once every execution queued, and
        every one those queue, has settled or parked. A write made with a credential the case's own
        work runs as — the case, one of its executions in flight, or the task or run one started —
        or on a project key with the `On-Behalf-Of` of a tool call that work's generation made, or
        by the work of a case reading it as an upstream, at any depth, is `403
        PLAYBOOK_SELF_WRITE`."
      operationId: writePlaybookCaseInputs
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WritePlaybookCaseInputsRequest"
      responses:
        "200":
          description: The case after the write
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "400":
          description: Bad request — an unknown input, a non-manual input or a value its schema refuses
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Case not found
        "409":
          description: A field changed since it was read; nothing was written
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/missing:
    get:
      tags:
        - Playbooks
      summary: List what a node still needs
      description: "Returns the manual inputs still unset, or refused by their schema, upstream of
        `node_id`, grouped in waves: nothing in a later wave can run before the earlier ones'
        required inputs are filled. Optional inputs are listed with `required: false`; they never
        block a node. An `upstream` node before `node_id` adds the missing inputs of the case it is
        bound to, each with that case's `case_id`, so one read lists everything to ask and each
        answer is written to its own case."
      operationId: getPlaybookCaseMissing
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - name: node_id
          in: query
          required: true
          description: The ID of the node to reach, usually a final node
          schema:
            type: string
            example: pricing
      responses:
        "200":
          description: The missing inputs, by wave
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCaseMissing"
        "400":
          description: Bad request — node_id names no node of the case's version
        "401":
          description: Unauthorized
        "404":
          description: Case not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/entries:
    get:
      tags:
        - Playbooks
      summary: List a case's entries
      description: Returns every value written to the case, newest first. Entries are never rewritten, so
        this is the case's full history.
      operationId: listPlaybookCaseEntries
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - name: node_id
          in: query
          description: Only the entries of this node
          schema:
            type: string
            example: revenue
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of entries, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookEntry"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "404":
          description: Case not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/nodes/{node}/run:
    post:
      tags:
        - Playbooks
      summary: Run a node of a case
      description: >-
        Runs one node now. The cascade already runs every node whose inputs are set once a write, a
        settle or a resume makes it runnable, and never reruns a `failed` or `done` one: this is how
        a failed node is retried, and how a `done` node is recomputed after an upgrade changed its
        executor. A `collect` node takes its own input values as its output, any other node starts
        its executor with the node's input values mapped through the executor's `input`. The
        executor runs as the author of the case's playbook version, whose permission to start it is
        checked again here (`403 PLAYBOOK_EXECUTOR_FORBIDDEN`); an author that no longer exists is
        `409 PLAYBOOK_AUTHOR_UNAVAILABLE`. Only a `ready`, `stale`, `failed` or `done` node with
        every required input set runs; anything else is `409 PLAYBOOK_NODE_NOT_RUNNABLE`. A paused
        case is `409 PLAYBOOK_CASE_PAUSED`, and a case in a paused project `409 PROJECT_PAUSED`.


        An `upstream` node has no executor: running it pulls the current output of the node it names
        in the bound case, which must be `done`. This is the only way to refresh one that an
        upstream change left `stale`.


        The output is written as an entry when the execution settles. A value equal to the node's
        current output writes no revision. If the node's inputs change while it runs, the execution
        settles as `superseded` and writes nothing.


        Without `wait` the execution answers `running` and settles in the background. With `wait:
        true` it answers once the executor settles, or once it parks: a workflow task still open, or
        an orchestration run waiting on a person or a timer, settles the node when it closes or
        ends.
      operationId: runPlaybookCaseNode
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - $ref: "#/components/parameters/NodeKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RunPlaybookCaseNodeRequest"
      responses:
        "201":
          description: The execution, `running` or settled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookExecution"
        "400":
          description: Bad request — a node the case's version does not declare
        "401":
          description: Unauthorized
        "403":
          description: Forbidden, or the author may not start the executor
        "404":
          description: Case not found
        "409":
          description: The node cannot run now, the case is paused, or the version has no author to run as
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/executions:
    get:
      tags:
        - Playbooks
      summary: List a case's executions
      description: Returns every run of the case's nodes, newest first.
      operationId: listPlaybookCaseExecutions
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - name: node_id
          in: query
          description: Only the executions of this node
          schema:
            type: string
            example: revenue
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of executions, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookExecution"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "404":
          description: Case not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/executions/{execution_id}:
    get:
      tags:
        - Playbooks
      summary: Get an execution
      description: Returns one run of a case's node.
      operationId: getPlaybookCaseExecution
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - $ref: "#/components/parameters/ExecutionId"
      responses:
        "200":
          description: The execution
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookExecution"
        "401":
          description: Unauthorized
        "404":
          description: Case or execution not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/nodes/{node}/exports:
    post:
      tags:
        - Playbooks
      summary: Export a final node
      description: >-
        Renders one revision of a final node as files, one export per transformation: each
        transformation's tool is called with the case's `case_id` and `subject`, the `node_id`, the
        `revision` and that revision's `output`, and must answer a file (`execute.response_mode:
        base64`) of its `format`'s content type. The file is kept in the project's files under
        `/.system/playbooks/`. An export never feeds a node.


        Without `revision` the node's current output is exported, which needs the node `done` (`409
        PLAYBOOK_NODE_NOT_EXPORTABLE`); a `revision` names an earlier output or confirmation of the
        node. Each tool runs as the author of the case's playbook version, whose permission to call
        it is checked here (`403 PLAYBOOK_EXECUTOR_FORBIDDEN`). A paused case is `409
        PLAYBOOK_CASE_PAUSED`, and a case in a paused project `409 PROJECT_PAUSED`.


        Without `wait` every export answers `running` and settles in the background; with `wait:
        true` it answers once each has its file or its error.
      operationId: exportPlaybookCaseNode
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - $ref: "#/components/parameters/NodeKey"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExportPlaybookCaseNodeRequest"
      responses:
        "201":
          description: One export per transformation, `running` or settled
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookExport"
        "400":
          description: Bad request — a node the version does not declare or that has no transformations, a
            transformation it does not declare, or a revision that is no output of the node
        "401":
          description: Unauthorized
        "403":
          description: Forbidden, or the author may not call a transformation's tool
        "404":
          description: Case not found
        "409":
          description: The node has no current output, the case is paused, or the version has no author to run
            as
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/exports:
    get:
      tags:
        - Playbooks
      summary: List a case's exports
      description: Returns every file rendered from the case's final nodes, newest first.
      operationId: listPlaybookCaseExports
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - name: node_id
          in: query
          description: Only the exports of this node
          schema:
            type: string
            example: pricing
        - name: transformation
          in: query
          description: Only the exports of this transformation
          schema:
            type: string
            example: proposal
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of exports, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookExport"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "404":
          description: Case not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/exports/{export_id}:
    get:
      tags:
        - Playbooks
      summary: Get an export
      description: Returns one file rendered from a case's final node.
      operationId: getPlaybookCaseExport
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - $ref: "#/components/parameters/ExportId"
      responses:
        "200":
          description: The export
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookExport"
        "401":
          description: Unauthorized
        "404":
          description: Case or export not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/upgrade:
    post:
      tags:
        - Playbooks
      summary: Upgrade a case
      description: >-
        Moves a case to another version of its playbook: the playbook's current version, or the
        `version` named, an earlier one included. Values carry over by node ID, and every node's
        state is read against the new version: a node added is open, a node removed leaves the
        structure while its entries stay, a node whose input reads another field is `stale`, and a
        value its input's new schema refuses is missing again. The cascade then runs what became
        runnable. With `wait: true` it answers once every execution queued, and every one those
        queue, has settled or parked. Each upgrade is recorded with what moved; the version the case
        is already on records nothing.


        The case's upstream bindings follow the target version: a name it declares on the same
        playbook keeps its case, any other binding is dropped, and each name it declares that the
        case does not bind is bound from `upstreams`, by the rules of create. A declared name left
        unbound is `400 PLAYBOOK_CASE_UPSTREAM_INVALID` and moves nothing.


        An execution running is `409 PLAYBOOK_CASE_BUSY`; one queued but not started settles
        `superseded`. An upgrade made with a credential the case's own work, or that of a case
        reading it as an upstream, runs as is `403 PLAYBOOK_SELF_WRITE`.
      operationId: upgradePlaybookCase
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpgradePlaybookCaseRequest"
      responses:
        "200":
          description: The case on its new version
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "400":
          description: "Bad request — a version the playbook does not have, or
            `PLAYBOOK_CASE_UPSTREAM_INVALID`: an upstream the target version declares left unbound,
            a name it does not declare or the case already binds, or a case that cannot be bound"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden, or an upgrade from the case's own work
        "404":
          description: Case not found
        "409":
          description: An execution of the case is running
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/upgrades:
    get:
      tags:
        - Playbooks
      summary: List a case's upgrades
      description: Returns every version change of the case, newest first.
      operationId: listPlaybookCaseUpgrades
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/Offset"
      responses:
        "200":
          description: List of upgrades, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/PlaybookUpgrade"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "404":
          description: Case not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/pause:
    post:
      tags:
        - Playbooks
      summary: Pause a case
      description: "Stops the case starting anything: no node is queued and no queued execution starts
        until it is resumed. Executions already running settle. Input writes are still accepted.
        Pausing a paused case keeps the first pause."
      operationId: pausePlaybookCase
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
      responses:
        "200":
          description: The paused case
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Case not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/resume:
    post:
      tags:
        - Playbooks
      summary: Resume a case
      description: Lifts the pause and starts what became runnable while the case was paused. A case that
        is not paused is `409 PLAYBOOK_CASE_NOT_PAUSED`; one in a paused project is `409
        PROJECT_PAUSED` and stays paused until the project resumes.
      operationId: resumePlaybookCase
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
      responses:
        "200":
          description: The resumed case
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Case not found
        "409":
          description: The case is not paused
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/playbook-cases/{case_id}/nodes/{node}/confirmation:
    post:
      tags:
        - Playbooks
      summary: Confirm a node of a case
      description: >-
        Approves or rejects a `confirm` node on the inputs the confirmer read. `expected_revisions`
        names the revision of each of the node's inputs as read (`nodes[].inputs[].revision` on the
        case); an input that moved since is `409 PLAYBOOK_INPUT_CONFLICT`, `meta.conflicts` naming
        each, and nothing is written.


        An approval writes a `confirmation` entry whose value is the node's input values: it is the
        node's output, and the nodes reading it run next. With `wait: true` it answers once every
        execution queued, and every one those queue, has settled or parked. A rejection writes a
        `rejection` entry with the `reason`: the node is `rejected`, and every node reading it is
        blocked, until its inputs change or it is approved. Another node, or a `confirm` node with a
        required input unset or reading a node that is not `done`, is `409
        PLAYBOOK_NODE_NOT_CONFIRMABLE`. A confirmation made with a credential the case's own work,
        or that of a case reading it as an upstream, runs as is `403 PLAYBOOK_SELF_WRITE`.
      operationId: confirmPlaybookCaseNode
      x-naturali-resource:
        kind: playbook_case
        from: case_id
      parameters:
        - $ref: "#/components/parameters/CaseId"
        - $ref: "#/components/parameters/ActorPin"
        - $ref: "#/components/parameters/NodeKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfirmPlaybookCaseNodeRequest"
      responses:
        "200":
          description: The case after the confirmation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaybookCase"
        "400":
          description: Bad request — an unknown decision, a rejection without a reason, or a node the version
            does not declare
        "401":
          description: Unauthorized
        "403":
          description: Forbidden, or a write from the case's own work
        "404":
          description: Case not found
        "409":
          description: The node cannot be confirmed now, or an input changed since it was read
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  schemas:
    PlaybookInputSource:
      description: "`manual` for an input a person or an integration writes, or the field of an earlier
        node it reads: `output[.path]`, or `input.<name>[.path]`. Sources are the playbook's edges."
      oneOf:
        - type: string
          enum:
            - manual
        - type: object
          additionalProperties: false
          required:
            - node_id
            - field
          properties:
            node_id:
              type: string
              description: The ID of the node it reads
              example: revenue
            field:
              type: string
              example: output.annual_revenue
    PlaybookInput:
      type: object
      additionalProperties: false
      required:
        - name
        - schema
        - required
        - source
      properties:
        name:
          type: string
          description: Letters, digits and underscores, starting with a letter or underscore
          example: revenue
        schema:
          type: object
          description: The JSON Schema a value must satisfy
          example:
            type: number
        required:
          type: boolean
          description: The node cannot run until a required input is set
        description:
          type: string
          description: What to ask for; shown with the input when it is missing
        source:
          $ref: "#/components/schemas/PlaybookInputSource"
    PlaybookExecutor:
      type: object
      additionalProperties: false
      required:
        - type
        - id
      properties:
        type:
          type: string
          enum:
            - agent
            - tool
            - decider
            - workflow
            - orchestration
        id:
          type: string
          description: The ID of the agent, tool, decider, workflow or orchestration
          example: dcd_V1StGXR8Z5jdHi6B
        operation_id:
          type: string
          minLength: 1
          description: "Only on a `tool` executor: which action of a multi-action (`mcp`) tool to call — the
            same selector an orchestration `tool` node's `operation_id` is. Required by an `mcp`
            tool."
        input:
          type: object
          description: "JSON Logic over the node's inputs, as an orchestration's `input_mapping`: each key is
            a field of the executor's input. Absent, the executor receives the node's input values
            as they are."
    PlaybookTransformation:
      type: object
      additionalProperties: false
      required:
        - name
        - format
        - tool
      properties:
        name:
          type: string
          example: proposal
        format:
          type: string
          description: "The file's format; the tool must answer its content type: `application/pdf` for `pdf`,
            the Office Open XML types for `docx`, `xlsx` and `pptx`, `text/csv`, `text/html`,
            `text/markdown`, `text/plain`, `application/json`, `image/png` and `image/jpeg` for
            `jpg`"
          enum:
            - csv
            - docx
            - html
            - jpg
            - json
            - md
            - pdf
            - png
            - pptx
            - txt
            - xlsx
          example: pdf
        tool:
          type: string
          description: "The tool that renders the final node's output as a file, answering with
            `execute.response_mode: base64`"
          example: tool_V1StGXR8Z5jdHi6B
    PlaybookNode:
      type: object
      additionalProperties: false
      required:
        - id
        - title
        - kind
        - inputs
      properties:
        id:
          type: string
          description: "Stable across versions: a case's values carry over by ID. Letters, digits and
            underscores, starting with a letter or underscore."
          example: regime
        title:
          type: string
          example: Tax regime
        description:
          type: string
        kind:
          type: string
          enum:
            - collect
            - execute
            - confirm
            - upstream
          description: "`collect` outputs its inputs, `execute` runs its executor, `confirm` waits for a
            person to confirm its inputs, `upstream` outputs the node it names in the case bound to
            one of the definition's `upstreams`."
        final:
          type: boolean
          description: A goal of the playbook; only a final node carries transformations
          default: false
        inputs:
          type: array
          maxItems: 50
          items:
            $ref: "#/components/schemas/PlaybookInput"
        executor:
          $ref: "#/components/schemas/PlaybookExecutor"
        upstream:
          $ref: "#/components/schemas/PlaybookUpstreamRef"
        transformations:
          type: array
          items:
            $ref: "#/components/schemas/PlaybookTransformation"
    PlaybookUpstreamRef:
      type: object
      additionalProperties: false
      description: "On an `upstream` node, required there and refused elsewhere; the node then has no
        inputs. It pulls `node_id` of the case bound under `name`: the first pull is automatic once
        that node is `done`, and afterwards a change upstream leaves this node `stale` until it is
        run. Later nodes read its output with an ordinary `{ node_id, field }` source."
      required:
        - name
        - node_id
      properties:
        name:
          type: string
          description: One of the definition's `upstreams`
          example: company
        node_id:
          type: string
          description: A node of the upstream playbook's current version
          example: profile
    PlaybookUpstream:
      type: object
      additionalProperties: false
      required:
        - name
        - playbook_id
      properties:
        name:
          type: string
          description: Letters, digits and underscores, starting with a letter or underscore; unique in the
            definition
          example: company
        playbook_id:
          x-naturali-ref: playbooks
          type: string
          description: A playbook of the same project that does not read this one back through its own upstreams
          example: pbk_V1StGXR8Z5jdHi6B
    PlaybookDefinition:
      type: object
      additionalProperties: false
      required:
        - nodes
      properties:
        upstreams:
          type: array
          maxItems: 20
          description: Playbooks whose cases a case of this one binds at create, one per name; many cases may
            share one upstream case.
          items:
            $ref: "#/components/schemas/PlaybookUpstream"
        nodes:
          type: array
          minItems: 1
          maxItems: 200
          items:
            $ref: "#/components/schemas/PlaybookNode"
    Playbook:
      type: object
      properties:
        id:
          type: string
          example: pbk_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
          example: proj_V1StGXR8Z5jdHi6B
        name:
          type: string
          example: client-pricing
        description:
          type: string
          nullable: true
        version:
          type: integer
          description: The definition's version. Bumped only by a write that changes `definition`.
          example: 1
        definition:
          $ref: "#/components/schemas/PlaybookDefinition"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CreatePlaybookRequest:
      type: object
      additionalProperties: false
      required:
        - name
        - definition
      properties:
        name:
          type: string
          description: Human-readable name, unique per project
          example: client-pricing
        description:
          description: What the playbook is for
          type: string
          nullable: true
        definition:
          description: The nodes and how their inputs are sourced
          allOf:
            - $ref: "#/components/schemas/PlaybookDefinition"
        version_label:
          type: string
          description: Optional tag for version 1, e.g. `initial`
          example: initial
    UpdatePlaybookRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          description: Human-readable name, unique per project
          type: string
        description:
          description: What the playbook is for
          type: string
          nullable: true
        definition:
          description: Replaces the whole definition; a change archives a new version
          allOf:
            - $ref: "#/components/schemas/PlaybookDefinition"
        version_label:
          type: string
          description: Optional tag for the version this write archives. Ignored when the write changes no
            definition, since no version is archived.
        expected_version:
          description: Refuses the write unless the playbook is at this version.
          allOf:
            - $ref: "#/components/schemas/ExpectedVersion"
    ValidatePlaybookRequest:
      type: object
      additionalProperties: false
      required:
        - definition
      properties:
        playbook_id:
          type: string
          description: The playbook the definition would update, so an upstream reading it back is
            `UPSTREAM_CYCLE`. Requires `project_id`.
          example: pbk_V1StGXR8Z5jdHi6B
        definition:
          description: The definition to check
          allOf:
            - $ref: "#/components/schemas/PlaybookDefinition"
    PlaybookValidationResult:
      type: object
      required:
        - valid
        - problems
      properties:
        valid:
          type: boolean
        problems:
          type: array
          description: "Every reason the definition cannot be stored: `DUPLICATE_NODE`, `DUPLICATE_INPUT`,
            `MISSING_NODE`, `UNKNOWN_FIELD`, `EXECUTOR_REQUIRED`, `EXECUTOR_FORBIDDEN`,
            `TRANSFORMATIONS_NOT_FINAL`, `DUPLICATE_TRANSFORMATION`, `CYCLE`, and, with
            `project_id`, `UNKNOWN_UPSTREAM` or `UPSTREAM_CYCLE`. The same items a refused write
            lists under `meta.problems`."
          items:
            $ref: "#/components/schemas/ValidationProblem"
    PlaybookVersion:
      type: object
      description: An immutable archive of a playbook's definition at one version.
      properties:
        id:
          type: string
          example: pbk_ver_V1StGXR8Z5jdHi6B
        playbook_id:
          x-naturali-ref: playbooks
          type: string
          example: pbk_V1StGXR8Z5jdHi6B
        version:
          type: integer
          example: 1
        config:
          type: object
          additionalProperties: true
          description: The versioned surface as it stood at this version.
          properties:
            definition:
              $ref: "#/components/schemas/PlaybookDefinition"
        label:
          type: string
          nullable: true
          example: restored from v2
        created_by_principal_type:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PrincipalType"
          description: Who produced this version. Null for a write with no principal behind it.
        created_by_principal_id:
          type: string
          nullable: true
          description: "Public id of that principal: the user (`user_…`), or for API key auth the key itself
            (`key_…`), so the record names which key acted."
          example: user_V1StGXR8Z5jdHi6B
        created_at:
          type: string
          format: date-time
    RestorePlaybookVersionRequest:
      type: object
      additionalProperties: false
      properties:
        label:
          type: string
          description: Optional tag for the new version. Defaults to `restored from vN`.
          example: rollback
    CreatePlaybookCaseRequest:
      type: object
      additionalProperties: false
      required:
        - subject
      properties:
        subject:
          type: string
          description: Who or what the case is about, as you name it
          example: ACME Ltda (CNPJ 12.345.678/0001-90)
        actor_id:
          x-naturali-ref: actors
          type: string
          description: The end user the case belongs to, an actor of the playbook's project; an unknown one is
            `400 ACTOR_NOT_FOUND`. Omitted, the case belongs to no actor.
          example: actor_V1StGXR8Z5jdHi6B
        upstreams:
          $ref: "#/components/schemas/PlaybookCaseUpstreams"
    PlaybookNodeState:
      type: string
      enum:
        - blocked
        - ready
        - running
        - waiting
        - done
        - stale
        - failed
        - rejected
      description: "`blocked` while a required input is unset or reads a node that is not `done`, or, on
        an `upstream` node, while the node it pulls is not `done`; `ready` when it can run;
        `running` while its execution is queued or runs its executor; `waiting` while it waits on
        the workflow task or orchestration run it started, named by `run`; `done` when its output is
        current; `stale` when an input changed after its output was produced; `failed` when its
        execution failed on its current inputs; `rejected` when a person rejected its current
        inputs."
    PlaybookNodeRun:
      type: object
      required:
        - type
        - id
      properties:
        type:
          type: string
          enum:
            - task
            - orchestration_run
        id:
          type: string
          description: The task's or orchestration run's ID
          example: task_V1StGXR8Z5jdHi6B
        state:
          type: string
          nullable: true
          description: A task's workflow state; absent for a run, null once the task no longer exists
        automation_status:
          type: string
          nullable: true
          description: "A task's automation status: `failed` when its state's dispatch failed and no
            `on_failure` routes it, so the task waits on a person to move it. Absent for a run."
        status:
          type: string
          nullable: true
          description: A run's status, such as `awaiting_input`; absent for a task, null once the run no
            longer exists
    PlaybookCaseNode:
      type: object
      properties:
        id:
          type: string
          example: regime
        state:
          $ref: "#/components/schemas/PlaybookNodeState"
        missing:
          type: array
          description: Required inputs that are unset or refused by their schema
          items:
            type: string
        blocked_by:
          type: array
          description: Nodes it reads that must be `done` first; on an `upstream` node, the
            `<upstream>.<node_id>` it pulls
          items:
            type: string
        run:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PlaybookNodeRun"
          description: The task or run a `waiting` node waits on, read live on every case read; null in every
            other state.
        inputs:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              value:
                description: The value it reads now; null when unset
              revision:
                type: integer
                nullable: true
                description: The revision of the entry the value comes from
        output:
          type: object
          nullable: true
          properties:
            value:
              description: The node's latest output
            revision:
              type: integer
    PlaybookCaseSummary:
      type: object
      properties:
        id:
          type: string
          example: pbk_case_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
          example: proj_V1StGXR8Z5jdHi6B
        playbook_id:
          x-naturali-ref: playbooks
          type: string
          example: pbk_V1StGXR8Z5jdHi6B
        playbook_version:
          type: integer
          description: The version the case was created on and is evaluated against
          example: 1
        subject:
          type: string
        actor_id:
          x-naturali-ref: actors
          type: string
          nullable: true
          description: The end user the case belongs to; null for none
          example: actor_V1StGXR8Z5jdHi6B
        upstreams:
          $ref: "#/components/schemas/PlaybookCaseUpstreams"
        revision:
          type: integer
          description: The case's revision counter; every entry takes the next value
          example: 0
        automation_status:
          $ref: "#/components/schemas/PlaybookAutomationStatus"
        pause_requested_at:
          type: string
          format: date-time
          nullable: true
          description: When the case was paused; null while it runs
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    PlaybookCaseUpstreams:
      type: object
      description: "The case bound to each of the playbook's declared `upstreams`, by name: every declared
        name and no other, each a case of the declared playbook. Bound at create and fitted to the
        version at each upgrade; empty when the version declares none."
      additionalProperties:
        type: string
      example:
        company: pbk_case_V1StGXR8Z5jdHi6B
    PlaybookAutomationStatus:
      type: string
      description: "`paused` while a pause of the case or of its project stands, else `running` while an
        execution is queued or runs its executor, else `waiting` while every execution in flight
        waits on a workflow task or orchestration run it started, else `idle`"
      enum:
        - idle
        - running
        - waiting
        - paused
    PlaybookCase:
      allOf:
        - $ref: "#/components/schemas/PlaybookCaseSummary"
        - type: object
          properties:
            nodes:
              type: array
              description: Every node of the case's version, in definition order
              items:
                $ref: "#/components/schemas/PlaybookCaseNode"
    PlaybookInputWrite:
      type: object
      additionalProperties: false
      required:
        - node_id
        - name
        - value
        - expected_revision
      properties:
        node_id:
          type: string
          example: revenue
        name:
          type: string
          example: revenue
        value:
          description: The value, which must satisfy the input's schema
          example: 4000000
        expected_revision:
          type: integer
          nullable: true
          description: The revision of the field the writer read, or `null` for a field it read as unset. Any
            other current revision refuses the batch.
          example: null
    WritePlaybookCaseInputsRequest:
      type: object
      additionalProperties: false
      required:
        - inputs
      properties:
        inputs:
          description: The writes, applied all or none
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: "#/components/schemas/PlaybookInputWrite"
        wait:
          type: boolean
          default: false
          description: Answer once every execution the write queued, and every one those queue in turn, has
            settled or parked, instead of right away
    PlaybookMissingInput:
      type: object
      properties:
        case_id:
          type: string
          description: "The case to write it to: this one, or a case bound as an upstream"
          example: pbk_case_V1StGXR8Z5jdHi6B
        node_id:
          type: string
        input:
          type: string
        schema:
          type: object
          description: The JSON Schema a value must satisfy
        description:
          type: string
        required:
          type: boolean
          description: Whether the node waits for this input; an optional one never blocks it
    PlaybookCaseMissing:
      type: object
      properties:
        case_id:
          type: string
          example: pbk_case_V1StGXR8Z5jdHi6B
        node_id:
          type: string
          example: pricing
        waves:
          type: array
          description: Missing inputs grouped by the wave of the node that holds them
          items:
            type: array
            items:
              $ref: "#/components/schemas/PlaybookMissingInput"
    PlaybookEntry:
      type: object
      properties:
        id:
          type: string
          example: pbk_ent_V1StGXR8Z5jdHi6B
        revision:
          type: integer
        node_id:
          type: string
        kind:
          type: string
          enum:
            - input
            - output
            - confirmation
            - rejection
        input:
          type: string
          nullable: true
          description: The input written, on an `input` entry
        value:
          description: The value written
        basis:
          type: object
          nullable: true
          description: What an output, confirmation or rejection was decided on
        playbook_version:
          type: integer
        created_by_principal_type:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PrincipalType"
          description: Who recorded this entry; null for one the case's own cascade recorded.
        created_by_principal_id:
          type: string
          nullable: true
          description: "Public id of that principal: the user (`user_…`), or for API key auth the key itself
            (`key_…`), so the record names which key acted."
          example: user_V1StGXR8Z5jdHi6B
        on_behalf_of:
          $ref: "#/components/schemas/OnBehalfOf"
        created_at:
          type: string
          format: date-time
    RunPlaybookCaseNodeRequest:
      type: object
      additionalProperties: false
      properties:
        wait:
          type: boolean
          default: false
          description: Answer once the executor settles or parks instead of right away
    PlaybookExecution:
      type: object
      properties:
        id:
          type: string
          example: pbk_exe_V1StGXR8Z5jdHi6B
        case_id:
          type: string
          example: pbk_case_V1StGXR8Z5jdHi6B
        node_id:
          type: string
          description: The ID of the node that ran
          example: revenue
        playbook_version:
          type: integer
          description: The playbook version the case runs on
        executor:
          type: object
          nullable: true
          description: What ran; null for a `collect` node
          properties:
            type:
              type: string
              enum:
                - agent
                - tool
                - decider
                - workflow
                - orchestration
            id:
              type: string
              description: The ID of the agent, tool, decider, workflow or orchestration
              example: agent_V1StGXR8Z5jdHi6B
        status:
          type: string
          description: "`queued` until a worker claims it, `running` while its executor runs or a task or run
            it started is open, then `completed` or `failed`"
          enum:
            - queued
            - running
            - completed
            - failed
        result:
          type: string
          nullable: true
          enum:
            - current
            - superseded
            - null
          description: "Once settled: `superseded` when the node's inputs changed while it ran, so its output
            was not written"
        run_id:
          type: string
          nullable: true
          description: The generation, decision, task or orchestration run it started
        input:
          description: What the executor was given
        output:
          description: What the executor answered; null until it succeeds
        output_revision:
          type: integer
          nullable: true
          description: The revision of the output entry it wrote; null when it wrote none
        error:
          allOf:
            - $ref: "#/components/schemas/StoredError"
          nullable: true
          description: "Why it failed: `{ code, message, meta? }`"
        created_by_principal_type:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PrincipalType"
          description: Who asked for the run; null when the case started it.
        created_by_principal_id:
          type: string
          nullable: true
          description: "Public id of that principal: the user (`user_…`), or for API key auth the key itself
            (`key_…`), so the record names which key acted."
          example: user_V1StGXR8Z5jdHi6B
        on_behalf_of:
          $ref: "#/components/schemas/OnBehalfOf"
        created_at:
          type: string
          format: date-time
        finished_at:
          type: string
          format: date-time
          nullable: true
    ExportPlaybookCaseNodeRequest:
      type: object
      additionalProperties: false
      properties:
        transformations:
          type: array
          minItems: 1
          description: The names of the transformations to render; every one the node declares when omitted
          items:
            type: string
            example: proposal
        revision:
          type: integer
          description: The output or confirmation of the node to render; its current output when omitted
        wait:
          type: boolean
          default: false
          x-naturali-tool-forced: true
          description: Answer once every export settles instead of right away. An MCP tool call always waits.
    PlaybookExport:
      type: object
      properties:
        id:
          type: string
          example: pbk_exp_V1StGXR8Z5jdHi6B
        case_id:
          type: string
          example: pbk_case_V1StGXR8Z5jdHi6B
        node_id:
          type: string
          description: The ID of the final node rendered
          example: pricing
        revision:
          type: integer
          description: The output or confirmation entry rendered
        playbook_version:
          type: integer
          description: The playbook version whose transformation rendered it
        transformation:
          type: string
          example: proposal
        format:
          type: string
          example: pdf
        tool_id:
          type: string
          example: tool_V1StGXR8Z5jdHi6B
        status:
          type: string
          description: "`running` while its tool runs, then `completed` or `failed`"
          enum:
            - running
            - completed
            - failed
        stale:
          type: boolean
          description: Whether `revision` is no longer the node's current output, or the node is no longer
            `done`
        file_id:
          type: string
          nullable: true
          x-naturali-ref: files
          description: The file rendered; null until it succeeds, and once the file is deleted
          example: file_V1StGXR8Z5jdHi6B
        error:
          allOf:
            - $ref: "#/components/schemas/StoredError"
          nullable: true
          description: "Why it failed: `{ code, message, meta? }`"
        created_by_principal_type:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PrincipalType"
          description: Who asked for the export.
        created_by_principal_id:
          type: string
          nullable: true
          description: "Public id of that principal: the user (`user_…`), or for API key auth the key itself
            (`key_…`), so the record names which key acted."
          example: user_V1StGXR8Z5jdHi6B
        on_behalf_of:
          $ref: "#/components/schemas/OnBehalfOf"
        created_at:
          type: string
          format: date-time
        finished_at:
          type: string
          format: date-time
          nullable: true
    UpgradePlaybookCaseRequest:
      type: object
      additionalProperties: false
      properties:
        version:
          type: integer
          description: The version to move the case to; the playbook's current one when omitted
          example: 2
        upstreams:
          description: A case for each upstream the target version declares and the case does not bind yet, by
            name, checked as at create. A binding the target version declares on the same playbook
            is kept and never named here; every other binding is dropped.
          allOf:
            - $ref: "#/components/schemas/PlaybookCaseUpstreams"
        wait:
          type: boolean
          default: false
          description: Answer once every execution the write queued, and every one those queue in turn, has
            settled or parked, instead of right away
    PlaybookUpgrade:
      type: object
      properties:
        id:
          type: string
          example: pbk_upg_V1StGXR8Z5jdHi6B
        case_id:
          type: string
          example: pbk_case_V1StGXR8Z5jdHi6B
        from_version:
          type: integer
          description: The version the case was on
        to_version:
          type: integer
          description: The version the case moved to
        diff:
          type: object
          description: What moved between the two versions, by node ID
          properties:
            added:
              type: array
              description: Nodes the new version adds
              items:
                type: string
            removed:
              type: array
              description: Nodes the new version drops
              items:
                type: string
            changed:
              type: array
              description: Nodes in both whose executor or inputs changed; a title, description or transformation
                change is not listed
              items:
                type: object
                properties:
                  node_id:
                    type: string
                  executor:
                    type: boolean
                    description: Whether the node's executor changed
                  inputs:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        change:
                          type: string
                          enum:
                            - added
                            - removed
                            - source
                            - schema
                            - required
        created_by_principal_type:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/PrincipalType"
          description: Who upgraded the case.
        created_by_principal_id:
          type: string
          nullable: true
          description: "Public id of that principal: the user (`user_…`), or for API key auth the key itself
            (`key_…`), so the record names which key acted."
          example: user_V1StGXR8Z5jdHi6B
        on_behalf_of:
          $ref: "#/components/schemas/OnBehalfOf"
        created_at:
          type: string
          format: date-time
    ConfirmPlaybookCaseNodeRequest:
      type: object
      additionalProperties: false
      required:
        - decision
        - expected_revisions
      properties:
        decision:
          type: string
          description: Whether the node's inputs, as read, are approved
          enum:
            - approve
            - reject
        reason:
          type: string
          description: Why the inputs were rejected; required on a rejection
          example: The revenue does not match the filed statement
        expected_revisions:
          type: object
          description: Each of the node's inputs, by name, mapped to the revision read, or null for one read
            as unset
          additionalProperties:
            type: integer
            nullable: true
          example:
            revenue: 3
        wait:
          type: boolean
          default: false
          description: Answer once every execution the write queued, and every one those queue in turn, has
            settled or parked, instead of right away
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          description: Structured error. Every error response uses this shape, so `code` can be read without
            first checking the type of `error`.
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: "Machine-readable error code: lower_snake for an error naturali raises
                (`access_denied`), UPPER_SNAKE for one the runtime reports (`RESOURCE_NOT_FOUND`)."
              example: access_denied
            message:
              type: string
              description: Human-readable explanation.
              example: Your role in this project does not carry this action.
            details:
              type: object
              additionalProperties: true
              description: Structured context for an error naturali raises, such as the `resource` and `limit` of
                a `plan_limit_reached`.
            meta:
              type: object
              description: Structured context for an error the runtime reports.
    ExpectedVersion:
      type: integer
      minimum: 1
      nullable: true
      description: >-
        The version the caller believes the resource holds. When the resource is at any other
        version the write is refused with `409 VERSION_CONFLICT` and nothing is written;
        `meta.current_version` on that response names the version in force.


        Omit it to write unconditionally. Omitting it does not make the write unordered: two writes
        that reach the server together are still serialized, and the one whose version was taken
        first is refused the same way. What the field adds is refusing a write whose author read the
        resource some time ago and has not seen what happened since.


        The `If-Match` header carries the same precondition for a client that prefers the HTTP
        spelling. Sending both with different versions is `400 VALIDATION_FAILED`.
      example: 3
    ValidationProblem:
      type: object
      description: One reason a definition was refused. A validation refusal lists every one under
        `meta.problems`, and a dry-run validate endpoint answers the same items.
      required:
        - code
        - path
        - message
      properties:
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]*$
          description: What kind of problem it is, stable across releases.
          example: MISSING_FIELD
        path:
          type: string
          description: Where in the request body the problem is.
          example: nodes[0].agent_id
        message:
          type: string
          description: The problem in words, naming the values involved.
          example: agent node 'a' is missing required field 'agent_id'.
    PrincipalType:
      type: string
      enum:
        - user
        - api_key
        - agent
        - automation
        - approval
        - system
      description: What kind of principal acted. `user` and `api_key` are authenticated callers, and the
        id is the user's (`user_…`) or the key's own (`key_…`), so a record names which key acted
        rather than its owner. `agent` is an agent writing under its own identity (`agent_…`).
        `automation` is the task engine acting on a dispatch outcome and `approval` an approval
        resolution; neither carries an id. `system` is the platform itself, with an id naming the
        mechanism (`retention_sweep`, `zero_retention`).
      example: user
    OnBehalfOf:
      type: object
      nullable: true
      description: "The tool call the write was made on behalf of, for a write a `naturali` tool made;
        null for any other write. Attribution only: the `created_by_principal_*` pair and the
        permissions checked are the project credential's."
      properties:
        tool_id:
          type: string
          nullable: true
          example: tool_V1StGXR8Z5jdHi6B
        agent_id:
          type: string
          nullable: true
          description: The agent whose turn called the tool
          example: agent_V1StGXR8Z5jdHi6B
        generation_id:
          type: string
          nullable: true
          description: The generation that called the tool
          example: gen_V1StGXR8Z5jdHi6B
        session_id:
          type: string
          nullable: true
          example: sess_V1StGXR8Z5jdHi6B
        actor_id:
          type: string
          nullable: true
          example: actor_V1StGXR8Z5jdHi6B
    StoredError:
      type: object
      description: Why a resource's work failed, in the shape of an error response's `error` without
        `hint` and `docs_url`. Every `error` field a resource records carries it, so `code` can be
        read without first checking the type of `error`.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: A key from the server's ERROR_CODES registry, documented on the Error Codes page.
            `INTERNAL_ERROR`, with the message `Internal Server Error`, records an unexpected
            failure whose detail reaches the server log only.
          example: AI_PROVIDER_ERROR
        message:
          type: string
          example: "Provider returned 402: insufficient credits"
        meta:
          type: object
          additionalProperties: true
          description: The failure's own structured context, when it names any.
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
    PlaybookId:
      name: playbook_id
      in: path
      required: true
      description: The playbook ID
      schema:
        type: string
        example: pbk_V1StGXR8Z5jdHi6B
    CaseId:
      name: case_id
      in: path
      required: true
      description: The case ID
      schema:
        type: string
        example: pbk_case_V1StGXR8Z5jdHi6B
    ActorPin:
      name: actor_id
      in: query
      required: false
      description: "Acts only on a case that belongs to this actor: any other case, one that belongs to no
        actor included, is `404` as if it did not exist. Pin it in a tool's `preset_parameters` as
        `{{context:actor_id}}` so an agent serving an end user reaches that user's cases only."
      schema:
        type: string
        example: actor_V1StGXR8Z5jdHi6B
    NodeKey:
      name: node
      in: path
      required: true
      description: The ID of a node of the case's playbook version
      schema:
        type: string
        example: revenue
    ExecutionId:
      name: execution_id
      in: path
      required: true
      description: The execution ID
      schema:
        type: string
        example: pbk_exe_V1StGXR8Z5jdHi6B
    ExportId:
      name: export_id
      in: path
      required: true
      description: The export ID
      schema:
        type: string
        example: pbk_exp_V1StGXR8Z5jdHi6B
    Version:
      name: version
      in: path
      required: true
      description: The archived version number
      schema:
        type: integer
        minimum: 1
    Limit:
      name: limit
      in: query
      required: false
      description: Results per page, 1 to 100 (default 50). Above 100 it is clamped; below 1 or not an
        integer it is `400`.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50
    Offset:
      name: offset
      in: query
      required: false
      description: Results to skip. Negative or not an integer it is `400`.
      schema:
        type: integer
        minimum: 0
        default: 0
    IfMatchVersion:
      name: If-Match
      in: header
      required: false
      description: The version the caller believes the resource holds, as an entity tag (`3` or `"3"`).
        Equivalent to `expected_version` in the request body; `*` states no precondition beyond the
        resource existing. A mismatch is `409 VERSION_CONFLICT`.
      schema:
        type: string
      example: "3"
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A naturali API key (nat_sk_…) or a session JWT.
    oauth2:
      type: oauth2
      description: "A connected app's OAuth access token, issued by this API's authorization server
        (discovery: /.well-known/oauth-authorization-server). Its one scope carries every operation,
        confined to the projects the user chose when approving the app."
      flows:
        authorizationCode:
          authorizationUrl: https://api.naturali.ai/authorize
          tokenUrl: https://api.naturali.ai/token
          refreshUrl: https://api.naturali.ai/token
          scopes:
            mcp:access: Every operation this API serves, on the projects the grant covers.
  responses:
    VersionConflict:
      description: "The resource has moved past the version this write read (`VERSION_CONFLICT`): a stated
        `expected_version` or `If-Match` no longer matches, or a concurrent write took the version
        first. `meta.current_version` names the version in force. Nothing is written."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
