Scheduled jobs & webhooks
Run something inside a machine on a schedule (cron) or when a URL is called (webhook). Both are available in the REST API, the platform MCP, and the machine's own MCP (so an agent can automate its own computer).
#Actions
Every schedule and webhook runs one action:
type |
Fields | What it does |
|---|---|---|
shell |
command, timeoutSeconds (default 300, max 900) |
Runs a bash command as the agent user. A non-zero exit is a failed run. |
tool |
tool, arguments, passInput |
Calls any machine MCP tool, for example browser_navigate. With passInput, a webhook's JSON body is merged into arguments. |
http |
port, path, method, headers, body |
Calls an app running inside the machine, even one listening only on 127.0.0.1. |
{ "type": "shell", "command": "python3 ~/reports/daily.py" }
{ "type": "tool", "tool": "browser_navigate", "arguments": { "url": "https://news.ycombinator.com" } }
{ "type": "http", "port": 5000, "path": "/hooks/stripe", "method": "POST" }
#Stopped machines
| Option | Default | |
|---|---|---|
wakeIfStopped |
true |
Start the machine for the run (it restores its saved state, usually in a few seconds) |
stopAfter |
false |
Stop the machine again after the run, if it was woken for it, so you only pay while jobs run |
#Scheduled jobs
Cron uses 5 fields in UTC: minute hour day-of-month month day-of-week. Lists, ranges, steps, month and day names, and @hourly, @daily, @weekly, @monthly all work. The shortest interval is one minute. If a run is still going when the next one is due, the new one is recorded as skipped.
| Method | Path | |
|---|---|---|
GET |
/api/machines/{id}/schedules |
List, with nextRunAt, lastRunAt and lastStatus |
POST |
/api/machines/{id}/schedules |
Create: name, cron, action, wakeIfStopped, stopAfter, enabled |
PATCH |
/api/machines/{id}/schedules/{scheduleId} |
Change any field |
DELETE |
/api/machines/{id}/schedules/{scheduleId} |
Delete |
POST |
/api/machines/{id}/schedules/{scheduleId}/run |
Run now and wait for the result |
curl -X POST https://burrowbox.dev/api/machines/2f6aeedcd3/schedules \
-H "Authorization: Bearer $BURROWBOX_KEY" -H "Content-Type: application/json" \
-d '{
"name": "Morning report",
"cron": "0 9 * * mon-fri",
"action": { "type": "shell", "command": "python3 ~/reports/daily.py" },
"wakeIfStopped": true,
"stopAfter": true
}'
The response includes the next three run times in upcoming, so you can check the expression.
#Webhooks
A webhook is a URL that runs an action when it's called with any HTTP method. The URL contains a secret: it's shown once, when you create or rotate the webhook.
| Method | Path | |
|---|---|---|
GET |
/api/machines/{id}/webhooks |
List (without URLs) |
POST |
/api/machines/{id}/webhooks |
Create: name, action, mode, wakeIfStopped, stopAfter. Returns url. |
PATCH |
/api/machines/{id}/webhooks/{webhookId} |
Change name, action, mode, enabled… |
POST |
/api/machines/{id}/webhooks/{webhookId}/rotate |
New secret and URL (the old URL stops working) |
DELETE |
/api/machines/{id}/webhooks/{webhookId} |
Delete |
curl -X POST https://burrowbox.dev/api/machines/2f6aeedcd3/webhooks \
-H "Authorization: Bearer $BURROWBOX_KEY" -H "Content-Type: application/json" \
-d '{ "name": "Orders", "mode": "sync", "action": { "type": "http", "port": 5000, "path": "/orders" } }'
# → { "id": "wh_…", "url": "https://burrowbox.dev/hooks/wh_…/…", … }
#Calling a webhook
curl -X POST "https://burrowbox.dev/hooks/wh_…/…" -H "Content-Type: application/json" -d '{"order": 42}'
mode |
Response |
|---|---|
sync (default) |
Waits for the run. For an http action, you get the app's own response (status, content type, body), so the webhook works like a reverse proxy to your app. For shell and tool actions: {"runId", "status", "output"}. |
async |
202 {"accepted": true, "runId": "run_…"} immediately; check the result under runs |
What the action receives:
http: the caller's method, body, content type and query string are forwarded.shell: the body is saved to a file whose path is in$BURROWBOX_INPUT_FILE.$BURROWBOX_RUN_IDis also set.toolwithpassInput: true: a JSON object body is merged into the tool'sarguments.
Payloads can be up to 1 MB.
#Runs
GET /api/machines/{id}/runs?limit=50 lists recent runs from schedules, webhooks and manual triggers: status (running, ok, error, skipped), output (up to 16 KB), start and finish times.
#MCP
Platform MCP (/mcp, takes id) |
Machine MCP (/api/machines/{id}/mcp) |
|---|---|
machine_schedule_create |
schedule_create |
machine_schedule_list |
schedule_list |
machine_schedule_update |
schedule_update |
machine_schedule_delete |
schedule_delete |
machine_schedule_run |
schedule_run |
machine_webhook_create |
webhook_create |
machine_webhook_list |
webhook_list |
machine_webhook_rotate |
webhook_rotate |
machine_webhook_delete |
webhook_delete |
machine_job_runs |
job_runs |
The machine MCP's automation tools work even while the machine is stopped.