---
title: "How do I run a playbook over the REST API?"
description: "TL;DR: Run a playbook by POSTing to /api/playbooks/:id/execute with your Bearer token. The response streams the run as Server-Sent Events on the same connection; the final run_stop event carries success and output. Read events as they arrive for live progress, or consume the stream to completion to get the result."
canonical_url: "https://docs.ballet.dev/articles/how-do-i-run-a-playbook-over-the-rest-api-KQIz0apagm"
md_url: "https://docs.ballet.dev/articles/how-do-i-run-a-playbook-over-the-rest-api-KQIz0apagm.md"
---
# How do I run a playbook over the REST API?

**TL;DR:** Run a playbook by POSTing to `/api/playbooks/:id/execute` with your Bearer token. The response streams the run as Server-Sent Events on the same connection; the final `run_stop` event carries `success` and `output`. Read events as they arrive for live progress, or consume the stream to completion to get the result.

## Who this is for

Developers triggering playbook runs from a backend, script, or CI job.

## How do I execute a playbook?

Send a `POST` to the execute endpoint with the playbook ID and your token. Include any inputs your playbook expects in the JSON body.

```bash
curl -N https://app.ballet.dev/api/playbooks/$PLAYBOOK_ID/execute \
  -H "Authorization: Bearer $BALLET_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "input": { "customerId": "cus_123" } }'
```

The connection stays open and streams the run as Server-Sent Events. The `-N` flag disables curl buffering so you see frames live.

## How do I get the result?

The run finishes with a `run_stop` event that includes `success`, `output`, and `totalDurationMs`. Read frames until you see it:

```ts
const res = await fetch(
  `https://app.ballet.dev/api/playbooks/${playbookId}/execute`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BALLET_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ input: { customerId: "cus_123" } }),
  },
);

const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";
let result;

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  for (const frame of buffer.split("\n\n")) {
    const line = frame.split("\n").find((l) => l.startsWith("data:"));
    if (!line) continue;
    const event = JSON.parse(line.slice(5).trim());
    if (event.type === "run_stop") result = event.run;
  }
  buffer = buffer.slice(buffer.lastIndexOf("\n\n") + 2);
}

console.log(result?.success, result?.output);
```

See [Stream run events](/articles/how-do-i-stream-playbook-run-events-SsXLWiWt9X) for the full event envelope and every frame type.

## How do I watch all runs on a playbook?

Subscribe to `GET /api/playbooks/:id/runs/stream` to receive run and step events for every run on that playbook — useful for dashboards and observers that aren't the caller that started the run.

## What about other resources?

A workspace API token also authenticates the rest of Ballet's management surface (listing playbooks, agents, skills, and tools). If you prefer a tool-based interface over raw REST, the same operations are exposed through [the MCP endpoint](/articles/how-do-i-connect-to-ballets-mcp-endpoint-1ydPKBzHZm).

## Related articles

- [How do I authenticate with the Ballet API?](/articles/how-do-i-authenticate-with-the-ballet-api-tWaqCPLyGT)
- [Stream run events](/articles/how-do-i-stream-playbook-run-events-SsXLWiWt9X)
- [Webhooks](/articles/how-do-i-trigger-and-react-to-playbooks-with-webhooks-DYv9XyeU51)
- [How do I read runs and run history?](/articles/how-do-i-read-runs-and-run-history-WukfYP5du3)
