Approve, then screenshot
A routine scrapes a page, waits for a person's approval as a signal, and only then takes a screenshot of it.
A wait step holds a run until someone answers it: here a routine reads a page with vision/scrape@1, waits for
the approve signal, and takes a screenshot with render/screenshot@1 only when the payload says
approved: true. The run costs nothing while it waits, and gives up after the step's ms (10 minutes here). The
recipe publishes the routine, starts a run, sends the approval once the run waits for it, and deletes the routine.
Owner: routines.
Not verified yet: no docs-examples run has covered the runnable snippets on this page.
The routine
Save it as routine.json:
json{ "name": "recipe-approve-then-screenshot", "inputs": { "type": "object", "properties": { "url": { "type": "string", "default": "https://example.com" } } }, "steps": [ { "id": "page", "uses": "vision/scrape@1", "with": { "url": "${{ inputs.url }}", "formats": ["markdown"] } }, { "id": "approval", "wait": { "signal": "approve", "ms": 600000 } }, { "id": "shot", "if": "${{ steps.approval.outputs.payload.approved == true }}", "uses": "render/screenshot@1", "with": { "url": "${{ inputs.url }}" } } ], "output": { "summary": "Approved by ${{ steps.approval.outputs.payload.by }}; screenshot taken", "screenshot": "${{ steps.shot.outputs.artifacts[0].url }}" }}
Run it
Publish it, start a run, and approve it once it waits:
bashcurl -s -X PUT https://beta-api.b3os.org/v1/routines/recipe-approve-then-screenshot \ -H "Authorization: Bearer $B3OS_API_KEY" -H "Content-Type: application/json" -d @routine.jsonRUN_ID=$(curl -s https://beta-api.b3os.org/v1/routines/recipe-approve-then-screenshot/runs \ -H "Authorization: Bearer $B3OS_API_KEY" -H "Content-Type: application/json" -d '{}' | jq -r .id)curl -s "https://beta-api.b3os.org/v1/routines/runs/$RUN_ID/signal" \ -H "Authorization: Bearer $B3OS_API_KEY" -H "Content-Type: application/json" \ -d '{"name": "approve", "payload": {"approved": true, "by": "ops"}}'
tsimport { readFileSync } from "node:fs";import { B3OS } from "@b3os/sdk";const b3os = new B3OS(); // reads B3OS_API_KEYconst def = JSON.parse(readFileSync("routine.json", "utf8"));await b3os.routines.publishRoutine(def.name, def);const run = await b3os.routines.createRun(def.name, {});await b3os.routines.signalRun(run.id, { name: "approve", payload: { approved: true, by: "ops" } });const done = await b3os.routines.getRun(run.id);console.log(done.status, done.result);
pythonimport jsonfrom b3os import B3OSb3os = B3OS() # reads B3OS_API_KEYdefinition = json.load(open("routine.json"))b3os.routines.publish_routine(definition["name"], definition)run = b3os.routines.create_run(definition["name"], {})b3os.routines.signal_run(run["id"], {"name": "approve", "payload": {"approved": True, "by": "ops"}})print(b3os.routines.get_run(run["id"])["status"])
bashb3os routine deploy routine.jsonb3os routine run recipe-approve-then-screenshot --no-waitb3os routine signal "$RUN_ID" approve --payload '{"approved": true, "by": "ops"}'
What it produced
bash$ node run.mjs# exit 0
json{ "run_id": "run_01m6awj44cqa6qx6h9v6d0crs2", "run_status": "succeeded", "waited_for_approval": true, "signal_to_finish_ms": 172, "steps": [ { "id": "page", "uses": "vision/scrape@1", "status": "succeeded", "duration_ms": 0.25 }, { "id": "approval", "uses": "core/wait@1", "status": "succeeded", "duration_ms": 564.373 }, { "id": "shot", "uses": "render/screenshot@1", "status": "succeeded", "duration_ms": 148.029 } ], "markdown_chars": 156, "screenshot_taken": true, "screenshot_format": "webp"}
The outputs this recipe publishes (its recipe.json publish.outputs), with workspace identifiers redacted. The run's stdout, stderr and other outputs are not published.
Captured 2026-11-01 from commit an unknown commit, label
live-recorded
(recorded from production). Case
recipes/approve-then-screenshot
.
How it works
A signal sent before the step waits is buffered, so the approval can come at any time after the run starts. The
waiting step completes with the payload as its outputs, and the shot step's if reads it: a payload without
approved: true skips the screenshot. In the dashboard, the run page shows the waiting step with a payload box
that sends the signal. A signal to a run that already finished answers 409 run_not_waiting.
Owner: Routines. Products: Routines, Vision, Render.
Output of the last green run
bash$ node run.mjs# exit 0
json{ "run_id": "run_01m6awj44cqa6qx6h9v6d0crs2", "run_status": "succeeded", "waited_for_approval": true, "signal_to_finish_ms": 172, "steps": [ { "id": "page", "uses": "vision/scrape@1", "status": "succeeded", "duration_ms": 0.25 }, { "id": "approval", "uses": "core/wait@1", "status": "succeeded", "duration_ms": 564.373 }, { "id": "shot", "uses": "render/screenshot@1", "status": "succeeded", "duration_ms": 148.029 } ], "markdown_chars": 156, "screenshot_taken": true, "screenshot_format": "webp"}
The outputs this recipe publishes (its recipe.json publish.outputs), with workspace identifiers redacted. The run's stdout, stderr and other outputs are not published.
Captured 2026-11-01 from commit an unknown commit, label
live-recorded
(recorded from production). Case
recipes/approve-then-screenshot
.