{"success":true,"data":{"service":"notifications-api","boundary":"Owns notification preferences, push subscriptions, delivery attempts, VAPID config, and delivery.","auth":"Machine callers pass Authorization: Bearer <NOTIFICATIONS_API_TOKEN>. Verticals normalize user identity before calling this API.","routes":[{"method":"GET","path":"/","description":"Human-readable service landing page.","auth":"public","responses":[200]},{"method":"GET","path":"/api/docs","description":"Static route metadata and environment expectations.","auth":"public","responses":[200]},{"method":"GET","path":"/api/docs/openapi","description":"Minimal OpenAPI JSON for the raw notification surface.","auth":"public","responses":[200]},{"method":"GET","path":"/api/v1/health","description":"Deployment health check with database reachability.","auth":"public","responses":[200,503]},{"method":"GET","path":"/api/v1/push/vapid-public-key","description":"Returns the public VAPID key used by browser push subscribe.","auth":"machine","responses":[200]},{"method":"GET","path":"/api/v1/preferences","description":"Lists effective preferences for a normalized principal.","auth":"machine","responses":[200]},{"method":"PATCH","path":"/api/v1/preferences","description":"Updates preferences for a normalized principal. Optional initializeOnly:true creates missing preference rows without overwriting saved choices.","auth":"machine","responses":[200]},{"method":"GET","path":"/api/v1/push-subscriptions","description":"Lists active push subscription IDs and endpoints for a normalized principal, without encryption keys.","auth":"machine","responses":[200,400,403]},{"method":"POST","path":"/api/v1/push-subscriptions","description":"Saves or refreshes a browser push subscription.","auth":"machine","responses":[201]},{"method":"DELETE","path":"/api/v1/push-subscriptions","description":"Revokes a browser push subscription.","auth":"machine","responses":[200]},{"method":"POST","path":"/api/v1/dispatch","description":"Queues a push notification broadcast for explicit normalized recipients. Optional idempotencyKey (1–256 characters, trimmed) is scoped to the registered event type and its source app. Retrying with the same payload and recipient set returns the original queued acknowledgement; reusing the key for different content returns 409. Omitting the key creates a new broadcast on every request. Email remains on the legacy notification path.","auth":"machine","responses":[202,400,403,409]},{"method":"GET","path":"/api/cron/dispatch","description":"Drains queued notification broadcast jobs.","auth":"cron","responses":[200,500]},{"method":"POST","path":"/api/v1/test-notification","description":"Sends a dev/staging test notification.","auth":"machine","responses":[202]}],"requiredEnv":["NOTIFICATIONS_DATABASE_URL","NOTIFICATIONS_API_TOKEN","CRON_SECRET","NEXT_PUBLIC_WEB_PUSH_VAPID_PUBLIC_KEY","WEB_PUSH_VAPID_PRIVATE_KEY","WEB_PUSH_VAPID_SUBJECT"]}}