ShipNext

Notifications

Push registration, subscription, and payment events to Telegram, Discord, Slack, or Feishu.

ShipNext operational notifications tell your team about important product events such as new users, subscription changes, and successful payments. They are separate from transactional email sent to end users.

The feature has two layers:

  • src/integrations/notify/ - third-party adapters for Telegram, Discord, Slack, and Feishu.
  • src/modules/notify/ - shared types, provider selection, and public handlers.

Business code calls notifyOnUserRegistered, notifyOnUserSubscription, and notifyOnUserPayment. The selected provider comes from websiteConfig.notify.provider.

Notification calls in auth and payment webhooks are commented out by default. Enable them only after configuring provider secrets.

Architecture

FileResponsibility
src/modules/notify/types.tsNotifyProvider interface and event payload types
src/modules/notify/handler.tsPublic notifyOnUser* APIs
src/modules/notify/provider.tsgetNotifyProvider() factory
src/integrations/notify/telegram.tsTelegram Bot API
src/integrations/notify/discord.tsDiscord Webhook embeds
src/integrations/notify/slack.tsSlack Incoming Webhook
src/integrations/notify/feishu.tsFeishu bot cards with optional signing

Website config

const notifyConfig = {
  provider: "telegram", // telegram | discord | slack | feishu
  events: {
    userRegistered: true,
    userSubscription: true,
    userPayment: true,
  },
};

If an event is false, the handler returns early and does not instantiate the provider.

Environment variables

Only configure the provider you use.

ProviderVariableRequiredDescription
telegramTELEGRAM_BOT_TOKENYesBot token from BotFather
telegramTELEGRAM_CHAT_IDYesTarget user, group, or channel ID
discordDISCORD_WEBHOOK_URLYesChannel webhook URL
slackSLACK_WEBHOOK_URLYesIncoming webhook URL
feishuFEISHU_WEBHOOK_URLYesFeishu custom bot webhook
feishuFEISHU_SIGNING_SECRETOptionalRequired when signature verification is enabled
TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
TELEGRAM_CHAT_ID=-1001234567890

Provider constructors validate their required variables. Handler errors are caught and logged so registration or payment flows are not interrupted.

Built-in events

EventHandlerTypical triggerMessage fields
User registerednotifyOnUserRegisteredBetter Auth user create hookName, email, user ID
Subscription changednotifyOnUserSubscriptionPayment webhook subscription syncUser ID, plan, status, optional interval
Payment succeedednotifyOnUserPaymentSubscription payment or one-time orderUser ID, plan/product ID, amount, currency

Amounts are passed in the smallest currency unit. For USD, 2900 displays as $29.00.

Server usage

import {
  notifyOnUserRegistered,
  notifyOnUserSubscription,
  notifyOnUserPayment,
} from "@/modules/notify/handler";

await notifyOnUserRegistered({
  id: user.id,
  email: user.email,
  name: user.name,
});

await notifyOnUserSubscription({
  userId: "user_xxx",
  plan: "pro",
  status: "active",
  interval: "month",
});

await notifyOnUserPayment({
  userId: "user_xxx",
  amount: 2900,
  currency: "usd",
  planId: "pro_monthly",
});

Enable business hooks

New user registration

In the Better Auth user create hook:

const { notifyOnUserRegistered } = await import("@/modules/notify/handler");
await notifyOnUserRegistered({
  id: user.id,
  email: user.email,
  name: user.name,
});

Subscriptions and payments

Search notifyOnUser in src/modules/billing/webhooks/webhook-processor.ts, uncomment the relevant calls, and make sure imports use:

"@/modules/notify/handler"

Use your own conditions to avoid duplicate notifications if needed.

Add a provider

  1. Create a class in src/integrations/notify/ that implements NotifyProvider.
  2. Export it from src/integrations/notify/index.ts.
  3. Add a branch in src/modules/notify/provider.ts.
  4. Add config typing/docs for the new provider value.
  5. Document required environment variables.
import type { NotifyProvider, NotifyUser } from "@/modules/notify/types";

class MyChannelProvider implements NotifyProvider {
  readonly name = "my-channel";

  async onUserRegistered(user: NotifyUser): Promise<void> {
    // Call third-party API.
  }
}

Notifications vs email

CapabilityConfigAudienceExamples
Operational notificationswebsiteConfig.notify + channel env varsYour teamRegistration, subscription, payment alerts
Transactional emailwebsiteConfig.email + RESEND_API_KEYEnd usersVerification, reset password, magic links

Turning off notifications does not affect email.

Troubleshooting

Configured website.ts but never receive messages

  • Confirm the notifyOnUser* calls are enabled.
  • Confirm the event switch is true.
  • Confirm environment variables match the selected provider.
  • Restart the dev server after editing .env.local.

TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID must be set

The Telegram provider was constructed without required variables. Add them or disable the related event.

Payment webhook has notify: true but no notification

notify: true only marks that a notification should be considered. The webhook processor must call the handler.

Feishu signature verification fails

Set FEISHU_SIGNING_SECRET to the exact secret from the Feishu bot settings.

On this page