---
title: "Update a playbook"
method: PATCH
path: "/v1/projects/{project_id}/playbooks/{playbook_id}"
tags: ["Playbooks"]
---

# Update a playbook

`PATCH /v1/projects/{project_id}/playbooks/{playbook_id}`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/playbooks/{playbook_id}`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

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.

## Path parameters

- `playbook_id` string, required

## Headers

- `If-Match` string

## Request body

- UpdatePlaybookRequest
  - `name` string: Human-readable name, unique per project
  - `description` string, nullable: What the playbook is for
  - `definition` PlaybookDefinition
    - `nodes` PlaybookNode[], required
      - `id` string, required: Stable across versions: a case's values carry over by ID. Letters, digits and underscores, starting with a letter or underscore.
      - `title` string, required
      - `description` string
      - `kind` 'collect' | 'execute' | 'confirm', required: `collect` outputs its inputs, `execute` runs its executor, `confirm` waits for a person to confirm its inputs.
      - `final` boolean: A goal of the playbook; only a final node carries transformations
      - `inputs` PlaybookInput[], required
        - `name` string, required: Letters, digits and underscores, starting with a letter or underscore
        - `schema` object, required: The JSON Schema a value must satisfy
        - `required` boolean, required: The node cannot run until a required input is set
        - `description` string: What to ask for; shown with the input when it is missing
        - `source` union, required: `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.
          - 'manual'
          - object
            - `node_id` string, required: The ID of the node it reads
            - `field` string, required
      - `executor` PlaybookExecutor
        - `type` 'agent' | 'tool' | 'decider' | 'workflow' | 'orchestration', required
        - `id` string, required: The ID of the agent, tool, decider, workflow or orchestration
        - `input` object: 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.
      - `transformations` PlaybookTransformation[]
        - `name` string, required
        - `format` 'csv' | 'docx' | 'html' | 'jpg' | 'json' | 'md' | 'pdf' | 'png' | 'pptx' | 'txt' | 'xlsx', required: 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`
        - `tool` string, required: The tool that renders the final node's output as a file, answering with `execute.response_mode: base64`
  - `version_label` string: Optional tag for the version this write archives. Ignored when the write changes no definition, since no version is archived.
  - `expected_version` integer, nullable: 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`.

## Response `200`

Playbook updated

- Playbook
  - `id` string
  - `project_id` string
  - `name` string
  - `description` string, nullable
  - `version` integer: The definition's version. Bumped only by a write that changes `definition`.
  - `definition` PlaybookDefinition
    - `nodes` PlaybookNode[], required
      - `id` string, required: Stable across versions: a case's values carry over by ID. Letters, digits and underscores, starting with a letter or underscore.
      - `title` string, required
      - `description` string
      - `kind` 'collect' | 'execute' | 'confirm', required: `collect` outputs its inputs, `execute` runs its executor, `confirm` waits for a person to confirm its inputs.
      - `final` boolean: A goal of the playbook; only a final node carries transformations
      - `inputs` PlaybookInput[], required
        - `name` string, required: Letters, digits and underscores, starting with a letter or underscore
        - `schema` object, required: The JSON Schema a value must satisfy
        - `required` boolean, required: The node cannot run until a required input is set
        - `description` string: What to ask for; shown with the input when it is missing
        - `source` union, required: `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.
          - 'manual'
          - object
            - `node_id` string, required: The ID of the node it reads
            - `field` string, required
      - `executor` PlaybookExecutor
        - `type` 'agent' | 'tool' | 'decider' | 'workflow' | 'orchestration', required
        - `id` string, required: The ID of the agent, tool, decider, workflow or orchestration
        - `input` object: 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.
      - `transformations` PlaybookTransformation[]
        - `name` string, required
        - `format` 'csv' | 'docx' | 'html' | 'jpg' | 'json' | 'md' | 'pdf' | 'png' | 'pptx' | 'txt' | 'xlsx', required: 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`
        - `tool` string, required: The tool that renders the final node's output as a file, answering with `execute.response_mode: base64`
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `400`: Bad request — a malformed or invalid definition
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Playbook not found
- `409`: 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.

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
