# Create or edit a playbook

> playbook_save — Without playbook_id: create.

Source: https://x.builera.app/docs/en/reference/playbook_save

`playbook_save` · Changes an X-Buddy setting

Without playbook_id: create. With playbook_id: replace name, description and tasks (expected_revision required). Accounts that already received it change only when the playbook is applied again.

| | |
|---|---|
| REST | `POST /api/public/v1/playbooks` |
| MCP tool | `playbook_save` |
| Permission needed | `tasks.manage` — Owner, Manager, Operator |
| Scope group | `automation:write` |
| Group | Tasks and playbooks |
| Shown up front in profiles | Automation |

## Input {#input}

| Field | Type | Required | Description |
|---|---|---|---|
| `playbook_id` | string (uuid) | No | Playbook to change (omit to create). |
| `name` | string (1–80 chars) | Yes |  |
| `description` | string (0–500 chars) | No |  |
| `tasks` | array of object (1–12 items) | Yes |  |
| `expected_revision` | integer (1–∞) | No | The revision you read (optimistic locking); a newer revision fails with revision_conflict. |

## Result {#output}

Returns the operation's result in the `{ data, meta }` envelope.

> [!IMPORTANT]
> Needs an idempotency key (`Idempotency-Key`, 8–128 characters). Retrying with the same key never repeats the action.

> [!NOTE]
> Versioned setting: send the `expected_revision` (or `If-Match`) you read, or the write is refused.

## Example {#example}

### Call over REST {#example-rest}

```bash
curl -X POST "https://reach-link-craft.lovable.app/api/public/v1/playbooks" \
  -H "Authorization: Bearer $XBUDDY_API_KEY" \
  -H "Idempotency-Key: 6f1c2a90-example-key" \
  -H 'If-Match: "3"' \
  -H "Content-Type: application/json" \
  -d '{"name":"Example","tasks":[{"type":"timeline_browse","name":"Example","settings":{},"schedule":{"days":[1],"runsPerDay":1}}]}'
```

### Call over MCP {#example-mcp}

```json
{
  "method": "tools/call",
  "params": {
    "name": "playbook_save",
    "arguments": {
      "name": "Example",
      "tasks": [
        {
          "type": "timeline_browse",
          "name": "Example",
          "settings": {},
          "schedule": {
            "days": [
              1
            ],
            "runsPerDay": 1
          }
        }
      ],
      "idempotency_key": "6f1c2a90-example-key",
      "expected_revision": 3
    }
  }
}
```

## Errors you may get {#errors}

| Code | HTTP | Retryable |
|---|---|---|
| [`unauthorized`](/docs/en/errors/unauthorized) | 401 | No |
| [`key_expired`](/docs/en/errors/key_expired) | 401 | No |
| [`integration_revoked`](/docs/en/errors/integration_revoked) | 401 | No |
| [`connection_not_linked`](/docs/en/errors/connection_not_linked) | 403 | No |
| [`integration_paused`](/docs/en/errors/integration_paused) | 403 | No |
| [`workspace_integrations_paused`](/docs/en/errors/workspace_integrations_paused) | 403 | No |
| [`integration_authorization_lost`](/docs/en/errors/integration_authorization_lost) | 403 | No |
| [`workspace_suspended`](/docs/en/errors/workspace_suspended) | 403 | No |
| [`forbidden`](/docs/en/errors/forbidden) | 403 | No |
| [`invalid_input`](/docs/en/errors/invalid_input) | 422 | No |
| [`rate_limited`](/docs/en/errors/rate_limited) | 429 | Yes |
| [`internal`](/docs/en/errors/internal) | 500 | Yes |
| [`read_only`](/docs/en/errors/read_only) | 403 | No |
| [`idempotency_key_required`](/docs/en/errors/idempotency_key_required) | 428 | No |
| [`idempotency_conflict`](/docs/en/errors/idempotency_conflict) | 409 | No |
| [`idempotency_processing`](/docs/en/errors/idempotency_processing) | 409 | Yes |
| [`conflict`](/docs/en/errors/conflict) | 409 | No |
| [`precondition_failed`](/docs/en/errors/precondition_failed) | 422 | No |
| [`revision_required`](/docs/en/errors/revision_required) | 428 | No |
| [`revision_conflict`](/docs/en/errors/revision_conflict) | 412 | No |
| [`not_found`](/docs/en/errors/not_found) | 404 | No |

[Back to all operations](/docs/en/reference)
