> ## Documentation Index
> Fetch the complete documentation index at: https://www.courier.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Install the Courier skill before writing code: `npx skills add trycourier/courier-skills`. It carries the verified SDK shapes and the rules you cannot get wrong.
> Authenticate every request with `Authorization: Bearer <API_KEY>`. A workspace has several environments and each has its own keys, which are plain `pk_` strings with no environment prefix. Start with Test.
> Send with `client.send.message`, the default import of the v7 Node SDK. Reference a template by its `nt_` id or its alias.
> A send accepts a bare Elemental element list, but storing content on a template requires the top-level elements wrapped in a channel element.
> Templates and journeys can be built in the Courier app or created through the API. Either way they live in the workspace and are referenced by ID when you send.
> The hosted MCP server is https://mcp.courier.com. For a briefing on what Courier is and when to use it, read https://www.courier.com/llms.txt.
> Prefer the Guides tab for how-do-I questions and the Docs tab for how-does-it-behave questions. The API reference lives under /api-reference.

# Migrate from SuprSend

> Map SuprSend workflows, templates, and subscribers to Courier, with an API mapping and a plan.

export const Endpoint = ({method, path, name, href, children, bare}) => {
  const verb = String(method || "").toUpperCase();
  const title = verb + " " + path;
  const label = children || name || path;
  if (bare) {
    return href ? <a href={href}><code>{title}</code></a> : <code>{title}</code>;
  }
  if (!href) {
    return <span className="cx-endpoint" data-method={verb} title={title}>
        <span className="cx-endpoint-label">{label}</span>
        <span className="cx-endpoint-method">{verb}</span>
      </span>;
  }
  return <a className="cx-endpoint" data-method={verb} href={href} title={title}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">{verb}</span>
    </a>;
};

