Supercommerce API Docs
Admin API

Notifications Module — Admin

HTTP surface for the admin notification operations: composing and sending broadcasts (one-shot email and/or push to a targeted audience) and inspecting the notification log…

HTTP surface for the admin notification operations: composing and sending broadcasts (one-shot email and/or push to a targeted audience) and inspecting the notification log (per-message audit of every send across every channel and trigger).

Source: api-modules/notifications/src/controllers/admin-broadcast.controller.ts, api-modules/notifications/src/controllers/admin-notification-log.controller.ts.

Channels are plugged in via the per-channel modules (notifications-email-mailer, notifications-push-fcm). Broadcast send / schedule are queue-backed — the controller returns immediately with a row whose status reflects scheduled/in-progress; per-recipient jobs run async.


Conventions

Authentication

All endpoints require a Better-Auth admin session and a role granting the matching notifications:* permission.

Endpoint groupPermission
POST /admin/notifications/broadcasts, POST /admin/notifications/broadcasts/:id/cancelnotifications: broadcast
GET /admin/notifications/broadcasts, GET /admin/notifications/broadcasts/:id, GET /admin/notifications/lognotifications: view

Response envelope

Successful responses are wrapped by ResponseInterceptor:

{
  "data": <payload>,
  "message": "Success",
  "statusCode": 200,
  "metadata": { /* optional, e.g. pagination */ }
}

Error envelope

statusCodeerrorCode examples
400BAD_REQUEST, VALIDATION_ERROR
401UNAUTHORIZED
403FORBIDDEN
404NOT_FOUND
500INTERNAL_SERVER_ERROR, DATABASE_ERROR

Domain types

BroadcastResponse

type BroadcastResponse = {
  id: string;
  status: string;                // service-defined: "scheduled", "in_progress", "completed", "cancelled", "failed"
  audienceType: string;          // "all_customers" | "all_vendors" | "user_ids"
  channels: string[];            // subset of ["email", "push"]
  scheduledFor: string | null;   // ISO (null = send-now)
  totalRecipients: number | null; // null until the audience is materialised
  sentCount: number;
  failedCount: number;
  createdAt: string;             // ISO
};

NotificationLogResponse

type NotificationLogResponse = {
  id: string;
  recipientUserId: string | null;
  channel: string;               // "email" | "push" | ...
  provider: string;              // e.g. "mailer", "fcm"
  eventType: string;             // domain event that triggered the send (or "broadcast")
  broadcastId: string | null;    // populated when triggered by a broadcast
  subject: string | null;        // for email
  status: string;                // "sent" | "failed" | provider-specific
  error: string | null;          // free-form provider error message
  sentAt: string;                // ISO
};

Broadcasts

Base path: /admin/notifications/broadcasts.

POST /admin/notifications/broadcasts — Create + send (or schedule) a broadcast

Required permission: notifications: broadcast. The controller enqueues the broadcast and returns the persisted row immediately. If scheduleFor is omitted, the broadcast starts dispatching at once; if present, it is queued as a delayed job.

Body

{
  "audience": { "type": "all_customers" },
  "channels": ["email", "push"],
  "content": {
    "email": {
      "subject": "Our biggest sale of the year",
      "html": "<h1>Hi!</h1>...",
      "text": "Hi! ..."
    },
    "push": {
      "title": "Sale starts now",
      "body": "Up to 50% off across the store",
      "imageUrl": "https://cdn.example/banners/sale.png",
      "data": { "deepLink": "/sale" }
    }
  },
  "scheduleFor": "2026-06-01T03:00:00.000Z"
}

Audience union

typeExtra fieldsBehaviour
"all_customers"Every customer (non-vendor user)
"all_vendors"Every vendor-org member
"user_ids"userIds: string[] (1..10000)Targeted list

Channels + content

channels is a 1..2 subset of ["email", "push"]. The merged content block must include a sub-payload for every channel listed in channels. Channel-content shapes:

ChannelShapeNotes
email{ subject (1..200), html (1..50_000), text? (max 20_000) }subject trimmed
push{ title (1..120), body (1..500), imageUrl? (URL, max 2048), data? Record<string,string> }title / body trimmed

Other fields

FieldTypeNotes
scheduleForISO datetime?Send-now if omitted. Past timestamps are rejected by the service

Response 201BroadcastResponse.

Errors

StatusCodeWhen
400VALIDATION_ERRORBody fails zod, including the cross-field "content must include every listed channel" rule

GET /admin/notifications/broadcasts — List broadcasts

Required permission: notifications: view. Newest first.

Query

NameTypeDefaultNotes
pageint1>= 1
limitint501..200
statusstring?Filter by status string

Response 200 — paginated envelope of BroadcastResponse[].


GET /admin/notifications/broadcasts/:id — Broadcast detail

Required permission: notifications: view.

Errors

StatusCodeWhen
404NOT_FOUNDUnknown id

POST /admin/notifications/broadcasts/:id/cancel — Cancel

Required permission: notifications: broadcast. Semantics:

  • Scheduled: removes the delayed start job and marks the row cancelled.
  • In-progress: marks the row cancelled; remaining per-recipient jobs skip when popped from the queue. In-flight provider calls already running are not aborted.
  • Already completed / cancelled: no-op service-side, response still 200.

Response 200 — cancelled BroadcastResponse.

Errors

StatusCodeWhen
404NOT_FOUNDUnknown id

Notification log

GET /admin/notifications/log — Audit of sends

Required permission: notifications: view. Newest first. Use to answer "did the customer get the e-mail?" from support, or to debug a stuck broadcast.

Query

NameTypeDefaultNotes
pageint1>= 1
limitint501..200
recipientUserIdstring?Filter to one recipient
eventTypestring?e.g. "order.placed", "broadcast"
broadcastIdstring?All sends triggered by one broadcast

Response 200 — paginated envelope of NotificationLogResponse[].


  • admin-rbac — gates every endpoint via notifications:view / notifications:broadcast. See admin-rbac.md.
  • notifications-email-mailer / notifications-push-fcm — channel plugins consumed by both broadcasts and per-event triggers; the log row's provider reflects which plugin handled the send.
  • order, vendor, reviews — emit domain events that the notifications module subscribes to and turns into log entries (visible via this admin log).

On this page