Push Notifications (Admin)
Configure Expo Push credentials, monitor device registrations, troubleshoot delivery, and manage category policies.
Push notifications in Sophon use Expo Push Service, which bridges to APNs (iOS) and FCM (Android). End-user experience is documented in the Mobile App page; this page is for administrators who need to configure, monitor, and troubleshoot push delivery.
How push works in Sophon
- The mobile app registers the device with the Gateway on first login, uploading its Expo push token.
- When an event fires (approval request, task completion, workflow alert), the Gateway looks up which users should be notified and which of their devices have relevant categories enabled.
- The Gateway composes a push payload and POSTs to Expo Push Service.
- Expo delivers to APNs (iOS) or FCM (Android).
- The device displays the notification; user interaction (tap, action button) fires a deep link.
Configuration
Push delivery requires configured Expo Push credentials. Sophon talks to Expo's hosted service (https://exp.host) by default, but you can run your own Expo Push Receiver or use direct APNs/FCM if you prefer.
Expo Push Service (recommended)
{
"Sophon": {
"PushNotifications": {
"Enabled": true,
"Provider": "Expo",
"Expo": {
"AccessToken": "{{vault:expo_access_token}}",
"ProjectId": "your-expo-project-id"
}
}
}
}Generate an access token at https://expo.dev/accounts/ → Access Tokens. Required scope: push notifications only.
Direct APNs + FCM (advanced)
For deployments that prefer not to go through Expo:
{
"Sophon": {
"PushNotifications": {
"Enabled": true,
"Provider": "Direct",
"Apns": {
"KeyId": "ABC123DEF",
"TeamId": "TEAMID123",
"BundleId": "com.buildersoft.sophon",
"PrivateKey": "{{vault:apns_private_key}}",
"Environment": "production"
},
"Fcm": {
"ServiceAccountJson": "{{vault:fcm_service_account}}"
}
}
}
}Requires an Apple Developer account for APNs and a Firebase project for FCM. Mobile apps must be rebuilt with the direct-push native module instead of Expo Notifications.
Device registrations
Every registered device appears in Admin → Settings → Push Notifications → Devices. Shows:
- User, device type (iOS / Android), OS version, app version
- Expo push token (truncated)
- Last seen
- Notification preferences per category
- Status — Active / Stale / Revoked
Stale tokens
Tokens become stale when:
- The user uninstalls the app (APNs / FCM returns
invalid_token) - The user logs out from the app (we revoke the token, but stale registrations might persist briefly)
- The token is rotated by the device (Expo auto-rotates periodically)
Stale tokens don't cost anything but clutter the UI. A daily cleanup job removes registrations unused for 90 days:
{
"Sophon": {
"PushNotifications": {
"StaleTokenRetention": "90.00:00:00"
}
}
}Manual cleanup:
sophon admin push cleanup --older-than 30dCategories and delivery rules
Four categories ship out of the box:
| Category | Event | Action buttons |
|---|---|---|
newMessages | Chat message received on a subscribed channel | Tap to open session |
approvalRequests | Agent needs approval ≥ High | Approve / Reject (iOS 14+) |
workflowAlerts | Workflow run completed, failed, or approval required | Tap to open workflow detail |
systemUpdates | New release, license expiring, tenant notices | Informational |
Custom categories (Enterprise):
{
"Sophon": {
"PushNotifications": {
"CustomCategories": {
"securityAlert": { "priority": "high", "sound": "alert.wav" },
"dealSigned": { "priority": "normal" }
}
}
}
}Custom categories appear in the user's notification preferences automatically.
Delivery rules
Per-category rules:
{
"approvalRequests": {
"priority": "high",
"ttlSeconds": 300,
"respectQuietHours": true,
"iosInterruptionLevel": "timeSensitive"
}
}priority—normalorhigh(affects APNs / FCM delivery prioritization)ttlSeconds— drop the push if undelivered after this longrespectQuietHours— skip delivery during user's quiet hours (default true)iosInterruptionLevel— iOS 15+ Focus Mode behavior:passive,active,timeSensitive,critical
Quiet hours policy
Users set their own quiet hours in the mobile app. Admins can enforce global quiet hours for specific categories:
{
"Sophon": {
"PushNotifications": {
"TenantQuietHours": {
"systemUpdates": { "start": "18:00", "end": "09:00" }
}
}
}
}System updates won't fire overnight even if a user has opted in — admin preference wins for non-Critical categories.
Monitoring
Admin → Settings → Push Notifications → Activity shows:
- Delivery attempts per minute
- Success / failure / dropped counts
- Failures by reason (
invalid_token,rate_limited,expired,apns_error,fcm_error) - Per-user delivery counts
Failure patterns to watch:
- High
invalid_tokenrate — Expo project ID mismatch, or mobile apps built against different keys rate_limited— exceeding Expo's per-app rate limit; batch notifications or throttleapns_error— Apple's push service is having a bad day (rare but real), or your APNs cert is expired
Test delivery
Admin → Settings → Push Notifications → Send Test. Choose a device and a category. Fires a dummy notification immediately.
CLI:
sophon admin push test --device <id> --category approvalRequests --title "Test" --body "This is a test notification"Rate limits
Expo Push Service applies rate limits per-project:
- Free tier: 600 notifications/hour
- Pro tier: 3,000/hour
- Enterprise: Negotiated with Expo
Sophon batches notifications — up to 100 per request — and spreads delivery if approaching the limit. Over the limit, older pushes drop first.
Monitor your rate-limit headroom at https://expo.dev/accounts/... → Usage.
Troubleshooting
Notifications don't arrive on device — Check:
- Device registered?
sophon admin push devices | grep <user> - Category enabled by user? Push preferences page in mobile app.
- Quiet hours in effect for that user + category?
- Expo delivery success? Admin → Activity.
- Device notification permission granted? User must re-grant in OS settings.
Pushes arrive very delayed (hours) — Expo uses push buffering for low-priority notifications. Check that the category's priority is high for time-sensitive pushes.
iOS action buttons don't appear — Actionable notifications require iOS 10+ and the app must declare the APPROVAL_ACTION category in Info.plist (bundled with Sophon Mobile, so normally a non-issue).
Android notifications show a generic icon — Requires the FCM default icon setup in the app manifest. Bundled with Sophon Mobile builds.
Security
- Push payloads contain message previews (truncated to 200 chars). For sensitive organizations, disable preview:
"PrivacyMode": true— the push shows "New message in Sophon" with no content, user opens the app to see full. - Expo push tokens are device-identifying but not user-identifying per se. A leaked token can send to the device but not read Sophon data.
- Push delivery is audited — every push attempt is logged (see Audit with category
push).
Where to go next
- Mobile App — end-user view of push preferences
- Approval Gates — the high-volume push source
docs/NOTIFICATIONS.mdin the Sophon repo — implementation details