Notifications and delivery

Define typed notifications, resolve recipient preferences, deliver across explicit channels, and retain one provider-independent history of every send.

Supported approaches

Typed notification definitions

available

Validate input, declare eligible channels, and render mail or in-app content from one application-owned registry.

Packages

@playstack/notifications@playstack/nest-notifications

Frameworks and integrations

In-app inbox

available

Persist recipient-scoped notifications and expose headless listing, read, archive, and retention behavior to React clients.

Multi-channel delivery

available

Route resolved sends through independent mail, push, in-app, SMS, or webhook slots behind one suppression gate.

Delivery history and feedback

available

Correlate provider acceptance, delivery, bounce, complaint, and suppression outcomes without storing message bodies.

Frameworks and integrations

framework

Expo

Use Playstack authentication and local-first contracts with Expo SecureStore, SQLite, and React Native lifecycle adapters.

integration

Web Push

A Web Push delivery seam without requiring an application notification inbox.

framework

React

Headless domain bindings plus optional Chakra application and admin compositions, with server authority kept outside the client.

framework

Next.js

Compose Playstack server contracts, React bindings, SSR handoff, and route adapters at the Next.js application edge.

framework

NestJS

Connect portable Playstack capabilities to dependency injection, guards, decorators, request context, workers, and lifecycle hooks.

integration

BullMQ

Dispatch and process typed Playstack jobs through BullMQ with explicit Redis ownership and native queue access.

integration

Inngest

Dispatch portable Playstack queue jobs through Inngest and serve the generated functions from Next.js, NestJS, or another supported framework.

integration

Amazon SES

Deliver rendered messages and SES templates through an application-configured AWS SDK client.

integration

Mailgun

Deliver rendered messages or stored Mailgun templates through an explicit regional provider client.

integration

Postmark

Deliver rendered Playstack mail or Postmark provider templates through an explicit driver and native client escape hatch.

integration

Resend

Deliver transactional mail and synchronize consent-aware audience contacts through explicit Resend adapters.

integration

SendGrid

Deliver rendered messages and dynamic templates through an isolated SendGrid mail client.

integration

SMTP

Deliver portable rendered email through an application-configured Nodemailer transport.

integration

Prisma

Persist Playstack capabilities through explicit application-owned Prisma clients, transactions, and managed schema fragments.

Package reference

@playstack/notifications

Decide what to say before choosing a provider

A notification definition owns validated input, category, eligible channels, and rendering. Recipient preference narrows those channels. The delivery layer then applies provider-independent suppression and dispatch policy before any send leaves the application.

ts
import { defineNotifications } from '@playstack/notifications'

export const notifications = defineNotifications({
  'account.invitation.accepted': {
    input: invitationAcceptedSchema,
    category: 'transactional',
    channels: ['mail', 'inApp'],
    digestible: true,
    render: {
      mail: ({ input }) => renderInvitationAcceptedEmail(input),
      inApp: ({ input }) => ({
        title: 'Invitation accepted',
        body: `${input.memberName} joined ${input.accountName}.`,
        href: `/accounts/${input.accountId}/members`,
      }),
    },
  },
})

Resolve twice

Preferences are checked when a notification is queued and again when its worker runs. An opt-out made between those moments must win. Transactional status may bypass a marketing preference, but it never bypasses invalid destinations, abuse policy, or an explicit stop-all suppression.

ts
await notificationService.enqueue(
  'account.invitation.accepted',
  input,
  { type: 'user', id: member.id },
  operationContext,
)

One immutable job is created per recipient and channel. Queue retries remain idempotent, and the operation trace follows the job into delivery feedback.

Keep a single send history

Mail providers, push services, the in-app inbox, SMS, and outbound webhooks all report different events. Delivery history normalizes those outcomes around the send, masks or encrypts destinations according to application policy, and gives support one place to answer what happened.

Data-only push does not require an inbox

Delivery exposes Expo, APNs, FCM and Web Push seams; Devices manages encrypted targets. Applications can send bounded sync hints without installing the notifications inbox. Recipient/enrollment checks, origin-device exclusion, coalescing, retry scheduling and permanent-token invalidation remain explicit application policy.

Provider acceptance is not proof of device execution. Keep content and credentials out of payloads and qualify each provider's response lifecycle and native background behavior.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software