Push Notification Endpoints
All send endpoints require a service JWT with scope=push:send (from client credentials grant). Device registration uses the user's access JWT. Config endpoints require an admin JWT (app admin or above).
PATCH /push/config/{app_id}
Enable or update push notification settings for an app.
Auth: Admin JWT (app admin, tenant admin, or platform admin)
Request:
{
"enabled": true,
"webhook_url": "https://yourapp.com/hooks/push-receipts",
"daily_quota": 10000
}
All fields are optional. Only provided fields are updated.
Response (200):
{
"enabled": true,
"webhook_url": "https://yourapp.com/hooks/push-receipts",
"webhook_secret": "whsec_abc123...",
"daily_quota": 10000
}
On first enable, a webhook_secret is auto-generated and returned. Subsequent updates preserve the existing secret unless explicitly regenerated.
GET /push/config/{app_id}
Get push notification configuration and current status for an app.
Auth: Admin JWT (app admin, tenant admin, or platform admin)
Response (200):
{
"enabled": true,
"webhook_url": "https://yourapp.com/hooks/push-receipts",
"webhook_secret": "whsec_abc123...",
"daily_quota": 10000,
"device_count": 42,
"notifications_today": 128
}
GET /push/devices/{app_id}
List all registered push devices for an app.
Auth: Admin JWT (app admin, tenant admin, or platform admin)
Response (200):
{
"devices": [
{
"device_id": "uuid",
"user_id": "uuid",
"platform": "ios",
"last_seen_at": "2026-03-19T08:00:00Z"
}
],
"total": 42
}
POST /push/devices/register
Register a device for push notifications.
Auth: User JWT
Request:
{
"app_id": "uuid",
"platform": "web" | "ios" | "android",
"push_token": "fcm-token-or-web-push-subscription"
}
Response (201):
{ "device_id": "uuid" }
Deduplicates on (app_id, token_hash). Re-registering updates last_seen_at.
Rate limit: 10 per user per hour.
DELETE /push/devices/{device_id}
Unregister a device.
Auth: User JWT (own devices) OR service JWT with push:send scope (app's devices)
Response (200):
{ "status": "ok" }
POST /push/send
Send a notification to a single user (all their registered devices for this app).
Auth: Service JWT with push:send scope
Request:
{
"user_id": "uuid",
"title": "Your shift starts in 30 min",
"body": "Open GymOps to view details.",
"data": { "route": "/schedule" },
"ttl": 3600
}
Response (200):
{ "notification_id": "uuid" }
Validation:
- user_id must be enrolled in the calling app
- data max 4KB
- title max 256 chars
- body max 4096 chars
POST /push/send/bulk
Send the same notification to multiple users.
Auth: Service JWT with push:send scope
Request:
{
"user_ids": ["uuid1", "uuid2", "uuid3"],
"title": "Gym closes early today",
"body": "Closing at 6pm.",
"data": {}
}
Response (200):
{ "notification_ids": ["uuid1", "uuid2", "uuid3"] }
Max user_ids: 500.
POST /push/send/batch
Send different notifications in one call.
Auth: Service JWT with push:send scope
Request:
{
"notifications": [
{ "user_id": "uuid1", "title": "Shift approved", "body": "Your request was approved." },
{ "user_id": "uuid2", "title": "Time off denied", "body": "Please contact your manager." }
]
}
Max notifications: 500.
Errors
| Status | Detail | Meaning |
|---|---|---|
| 400 | User not enrolled | Target user_id not in the calling app |
| 400 | Data payload too large | data field exceeds 4KB |
| 401 | Invalid token | Missing or invalid JWT |
| 403 | Missing required scope | JWT doesn't have push:send |
| 403 | Push not enabled | App does not have push notifications enabled (device registration) |
| 403 | Insufficient permissions | JWT does not have admin access for config endpoints |
| 404 | App not found | App ID does not exist |
| 404 | Device not found | Device ID doesn't exist or belongs to another app |
| 429 | Daily quota exceeded | App has hit its notification limit |