Recipesn8nKPI and metric boards
Morning numbers
FreeAn n8n schedule posts MRR, signups, open tickets, a 7-day trend, and a channel table each morning. Botopticon is not affiliated with n8n.
- Morning MRR
- New signups
- Tickets open
- Week trend
- Channel stats
Board
Sample data. Your agent replaces a tile when it posts that tile.
Morning MRR
+1%
New signups
+3
Tickets open
-4
Week trend
min 18max 30
Channel stats
- Site$28k16
- Partners$14k8
Setup
01
Create a connection token
Name it n8n morning. Copy it once.
02
Add an HTTP Request node
Method POST. URL is your origin plus /api/ingest. Header Authorization: Bearer and the token. Header Content-Type: application/json. Body is one JSON object from the n8n snippet. Duplicate the node once per tile, or loop the bodies.
03
Trigger it every morning
Use a Schedule trigger once a day. These tiles are numbers no agent touches, and ttl_seconds is 86400. Skip the run when the numbers have not changed. Do not add a midday run just to keep them fresh.
Snippets
Replace $BOTOPTICON_TOKEN with a connection token. The URL is this app origin plus /api/ingest or /api/mcp.
#!/bin/sh
# Morning numbers. 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":"Ledger","tile":"morning-mrr","type":"kpi","title":"Morning MRR","props":{"value":42000,"unit":"USD","delta":0.01,"delta_format":"percent"},"needs_you":false,"ttl_seconds":86400,"subtitle":"USD, posted at 7am"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Ledger","tile":"new-signups","type":"kpi","title":"New signups","props":{"value":24,"unit":"accounts","delta":3,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":86400,"subtitle":"Since yesterday"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Pager","tile":"tickets-open","type":"kpi","title":"Tickets open","props":{"value":17,"unit":"tickets","delta":-4,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":86400,"subtitle":"Still open"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Ledger","tile":"week-trend","type":"trend","title":"Week trend","props":{"window":7,"unit":"accounts","series":[{"name":"signups","points":[{"date":"2026-03-24","value":18},{"date":"2026-03-25","value":21},{"date":"2026-03-26","value":19},{"date":"2026-03-27","value":30},{"date":"2026-03-28","value":22},{"date":"2026-03-29","value":25},{"date":"2026-03-30","value":24}]}]},"needs_you":false,"ttl_seconds":86400,"subtitle":"Signups, 7 days"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Ledger","tile":"channel-stats","type":"stat_table","title":"Channel stats","props":{"columns":[{"key":"mrr","label":"MRR","format":"usd_compact"},{"key":"signups","label":"Signups","format":"number"}],"rows":[{"label":"Site","values":{"mrr":28000,"signups":16}},{"label":"Partners","values":{"mrr":14000,"signups":8},"emphasis":true}]},"needs_you":false,"ttl_seconds":86400,"subtitle":"One metric per column"}'
Keep the Botopticon board "Morning numbers" 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":"Ledger","tile":"morning-mrr","type":"kpi","title":"Morning MRR","props":{"value":42000,"unit":"USD","delta":0.01,"delta_format":"percent"},"needs_you":false,"ttl_seconds":86400,"subtitle":"USD, posted at 7am"}
{"bot":"Ledger","tile":"new-signups","type":"kpi","title":"New signups","props":{"value":24,"unit":"accounts","delta":3,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":86400,"subtitle":"Since yesterday"}
{"bot":"Pager","tile":"tickets-open","type":"kpi","title":"Tickets open","props":{"value":17,"unit":"tickets","delta":-4,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":86400,"subtitle":"Still open"}
{"bot":"Ledger","tile":"week-trend","type":"trend","title":"Week trend","props":{"window":7,"unit":"accounts","series":[{"name":"signups","points":[{"date":"2026-03-24","value":18},{"date":"2026-03-25","value":21},{"date":"2026-03-26","value":19},{"date":"2026-03-27","value":30},{"date":"2026-03-28","value":22},{"date":"2026-03-29","value":25},{"date":"2026-03-30","value":24}]}]},"needs_you":false,"ttl_seconds":86400,"subtitle":"Signups, 7 days"}
{"bot":"Ledger","tile":"channel-stats","type":"stat_table","title":"Channel stats","props":{"columns":[{"key":"mrr","label":"MRR","format":"usd_compact"},{"key":"signups","label":"Signups","format":"number"}],"rows":[{"label":"Site","values":{"mrr":28000,"signups":16}},{"label":"Partners","values":{"mrr":14000,"signups":8},"emphasis":true}]},"needs_you":false,"ttl_seconds":86400,"subtitle":"One metric per column"}
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":"Ledger","tile":"morning-mrr","type":"kpi","title":"Morning MRR","props":{"value":42000,"unit":"USD","delta":0.01,"delta_format":"percent"},"needs_you":false,"ttl_seconds":86400,"subtitle":"USD, posted at 7am"}
Then:
{"bot":"Ledger","tile":"new-signups","type":"kpi","title":"New signups","props":{"value":24,"unit":"accounts","delta":3,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":86400,"subtitle":"Since yesterday"}
{"bot":"Pager","tile":"tickets-open","type":"kpi","title":"Tickets open","props":{"value":17,"unit":"tickets","delta":-4,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":86400,"subtitle":"Still open"}
{"bot":"Ledger","tile":"week-trend","type":"trend","title":"Week trend","props":{"window":7,"unit":"accounts","series":[{"name":"signups","points":[{"date":"2026-03-24","value":18},{"date":"2026-03-25","value":21},{"date":"2026-03-26","value":19},{"date":"2026-03-27","value":30},{"date":"2026-03-28","value":22},{"date":"2026-03-29","value":25},{"date":"2026-03-30","value":24}]}]},"needs_you":false,"ttl_seconds":86400,"subtitle":"Signups, 7 days"}
{"bot":"Ledger","tile":"channel-stats","type":"stat_table","title":"Channel stats","props":{"columns":[{"key":"mrr","label":"MRR","format":"usd_compact"},{"key":"signups","label":"Signups","format":"number"}],"rows":[{"label":"Site","values":{"mrr":28000,"signups":16}},{"label":"Partners","values":{"mrr":14000,"signups":8},"emphasis":true}]},"needs_you":false,"ttl_seconds":86400,"subtitle":"One metric per column"}
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
- One node or one per tile?
- One POST per tile. Same URL and headers. The body changes. n8n can loop an array of the JSON bodies in the snippet.
- 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.