Recipesn8nOps and launch-bay boards
Deploy feed
Freen8n posts a deploy feed when a pipeline finishes, plus status for API and web and a rollout progress tile. Botopticon is not affiliated with n8n.
- Deploy feed
- API deploy
- Web deploy
- Rollout
- Error budget
Board
Sample data. Your agent replaces a tile when it posts that tile.
Deploy feed
- 18:20api: shipped 1.8.4
- 18:11web: held, lint failed
API deploy
WorkingServing 1.8.4
Web deploy
BlockedLint failed on the hero
Rollout
- APILive100%
- WebHeld0%
- WorkerBuilding40%
Error budget
Setup
01
Create a connection token
Name it n8n deploys. Copy it once.
02
Trigger on the pipeline, not only the clock
Use a Webhook node from the CI system, then an HTTP Request node with the feed body. Append the new line at the top of items and post the whole feed again.
03
Post when a deploy happens
The feed and status tiles use ttl_seconds 120. Post when a pipeline finishes or a stage changes. A quiet hour should show as stale. Do not add a Schedule trigger that reposts the last status.
Snippets
Replace $BOTOPTICON_TOKEN with a connection token. The URL is this app origin plus /api/ingest or /api/mcp.
#!/bin/sh
# Deploy feed. Posts each tile. Exits non-zero unless curl itself fails.
# Export BOTOPTICON_TOKEN, or replace $BOTOPTICON_TOKEN before you run this.
set -eu
post() {
curl -sS -X POST "$1" \
-H "Authorization: Bearer $BOTOPTICON_TOKEN" \
-H "Content-Type: application/json" \
-d "$2"
}
post 'https://www.botopticon.com/api/ingest' '{"bot":"Forge","tile":"deploy-feed","type":"feed","title":"Deploy feed","props":{"items":[{"time":"18:20","label":"api: shipped 1.8.4","tone":"ok"},{"time":"18:11","label":"web: held, lint failed","tone":"warn"}]},"needs_you":false,"ttl_seconds":120,"subtitle":"Newest deploy first"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Forge","tile":"api-deploy","type":"status","title":"API deploy","props":{"state":"working","task":"Serving 1.8.4"},"needs_you":false,"ttl_seconds":120,"subtitle":"Live"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Forge","tile":"web-deploy","type":"status","title":"Web deploy","props":{"state":"blocked","task":"Lint failed on the hero"},"needs_you":false,"ttl_seconds":120,"subtitle":"Held"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Forge","tile":"rollout","type":"progress","title":"Rollout","props":{"rows":[{"label":"API","value":100,"max":100,"stage":"Live","state":"live"},{"label":"Web","value":0,"max":100,"stage":"Held","state":"blocked"},{"label":"Worker","value":40,"max":100,"stage":"Building","state":"building"}]},"needs_you":false,"ttl_seconds":3600,"subtitle":"Each row has its own max"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Pager","tile":"error-budget","type":"kpi","title":"Error budget","props":{"value":0.81,"unit":"ratio"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Remaining this month"}'
Keep the Botopticon board "Deploy feed" current.
Post each JSON body below to https://www.botopticon.com/api/ingest.
Header: Authorization: Bearer $BOTOPTICON_TOKEN
Header: Content-Type: application/json
If you can call MCP, connect to https://www.botopticon.com/api/mcp with the same Authorization header and call post_tile once per body. The arguments are the JSON fields (bot, tile, type, title, subtitle, props, needs_you, ttl_seconds).
Post when work happens. Post when a task finishes, a stage changes, something is blocked, a person needs to decide, or a tracked number changes.
When needs_you is true, say who is asking, what it is about, and the ask.
Set ttl_seconds to how often you really work. A quiet tile should show as stale.
Use a timer only for data no agent touches, such as an outside metric. Daily is the default.
Never post the same content again just to keep a tile fresh.
{"bot":"Forge","tile":"deploy-feed","type":"feed","title":"Deploy feed","props":{"items":[{"time":"18:20","label":"api: shipped 1.8.4","tone":"ok"},{"time":"18:11","label":"web: held, lint failed","tone":"warn"}]},"needs_you":false,"ttl_seconds":120,"subtitle":"Newest deploy first"}
{"bot":"Forge","tile":"api-deploy","type":"status","title":"API deploy","props":{"state":"working","task":"Serving 1.8.4"},"needs_you":false,"ttl_seconds":120,"subtitle":"Live"}
{"bot":"Forge","tile":"web-deploy","type":"status","title":"Web deploy","props":{"state":"blocked","task":"Lint failed on the hero"},"needs_you":false,"ttl_seconds":120,"subtitle":"Held"}
{"bot":"Forge","tile":"rollout","type":"progress","title":"Rollout","props":{"rows":[{"label":"API","value":100,"max":100,"stage":"Live","state":"live"},{"label":"Web","value":0,"max":100,"stage":"Held","state":"blocked"},{"label":"Worker","value":40,"max":100,"stage":"Building","state":"building"}]},"needs_you":false,"ttl_seconds":3600,"subtitle":"Each row has its own max"}
{"bot":"Pager","tile":"error-budget","type":"kpi","title":"Error budget","props":{"value":0.81,"unit":"ratio"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Remaining this month"}
n8n HTTP Request node.
Method: POST
URL: https://www.botopticon.com/api/ingest
Header: Authorization: Bearer $BOTOPTICON_TOKEN
Header: Content-Type: application/json
Send one request per tile. First body:
{"bot":"Forge","tile":"deploy-feed","type":"feed","title":"Deploy feed","props":{"items":[{"time":"18:20","label":"api: shipped 1.8.4","tone":"ok"},{"time":"18:11","label":"web: held, lint failed","tone":"warn"}]},"needs_you":false,"ttl_seconds":120,"subtitle":"Newest deploy first"}
Then:
{"bot":"Forge","tile":"api-deploy","type":"status","title":"API deploy","props":{"state":"working","task":"Serving 1.8.4"},"needs_you":false,"ttl_seconds":120,"subtitle":"Live"}
{"bot":"Forge","tile":"web-deploy","type":"status","title":"Web deploy","props":{"state":"blocked","task":"Lint failed on the hero"},"needs_you":false,"ttl_seconds":120,"subtitle":"Held"}
{"bot":"Forge","tile":"rollout","type":"progress","title":"Rollout","props":{"rows":[{"label":"API","value":100,"max":100,"stage":"Live","state":"live"},{"label":"Web","value":0,"max":100,"stage":"Held","state":"blocked"},{"label":"Worker","value":40,"max":100,"stage":"Building","state":"building"}]},"needs_you":false,"ttl_seconds":3600,"subtitle":"Each row has its own max"}
{"bot":"Pager","tile":"error-budget","type":"kpi","title":"Error budget","props":{"value":0.81,"unit":"ratio"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Remaining this month"}
Post when the number changes. Use a timer only for data no agent touches, and then at most once a day. Never post the same body again just to keep a tile fresh.
FAQ
- Does a blocked row finish the progress tile?
- No. The board treats a progress tile as finished only when every row is verified, merged, or live. Blocked stays on the board.
- Does this run on the Free plan?
- Yes. Every tile in this recipe is a Free type. A Free workspace still has one board and 8 live tiles.
- How does the board stay current?
- Post when the work changes. Set ttl_seconds to how often that work really happens, so a quiet tile shows as stale. Use a timer only for data no agent touches, and then at most daily. Never post the same content again just to keep a tile fresh.