Send push notifications to iOS and Android devices from your nself backend. Supports Firebase Cloud Messaging (FCM) for Android and Apple Push Notification service (APNs) for iOS, plus Web Push for browsers.
nself-notify is part of the ɳClaw bundle. Set your key with nself license set nself_pro_... before installing.
nself license set nself_pro_...
nself plugin install notify
nself build
nself start| Variable | Required | Description |
|---|---|---|
PLUGIN_NOTIFY_FCM_SERVER_KEY | Yes (Android) | Firebase Cloud Messaging server key from Firebase Console |
PLUGIN_NOTIFY_APNS_KEY_ID | Yes (iOS) | APNs auth key ID from Apple Developer account |
PLUGIN_NOTIFY_APNS_TEAM_ID | Yes (iOS) | Apple Developer Team ID |
PLUGIN_NOTIFY_APNS_KEY_PATH | Yes (iOS) | Path to the APNs .p8 auth key file |
Add these to your .env file after installing the plugin:
PLUGIN_NOTIFY_FCM_SERVER_KEY=AAAAxxxxxxxx...
PLUGIN_NOTIFY_APNS_KEY_ID=ABCD1234EF
PLUGIN_NOTIFY_APNS_TEAM_ID=TEAM123456
PLUGIN_NOTIFY_APNS_KEY_PATH=/etc/nself/keys/AuthKey_ABCD1234EF.p8# Send a push notification to a user
curl -X POST http://127.0.0.1:3712/notify/send \
-H "Content-Type: application/json" \
-d '{
"channel": "fcm",
"user_id": "user-uuid",
"title": "New message",
"body": "You have a new message from Alice",
"data": {
"conversation_id": "conv-123",
"type": "chat"
}
}'# Register a device token for a user
curl -X POST http://127.0.0.1:3712/notify/subscribe \
-H "Content-Type: application/json" \
-d '{
"user_id": "user-uuid",
"platform": "android",
"device_token": "fcm-device-token-here"
}'| Channel | Platform | Required credentials |
|---|---|---|
fcm | Android | PLUGIN_NOTIFY_FCM_SERVER_KEY |
apns | iOS | PLUGIN_NOTIFY_APNS_KEY_ID, PLUGIN_NOTIFY_APNS_TEAM_ID, PLUGIN_NOTIFY_APNS_KEY_PATH |
webpush | Web browsers | VAPID key pair (auto-generated on first run) |
| Endpoint | Method | Description |
|---|---|---|
/notify/send | POST | Send a push notification to a user or device token |
/notify/subscribe | POST | Register a device token for a user |
/notify/unsubscribe | POST | Remove a device token (call on logout or token refresh) |
/notify/health | GET | Check plugin health and credential status |
{
"channel": "fcm", // fcm | apns | webpush
"user_id": "uuid", // send to all devices for this user
"device_token": "token", // OR send to a specific device token
"title": "string", // notification title
"body": "string", // notification body text
"data": {}, // optional key-value payload for the app
"badge": 1, // iOS badge count (optional)
"sound": "default" // notification sound (optional)
}{
"user_id": "uuid",
"platform": "android", // android | ios | web
"device_token": "string" // FCM token, APNs token, or Web Push subscription
}The plugin creates a np_notify schema. Tables are auto-tracked by Hasura and accessible via GraphQL.
| Table | Key Columns | Purpose |
|---|---|---|
np_notify.devices | id, user_id, platform, device_token, active, registered_at, last_seen | Device token registry — one row per registered device per user. |
np_notify.send_log | id, user_id, channel, title, status, error_message, sent_at | Delivery history for every notification sent. Used for debugging and analytics. |
# List active devices for a user
query UserDevices($userId: uuid!) {
np_notify_devices(where: {
user_id: { _eq: $userId }
active: { _eq: true }
}) {
platform
registered_at
last_seen
}
}
# Recent delivery failures
query RecentFailures {
np_notify_send_log(
where: { status: { _eq: "failed" } }
order_by: { sent_at: desc }
limit: 20
) {
user_id
channel
title
error_message
sent_at
}
}These events are published to the nself event bus and can be consumed by nself-mux rules.
| Event | Trigger | Payload fields |
|---|---|---|
notify.send.success | Notification delivered to provider (FCM/APNs/Web Push) | user_id, channel, title, device_count |
notify.send.error | Provider rejects the notification | user_id, channel, error_code, error_message |
notify.device.registered | New device token subscribed | user_id, platform, device_id |
notify.device.removed | Device token unsubscribed or marked invalid | user_id, platform, device_id |
| Option | Cost | Data ownership | Self-hostable |
|---|---|---|---|
| OneSignal | Free up to 10k subscribers, then $9–$99/mo | OneSignal holds device tokens | No |
| Firebase Cloud Messaging (direct) | Free but requires custom server code | You hold tokens locally | N/A |
| Expo Push Notifications | Free up to 1k/mo, then $29/mo | Expo holds routing data | No |
| nself-notify | $0.99/mo (ɳClaw bundle) | Your Postgres — all tokens and logs | Yes — on your VPS |
InvalidRegistration errorThe device token is stale — the app was reinstalled or the user cleared app data. Delete the row from np_notify.devices where device_token matches, then have the app re-register.
BadDeviceToken errorSame as FCM invalid registration — the token expired. Ensure your app calls the subscribe endpoint with a fresh token after every app launch and after the OS generates a new APNs token.
Confirm PLUGIN_NOTIFY_APNS_KEY_PATH points to a .p8 file that corresponds to the PLUGIN_NOTIFY_APNS_KEY_ID. Development and production APNs environments use the same key file but the apns-environment header differs — the plugin uses production by default.
VAPID keys are auto-generated on first run and stored in np_notify config. If you see “VAPID keys not configured”, run nself restart to let the plugin initialize, then check /notify/health.
# View recent send log
curl http://127.0.0.1:3712/notify/health
# Or query directly via GraphQL
# SELECT status, error_message FROM np_notify.send_log ORDER BY sent_at DESC LIMIT 20;nself plugin remove notifyPort: 3712 | Bundle: ɳClaw ($0.99/mo) or ɳSelf+ ($3.99/mo) | Last Updated: May 2026 | Plugin Version 1.1.0