export const AppLink = ({href, children, name, bare}) => {
  const label = children || name || "Open in Courier";
  if (bare) {
    return <a href={href} target="_blank" rel="noreferrer">{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="app" href={href} target="_blank" rel="noreferrer">
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method" aria-hidden="true">↗</span>
    </a>;
};

export const Guide = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="guide" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">GUIDE</span>
    </a>;
};

export const Doc = ({href, children, name, bare}) => {
  const label = children || name || href;
  if (bare) {
    return <a href={href}>{label}</a>;
  }
  return <a className="cx-endpoint" data-kind="doc" href={href}>
      <span className="cx-endpoint-label">{label}</span>
      <span className="cx-endpoint-method">DOC</span>
    </a>;
};

This guide maps SuprSend concepts to Courier and gives you a step-by-step migration plan.

## Map SuprSend concepts to Courier

| SuprSend                                                                | Courier                                                                                     |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| [Workflows](https://docs.suprsend.com/docs/workflows)                   | <Doc href="/docs/journeys/overview">Journeys</Doc>                                               |
| [Templates](https://docs.suprsend.com/docs/templates)                   | <Doc href="/docs/design/templates/overview">Templates</Doc>                                      |
| [Vendors](https://docs.suprsend.com/docs/vendors)                       | <Doc href="/docs/integrations/overview">Integrations</Doc>                                       |
| [Smart Channel Routing](https://docs.suprsend.com/docs/design-workflow) | <Doc href="/docs/send/routing">Channel Priority</Doc> + <Doc href="/docs/send/routing">Failover</Doc> |
| [Batching](https://docs.suprsend.com/docs/batch)                        | <Doc href="/docs/journeys/nodes/batch">Batch node</Doc>                                          |
| [Subscribers](https://docs.suprsend.com/docs/users)                     | <Doc href="/docs/recipients/overview">Users / Profiles</Doc>                                     |
| [Preferences](https://docs.suprsend.com/docs/preferences)               | <Doc href="/docs/recipients/preferences/overview">Preferences</Doc>                              |
| [Inbox](https://docs.suprsend.com/docs/inbox-quick-start)               | <Doc href="/docs/in-app/overview">Inbox</Doc>                                                    |
| [Tenants](https://docs.suprsend.com/docs/tenants)                       | <Doc href="/docs/tenants/overview">Tenants</Doc>                                                 |

### Workflows and Journeys

SuprSend [workflows](https://docs.suprsend.com/docs/workflows) orchestrate notifications with four node types: trigger, function, branch, and delivery. Courier <Doc href="/docs/journeys/overview">Journeys</Doc> do the same with a visual builder and an API.

| SuprSend Node         | Courier Equivalent                                                                                                      |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Trigger node          | <Doc href="/docs/journeys/invoke">Journey trigger</Doc> (API invoke, inbound webhook, Segment event, or audience membership) |
| Delay (function node) | <Doc href="/docs/journeys/nodes/delay">Delay node</Doc>                                                                      |
| Batch (function node) | <Doc href="/docs/journeys/nodes/batch">Batch node</Doc>                                                                      |
| Branch node           | <Doc href="/docs/journeys/nodes/branch">Branch node</Doc>                                                                    |
| Delivery node         | <Doc href="/docs/journeys/nodes/send">Send node</Doc> (references a template + routing)                                      |

The important difference: journeys and templates are separate resources in Courier. A send node references a template by ID. Your product team changes what a notification says without opening the journey. Engineers change timing and conditions without touching copy.

Build journeys visually <Doc href="/docs/journeys/build">in the UI</Doc> or define them through the <Doc href="/docs/journeys/build">Journeys API</Doc>.

### Templates

SuprSend splits notification content into [template groups](https://docs.suprsend.com/docs/templates), one template per channel. A Courier <Doc href="/docs/design/templates/overview">template</Doc> is a single resource holding content for every channel: email, SMS, push, chat, and inbox.

<Doc href="/docs/design/templates/design-studio">Design Studio</Doc> uses drag-and-drop content blocks. <Doc href="/docs/design/elemental/overview">Elemental</Doc> JSON is the code-first path. Both support <Doc href="/docs/design/templates/variables">`{{variable}}`</Doc> Handlebars-style personalization, channel-specific overrides, and <Doc href="/docs/design/elemental/locales">localization</Doc>.

Your product team updates copy, layout, and branding in Design Studio without code or a deploy. Publish and it goes live immediately.

### Integrations

SuprSend [vendors](https://docs.suprsend.com/docs/vendors) are Courier <Doc href="/docs/integrations/overview">Integrations</Doc>: the provider connections that deliver messages, such as SendGrid, Twilio, FCM, and Slack.

Courier supports providers on every channel. Wire up multiple providers for the same channel type and Courier <Doc href="/docs/send/routing">fails over</Doc> between them automatically. If your primary email provider goes down, traffic shifts to the backup with no code change and no manual step.

### Routing

Courier's `single` <Doc href="/docs/send/routing">routing method</Doc> replicates SuprSend's [smart channel routing](https://docs.suprsend.com/docs/design-workflow). Courier tries channels in the order you specify and stops at the first success. The `all` method sends to every listed channel at once.

Set routing per send request or per template. Provider-level <Doc href="/docs/send/routing">failover</Doc> layers on top, so you get fallback at both the channel and provider layers.

### Digests and batching

Courier's <Doc href="/docs/journeys/nodes/batch">Batch node</Doc> works like SuprSend's [batch function](https://docs.suprsend.com/docs/batch). It collects matching events over a window you configure, then releases them as one payload for a single summary notification. Your template gets the full list of collected events, so you can render summaries across email, push, or in-app. For summaries that release on a fixed schedule the user controls, use the <Doc href="/docs/journeys/nodes/digest">Digest node</Doc>.

### Users and profiles

Courier <Doc href="/docs/recipients/overview">profiles</Doc> store recipient data: email, phone, push tokens, and any custom properties. Profiles accept nested JSON, which suits structured data like subscription tiers, team roles, or feature flags.

Create profiles ahead of time through the API, or inline at send time. Pass a `user_id` that doesn't exist yet and Courier creates the profile.

### Preferences

SuprSend's [preference system](https://docs.suprsend.com/docs/preferences) supports global channel opt-outs, category-level controls, and per-category overrides. Courier <Doc href="/docs/recipients/preferences/overview">Preferences</Doc> support the same hierarchy and enforce it at send time. You write no conditional logic.

Courier also ships a <Guide href="/docs/guides/build-a-preference-center#hosted-page">hosted preference page</Guide> you can deploy in minutes and embeddable <Guide href="/docs/guides/build-a-preference-center#embedded-component">React components</Guide> for in-app preference centers. No custom UI required.

### In-app notifications

Courier <Doc href="/docs/in-app/overview">Inbox</Doc> is a real-time in-app notification center with drop-in components for <Doc href="/docs/sdk-libraries/courier-react-web">React</Doc>, <Doc href="/docs/sdk-libraries/ios">iOS</Doc>, <Doc href="/docs/sdk-libraries/android">Android</Doc>, and <Doc href="/docs/sdk-libraries/courier-js-web">vanilla JS</Doc>. It runs on the same delivery pipeline as email, push, and SMS, so there's no separate provider to configure.

You get read/unread state, archiving, per-user history, <Doc href="/docs/in-app/add-toasts">toast notifications</Doc>, and <Doc href="/docs/in-app/customize-the-inbox">tab-based organization</Doc>.

### Tenants

<Doc href="/docs/tenants/overview">Tenants</Doc> work much the same on both platforms. You scope branding, preference defaults, and notification feeds to a customer organization. Pass a `tenant_id` at send time and Courier applies that tenant's branding and preferences. Branding lives on the Tenant resource rather than a separate object.

## Why Courier

* **Content and logic stay separate.** Templates and journeys are independent. Your product team updates copy in Design Studio while engineers tune journey timing.
* **Design Studio.** Build email, SMS, push, and chat content with drag-and-drop blocks. Preview across channels, personalize with variables, and publish without deploying code.
* **Routing you control.** Priority-based fallback (`single`) or broadcast to all channels (`all`). Configure multiple providers per channel for automatic failover at the provider level too.
* **Built-in in-app channel.** Courier Inbox works without a third-party provider. Drop in a React, iOS, or Android component and deliver in-app notifications on the same pipeline as email and push.
* **Hosted preferences out of the box.** Ship a user-facing preference center with a single config, or embed React components in your app. No custom UI required.
* **The providers you already use.** Email, SMS, push, chat, webhooks, CDPs, and observability tools. Switch providers without changing your send code.
* **Full delivery observability.** <Doc href="/docs/monitor/overview">Message Logs</Doc> track every message from API request to provider delivery, with a timeline, error details, and rendered content inspection.

## Migrate step by step

<Steps>
  <Step title="Create your Courier workspace">
    <AppLink href="https://app.courier.com/signup">Sign up</AppLink> and create a workspace. Courier gives you separate Test and Production <Doc href="/docs/workspaces/overview#environments-and-api-keys">environments</Doc> with their own API keys, so you can migrate without touching live traffic.
  </Step>

  <Step title="Configure integrations">
    Go to **Integrations** in your Courier dashboard and connect the same providers you use as SuprSend vendors (SendGrid, Twilio, FCM, etc.). Each provider maps to a channel type (email, SMS, push, chat). Configure multiple providers per channel for <Doc href="/docs/send/routing">failover</Doc>.

    If you use SuprSend's inbox, enable <Doc href="/docs/in-app/overview">Courier Inbox</Doc>. No external provider needed.
  </Step>

  <Step title="Recreate templates">
    SuprSend template groups contain per-channel templates. In Courier, a single <Doc href="/docs/design/templates/overview">template</Doc> holds content for all channels:

    1. Create a new template for each notification type
    2. Add channel-specific content blocks (email body, SMS text, push title/body, etc.)
    3. Use `{{variable}}` syntax for dynamic data. Both platforms use Handlebars-style variables
    4. Publish the template to make it available for sending
  </Step>

  <Step title="Recreate workflows as journeys">
    Recreate SuprSend workflows that use delays, batching, or branching in <Doc href="/docs/journeys/overview">Journeys</Doc>. Build them visually <Doc href="/docs/journeys/build">in the UI</Doc> or define them through the <Doc href="/docs/journeys/build">Journeys API</Doc>.

    For notification types that need no orchestration (no delays, no batching), skip journeys and send directly via the <Doc href="/docs/send/overview">Send API</Doc>.
  </Step>

  <Step title="Migrate user data">
    Create Courier profiles with the same identifiers you use in SuprSend. Use the <Endpoint method="POST" path="/profiles/{user_id}" name="Create a Profile" href="/docs/api-reference/user-profiles/create-a-profile">Profiles API</Endpoint>, or create them inline at send time.

    ```json theme={null}
    {
      "user_id": "user_123",
      "profile": {
        "email": "sarah@acme-corp.com",
        "phone_number": "+15551234567",
        "custom": {
          "name": "Sarah Bennett",
          "plan": "enterprise"
        }
      }
    }
    ```
  </Step>

  <Step title="Set up preferences">
    If you use SuprSend's preference system, recreate your structure in Courier:

    1. Define <Doc href="/docs/recipients/preferences/overview">subscription topics</Doc> that map to your SuprSend categories
    2. Configure default channel routing per topic
    3. Migrate user preference selections via the <Endpoint method="GET" path="/users/{user_id}/preferences" name="Get user's Preferences" href="/docs/api-reference/user-preferences/get-users-preferences">Preferences API</Endpoint>

    Courier also provides a <Guide href="/docs/guides/build-a-preference-center#hosted-page">hosted preference page</Guide> you can deploy immediately, or <Guide href="/docs/guides/build-a-preference-center#embedded-component">React components</Guide> for embedding preferences in your app.
  </Step>

  <Step title="Update your send calls">
    Replace SuprSend's workflow trigger calls with Courier's <Doc href="/docs/send/overview">Send API</Doc>. A basic send looks like this:

    <CodeGroup>
      ```javascript Node.js theme={null}
      const { requestId } = await client.send.message({
        message: {
          to: { user_id: 'user_123' },
          template: 'nt_01kx4h2jdafq8bk9aftxak4b40',
          data: {
            order_id: 'ORD-456',
            total: 79.99,
          },
        },
      });
      ```

      ```python Python theme={null}
      response = client.send.message(
          message={
              "to": {"user_id": "user_123"},
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "data": {
                  "order_id": "ORD-456",
                  "total": 79.99,
              },
          },
      )
      ```

      ```bash cURL theme={null}
      curl -X POST https://api.courier.com/send \
        -H "Authorization: Bearer $COURIER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "message": {
            "to": { "user_id": "user_123" },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
            "data": {
              "order_id": "ORD-456",
              "total": 79.99
            }
          }
        }'
      ```

      ```ruby Ruby theme={null}
      response = courier.send_.message(
        message: {
          to: { user_id: "user_123" },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          data: { order_id: "ORD-456", total: 79.99 }
        }
      )
      ```

      ```go Go theme={null}
      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfUserRecipient: &shared.UserRecipientParam{
      				UserID: courier.String("user_123"),
      			},
      		},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      		Data: map[string]any{
      			"order_id": "ORD-456",
      			"total": 79.99,
      		},
      	},
      })
      ```

      ```java Java theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(JsonValue.from(java.util.Map.of("user_id", "user_123")))
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .data(JsonValue.from(java.util.Map.of(
                  "order_id", "ORD-456",
                  "total", 79.99)))
              .build())
          .build();
      client.send().message(params);
      ```

      ```php PHP theme={null}
      $response = $client->send->message(
        message: [
          'to' => ['userID' => 'user_123'],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'data' => ['order_id' => 'ORD-456', 'total' => 79.99],
        ],
      );
      ```

      ```csharp C# theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Data = new Dictionary<string, JsonElement>()
              {
                  { "order_id", JsonSerializer.SerializeToElement("ORD-456") },
                  { "total", JsonSerializer.SerializeToElement(79.99) },
              },
          },
      };

      await client.Send.Message(parameters);
      ```

      ```bash CLI theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message '{"to":{"user_id":"user_123"},"template":"nt_01kx4h2jdafq8bk9aftxak4b40","data":{"order_id":"ORD-456","total":79.99}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 with the order details.
      ```
    </CodeGroup>

    Courier applies routing, preferences, and failover from your template and workspace configuration. For multi-channel sends, set the `routing` field:

    <CodeGroup>
      ```javascript Node.js highlight={7} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            user_id: "user_123",
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          routing: {
            method: "single",
            channels: [
              "push",
              "email",
              "sms",
            ],
          },
        },
      });
      ```

      ```python Python highlight={7} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "user_id": "user_123",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "routing": {
                  "method": "single",
                  "channels": [
                      "push",
                      "email",
                      "sms",
                  ],
              },
          },
      )
      ```

      ```bash cURL highlight={10} wrap theme={null}
      curl -X POST https://api.courier.com/send \
        -H "Authorization: Bearer $COURIER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "message": {
            "to": {
              "user_id": "user_123"
            },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
            "routing": {
              "method": "single",
              "channels": [
                "push",
                "email",
                "sms"
              ]
            }
          }
        }'
      ```

      ```ruby Ruby highlight={7} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            user_id: "user_123"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          routing: {
            method: "single",
            channels: [
              "push",
              "email",
              "sms"
            ]
          }
        }
      )
      ```

      ```go Go highlight={9} theme={null}
      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfUserRecipient: &shared.UserRecipientParam{
      				UserID: courier.String("user_123"),
      			},
      		},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      		Routing: courier.SendMessageParamsMessageRouting{
      			Channels: []shared.MessageRoutingChannelUnionParam{
      				{OfString: courier.String("push")},
      				{OfString: courier.String("email")},
      				{OfString: courier.String("sms")},
      			},
      			Method: "single",
      		},
      	},
      })
      ```

      ```java Java highlight={5} theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(UserRecipient.builder().userId("user_123").build())
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .routing(SendMessageParams.Message.Routing.builder()
                  .addChannel("push")
                  .addChannel("email")
                  .addChannel("sms")
                  .method(SendMessageParams.Message.Routing.Method.SINGLE)
                  .build())
              .build())
          .build();
      SendMessageResponse response = client.send().message(params);
      ```

      ```php PHP highlight={7} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'user_id' => 'user_123',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'routing' => [
            'method' => 'single',
            'channels' => [
              'push',
              'email',
              'sms',
            ],
          ],
        ],
      );
      ```

      ```csharp C# highlight={7} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Routing = new() { Channels = ["push", "email", "sms"], Method = Send::Method.Single },
          },
      };

      var response = await client.Send.Message(parameters);
      ```

      ```bash CLI highlight={5} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"user_id": "user_123"}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
        --message.routing '{"method": "single", "channels": ["push", "email", "sms"]}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 over email and push.
      ```
    </CodeGroup>

    This tries push first, then email, then SMS. The same pattern as SuprSend's smart channel routing.
  </Step>

  <Step title="Test and cut over">
    1. Send test messages in your Test environment and verify delivery in <AppLink href="https://app.courier.com/logs">Message Logs</AppLink>
    2. Validate that preferences, routing, and template rendering match your SuprSend setup
    3. Switch your production code to Courier's Production API key
    4. Monitor <Doc href="/docs/monitor/overview">Message Logs</Doc> and <Doc href="/docs/monitor/overview">Analytics</Doc> for delivery confirmation
  </Step>
</Steps>

## Map the API

| Operation            | SuprSend                                        | Courier                                                                                                                                                                                                                                      |
| -------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Send a notification  | `POST /trigger`                                 | <Endpoint method="POST" path="/send" name="Send a message" href="/docs/api-reference/send/send-a-message" />                                                                                                                                      |
| Create/update a user | `POST /subscriber`                              | <Endpoint method="PUT" path="/profiles/{user_id}" name="Replace a Profile" href="/docs/api-reference/user-profiles/replace-a-profile" />                                                                                                          |
| Get a user           | `GET /subscriber/:id`                           | <Endpoint method="GET" path="/profiles/{user_id}" name="Get a Profile" href="/docs/api-reference/user-profiles/get-a-profile" />                                                                                                                  |
| Set user preferences | `POST /subscriber/:id/category/:cat/preference` | <Endpoint method="PUT" path="/users/{user_id}/preferences/{topic_id}" name="Update or create user Preferences for a Subscription Topic" href="/docs/api-reference/user-preferences/update-or-create-user-preferences-for-a-subscription-topic" /> |
| Get message status   | `GET /event/:id`                                | <Endpoint method="GET" path="/messages/{message_id}" name="Get message" href="/docs/api-reference/messages/get-message" />                                                                                                                        |
| Create/update tenant | Tenant API                                      | <Endpoint method="PUT" path="/tenants/{tenant_id}" name="Create or replace a Tenant" href="/docs/api-reference/tenants/create-or-replace-a-tenant" />                                                                                             |
