> ## 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 Braze

> Move transactional flows from Braze canvases and campaigns to Courier journeys and templates.

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>;
};

Plan and run a migration of your transactional notification flows from Braze to Courier.

<Note>
  Braze is a marketing automation platform: campaigns, analytics, and customer engagement. Only the transactional and product notification workflows move to Courier. Marketing campaign orchestration is outside its scope.
</Note>

## Map Braze concepts to Courier

| Braze                                                                                                                     | Courier                                                                                            |
| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [Campaigns](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/)                                            | <Doc href="/docs/design/templates/overview">Templates</Doc> + <Doc href="/docs/send/overview">Send API</Doc> |
| [Canvas](https://www.braze.com/docs/user_guide/engagement_tools/canvas/)                                                  | <Doc href="/docs/journeys/overview">Journeys</Doc>                                                      |
| [Segments](https://www.braze.com/docs/user_guide/engagement_tools/segments/)                                              | <Doc href="/docs/recipients/lists-and-audiences/audiences">Audiences</Doc>                              |
| [User Profiles](https://www.braze.com/docs/user_guide/data/unification/user_data/)                                        | <Doc href="/docs/recipients/overview">Users / Profiles</Doc>                                            |
| [Subscription Groups](https://www.braze.com/docs/user_guide/message_building_by_channel/sms_mms_rcs/subscription_groups/) | <Doc href="/docs/recipients/preferences/overview">Preferences</Doc>                                     |
| [Content Cards](https://www.braze.com/docs/user_guide/message_building_by_channel/content_cards/)                         | <Doc href="/docs/in-app/overview">Inbox</Doc>                                                           |
| [Connected Content](https://www.braze.com/docs/user_guide/personalization_and_dynamic_content/connected_content/)         | `data` on send + <Doc href="/docs/journeys/nodes/fetch-data">Fetch data node</Doc>                      |
| [Currents](https://www.braze.com/docs/user_guide/data/distribution/braze_currents/)                                       | <Doc href="/docs/monitor/webhooks/outbound">Outbound Webhooks</Doc>                                     |
| [Liquid templating](https://www.braze.com/docs/user_guide/personalization_and_dynamic_content/liquid/)                    | <Doc href="/docs/design/templates/variables">Handlebars variables</Doc>                                 |

### Campaigns and templates

Braze [campaigns](https://www.braze.com/docs/user_guide/engagement_tools/campaigns/) bundle content, audience targeting, scheduling, and delivery into one resource. Courier splits them: <Doc href="/docs/design/templates/overview">templates</Doc> own the content, and the <Doc href="/docs/send/overview">Send API</Doc> or <Doc href="/docs/journeys/overview">Journeys</Doc> handle delivery.

Your product team updates copy in Design Studio without touching delivery logic. Engineers change routing or timing without breaking template content.

One template holds content for every channel (email, SMS, push, chat, inbox). Build it with drag-and-drop blocks, or define it in <Doc href="/docs/design/elemental/overview">Elemental</Doc> JSON.

### Canvas and Journeys

Braze [Canvas](https://www.braze.com/docs/user_guide/engagement_tools/canvas/) is a visual builder for multi-step, multi-channel journeys. Courier <Doc href="/docs/journeys/overview">Journeys</Doc> do the same for transactional flows: <Doc href="/docs/journeys/nodes/delay">delays</Doc>, <Doc href="/docs/journeys/nodes/branch">branching</Doc>, <Doc href="/docs/journeys/nodes/batch">batching</Doc>, <Doc href="/docs/journeys/nodes/digest">digests</Doc>, and <Doc href="/docs/journeys/nodes/cancel">cancellation</Doc>.

| Canvas Step    | Courier Equivalent                                                                                                                                 |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Message step   | <Doc href="/docs/journeys/nodes/send">Send node</Doc> (references a template)                                                                           |
| Delay step     | <Doc href="/docs/journeys/nodes/delay">Delay node</Doc>                                                                                                 |
| Decision split | <Doc href="/docs/journeys/nodes/branch">Branch node</Doc>                                                                                               |
| Action paths   | <Doc href="/docs/journeys/nodes/branch">Branch node</Doc> with conditions                                                                               |
| Audience paths | <Doc href="/docs/recipients/lists-and-audiences/audiences">Audiences</Doc> + <Doc href="/docs/journeys/nodes/branch">branch</Doc> conditions on profile data |

Canvas entry criteria become <Doc href="/docs/journeys/invoke">journey triggers</Doc>: an API invoke, an inbound webhook, a Segment event, or a user joining an audience. Build journeys <Doc href="/docs/journeys/build">in the UI</Doc> or through the <Doc href="/docs/journeys/build">Journeys API</Doc>.

### Segments and audiences

Braze [segments](https://www.braze.com/docs/user_guide/engagement_tools/segments/) filter on user attributes, behaviors, and events. Courier <Doc href="/docs/recipients/lists-and-audiences/audiences">Audiences</Doc> group users dynamically by profile attributes. Define the filter rules once and Courier keeps membership current as profiles change.

You can also target a <Endpoint method="GET" path="/lists" name="List Lists" href="/docs/api-reference/lists/list-lists">List</Endpoint>, a static subscriber group, or send to individual users.

### Users and profiles

Courier <Doc href="/docs/recipients/overview">profiles</Doc> store a recipient's email, phone, push tokens, and any custom properties you personalize with. Profiles accept nested JSON, so structured data like account tiers, team roles, or feature flags fits as-is.

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

### Subscription groups and preferences

Braze [subscription groups](https://www.braze.com/docs/user_guide/message_building_by_channel/sms_mms_rcs/subscription_groups/) manage opt-in and opt-out per channel. Courier <Doc href="/docs/recipients/preferences/overview">Preferences</Doc> go further. Users opt out by channel, category, or notification topic, and Courier enforces that at send time.

Courier also ships a <Guide href="/docs/guides/build-a-preference-center#hosted-page">hosted preference page</Guide> and embeddable <Guide href="/docs/guides/build-a-preference-center#embedded-component">React components</Guide> for a preference center inside your app.

### Content Cards and Inbox

Braze [Content Cards](https://www.braze.com/docs/user_guide/message_building_by_channel/content_cards/) deliver persistent in-app content. 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>.

Inbox runs on the same delivery pipeline as email, push, and SMS. 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>.

### Connected content and dynamic data

Braze's [Connected Content](https://www.braze.com/docs/user_guide/personalization_and_dynamic_content/connected_content/) pulls external data into templates at render time. In Courier, you pass the data a template needs in the `data` field on the send request. To fetch data mid-journey, the <Doc href="/docs/journeys/nodes/fetch-data">Fetch data node</Doc> calls an external API and passes the response to later nodes.

### Templating language

Braze uses [Liquid](https://www.braze.com/docs/user_guide/personalization_and_dynamic_content/liquid/) to personalize messages. Courier uses <Doc href="/docs/design/templates/variables">Handlebars variables</Doc> and `{{variable}}` syntax. Both support conditionals, loops, and helpers for date formatting, string manipulation, and conditional rendering.

## Why Courier

* **API-first for developers.** Courier is built for transactional and product notifications. A single API call sends to any channel. No campaign setup required.
* **Content and logic stay separate.** Templates and journeys are independent resources. 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.
* **Built-in in-app channel.** Courier Inbox works without a third-party provider. Drop in a React, iOS, or Android component and send on the same pipeline as email and push.
* **Automatic failover.** Configure multiple providers per channel. If your primary email provider goes down, traffic shifts to the backup without a code change.
* **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 the rendered content.

## 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. Test and Production <Doc href="/docs/workspaces/overview#environments-and-api-keys">environments</Doc> have independent API keys, so you can migrate without touching live traffic.
  </Step>

  <Step title="Configure integrations">
    Go to **Integrations** in the Courier dashboard and connect the same providers you use in Braze (SendGrid, Twilio, FCM). 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 Braze Content Cards for in-app messaging, enable <Doc href="/docs/in-app/overview">Courier Inbox</Doc>. No external provider needed.
  </Step>

  <Step title="Recreate templates">
    Braze campaigns bundle content and delivery config. Extract the content into <Doc href="/docs/design/templates/overview">templates</Doc> in Design Studio:

    1. Create a new template for each transactional notification type
    2. Add content blocks for each channel (email, SMS, push, etc.)
    3. Convert Liquid variables (`{{user.first_name}}`) to <Doc href="/docs/design/templates/variables">Handlebars syntax</Doc> (`{{first_name}}`). The data comes from the send request's `data` field
    4. Publish the template to make it available for sending

    Recreate multi-step Canvas flows separately in <Doc href="/docs/journeys/overview">Journeys</Doc>.
  </Step>

  <Step title="Migrate user data">
    Create profiles with the same identifiers you use in Braze, through 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 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 Braze subscription groups, recreate your preference structure in Courier:

    1. Define <Doc href="/docs/recipients/preferences/overview">subscription topics</Doc> that map to your Braze subscription groups
    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>, 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 Braze's campaign trigger or Canvas trigger API calls with Courier's <Doc href="/docs/send/overview">Send API</Doc>:

    <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 handles routing, preferences, and failover from your template and workspace configuration.
  </Step>

  <Step title="Set up event streaming">
    If you stream delivery events with Braze Currents, configure <Doc href="/docs/monitor/webhooks/outbound">Outbound Webhooks</Doc> to forward message events (sent, delivered, opened, clicked) to your data warehouse or analytics pipeline.
  </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 Braze setup
    3. Switch your production code to use 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>

## Migrate with an AI agent

Most of this migration is mechanical: read a campaign definition, create the equivalent Courier template, rewrite the call site. A coding agent handles that well.

Three things to set up first:

* **<Doc href="/docs/resources/skills">Courier Skills</Doc>** teaches the agent Courier's real APIs and patterns so it stops guessing at endpoints:

  ```bash theme={null}
  npx skills add trycourier/courier-skills
  ```

  In Claude Code, install it as a plugin instead. The plugin self-updates and ships the Courier docs MCP server.

* **<Doc href="/docs/resources/mcp">Courier's MCP server</Doc>** gives the agent direct access to your workspace: create templates, build journeys, upsert users, and read send logs. See <Doc href="/docs/get-started/build-with-ai">Build with AI</Doc> for setup in Claude Code, Cursor, Codex, and VS Code.

* **Docs for agents.** Point it at [llms-full.txt](https://www.courier.com/docs/llms-full.txt) when it needs API detail beyond this page.

Then start it with a prompt like this one. Fill in the bracketed parts.

```text theme={null}
Help me migrate our notifications from Braze to Courier.

Read first:
- https://www.courier.com/docs/guides/migrate/from-braze
  (concept mapping and the migration steps you should follow)
- https://www.courier.com/docs/llms-full.txt
  (full Courier docs, for API detail as you need it)

Our setup:
- Codebase: [path]
- Braze API credentials: [env var]
- Courier: connected through the Courier MCP server

Work in this order. Stop after step 3 and wait for my review
before you create anything in Courier.

0. Setup. Install Courier Skills so you build against Courier's real
   APIs and patterns rather than guessing:
   npx skills add trycourier/courier-skills
   Then confirm the Courier MCP server is connected. If either one is
   missing, stop and tell me before going further.

1. Inventory. Use the Braze REST API to list every active campaign
   and Canvas. For each, record: name, entry trigger (API-triggered,
   scheduled, or action-based), channels, target segment, and whether
   it is a single send or a multi-step flow.

2. Classify each as:
   - transactional (an action in our product must produce it)
   - marketing (campaign or lifecycle)
   - in-app guidance (Content Cards, in-app messages)

3. Find the send sites. Grep the codebase for Braze
   POST /messages/send, POST /campaigns/trigger/send, and
   POST /canvas/trigger/send calls, plus POST /users/track calls that
   only exist to set an attribute a campaign triggers on. Map each to
   the campaign or Canvas it fires.

   Report the inventory, the classification, and the send-site list.
   Then wait.

4. Transactional first. For each transactional campaign: create the
   Courier template with the channels it needs, convert its Liquid
   personalization to Courier variables, then rewrite the call site to
   a Courier /send that names the template and passes data. Leave the
   Braze call in place behind a flag so we can run both and compare.

5. Canvases become journeys. Recreate each multi-step Canvas through
   the Courier journeys API. Delay steps become delay nodes, decision
   splits become branch nodes, message steps become send nodes, and
   exit criteria become cancel nodes.

6. Preferences. Map each Braze subscription group to a Courier
   subscription topic and import the opt-out state before any
   marketing send.

Rules:
- Never send to a real user. Use a test profile in the Test
  environment for every verification send.
- Never pause, disable, or delete anything in Braze. That is my call.
- If something in Braze has no Courier equivalent, say so in your
  report rather than approximating it.
```

Review the Liquid conversion in step 4 closely. Braze resolves `{{user.*}}` against its own user store. Each one becomes either a Profile attribute you sync or a value you pass in `data`. Decide that before the Templates exist, not after.

## Map the API

| Operation            | Braze                           | Courier                                                                                                                                                                                                                                      |
| -------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Send a notification  | `POST /messages/send`           | <Endpoint method="POST" path="/send" name="Send a message" href="/docs/api-reference/send/send-a-message" />                                                                                                                                      |
| Trigger a workflow   | `POST /canvas/trigger/send`     | <Endpoint method="POST" path="/journeys/{templateId}/invoke" name="Invoke a Journey" href="/docs/api-reference/journeys/invoke-a-journey" />                                                                                                      |
| Create/update a user | `POST /users/track`             | <Endpoint method="PUT" path="/profiles/{user_id}" name="Replace a Profile" href="/docs/api-reference/user-profiles/replace-a-profile" />                                                                                                          |
| Get a user           | `POST /users/export/ids`        | <Endpoint method="GET" path="/profiles/{user_id}" name="Get a Profile" href="/docs/api-reference/user-profiles/get-a-profile" />                                                                                                                  |
| Manage subscriptions | `POST /subscription/status/set` | <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   | Campaign analytics endpoints    | <Endpoint method="GET" path="/messages/{message_id}" name="Get message" href="/docs/api-reference/messages/get-message" />                                                                                                                        |
| Bulk operations      | `POST /users/track` (batch)     | <Endpoint method="POST" path="/bulk" name="Create a bulk job" href="/docs/api-reference/bulk/create-a-bulk-job" />                                                                                                                                |
