Handler: outbound.apprise_notify
Generic notification delivery via Apprise. One handler for every push destination — the channel is described entirely by the row’s config.apprise_url, so adding Slack / ntfy / Pushover / email / SMS is a row insert, not a new handler module.
Payload
content / text / body / message). The notification title is always empty, matching the plain-content posts the legacy Discord/Telegram handlers produced.
The apprise_url template
config.apprise_url is an Apprise URL with optional placeholders, substituted at dispatch time:
{secret}→ the value resolved fromsecret_key_ref(anapp_settingskey, decrypted live each call){<config-key>}→ any other key in the row’sconfig(e.g.{chat_id})
app_settings; only the non-secret template lives on the row. Rotation works (the secret is resolved per call) and the token never lands in the config column.
Seeded rows
Apprise accepts the native Discord webhook URL (
https://discord.com/api/webhooks/<id>/<token>) directly, so the Discord secret is passed straight through.
Caller usage
Operator runbook
Adding a new channel (no code)
Example: an ntfy push channel.- Store the secret (if the service needs one):
poindexter settings set ntfy_token '<token>' --secret - Insert a row:
- Call it:
await deliver("ntfy_ops", "hello from poindexter", db_service=db, site_config=sc)
config.apprise_url is the only field that changes per service.
Rotating a secret
Disabling
deliver() then raises OutboundWebhookError; notify_operator treats that as “operator turned this channel off” and moves on.
Discord rendering note
If a Discord destination renders as an embed rather than plain content, append?format=text to its apprise_url:
Response contract
- Success → the dispatcher returns
{"ok": true, "name": "<row>", "delivered": true} - Failure (malformed URL, delivery failure, missing
apprise_url, unresolved{secret}, or unknown placeholder) → the handler raises; the dispatcher recordslast_erroron the row and re-raises.
Telegram streaming note
Only the fire-and-forget notify path uses Apprise. The pipeline edit-in-place streaming path (services/pipeline_streaming.py) still uses the Bot API helpers retained in services/integrations/handlers/outbound_telegram.py (send_telegram_message / edit_telegram_message) — Apprise cannot edit a message after sending.
Related
- Framework overview: Integrations
- Sibling handler:
outbound.vercel_isr - Operator-notify shim:
services.integrations.operator_notify.notify_operator