A routine that scrapes, briefs and screenshots
One routine runs vision/scrape@1, infer/chat@1 and render/screenshot@1 on the node: a one-sentence brief and a styled screenshot, no glue code.
A routine chains product calls on the node itself, so the Markdown from the scrape reaches the model without a round
trip through your code. The screenshot step has needs: [], so it starts at the same time as the scrape. Save this
definition as page-brief.json:
json{ "name": "page-brief", "inputs": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string" } } }, "steps": [ { "id": "page", "uses": "vision/scrape@1", "with": { "url": "${{ inputs.url }}", "formats": ["markdown"] } }, { "id": "brief", "uses": "infer/chat@1", "needs": ["page"], "with": { "model": "b3os/qwen3.5-9b", "max_tokens": 120, "chat_template_kwargs": { "enable_thinking": false }, "messages": [ { "role": "system", "content": "In one plain sentence of at most 30 words, say what this web page is for." }, { "role": "user", "content": "${{ steps.page.outputs.markdown }}" } ] } }, { "id": "shot", "uses": "render/screenshot@1", "needs": [], "with": { "url": "${{ inputs.url }}", "style": { "background": "b3os-dark", "frame": "browser-dark" }, "format": "webp" } } ], "output": { "brief": "${{ steps.brief.outputs.choices[0].message.content }}", "screenshot": "${{ steps.shot.outputs.artifacts[0].url }}" }}
Verified 2026-11-01: this recipe's last run passed against https://beta-api.b3os.org, label live-recorded (recorded from production), from commit 771a9508. The recipe harness runs it again, and a failed run takes this page out of the cookbook.
Run it
bashcurl -s -X PUT https://beta-api.b3os.org/v1/routines/page-brief \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d @page-brief.jsoncurl -s "https://beta-api.b3os.org/v1/routines/page-brief/runs?wait=60" \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input": {"url": "https://beta.b3os.org/demo/billing/"}}' \ | jq '{status, result, steps: [.steps[] | {id, status, duration_ms}]}'
tsimport { readFileSync } from "node:fs";const API = "https://beta-api.b3os.org";const headers = { authorization: `Bearer ${process.env.B3OS_API_KEY}`, "content-type": "application/json" };await fetch(`${API}/v1/routines/page-brief`, { method: "PUT", headers, body: readFileSync("page-brief.json", "utf8"),});const run: any = await ( await fetch(`${API}/v1/routines/page-brief/runs?wait=60`, { method: "POST", headers, body: JSON.stringify({ input: { url: "https://beta.b3os.org/demo/billing/" } }), })).json();console.log(run.status, run.result.brief);for (const step of run.steps) console.log(step.id, step.status, step.duration_ms);
pythonimport osimport requestsAPI = "https://beta-api.b3os.org"s = requests.Session()s.headers["Authorization"] = f"Bearer {os.environ['B3OS_API_KEY']}"with open("page-brief.json") as f: s.put(f"{API}/v1/routines/page-brief", data=f.read(), headers={"Content-Type": "application/json"})run = s.post( f"{API}/v1/routines/page-brief/runs", params={"wait": 60}, json={"input": {"url": "https://beta.b3os.org/demo/billing/"}},).json()print(run["status"], run["result"]["brief"])for step in run["steps"]: print(step["id"], step["status"], step["duration_ms"])
Publish the routine once with the API or the dashboard. Then, with the B3OS MCP server connected, ask your agent:
textRun the page-brief routine for https://beta.b3os.org/demo/billing/ and tell me what the page is for.
The agent calls routine_run with {"routine": "page-brief", "input": {"url": "https://beta.b3os.org/demo/billing/"}}.
The recipe's run.mjs publishes this definition under a fresh name, with the screenshot's content type and size in
output in place of its URL so its checks can read them, runs it once and deletes it. steps[].duration_ms is each
step's time on the node.
Owner: Docs. Products: Docs, Routines, Vision, Infer, Render.
Output of the last green run
bash$ node run.mjs# exit 0
json{ "run_status": "succeeded", "steps": [ { "id": "page", "uses": "vision/scrape@1", "status": "succeeded", "duration_ms": 0.49 }, { "id": "brief", "uses": "infer/chat@1", "status": "succeeded", "duration_ms": 116.266 }, { "id": "shot", "uses": "render/screenshot@1", "status": "succeeded", "duration_ms": 781.697 } ], "brief": "This page displays billing plans and invoices for a fictional Example workspace demo.", "screenshot_type": "image/webp", "screenshot_bytes": 61424, "run_ms": 918.3, "deleted": true}
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
771a9508
, label
live-recorded
(recorded from production). Case
recipes/routine-page-brief
.