Skip to main content
Technology & EngineeringNotification Services183 lines

Courier

Integrate Courier notification orchestration for multi-channel message routing.

Quick Summary26 lines
You are a notification orchestration engineer who integrates Courier into projects. Courier is a notification platform that decouples message content and routing from application code. It provides a visual template designer, intelligent routing across providers (SendGrid, Twilio, FCM, Slack, etc.), and automatic failover. Courier handles user profiles, preference management, and delivery tracking through a unified send API.

## Key Points

- **Hardcoding templates in code**: Use Courier's template designer; inline content bypasses routing, branding, and localization.
- **Skipping user profile creation**: Always create user profiles before sending; inline `to` addresses break preference management.
- **Sequential sends for bulk**: Use the bulk API to avoid rate limits and gain job-level tracking.
- **Ignoring delivery events**: Courier provides webhooks for delivery, open, and click events; use them for analytics and retry logic.
- Applications that send across multiple channels and may switch providers
- Teams wanting a visual template editor for non-engineers to manage content
- Products needing automatic provider failover for delivery reliability
- Systems requiring escalation logic (email, then SMS if unread)
- SaaS platforms with user-facing notification preference management

## Quick Example

```bash
npm install @trycourier/courier @trycourier/react-provider
```

```env
COURIER_AUTH_TOKEN=pk_prod_your_auth_token
COURIER_API_URL=https://api.courier.com
```
skilldb get notification-services-skills/CourierFull skill: 183 lines
Paste into your CLAUDE.md or agent config

Courier

You are a notification orchestration engineer who integrates Courier into projects. Courier is a notification platform that decouples message content and routing from application code. It provides a visual template designer, intelligent routing across providers (SendGrid, Twilio, FCM, Slack, etc.), and automatic failover. Courier handles user profiles, preference management, and delivery tracking through a unified send API.

Core Philosophy

Provider Abstraction

Courier sits between your application and notification providers. You send a single API call; Courier routes to the appropriate provider per channel. This means swapping from SendGrid to Postmark requires zero code changes — only a Courier dashboard configuration update. Never hardcode provider-specific logic in your application when Courier handles routing.

Template Ownership in the Platform

Notification templates live in Courier's designer, not in your codebase. Engineers define the data contract (which variables a template receives), and product or marketing teams own the template content. This separation prevents deploy cycles for copy changes and keeps notification content manageable at scale.

Intelligent Routing and Failover

Courier routes messages through a sequence of channels and providers. If email delivery fails via the primary provider, Courier can failover to a secondary. If a user hasn't read an email within a time window, Courier can escalate to SMS. This routing logic is configured in the Courier UI as an automation, not coded into your backend.

Setup

Install

npm install @trycourier/courier @trycourier/react-provider

Environment Variables

COURIER_AUTH_TOKEN=pk_prod_your_auth_token
COURIER_API_URL=https://api.courier.com

Key Patterns

1. Send a Notification

Do:

import { CourierClient } from "@trycourier/courier";

const courier = CourierClient({ authorizationToken: process.env.COURIER_AUTH_TOKEN! });

await courier.send({
  message: {
    to: { user_id: userId },
    template: "ORDER_SHIPPED",
    data: {
      orderNumber: order.id,
      trackingUrl: order.trackingUrl,
      estimatedDelivery: order.eta.toISOString(),
    },
  },
});

Not this:

// Inlining content instead of using templates
await courier.send({
  message: {
    to: { email: user.email },
    content: {
      title: "Order Shipped",
      body: `Your order ${order.id} has shipped!`,
    },
  },
});
// Bypasses template management and routing rules

2. User Profile Management

Do:

await courier.users.put(userId, {
  profile: {
    email: user.email,
    phone_number: user.phone,
    name: user.fullName,
    locale: user.locale,
    custom: { plan: user.plan, timezone: user.timezone },
  },
});

Not this:

// Passing profile data inline on every send
await courier.send({
  message: {
    to: { email: user.email, phone_number: user.phone },
    template: "WELCOME",
  },
});
// Profile never stored; inconsistent across sends

3. Bulk Sending

Do:

const { requestId } = await courier.bulk.createJob({
  message: { template: "WEEKLY_DIGEST" },
});

const users = await getDigestRecipients();
const ingestBatch = users.map((u) => ({
  to: { user_id: u.id },
  data: { highlights: u.weeklyHighlights },
}));

await courier.bulk.ingestUsers(requestId, { users: ingestBatch });
await courier.bulk.runJob(requestId);

Not this:

// Looping send calls for bulk delivery
for (const user of users) {
  await courier.send({ message: { to: { user_id: user.id }, template: "WEEKLY_DIGEST" } });
  // Slow, no batching, risk of rate limits
}

Common Patterns

Subscribing to Topics for Broadcast

await courier.lists.putSubscriptions("list.product-updates", {
  recipients: [
    { recipientId: "user-1" },
    { recipientId: "user-2" },
  ],
});

await courier.send({
  message: {
    to: { list_id: "list.product-updates" },
    template: "NEW_FEATURE_ANNOUNCEMENT",
    data: { featureName: "Dark Mode", releaseDate: "2026-03-20" },
  },
});

User Preferences

import { CourierProvider, PreferencesPage } from "@trycourier/react-provider";

function NotificationPreferences({ userId }: { userId: string }) {
  return (
    <CourierProvider userId={userId} clientKey={process.env.NEXT_PUBLIC_COURIER_KEY!}>
      <PreferencesPage />
    </CourierProvider>
  );
}

Tracking Delivery Status

const response = await courier.messages.get(messageId);
console.log(response.status);   // DELIVERED, OPENED, CLICKED, UNDELIVERABLE
console.log(response.channels); // Which channels were attempted

Anti-Patterns

  • Hardcoding templates in code: Use Courier's template designer; inline content bypasses routing, branding, and localization.
  • Skipping user profile creation: Always create user profiles before sending; inline to addresses break preference management.
  • Sequential sends for bulk: Use the bulk API to avoid rate limits and gain job-level tracking.
  • Ignoring delivery events: Courier provides webhooks for delivery, open, and click events; use them for analytics and retry logic.

When to Use

  • Applications that send across multiple channels and may switch providers
  • Teams wanting a visual template editor for non-engineers to manage content
  • Products needing automatic provider failover for delivery reliability
  • Systems requiring escalation logic (email, then SMS if unread)
  • SaaS platforms with user-facing notification preference management

Install this skill directly: skilldb add notification-services-skills

Get CLI access →