One routine, four products: a cron trigger starts it every day at 13:00 UTC, vision/scrape@1 reads the page, infer/chat@1 writes the digest with b3os/qwen3.5-9b, and io.b3os/slack/send-message@1 posts it with the project's Slack connection from the vault. The recipe publishes the routine, runs it once instead of waiting for 13:00, reads the trigger's next fire time, and deletes the routine. Owner: routines.

Info

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-cron-scrape-slack", "inputs": { "type": "object", "properties": { "url": { "type": "string", "default": "https://news.ycombinator.com" }, "channel": { "type": "string", "default": "b3os-recipes" } } }, "triggers": [{ "type": "cron", "id": "daily", "cron": "0 0 13 * * *", "tz": "UTC", "catch_up": "skip" }], "steps": [ { "id": "page", "uses": "vision/scrape@1", "with": { "url": "${{ inputs.url }}", "formats": ["markdown"] } }, { "id": "digest", "uses": "infer/chat@1", "with": { "model": "b3os/qwen3.5-9b", "max_tokens": 300, "messages": [ { "role": "system", "content": "You write a three-bullet digest of a web page for a Slack channel. Plain text, one line per bullet, each starting with '- '. No preamble." }, { "role": "user", "content": "Page: ${{ inputs.url }}\n\n${{ steps.page.outputs.markdown }}" } ] } }, { "id": "post", "uses": "io.b3os/slack/send-message@1", "with": { "channel": "${{ inputs.channel }}", "text": "Digest of ${{ inputs.url }}\n${{ steps.digest.outputs.choices[0].message.content }}" } } ], "output": { "summary": "Posted a digest of ${{ inputs.url }} to ${{ inputs.channel }}", "digest": "${{ steps.digest.outputs.choices[0].message.content }}" }}

Run it

Publish it, then start a run now: the same run the cron starts at 13:00.

bash
curl -s -X PUT https://beta-api.b3os.org/v1/routines/recipe-cron-scrape-slack \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d @routine.jsoncurl -s "https://beta-api.b3os.org/v1/routines/recipe-cron-scrape-slack/runs?wait=60" \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{}'

What it produced

bash
$ node run.mjs# exit 0

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 fixture (recorded against a pinned fixture). Case recipes/cron-scrape-slack .

How it works

The post step needs a Slack connection in your project's vault: a bot token with chat:write, the bot invited to the channel. The step uses the project's newest Slack connection, or the one you bind in the step's connections. Without the recipes' Slack workspace, the harness runs the same routine with the post step calling Slack's api.test method through core/http@1, which echoes the message and needs no token (label fixture).

The run's summary is the outcome line the run list and the dashboard show. If the post fails, replay the run: the scrape and the digest are copied, and only the post runs again. Delete the routine (DELETE /v1/routines/recipe-cron-scrape-slack) to stop the daily runs.

Owner: Routines. Products: Routines, Vision, Infer, Actions and triggers, Vault.

The last green run used fixture origins in place of the slack workspace, so the outputs below come from the fixture.

Output of the last green run

bash
$ node run.mjs# exit 0

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 fixture (recorded against a pinned fixture). Case recipes/cron-scrape-slack .

Ask a question... ⌘I