A multi-channel notification router for Xano. Define channels (email, SMS, Slack, push, webhook…), let users set per-topic preferences on each channel, and then fan-out a single notify call into a delivery per opted-in channel. Every delivery is recorded for audit/retry/analysis.
This is a module, not a full app: no frontend, no auth, and no actual SMTP/SMS/Slack integration — it writes delivery rows with status=sent and hands them to you. Point a task at the nr_delivery table to actually dispatch them (or swap the simulation in nr_dispatch for real provider calls).
- 3 tables —
nr_channel,nr_preference,nr_delivery - Function
nr_dispatch— given a user + topic + subject/body, resolves channels, checks preferences, creates delivery rows. Call it from any XanoScript. - HTTP API
notify-router— admin endpoints plus a genericnotifyendpoint callable from any service
npm install -g @xano/cli
xano profile:wizard
cd backend
xano workspace:pushCreates 3 tables, 1 function, 1 api group, and 6 endpoints.
POST /api:notify-router/channels { key, name, channel_type, active?, target_template?, config? }
GET /api:notify-router/channels ?active=true
PUT /api:notify-router/preferences { user_id, channel_key, topic, enabled } # upsert
GET /api:notify-router/users/{user_id}/preferences list prefs + enriched channel info
POST /api:notify-router/notify { user_id, topic, subject?, body?, metadata? }
GET /api:notify-router/deliveries ?user_id&topic&status&page&per_page
channel_type is one of: email, sms, push, webhook, slack, in_app.
B=https://YOUR-INSTANCE.n7d.xano.io
curl -XPOST $B/api:notify-router/channels -H 'Content-Type: application/json' -d '{"key":"email","name":"Email","channel_type":"email"}'
curl -XPOST $B/api:notify-router/channels -H 'Content-Type: application/json' -d '{"key":"sms","name":"SMS","channel_type":"sms"}'
curl -XPOST $B/api:notify-router/channels -H 'Content-Type: application/json' -d '{"key":"slack","name":"Slack","channel_type":"slack"}'curl -XPUT $B/api:notify-router/preferences -H 'Content-Type: application/json' -d '{"user_id":"42","channel_key":"email","topic":"alerts","enabled":true}'
curl -XPUT $B/api:notify-router/preferences -H 'Content-Type: application/json' -d '{"user_id":"42","channel_key":"sms","topic":"alerts","enabled":true}'
curl -XPUT $B/api:notify-router/preferences -H 'Content-Type: application/json' -d '{"user_id":"42","channel_key":"slack","topic":"alerts","enabled":false}'curl -XPOST $B/api:notify-router/notify \
-H 'Content-Type: application/json' \
-d '{"user_id":"42","topic":"alerts","subject":"Server down","body":"DB unreachable","metadata":{"severity":"critical"}}'Response:
{
"user_id": "42",
"topic": "alerts",
"active_channels": 3,
"matched_prefs": 2,
"deliveries_created": 2,
"deliveries": [ /* two rows — email + sms */ ]
}Slack is skipped because that user's preference for slack:alerts is false.
function.run "nr_dispatch" {
input = {
user_id : $auth.id|to_text,
topic : "order.shipped",
subject : "Your order has shipped",
body : "Tracking: " ~ $tracking,
metadata: { order_id: $order.id }
}
} as $dispatchnr_channel—key(unique),name,channel_type(enum),active,target_template,config(json)nr_preference—user_id(text) +channel_id+topic+enablednr_delivery— per-delivery row: status (queued/sent/failed/skipped), sent_at, error, metadata
- Opt-in model. A channel-topic pair has to have an explicit preference row with
enabled=trueto deliver. Missing preferences don't deliver. Change this policy innr_dispatchif you prefer opt-out. - Text user IDs. Plug in any id scheme; the module doesn't assume a
usertable. - Delivery recorded, actual send is up to you.
nr_dispatchwrites rows withstatus=sentas a simulation. Wire a Xano task tonr_deliverywithstatus=queuedand your real provider to make this production-ready, or editnr_dispatchdirectly to call your provider inline. topicis free-form. Convention-over-schema — use whatever namespace you like (billing.invoice,security.login_anomaly,marketing.weekly_digest).
MIT.