Notifications Module — Storefront
HTTP surface for the customer mobile app to register and unregister its FCM device token for push notifications. Customer devices are tagged appKind: "customer" (distinct from the…
HTTP surface for the customer mobile app to register and unregister its FCM device token for push notifications. Customer devices are tagged appKind: "customer" (distinct from the vendor app's appKind: "vendor") so broadcasts can target the right audience without leaking notifications across apps.
Source:
api-modules/notifications/src/controllers/store-device.controller.ts.Admin broadcast composition and log inspection live in
docs/separated/admin/notifications.md. The vendor device-register surface mirrors this one and lives indocs/separated/vendor/notifications.md.
Conventions
Authentication
| Endpoint | Auth |
|---|---|
POST /store/devices | required (customer session) |
DELETE /store/devices | required (customer session) |
Device registration is tied to the session user via session.user.id — there's no "register a device for someone else" surface.
Response envelope
{
"data": <payload>,
"message": "Success",
"statusCode": 200
}Error envelope
statusCode | errorCode examples |
|---|---|
| 400 | BAD_REQUEST, VALIDATION_ERROR |
| 401 | UNAUTHORIZED |
| 500 | INTERNAL_SERVER_ERROR, DATABASE_ERROR |
Domain types
DeviceResponse
type DeviceResponse = {
id: string;
platform: "android" | "ios";
appKind: "customer"; // always "customer" on this surface
lastSeenAt: string; // ISO — refreshed on every re-register
};Endpoints
POST /store/devices — Register a customer device
Register or refresh an FCM registration token for the calling customer. Idempotent on (userId, token) — re-registering the same token refreshes lastSeenAt instead of creating a duplicate row. Stale tokens (where the FCM SDK has rotated the registration) are naturally swept by the next register call.
Body
{
"platform": "android", // or "ios"
"token": "fXyZ...firebase-registration-token..."
}| Field | Type | Constraints |
|---|---|---|
platform | enum | android / ios |
token | string | trimmed, 10..4096 chars |
Response 201 — DeviceResponse.
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Token too short / wrong platform value |
| 401 | UNAUTHORIZED | No customer session |
DELETE /store/devices — Unregister a device
Unregister an FCM token. Idempotent — unregistering an unknown or already-unregistered token still returns 200; the response body is { "ok": true }. Use this on user-initiated sign-out so the device stops receiving pushes.
Body
{ "token": "fXyZ...firebase-registration-token..." }| Field | Type | Constraints |
|---|---|---|
token | string | trimmed, 1..4096 chars |
Response 200
{ "data": { "ok": true }, "message": "Success", "statusCode": 200 }Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Empty token |
| 401 | UNAUTHORIZED | No customer session |
Related modules
notifications-push-fcm— the FCM transport that actually delivers pushes to the tokens this endpoint registers.auth— owns the session whoseuser.idkeys the device row.
Guest Checkout Module — Storefront
HTTP surface for guest (no-account) checkout — contact capture for an anonymous cart, a non-blocking "you already have an account" hint, and public order tracking after the order…
Order Module — Storefront
HTTP surface for the customer-side order lifecycle — payment provider discovery, place-order, list/detail, customer-initiated cancel, and the customer-side return flow…