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

# Channel settings and overrides

> Change channel routing, email fields, and provider payloads on a single send.

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

Channel settings control how each channel behaves within a template, and send-time overrides let you adjust a single message.

## How overrides work

An override changes part of a message at send time, without editing the template. Courier applies it just before handing the message to the provider, so it works with template-based sends and with any <Doc href="/docs/design/elemental/overview">Elemental</Doc> content the template renders.

There are two levels:

| Level                 | Where you set it                      | Applies to                                                |
| :-------------------- | :------------------------------------ | :-------------------------------------------------------- |
| **Channel override**  | `message.channels.<channel>.override` | Every provider configured for that channel                |
| **Provider override** | `message.providers.<key>.override`    | One provider, passing fields from that provider's own API |

Use both on the same send if you need to. When the two set the same field, **the provider override wins**.

<Note>
  Courier applies overrides **after** the render step. The Rendered tab in the logs will not show them, only the pre-override output. To see the final payload, look at the provider request in the Raw tab.
</Note>

The fields each channel accepts live with that channel: <Doc href="/docs/integrations/email/overview#channel-overrides">email</Doc>, <Doc href="/docs/integrations/sms/overview#channel-overrides">SMS</Doc>, <Doc href="/docs/integrations/push/overview#channel-overrides">push</Doc>, and <Doc href="/docs/integrations/direct-message/overview#channel-overrides">chat</Doc>. Each provider page documents its own provider-specific schema.

## Prerequisites

* A template with more than one channel
* A connected provider for each of those channels

## Configure channels

