{
  "slug": "pilot/sdk/python",
  "title": "Pilot Python SDK",
  "description": "Async and sync Python clients for the Cuitty Pilot API — manage playbooks, runs, and providers with httpx.",
  "url": "https://cuitty.com/docs/pilot/sdk/python",
  "markdown_url": "https://cuitty.com/docs/pilot/sdk/python.md",
  "json_url": "https://cuitty.com/docs/pilot/sdk/python.json",
  "frontmatter": {
    "title": "Pilot Python SDK",
    "description": "Async and sync Python clients for the Cuitty Pilot API — manage playbooks, runs, and providers with httpx.",
    "order": 16,
    "section": "Pilot",
    "updatedAt": "2026-06-01"
  },
  "headings": [
    {
      "depth": 1,
      "slug": "pilot-python-sdk",
      "text": "Pilot Python SDK"
    },
    {
      "depth": 2,
      "slug": "install",
      "text": "Install"
    },
    {
      "depth": 2,
      "slug": "quick-start",
      "text": "Quick start"
    },
    {
      "depth": 3,
      "slug": "async",
      "text": "Async"
    },
    {
      "depth": 3,
      "slug": "synchronous",
      "text": "Synchronous"
    },
    {
      "depth": 2,
      "slug": "pilotclient-async",
      "text": "PilotClient (async)"
    },
    {
      "depth": 2,
      "slug": "pilotclientsync",
      "text": "PilotClientSync"
    },
    {
      "depth": 2,
      "slug": "playbooks",
      "text": "Playbooks"
    },
    {
      "depth": 2,
      "slug": "runs",
      "text": "Runs"
    },
    {
      "depth": 2,
      "slug": "types",
      "text": "Types"
    },
    {
      "depth": 3,
      "slug": "core-types",
      "text": "Core types"
    },
    {
      "depth": 3,
      "slug": "request-types",
      "text": "Request types"
    },
    {
      "depth": 2,
      "slug": "error-handling",
      "text": "Error handling"
    },
    {
      "depth": 2,
      "slug": "see-also",
      "text": "See also"
    }
  ],
  "body_markdown": "# Pilot Python SDK\n\n`cuitty-pilot` provides both async (`PilotClient`) and synchronous (`PilotClientSync`) Python clients for the Pilot API. Built on `httpx` with dataclass types and context manager support.\n\n## Install\n\n```bash\nsfw pip install cuitty-pilot\n```\n\nRequires Python 3.10+ and `httpx`.\n\n## Quick start\n\n### Async\n\n```python\nimport asyncio\nfrom cuitty_pilot import PilotClient, StartRunRequest\n\nasync def main():\n    async with PilotClient(\"http://localhost:4320\", auth_token=\"pk_...\") as client:\n        # List all playbooks\n        playbooks = await client.list_playbooks()\n\n        # Start a run\n        resp = await client.start_run(StartRunRequest(\n            playbook_id=\"cloudflare.dns.add-a-record\",\n            mode=\"autonomous\",\n            inputs={\"zone\": \"example.com\", \"record_name\": \"app\", \"ip_address\": \"1.2.3.4\"},\n        ))\n        print(f\"Run started: {resp['id']} ({resp['status']})\")\n\n        # Get run details\n        run = await client.get_run(resp[\"id\"])\n        print(f\"Steps: {len(run.steps)}\")\n\nasyncio.run(main())\n```\n\n### Synchronous\n\n```python\nfrom cuitty_pilot import PilotClientSync, StartRunRequest\n\nwith PilotClientSync(\"http://localhost:4320\", auth_token=\"pk_...\") as client:\n    playbooks = client.list_playbooks()\n    resp = client.start_run(StartRunRequest(\n        playbook_id=\"cloudflare.dns.add-a-record\",\n        inputs={\"zone\": \"example.com\", \"record_name\": \"app\", \"ip_address\": \"1.2.3.4\"},\n    ))\n    run = client.get_run(resp[\"id\"])\n    client.close()\n```\n\n## PilotClient (async)\n\nThe primary client. Supports `async with` as a context manager.\n\n```python\nclient = PilotClient(\n    base_url=\"http://localhost:4320\",\n    auth_token=\"ey...\",     # Optional bearer token\n    timeout=30.0,           # Per-request timeout in seconds (default 30.0)\n)\n```\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `health()` | `HealthStatus` | Check server health |\n| `list_playbooks(target?)` | `list[PlaybookSummary]` | List playbooks, optionally filtered |\n| `get_playbook(id)` | `Playbook` | Get playbook by ID (latest version) |\n| `create_playbook(req)` | `dict` (`{id, version, created}`) | Create a new playbook |\n| `update_playbook(id, req)` | `dict` (`{id, version}`) | Update (version-bump) a playbook |\n| `delete_playbook(id)` | `dict` (`{deleted}`) | Delete all versions |\n| `start_run(req)` | `dict` (`{id, status}`) | Start a new run |\n| `list_runs(status?, playbook_id?, limit?)` | `list[Run]` | List runs with optional filters |\n| `get_run(id)` | `Run` | Get run details including steps |\n| `abort_run(id)` | `dict` (`{aborted, runId}`) | Abort a running or queued run |\n| `approve_run(id, req)` | `dict` (`{approved, runId, stepId, decision}`) | Submit approval decision |\n| `list_providers()` | `list[Provider]` | List all provider profiles |\n| `close()` | `None` | Close the underlying HTTP client |\n\n## PilotClientSync\n\nSynchronous wrapper with an identical API. Supports `with` as a context manager.\n\n```python\nclient = PilotClientSync(\n    base_url=\"http://localhost:4320\",\n    auth_token=\"ey...\",\n    timeout=30.0,\n)\n```\n\nAll methods are the same as `PilotClient` but without `await`.\n\n## Playbooks\n\n```python\n# List all\nplaybooks = await client.list_playbooks()\n\n# Filter by target\ncf_playbooks = await client.list_playbooks(target=\"cloudflare\")\n\n# Get one\npb = await client.get_playbook(\"cloudflare.dns.add-a-record\")\nprint(pb.title, pb.document_yaml)\n\n# Create\nresp = await client.create_playbook(CreatePlaybookRequest(\n    target=\"cloudflare\",\n    title=\"Add CNAME record\",\n    yaml=playbook_yaml,\n    authored_by=\"human\",\n))\n\n# Update\nawait client.update_playbook(\"cloudflare.dns.add-cname\",\n    UpdatePlaybookRequest(yaml=updated_yaml))\n\n# Delete\nawait client.delete_playbook(\"cloudflare.dns.add-cname\")\n```\n\n## Runs\n\n```python\n# Start a run\nresp = await client.start_run(StartRunRequest(\n    playbook_id=\"cloudflare.dns.add-a-record\",\n    mode=\"interactive\",\n    inputs={\"zone\": \"example.com\", \"record_name\": \"api\", \"ip_address\": \"1.2.3.4\"},\n    provider_profile_id=\"cf-prod\",\n))\n\n# Get run with steps\nrun = await client.get_run(resp[\"id\"])\nfor step in run.steps:\n    print(f\"{step.step_id}: {step.status} ({step.duration_ms}ms)\")\n\n# List runs with filters\nrunning = await client.list_runs(status=\"running\", limit=10)\n\n# Abort\nawait client.abort_run(run_id)\n\n# Approve a step\nawait client.approve_run(run_id, ApproveRequest(\n    step_id=\"save\",\n    decision=\"approve\",\n))\n```\n\n## Types\n\nAll types are Python `dataclass` instances.\n\n### Core types\n\n| Type | Fields |\n|------|--------|\n| `Playbook` | `id`, `version`, `target`, `title`, `document_yaml`, `authored_by`, `created_at` |\n| `PlaybookSummary` | `id`, `version`, `target`, `title`, `authored_by` |\n| `Run` | `id`, `project_id`, `playbook_id`, `playbook_version`, `mode`, `status`, `inputs_json`, `outputs_json`, `provider_profile_id?`, `started_at?`, `completed_at?`, `failed_step_id?`, `error_message?`, `steps: list[RunStep]` |\n| `RunStep` | `id`, `run_id`, `step_id`, `action`, `status`, `selector_tried_json`, `ai_used`, `started_at?`, `completed_at?`, `duration_ms?`, `error_message?` |\n| `Provider` | `id`, `project_id`, `provider`, `label`, `base_url`, `last_authenticated?`, `expires_at?` |\n| `HealthStatus` | `status`, `database`, `driver_pool: dict`, `provider_sessions: dict`, `last_checked` |\n\n### Request types\n\n| Type | Fields |\n|------|--------|\n| `StartRunRequest` | `playbook_id`, `mode=\"autonomous\"`, `version?`, `inputs?: dict`, `provider_profile_id?`, `project_id?` |\n| `CreatePlaybookRequest` | `target`, `title`, `yaml`, `authored_by?` |\n| `UpdatePlaybookRequest` | `yaml`, `target?`, `title?`, `authored_by?` |\n| `ApproveRequest` | `step_id`, `decision=\"approve\"` |\n\n## Error handling\n\nThe SDK raises typed exceptions from a common `PilotError` base class:\n\n```python\nfrom cuitty_pilot import PilotAPIError, PilotConnectionError\n\ntry:\n    await client.start_run(StartRunRequest(playbook_id=\"nonexistent\"))\nexcept PilotAPIError as e:\n    print(f\"API error {e.status}: {e.message}\")\nexcept PilotConnectionError as e:\n    print(f\"Cannot reach Pilot: {e}\")\n    if e.cause:\n        print(f\"Underlying: {e.cause}\")\n```\n\n| Exception | Properties | When |\n|-----------|-----------|------|\n| `PilotError` | base class | All SDK errors |\n| `PilotAPIError` | `status: int`, `message: str` | Non-2xx HTTP response |\n| `PilotConnectionError` | `message: str`, `cause: Exception \\| None` | Network or timeout failure |\n\n## See also\n\n- [JavaScript SDK](/docs/pilot/sdk/javascript) — TypeScript client with event streaming\n- [Playbook reference](/docs/pilot/playbook-reference) — YAML schema for playbook documents\n- [Quickstart](/docs/pilot/quickstart) — record and replay your first workflow",
  "body_html": "<h1 id=\"pilot-python-sdk\">Pilot Python SDK</h1>\n<p><code>cuitty-pilot</code> provides both async (<code>PilotClient</code>) and synchronous (<code>PilotClientSync</code>) Python clients for the Pilot API. Built on <code>httpx</code> with dataclass types and context manager support.</p>\n<h2 id=\"install\">Install</h2>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"bash\"><code><span class=\"line\"><span style=\"color:#B392F0\">sfw</span><span style=\"color:#9ECBFF\"> pip</span><span style=\"color:#9ECBFF\"> install</span><span style=\"color:#9ECBFF\"> cuitty-pilot</span></span></code></pre>\n<p>Requires Python 3.10+ and <code>httpx</code>.</p>\n<h2 id=\"quick-start\">Quick start</h2>\n<h3 id=\"async\">Async</h3>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> asyncio</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">from</span><span style=\"color:#E1E4E8\"> cuitty_pilot </span><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> PilotClient, StartRunRequest</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#F97583\">async</span><span style=\"color:#F97583\"> def</span><span style=\"color:#B392F0\"> main</span><span style=\"color:#E1E4E8\">():</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">    async</span><span style=\"color:#F97583\"> with</span><span style=\"color:#E1E4E8\"> PilotClient(</span><span style=\"color:#9ECBFF\">\"http://localhost:4320\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#FFAB70\">auth_token</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"pk_...\"</span><span style=\"color:#E1E4E8\">) </span><span style=\"color:#F97583\">as</span><span style=\"color:#E1E4E8\"> client:</span></span>\n<span class=\"line\"><span style=\"color:#6A737D\">        # List all playbooks</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">        playbooks </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.list_playbooks()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\">        # Start a run</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">        resp </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.start_run(StartRunRequest(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">            playbook_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"cloudflare.dns.add-a-record\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">            mode</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"autonomous\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">            inputs</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">{</span><span style=\"color:#9ECBFF\">\"zone\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"example.com\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">\"record_name\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"app\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">\"ip_address\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"1.2.3.4\"</span><span style=\"color:#E1E4E8\">},</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">        ))</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">        print</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"Run started: </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">resp[</span><span style=\"color:#9ECBFF\">'id'</span><span style=\"color:#E1E4E8\">]</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\"> (</span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">resp[</span><span style=\"color:#9ECBFF\">'status'</span><span style=\"color:#E1E4E8\">]</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">)\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\">        # Get run details</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">        run </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.get_run(resp[</span><span style=\"color:#9ECBFF\">\"id\"</span><span style=\"color:#E1E4E8\">])</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">        print</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"Steps: </span><span style=\"color:#79B8FF\">{len</span><span style=\"color:#E1E4E8\">(run.steps)</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">asyncio.run(main())</span></span></code></pre>\n<h3 id=\"synchronous\">Synchronous</h3>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#F97583\">from</span><span style=\"color:#E1E4E8\"> cuitty_pilot </span><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> PilotClientSync, StartRunRequest</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#F97583\">with</span><span style=\"color:#E1E4E8\"> PilotClientSync(</span><span style=\"color:#9ECBFF\">\"http://localhost:4320\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#FFAB70\">auth_token</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"pk_...\"</span><span style=\"color:#E1E4E8\">) </span><span style=\"color:#F97583\">as</span><span style=\"color:#E1E4E8\"> client:</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    playbooks </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> client.list_playbooks()</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    resp </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> client.start_run(StartRunRequest(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">        playbook_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"cloudflare.dns.add-a-record\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">        inputs</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">{</span><span style=\"color:#9ECBFF\">\"zone\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"example.com\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">\"record_name\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"app\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">\"ip_address\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"1.2.3.4\"</span><span style=\"color:#E1E4E8\">},</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    ))</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    run </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> client.get_run(resp[</span><span style=\"color:#9ECBFF\">\"id\"</span><span style=\"color:#E1E4E8\">])</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    client.close()</span></span></code></pre>\n<h2 id=\"pilotclient-async\">PilotClient (async)</h2>\n<p>The primary client. Supports <code>async with</code> as a context manager.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#E1E4E8\">client </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> PilotClient(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    base_url</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"http://localhost:4320\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    auth_token</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"ey...\"</span><span style=\"color:#E1E4E8\">,     </span><span style=\"color:#6A737D\"># Optional bearer token</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    timeout</span><span style=\"color:#F97583\">=</span><span style=\"color:#79B8FF\">30.0</span><span style=\"color:#E1E4E8\">,           </span><span style=\"color:#6A737D\"># Per-request timeout in seconds (default 30.0)</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">)</span></span></code></pre>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<table><thead><tr><th>Method</th><th>Returns</th><th>Description</th></tr></thead><tbody><tr><td><code>health()</code></td><td><code>HealthStatus</code></td><td>Check server health</td></tr><tr><td><code>list_playbooks(target?)</code></td><td><code>list[PlaybookSummary]</code></td><td>List playbooks, optionally filtered</td></tr><tr><td><code>get_playbook(id)</code></td><td><code>Playbook</code></td><td>Get playbook by ID (latest version)</td></tr><tr><td><code>create_playbook(req)</code></td><td><code>dict</code> (<code>{id, version, created}</code>)</td><td>Create a new playbook</td></tr><tr><td><code>update_playbook(id, req)</code></td><td><code>dict</code> (<code>{id, version}</code>)</td><td>Update (version-bump) a playbook</td></tr><tr><td><code>delete_playbook(id)</code></td><td><code>dict</code> (<code>{deleted}</code>)</td><td>Delete all versions</td></tr><tr><td><code>start_run(req)</code></td><td><code>dict</code> (<code>{id, status}</code>)</td><td>Start a new run</td></tr><tr><td><code>list_runs(status?, playbook_id?, limit?)</code></td><td><code>list[Run]</code></td><td>List runs with optional filters</td></tr><tr><td><code>get_run(id)</code></td><td><code>Run</code></td><td>Get run details including steps</td></tr><tr><td><code>abort_run(id)</code></td><td><code>dict</code> (<code>{aborted, runId}</code>)</td><td>Abort a running or queued run</td></tr><tr><td><code>approve_run(id, req)</code></td><td><code>dict</code> (<code>{approved, runId, stepId, decision}</code>)</td><td>Submit approval decision</td></tr><tr><td><code>list_providers()</code></td><td><code>list[Provider]</code></td><td>List all provider profiles</td></tr><tr><td><code>close()</code></td><td><code>None</code></td><td>Close the underlying HTTP client</td></tr></tbody></table>\n<h2 id=\"pilotclientsync\">PilotClientSync</h2>\n<p>Synchronous wrapper with an identical API. Supports <code>with</code> as a context manager.</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#E1E4E8\">client </span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\"> PilotClientSync(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    base_url</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"http://localhost:4320\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    auth_token</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"ey...\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    timeout</span><span style=\"color:#F97583\">=</span><span style=\"color:#79B8FF\">30.0</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">)</span></span></code></pre>\n<p>All methods are the same as <code>PilotClient</code> but without <code>await</code>.</p>\n<h2 id=\"playbooks\">Playbooks</h2>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#6A737D\"># List all</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">playbooks </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.list_playbooks()</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Filter by target</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">cf_playbooks </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.list_playbooks(</span><span style=\"color:#FFAB70\">target</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"cloudflare\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Get one</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">pb </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.get_playbook(</span><span style=\"color:#9ECBFF\">\"cloudflare.dns.add-a-record\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">print</span><span style=\"color:#E1E4E8\">(pb.title, pb.document_yaml)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Create</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">resp </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.create_playbook(CreatePlaybookRequest(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    target</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"cloudflare\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    title</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"Add CNAME record\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    yaml</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">playbook_yaml,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    authored_by</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"human\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">))</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Update</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">await</span><span style=\"color:#E1E4E8\"> client.update_playbook(</span><span style=\"color:#9ECBFF\">\"cloudflare.dns.add-cname\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">    UpdatePlaybookRequest(</span><span style=\"color:#FFAB70\">yaml</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">updated_yaml))</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Delete</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">await</span><span style=\"color:#E1E4E8\"> client.delete_playbook(</span><span style=\"color:#9ECBFF\">\"cloudflare.dns.add-cname\"</span><span style=\"color:#E1E4E8\">)</span></span></code></pre>\n<h2 id=\"runs\">Runs</h2>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#6A737D\"># Start a run</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">resp </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.start_run(StartRunRequest(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    playbook_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"cloudflare.dns.add-a-record\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    mode</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"interactive\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    inputs</span><span style=\"color:#F97583\">=</span><span style=\"color:#E1E4E8\">{</span><span style=\"color:#9ECBFF\">\"zone\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"example.com\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">\"record_name\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"api\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#9ECBFF\">\"ip_address\"</span><span style=\"color:#E1E4E8\">: </span><span style=\"color:#9ECBFF\">\"1.2.3.4\"</span><span style=\"color:#E1E4E8\">},</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    provider_profile_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"cf-prod\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">))</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Get run with steps</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">run </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.get_run(resp[</span><span style=\"color:#9ECBFF\">\"id\"</span><span style=\"color:#E1E4E8\">])</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">for</span><span style=\"color:#E1E4E8\"> step </span><span style=\"color:#F97583\">in</span><span style=\"color:#E1E4E8\"> run.steps:</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">    print</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"</span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">step.step_id</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">: </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">step.status</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\"> (</span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">step.duration_ms</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">ms)\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># List runs with filters</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">running </span><span style=\"color:#F97583\">=</span><span style=\"color:#F97583\"> await</span><span style=\"color:#E1E4E8\"> client.list_runs(</span><span style=\"color:#FFAB70\">status</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"running\"</span><span style=\"color:#E1E4E8\">, </span><span style=\"color:#FFAB70\">limit</span><span style=\"color:#F97583\">=</span><span style=\"color:#79B8FF\">10</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Abort</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">await</span><span style=\"color:#E1E4E8\"> client.abort_run(run_id)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6A737D\"># Approve a step</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">await</span><span style=\"color:#E1E4E8\"> client.approve_run(run_id, ApproveRequest(</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    step_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"save\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#FFAB70\">    decision</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"approve\"</span><span style=\"color:#E1E4E8\">,</span></span>\n<span class=\"line\"><span style=\"color:#E1E4E8\">))</span></span></code></pre>\n<h2 id=\"types\">Types</h2>\n<p>All types are Python <code>dataclass</code> instances.</p>\n<h3 id=\"core-types\">Core types</h3>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<table><thead><tr><th>Type</th><th>Fields</th></tr></thead><tbody><tr><td><code>Playbook</code></td><td><code>id</code>, <code>version</code>, <code>target</code>, <code>title</code>, <code>document_yaml</code>, <code>authored_by</code>, <code>created_at</code></td></tr><tr><td><code>PlaybookSummary</code></td><td><code>id</code>, <code>version</code>, <code>target</code>, <code>title</code>, <code>authored_by</code></td></tr><tr><td><code>Run</code></td><td><code>id</code>, <code>project_id</code>, <code>playbook_id</code>, <code>playbook_version</code>, <code>mode</code>, <code>status</code>, <code>inputs_json</code>, <code>outputs_json</code>, <code>provider_profile_id?</code>, <code>started_at?</code>, <code>completed_at?</code>, <code>failed_step_id?</code>, <code>error_message?</code>, <code>steps: list[RunStep]</code></td></tr><tr><td><code>RunStep</code></td><td><code>id</code>, <code>run_id</code>, <code>step_id</code>, <code>action</code>, <code>status</code>, <code>selector_tried_json</code>, <code>ai_used</code>, <code>started_at?</code>, <code>completed_at?</code>, <code>duration_ms?</code>, <code>error_message?</code></td></tr><tr><td><code>Provider</code></td><td><code>id</code>, <code>project_id</code>, <code>provider</code>, <code>label</code>, <code>base_url</code>, <code>last_authenticated?</code>, <code>expires_at?</code></td></tr><tr><td><code>HealthStatus</code></td><td><code>status</code>, <code>database</code>, <code>driver_pool: dict</code>, <code>provider_sessions: dict</code>, <code>last_checked</code></td></tr></tbody></table>\n<h3 id=\"request-types\">Request types</h3>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<table><thead><tr><th>Type</th><th>Fields</th></tr></thead><tbody><tr><td><code>StartRunRequest</code></td><td><code>playbook_id</code>, <code>mode=\"autonomous\"</code>, <code>version?</code>, <code>inputs?: dict</code>, <code>provider_profile_id?</code>, <code>project_id?</code></td></tr><tr><td><code>CreatePlaybookRequest</code></td><td><code>target</code>, <code>title</code>, <code>yaml</code>, <code>authored_by?</code></td></tr><tr><td><code>UpdatePlaybookRequest</code></td><td><code>yaml</code>, <code>target?</code>, <code>title?</code>, <code>authored_by?</code></td></tr><tr><td><code>ApproveRequest</code></td><td><code>step_id</code>, <code>decision=\"approve\"</code></td></tr></tbody></table>\n<h2 id=\"error-handling\">Error handling</h2>\n<p>The SDK raises typed exceptions from a common <code>PilotError</code> base class:</p>\n<pre class=\"astro-code github-dark\" style=\"background-color:#24292e;color:#e1e4e8; overflow-x: auto;\" tabindex=\"0\" data-language=\"python\"><code><span class=\"line\"><span style=\"color:#F97583\">from</span><span style=\"color:#E1E4E8\"> cuitty_pilot </span><span style=\"color:#F97583\">import</span><span style=\"color:#E1E4E8\"> PilotAPIError, PilotConnectionError</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#F97583\">try</span><span style=\"color:#E1E4E8\">:</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">    await</span><span style=\"color:#E1E4E8\"> client.start_run(StartRunRequest(</span><span style=\"color:#FFAB70\">playbook_id</span><span style=\"color:#F97583\">=</span><span style=\"color:#9ECBFF\">\"nonexistent\"</span><span style=\"color:#E1E4E8\">))</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">except</span><span style=\"color:#E1E4E8\"> PilotAPIError </span><span style=\"color:#F97583\">as</span><span style=\"color:#E1E4E8\"> e:</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">    print</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"API error </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">e.status</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">: </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">e.message</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">except</span><span style=\"color:#E1E4E8\"> PilotConnectionError </span><span style=\"color:#F97583\">as</span><span style=\"color:#E1E4E8\"> e:</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">    print</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"Cannot reach Pilot: </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">e</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">\"</span><span style=\"color:#E1E4E8\">)</span></span>\n<span class=\"line\"><span style=\"color:#F97583\">    if</span><span style=\"color:#E1E4E8\"> e.cause:</span></span>\n<span class=\"line\"><span style=\"color:#79B8FF\">        print</span><span style=\"color:#E1E4E8\">(</span><span style=\"color:#F97583\">f</span><span style=\"color:#9ECBFF\">\"Underlying: </span><span style=\"color:#79B8FF\">{</span><span style=\"color:#E1E4E8\">e.cause</span><span style=\"color:#79B8FF\">}</span><span style=\"color:#9ECBFF\">\"</span><span style=\"color:#E1E4E8\">)</span></span></code></pre>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<table><thead><tr><th>Exception</th><th>Properties</th><th>When</th></tr></thead><tbody><tr><td><code>PilotError</code></td><td>base class</td><td>All SDK errors</td></tr><tr><td><code>PilotAPIError</code></td><td><code>status: int</code>, <code>message: str</code></td><td>Non-2xx HTTP response</td></tr><tr><td><code>PilotConnectionError</code></td><td><code>message: str</code>, <code>cause: Exception | None</code></td><td>Network or timeout failure</td></tr></tbody></table>\n<h2 id=\"see-also\">See also</h2>\n<ul>\n<li><a href=\"/docs/pilot/sdk/javascript\">JavaScript SDK</a> — TypeScript client with event streaming</li>\n<li><a href=\"/docs/pilot/playbook-reference\">Playbook reference</a> — YAML schema for playbook documents</li>\n<li><a href=\"/docs/pilot/quickstart\">Quickstart</a> — record and replay your first workflow</li>\n</ul>",
  "links_out": [
    "/docs/pilot/sdk/javascript",
    "/docs/pilot/playbook-reference",
    "/docs/pilot/quickstart"
  ]
}