Run from your environment via API
3min read
Use Run in the app, or call the API from your environment. Set BALLET_API_TOKEN to a workspace API token.
HTTP
Starts the run, then polls GET /runs/:id until it's completed, failed, or cancelled.
:::warning
API runs need a complete input body. You will need to manually input this.
:::
cURL
# 1. Start the run and capture the runId
RUN_ID=$(curl -s --request POST \
--url '/api/playbooks/:id/run' \
--header "Authorization: Bearer $BALLET_API_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"input":{}}' | jq -r .runId)
# 2. Poll until the run finishes
while true; do
RUN=$(curl -s -H "Authorization: Bearer $BALLET_API_TOKEN" \
'/api/runs/"$RUN_ID"')
echo "$RUN" | jq -r .status
case "$(echo "$RUN" | jq -r .status)" in
completed|failed|cancelled) echo "$RUN" | jq .; break ;;
esac
sleep 2
done
Node
// 1. Start the run
const res = await fetch('/api/playbooks/:id/run', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BALLET_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"input": {}
}),
});
const { runId } = await res.json();
// 2. Poll until the run finishes
const terminal = new Set(['completed', 'failed', 'cancelled']);
while (true) {
const r = await fetch(`/api/runs/${runId}`, {
headers: { Authorization: `Bearer ${process.env.BALLET_API_TOKEN}` },
});
const run = await r.json();
console.log(run.status);
if (terminal.has(run.status)) { console.log(run); break; }
await new Promise(r => setTimeout(r, 2000));
}
Python
import json, os, time, requests
# Round-trip through json.loads so JSON booleans / null work in Python.
payload = json.loads('''{
"input": {}
}''')
# 1. Start the run
res = requests.post(
'/api/playbooks/:id/run',
headers={
'Authorization': f"Bearer {os.environ['BALLET_API_TOKEN']}",
'Content-Type': 'application/json',
},
json=payload,
)
res.raise_for_status()
run_id = res.json()['runId']
# 2. Poll until the run finishes
terminal = {'completed', 'failed', 'cancelled'}
headers = {'Authorization': f"Bearer {os.environ['BALLET_API_TOKEN']}"}
while True:
run = requests.get(f"/api/runs/{run_id}", headers=headers).json()
print(run['status'])
if run['status'] in terminal:
print(run)
break
time.sleep(2)
Stream
One request — streams live step events over SSE and closes on run_stop. Add Accept: text/event-stream.
cURL
curl -N --request POST \
--url '/api/playbooks/:id/run' \
--header "Authorization: Bearer $BALLET_API_TOKEN" \
--header 'Accept: text/event-stream' \
--header 'Content-Type: application/json' \
--data '{"input":{}}'
# Streams SSE on one connection: stream_start, step_start,
# step_stop, … then run_stop (final output) or
# stream:timeout (run still in progress — poll GET /runs/:id).
Node
const res = await fetch('/api/playbooks/:id/run', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.BALLET_API_TOKEN}`,
Accept: 'text/event-stream',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"input": {}
}),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buf = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
const parts = buf.split('\n\n');
buf = parts.pop() ?? '';
for (const part of parts) {
if (!part.includes('data:')) continue;
const data = JSON.parse(part.match(/data: (.+)/)?.[1] ?? '{}');
console.log(part.match(/event: (.+)/)?.[1] ?? 'message', data);
if (part.includes('event: run_stop')) process.exit(0);
}
}
Python
import json, os, requests
# Round-trip through json.loads so JSON booleans / null work in Python.
payload = json.loads('''{
"input": {}
}''')
headers = {
'Authorization': f"Bearer {os.environ['BALLET_API_TOKEN']}",
'Accept': 'text/event-stream',
'Content-Type': 'application/json',
}
with requests.post('/api/playbooks/:id/run', headers=headers, json=payload, stream=True) as res:
res.raise_for_status()
buf = ''
for chunk in res.iter_content(decode_unicode=True):
if not chunk: continue
buf += chunk
while '\n\n' in buf:
part, buf = buf.split('\n\n', 1)
if 'data:' not in part: continue
data = json.loads(part.split('data:', 1)[1].strip())
print(data)
if 'run_stop' in part:
raise SystemExit(0)
:::tip Looking to create an API token?
You can create an API token by going to Settings > API Tokens
:::