<Steps>
  <Step title="Choose Always or Best-Of per channel">
    In a template's channel settings, **Always Send To** decides how a channel relates to the others:

    * **Best Of**: the channel sends only if a higher-priority channel fails. This is the fallback pattern (SMS backs up email).
    * **Always**: the channel sends to every recipient regardless of the others. Use it for guaranteed multi-channel delivery (email and push for a critical alert).

    A **Disabled** channel never sends and is skipped in the Best-Of order. Best-Of works with the <Doc href="/docs/send/routing#channel-priority">channel priority</Doc> order to build fallback logic.
  </Step>

  <Step title="Set email address fields">
    Email channels carry sender and recipient fields you set in the channel settings, with variables allowed for dynamic values:

    * **From**: `"Sarah Bennett <notify@acme-corp.com>"`. The `Name <email>` form carries the display name, and it is the only way to set one when a provider's integration has no From Name field.
    * **Reply-To**: where replies go.
    * **CC / BCC**: additional recipients, often driven by a variable.

    The sender resolves at three levels, and the first one set wins:

    1. `message.channels.email.override.from` on the send, for one message.
    2. The **From** field here, for every send of this template.
    3. **From Address** on the provider integration, for every email through that provider.

    With none of the three set, the send fails at the provider rather than falling back to a Courier address. <Guide href="/docs/guides/send-from-your-domain">Send from your own domain</Guide> walks the whole setup.

    <Note>
      Gmail is the exception. It always sends as the authorized inbox, so a **From** value here has no effect on it. Set the display name with the integration's From Name instead.
    </Note>
  </Step>

  <Step title="Add per-channel conditions">
    A channel condition skips only that channel, while the rest of the notification still delivers (a template condition, by contrast, stops the whole send). Set them in the channel's **Conditions** tab, or over the API with `message.channels.<channel>.if`, where each value is an expression evaluated against `data` and `profile`:

    <CodeGroup>
      ```javascript Node.js highlight={9} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            user_id: "user_123",
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          channels: {
            sms: {
              if: "data.priority === 'high'",
            },
          },
        },
      });
      ```

      ```python Python highlight={9} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "user_id": "user_123",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "channels": {
                  "sms": {
                      "if": "data.priority === 'high'",
                  },
              },
          },
      )
      ```

      ```bash cURL highlight={12} 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",
            "channels": {
              "sms": {
                "if": "data.priority === '\''high'\''"
              }
            }
          }
        }'
      ```

      ```ruby Ruby highlight={9} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            user_id: "user_123"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          channels: {
            sms: {
              if: "data.priority === 'high'"
            }
          }
        }
      )
      ```

      ```go Go highlight={10} 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"),
      		Channels: shared.MessageChannelsParam{
      			"sms": shared.ChannelParam{If: courier.String("data.priority === 'high'")},
      		},
      	},
      })
      ```

      ```java Java highlight={7} theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(UserRecipient.builder().userId("user_123").build())
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .channels(MessageChannels.builder()
                  .putAdditionalProperty("sms", JsonValue.from(
                      java.util.Map.of("if", "data.priority === 'high'")))
                  .build())
              .build())
          .build();
      SendMessageResponse response = client.send().message(params);
      ```

      ```php PHP highlight={9} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'user_id' => 'user_123',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'channels' => [
            'sms' => [
              'if' => 'data.priority === \'high\'',
            ],
          ],
        ],
      );
      ```

      ```csharp C# highlight={9} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Channels = new Dictionary<string, Channel>
              {
                  { "sms", new Channel { If = "data.priority === 'high'" } },
              },
          },
      };

      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.channels '{"sms": {"if": "data.priority === '\''high'\''"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 and only use SMS when the priority is high.
      ```
    </CodeGroup>

    <Warning>
      Setting `routing` on a send drops the template's default routing conditions, so include any channel conditions you want in `message.channels`.
    </Warning>

    User preferences take precedence: if a user has custom channel routing, only their selected channels are considered, and channel conditions run within that selection but cannot override it.
  </Step>

  <Step title="Override a provider at send time">
    Adjust how a specific provider sends this one message with `providers.<provider>.override`, without changing the template. For example, filter push tokens or set a provider-specific field:

    <CodeGroup>
      ```javascript Node.js highlight={17-18} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            user_id: "user_123",
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          routing: {
            method: "all",
            channels: [
              "push",
            ],
          },
          providers: {
            "firebase-fcm": {
              override: {
                config: {
                  filterByBundleId: true,
                  bundleId: "com.acme.app",
                },
              },
            },
          },
        },
      });
      ```

      ```python Python highlight={17-18} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "user_id": "user_123",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "routing": {
                  "method": "all",
                  "channels": [
                      "push",
                  ],
              },
              "providers": {
                  "firebase-fcm": {
                      "override": {
                          "config": {
                              "filterByBundleId": True,
                              "bundleId": "com.acme.app",
                          },
                      },
                  },
              },
          },
      )
      ```

      ```bash cURL highlight={20-21} 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": "all",
              "channels": [
                "push"
              ]
            },
            "providers": {
              "firebase-fcm": {
                "override": {
                  "config": {
                    "filterByBundleId": true,
                    "bundleId": "com.acme.app"
                  }
                }
              }
            }
          }
        }'
      ```

      ```ruby Ruby highlight={17-18} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            user_id: "user_123"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          routing: {
            method: "all",
            channels: [
              "push"
            ]
          },
          providers: {
            "firebase-fcm": {
              override: {
                config: {
                  filterByBundleId: true,
                  bundleId: "com.acme.app"
                }
              }
            }
          }
        }
      )
      ```

      ```go Go highlight={19-20} 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")},
      			},
      			Method: "all",
      		},
      		Providers: shared.MessageProvidersParam{
      			"firebase-fcm": shared.MessageProvidersTypeParam{
      				Override: map[string]any{
      					"config": map[string]any{
      						"filterByBundleId": true,
      						"bundleId": "com.acme.app",
      					},
      				},
      			},
      		},
      	},
      })
      ```

      ```java Java highlight={12-13} 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")
                  .method(SendMessageParams.Message.Routing.Method.ALL)
                  .build())
              .providers(MessageProviders.builder()
                  .putAdditionalProperty("firebase-fcm", JsonValue.from(java.util.Map.of("override", java.util.Map.of(
                          "config", java.util.Map.of(
                              "filterByBundleId", true,
                              "bundleId", "com.acme.app"
                          )
                      ))))
                  .build())
              .build())
          .build();
      SendMessageResponse response = client.send().message(params);
      ```

      ```php PHP highlight={17-18} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'user_id' => 'user_123',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'routing' => [
            'method' => 'all',
            'channels' => [
              'push',
            ],
          ],
          'providers' => [
            'firebase-fcm' => [
              'override' => [
                'config' => [
                  'filterByBundleId' => true,
                  'bundleId' => 'com.acme.app',
                ],
              ],
            ],
          ],
        ],
      );
      ```

      ```csharp C# highlight={18-19} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Routing = new() { Channels = ["push"], Method = Send::Method.All },
              Providers = new Dictionary<string, MessageProvidersType>()
              {
                  {
                      "firebase-fcm",
                      new()
                      {
                          Override = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
                              """
                              {
                                "config": {
                                  "filterByBundleId": true,
                                  "bundleId": "com.acme.app"
                                }
                              }
                              """
                          ),
                      }
                  },
              },
          },
      };

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

      ```bash CLI highlight={6} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"user_id": "user_123"}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
        --message.routing '{"method": "all", "channels": ["push"]}' \
        --message.providers '{"firebase-fcm": {"override": {"config": {"filterByBundleId": true, "bundleId": "com.acme.app"}}}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 with a provider-specific override.
      ```
    </CodeGroup>

    See <Doc href="/docs/integrations/push/overview#channel-overrides">Push providers</Doc> for the push override fields, and each <Doc href="/docs/integrations/overview">provider integration</Doc> for its override options.
  </Step>

  <Step title="Check the logs">
    Send with your settings and open the message in <AppLink href="https://app.courier.com/logs">Logs</AppLink>. Confirm the channels that sent, and that a Best-Of fallback or a channel condition behaved as expected.
  </Step>
</Steps>
