Notifications & messaging

Keep members informed and engaged.

View as MarkdownInstall skills

Notifications turn engagement events into messages: a member earns points, climbs a tier, sees a balance about to expire, or unlocks an offer. VINR listens to the same events that drive loyalty and dispatches the right message on the right channel — so you never have to poll for state or hand-roll your own send loop.

Notification triggersAsk

Every notification starts from an engagement event. You attach a notification rule to a trigger, and VINR evaluates it the moment the event fires.

TriggerTypical message
loyalty.points.earned"You earned 250 points on your purchase."
loyalty.tier.changed"Welcome to Gold — here's what's new."
loyalty.points.expiring"1,200 points expire in 14 days."
reward.unlocked"You can now redeem a free coffee."
redemption.completed"Your reward is applied — enjoy."
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

// Notify members 14 days before points lapse.
const rule = await vinr.notifications.rules.create({
  trigger: 'loyalty.points.expiring',
  leadTime: '14d',            // anticipatory triggers fire ahead of the event
  channels: ['email', 'push'],
  template: 'points-expiring',
});                            // "we_..." style id under the hood

Anticipatory triggers like loyalty.points.expiring are evaluated by VINR on a daily schedule against each member's balance and expiry window — you don't run the cron. Reactive triggers (loyalty.points.earned) fire in near real time.

ChannelsAsk

A rule can fan out to one or more channels. VINR resolves the member's contact details from the linked customer and respects per-channel opt-in.

ChannelAddress sourceNotes
emailcustomer.emailAlways available; supports rich templates.
smscustomer.phoneRequires verified phone; short body only.
pushRegistered device tokensMobile/web push via your app.
webhookYour endpointDispatch to your own messaging stack.

For full control, route a notification to your own systems with the webhook channel and send through any provider you already operate:

// Verify the dispatch, then send via your provider of choice.
export async function POST(req: Request) {
  const sig = req.headers.get('x-vinr-signature') ?? '';
  const event = vinr.webhooks.verify(await req.text(), sig);

  if (event.type === 'notification.dispatched') {
    const { channel, member, payload } = event.data;
    await myMailer.send(payload.to, payload.subject, payload.html);
  }
  return new Response(null, { status: 200 });
}

Templates & personalizationAsk

Templates separate copy from logic. Each template is a named, versioned document with merge fields drawn from the triggering event and the member's profile.

await vinr.notifications.templates.create({
  name: 'points-earned',
  subject: 'You earned {{points}} points',
  body: 'Hi {{member.firstName}}, your {{currencyName}} balance is now {{balance}}.',
  locales: ['en', 'de', 'fr'],   // VINR picks by customer.locale, falls back to en
});

Merge fields resolve against a typed context, so a missing or misspelled field fails validation at create time rather than rendering blank in production.

Prop

Type

Quiet hours & preferencesAsk

Members control how and when they hear from you. VINR enforces preferences and quiet hours before any send — a suppressed message is recorded as skipped, never silently dropped.

Set member preferences

Store per-channel opt-in on the loyalty account. Honor unsubscribe links automatically on the email channel.

await vinr.loyalty.accounts.update('loy_abc123', {
  notificationPrefs: { email: true, sms: false, push: true },
});

Define quiet hours

Quiet hours defer non-urgent messages into a member's local daytime window, computed from customer.timezone.

await vinr.notifications.settings.update({
  quietHours: { start: '21:00', end: '08:00' },  // member-local time
  deferToleranceHours: 12,                        // drop if still suppressed after this
});

Reserve transactional sends

Mark a rule transactional: true to bypass marketing opt-out and quiet hours — use only for messages a member must receive, such as a redemption.completed confirmation.

Quiet hours and opt-out apply to engagement and marketing messages. Keep them off transactional rules, but never use transactional: true to evade an unsubscribe — that undermines deliverability and member trust.

Delivery reportingAsk

Every dispatch produces a record you can query and that emits follow-up events: notification.dispatched, notification.delivered, notification.bounced, and notification.skipped.

const recent = await vinr.notifications.deliveries.list({
  member: 'loy_abc123',
  status: 'bounced',
  limit: 20,
});

for (const d of recent.data) {
  console.log(d.channel, d.template, d.failureReason);
}
StatusMeaning
dispatchedHanded to the channel provider.
deliveredConfirmed delivery (where the channel reports it).
bouncedPermanent failure; address may be suppressed.
skippedSuppressed by opt-out or quiet hours.

Subscribe to notification.bounced to clean stale addresses, and watch your skipped rate as a signal that triggers are too aggressive. See Webhooks for verification and retry behavior.

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page