Notifications
In-app notifications, email alerts, user preferences, and real-time delivery.
Base path:
/api/v1/{workspace_id}/notifications/
List notifications
GET /api/v1/{workspace_id}/notifications/
Authorization: Bearer <token>
Unread count
GET /api/v1/{workspace_id}/notifications/unread-count/
Mark read / archive
POST /api/v1/{workspace_id}/notifications/mark-all-read/
PATCH /api/v1/{workspace_id}/notifications/{notification_id}/read/
PATCH /api/v1/{workspace_id}/notifications/{notification_id}/archive/
DELETE /api/v1/{workspace_id}/notifications/{notification_id}/
Preferences
GET /api/v1/{workspace_id}/notifications/preferences/
PATCH /api/v1/{workspace_id}/notifications/preferences/
In-app (App) and Email channels are independent per category.
| Channel | Field | Default (no saved row) |
|---|---|---|
| App (bell + WebSocket) | in_app_enabled | true |
| Email (Resend) | email_enabled | false |
GET response shape
{
"daily_digest_enabled": true,
"categories": [
{
"category": "lead",
"in_app_enabled": true,
"email_enabled": false
}
]
}
Categories: task, lead, demo_booking, event, expense, admin.
PATCH example
{
"daily_digest_enabled": true,
"categories": [
{
"category": "task",
"in_app_enabled": true,
"email_enabled": true
},
{
"category": "lead",
"in_app_enabled": false,
"email_enabled": true
}
]
}
Daily digest
When daily_digest_enabled is true, a morning email is sent (via Celery Beat) listing overdue and due-today tasks assigned to the user in that workspace. No digest is sent if there are no matching tasks.
WebSocket (real-time in-app)
wss://events.pana.space/ws/notifications/?token=<access_token>
Used by the web app for live in-app notification updates when in_app_enabled is on.
Mobile push (FCM)
The FCM token identifies an app install, not a user. The backend stores which user is logged in on that install. A push is sent only when an in-app notification is created or updated (in_app_enabled is on). Registering a device does not replay the inbox.
Mobile implementation steps, payloads, and responses: Mobile push (FCM).
Requires FIREBASE_CREDENTIALS_JSON or FIREBASE_CREDENTIALS_PATH on the backend. Without it, register and unregister still succeed and pushes are skipped.
Register (after login, and on token refresh)
POST /api/v1/devices/
Authorization: Bearer <token>
Content-Type: application/json
{ "fcm_token": "<firebase token>", "platform": "ios" }
platform is ios or android. If this token was stored for another user, it is reassigned to the current user.
Unregister (on logout)
DELETE /api/v1/devices/
Authorization: Bearer <token>
Content-Type: application/json
{ "fcm_token": "<firebase token>" }
Deletes the row only when it belongs to the current user.
Push data includes notification_id, workspace_id, category, verb, target_url, target_type, and target_id. A token Firebase reports as unregistered is removed.
Email delivery
When email_enabled is true for a category, the platform sends a transactional email (React Email + Resend) when that notification fires. Requires RESEND_API_KEY on the backend.
Emails include a link back to the relevant record in the app and a link to notification settings.
Webhooks
The same domain events that create notifications can trigger outbound developer webhooks. See Webhooks.