The plugin adds endpoints to auth.api. Each one is one of two kinds: server-only, or own rows.
- Server-only endpoints are callable through
auth.apifrom your server code. They are never exposed as an HTTP route. - Own rows endpoints need a session. They only ever see or change the caller’s own notifications.
Server-only endpoints
auth.api.notify
Creates notifications. Pass userId, organizationId, or both.
typeandtitleare required.userIdcreates one row for that user.organizationIdalone fans out one row per member. Fan-out also takesrolesandexcludeUserIds.userIdwithorganizationIdcreates one row for that member, stamped with the org. The user must be a member.body,hrefanddata(JSON) are optional.
Returns { count, notifications }.
See Notify an organization for the fan-out rules.
Inside a better-auth transaction (runWithTransaction, as in a @better-auth/scim projection), the notification and delivery rows are written in that transaction and commit or roll back with it. Nothing is sent inside the call: the emails go out from processDeliveries after the commit.
auth.api.processDeliveries
Sends due email deliveries. Call it from a cron. The optional body is { limit }, which overrides batchSize.
Returns { sent, skipped, retrying, failed, hasMore }.
See Email if unread.
notify rejects a type that starts with notifications. with BAD_REQUEST (RESERVED_NOTIFICATION_TYPE): those are the plugin’s own events. Both endpoints emit events through inbox({ onEvent }).
Own-row endpoints
auth.api.listNotifications
Query: filter ("unread" or "all"), limit (up to 100), offset, and organizationId.
Returns { notifications, hasMore }.
auth.api.markRead
Body: { id }. Marks one of the caller’s notifications read.
Returns { notification }.
auth.api.markSeen
Body: { ids }, up to 100 ids. Stamps seenAt on the caller’s notifications, which cancels pending emails. The panel calls it for rows that have been on screen.
Returns { count }.
auth.api.markAllRead
Body: an optional { organizationId }, which scopes the update to one org.
Returns { count }.
auth.api.unreadCount
Query: an optional organizationId.
Returns { count }.
Server-only functions
These are functions, not endpoints. Use them in your own scripts and hooks.
| Function | Description |
|---|---|
deleteOrganizationData(auth, organizationId) |
Deletes an org’s notifications and their deliveries. Returns { notifications }, the count |
deleteMemberData(auth, { userId, organizationId }) |
Deletes one member’s notifications in one org. Returns { notifications } |
exportNotificationsData(auth, { organizationId?, cursor?, limit? }) |
One page (500 by default) of notifications, each with its deliveries and the user’s userEmail, as plain JSON with ISO dates. Returns { notifications, cursor }. Pass cursor back until it’s null |
The plugin also declares byo hooks, so @byo-oss/core reaches it. sweepAll(auth) runs the same sweep as processDeliveries, so one cron route for every BYO plugin can replace the processDeliveries call. Core’s deleteOrganizationData and deleteMemberData call the functions above. With organizationCleanup: false only the sweep is declared.
exportNotificationsData pages by createdAt and resumes after the last row it returned, so a row deleted mid-export (a member leaving, say) doesn’t make the next page skip one. The cursor is opaque. A cursor from an earlier release (a bare number) throws: start over with no cursor and keep one copy of each id. See Upgrading.
Channel exports
@byo-oss/notifications/mailer exports mailerChannel, which returns an InboxChannel for inbox({ channels }):
| Option | Type | Meaning |
|---|---|---|
mailer |
Mailer |
Any mailer from @byo-oss/mailer. It owns the sender address |
replyTo |
string, optional | Reply-to address on every email |
render |
(ctx) => { subject, html, text }, optional |
Builds the email, sync or async. Default: the title, the body and a View button |
The entry also re-exports the Mailer, MailMessage and MailResult types from @byo-oss/mailer, and the RenderedEmail type with the renderEmail and notificationUrl helpers, for a custom render that wraps the default email or builds the link.
@byo-oss/notifications/wraps exports wrapsEmail, which is deprecated. Use mailerChannel with wrapsMailer from @byo-oss/mailer/wraps.