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

# Elemental channel element

> Set different content per channel inside one document, and override that channel's routing.

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

The channel element sets content per channel. Send a detailed message on email and a shorter one on push. The type value is `"channel"`.

**When to use:**

* Provide channel-specific content (different content for email vs push vs SMS)
* Use raw channel data for provider-specific formats (MJML for email, Slack blocks, etc.)
* Create channel-specific layouts and structures

<Warning>
  Channel elements are only valid at the top level and cannot be nested. If one appears at the top level, every sibling must also be a channel element.
</Warning>

<Tip>
  **Alternative**: most elements support a `channels` property that shows a single element on chosen channels only. See <Doc href="/docs/design/elemental/control-flow#channels">Control flow</Doc>.
</Tip>

**Basic example**

<CodeGroup>
  ```json Elemental theme={null}
  {
    "type": "channel",
    "channel": "email",
    "elements": [
      {
        "type": "meta",
        "title": "My Subject"
      },
      {
        "type": "text",
        "content": "My email body"
      }
    ]
  }
  ```

  ```javascript Node.js highlight={9-22} theme={null}
  const { requestId } = await client.send.message({
    message: {
      to: {
        email: "sarah@acme-corp.com",
      },
      content: {
        version: "2022-01-01",
        elements: [
          {
            type: "channel",
            channel: "email",
            elements: [
              {
                type: "meta",
                title: "My Subject",
              },
              {
                type: "text",
                content: "My email body",
              },
            ],
          },
        ],
      },
    },
  });
  ```

  ```python Python highlight={9-22} theme={null}
  response = client.send.message(
      message={
        "to": {
          "email": "sarah@acme-corp.com",
        },
        "content": {
          "version": "2022-01-01",
          "elements": [
            {
              "type": "channel",
              "channel": "email",
              "elements": [
                {
                  "type": "meta",
                  "title": "My Subject",
                },
                {
                  "type": "text",
                  "content": "My email body",
                },
              ],
            },
          ],
        },
      },
  )
  ```

  ```bash cURL highlight={12-25} theme={null}
  curl -X POST https://api.courier.com/send \
    -H "Authorization: Bearer $COURIER_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
        "message": {
          "to": {
            "email": "sarah@acme-corp.com"
          },
          "content": {
            "version": "2022-01-01",
            "elements": [
              {
                "type": "channel",
                "channel": "email",
                "elements": [
                  {
                    "type": "meta",
                    "title": "My Subject"
                  },
                  {
                    "type": "text",
                    "content": "My email body"
                  }
                ]
              }
            ]
          }
        }
      }'
  ```

  ```ruby Ruby highlight={9-22} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        email: "sarah@acme-corp.com"
      },
      content: {
        version: "2022-01-01",
        elements: [
          {
            type: "channel",
            channel: "email",
            elements: [
              {
                type: "meta",
                title: "My Subject"
              },
              {
                type: "text",
                content: "My email body"
              }
            ]
          }
        ]
      }
    }
  )
  ```

  ```go Go highlight={5-18} theme={null}
  // Elemental nodes carry no typed content fields, so pass the document as raw JSON.
  content := param.Override[shared.ElementalContentParam](json.RawMessage(`{
    "version": "2022-01-01",
    "elements": [
      {
        "type": "channel",
        "channel": "email",
        "elements": [
          {
            "type": "meta",
            "title": "My Subject"
          },
          {
            "type": "text",
            "content": "My email body"
          }
        ]
      }
    ]
  }`))

  response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
  	Message: courier.SendMessageParamsMessage{
  		To: courier.SendMessageParamsMessageToUnion{
  			OfUserRecipient: &shared.UserRecipientParam{
  				Email: courier.String("sarah@acme-corp.com"),
  			},
  		},
  		Content: courier.SendMessageParamsMessageContentUnion{OfElementalContent: &content},
  	},
  })
  ```

  ```java Java highlight={7-16} theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(JsonValue.from(java.util.Map.of("email", "sarah@acme-corp.com")))
          .content(JsonValue.from(java.util.Map.of(
                  "version", "2022-01-01",
                  "elements", java.util.List.of(
                    java.util.Map.of(
                      "type", "channel",
                      "channel", "email",
                      "elements", java.util.List.of(
                        java.util.Map.of(
                          "type", "meta",
                          "title", "My Subject"),
                        java.util.Map.of(
                          "type", "text",
                          "content", "My email body")))))))
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={9-22} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'email' => 'sarah@acme-corp.com',
      ],
      'content' => [
        'version' => '2022-01-01',
        'elements' => [
          [
            'type' => 'channel',
            'channel' => 'email',
            'elements' => [
              [
                'type' => 'meta',
                'title' => 'My Subject',
              ],
              [
                'type' => 'text',
                'content' => 'My email body',
              ],
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={12-25} theme={null}
  // Elemental nodes expose no typed content fields, so build the document from raw JSON.
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { Email = "sarah@acme-corp.com" },
          Content = ElementalContent.FromRawUnchecked(
              JsonSerializer.Deserialize<Dictionary<string, JsonElement>>("""
              {
                "version": "2022-01-01",
                "elements": [
                  {
                    "type": "channel",
                    "channel": "email",
                    "elements": [
                      {
                        "type": "meta",
                        "title": "My Subject"
                      },
                      {
                        "type": "text",
                        "content": "My email body"
                      }
                    ]
                  }
                ]
              }
              """)),
      },
  };

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

  ```bash CLI highlight={4} theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message.to '{"email": "sarah@acme-corp.com"}' \
    --message.content '{"version": "2022-01-01", "elements": [{"type": "channel", "channel": "email", "elements": [{"type": "meta", "title": "My Subject"}, {"type": "text", "content": "My email body"}]}]}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send an email and an SMS to sarah@acme-corp.com with different content on each channel.
  ```
</CodeGroup>

**Fields**

<ParamField path="channel" type="string" required>
  The channel the contents of this element should be applied to. Can be:

  * Standard channels: `"email"`, `"push"`, `"direct_message"`, `"sms"`
  * Provider names: `"slack"`, `"discord"`, `"teams"`, etc.
  * `"default"` - applies to all channels not explicitly specified
</ParamField>

<ParamField path="elements" type="CourierElement[]">
  An array of Elemental elements to apply to the channel. If `raw` has not been specified, `elements` is required.
</ParamField>

<ParamField path="raw" type="object">
  Raw data to apply to the channel. Use channel-specific formats like MJML for email, Slack blocks, or webhook payloads. If `elements` has not been specified, `raw` is required.
</ParamField>

<ParamField path="font_family" type="string">
  The CSS `font-family` stack to apply to the rendered email. Only valid when `channel` is `"email"`. Use the exact `fontFamily` value from the <Doc href="/docs/design/elemental/fonts#font-catalog">font catalog</Doc>. Google Fonts are loaded automatically. Defaults to `Helvetica, Arial, sans-serif` if omitted.

  ```json theme={null}
  {
    "type": "channel",
    "channel": "email",
    "font_family": "Roboto, Arial, sans-serif",
    "elements": [...]
  }
  ```
</ParamField>

**Examples and variants**

**Using elements**

Provide different Elemental content per channel:

```json theme={null}
{
  "type": "channel",
  "channel": "email",
  "elements": [
    {
      "type": "meta",
      "title": "Order Confirmation"
    },
    {
      "type": "text",
      "content": "Your order #{{order_id}} has been confirmed. We'll send you tracking information once your order ships."
    },
    {
      "type": "action",
      "content": "View Order",
      "href": "https://app.example.com/orders/{{order_id}}"
    }
  ]
},
{
  "type": "channel",
  "channel": "push",
  "elements": [
    {
      "type": "meta",
      "title": "Order Confirmed"
    },
    {
      "type": "text",
      "content": "Order #{{order_id}} confirmed"
    }
  ]
}
```

**Using raw data**

Use raw channel data for provider-specific formats.

Email with MJML:

```json theme={null}
{
  "type": "channel",
  "channel": "email",
  "raw": {
    "subject": "Order Confirmation",
    "html": "<mjml><mj-body><mj-section><mj-column><mj-text>Your order has been confirmed!</mj-text></mj-column></mj-section></mj-body></mjml>",
    "text": "Your order has been confirmed!",
    "transformers": ["handlebars", "mjml"]
  }
}
```

Slack:

```json theme={null}
{
  "type": "channel",
  "channel": "slack",
  "raw": {
    "text": "Hello World!",
    "blocks": [
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "Your order has been confirmed!"
        }
      }
    ]
  }
}
```

Webhook:

```json theme={null}
{
  "type": "channel",
  "channel": "webhook",
  "raw": {
    "payload": {
      "body": {
        "event": "order.confirmed",
        "order_id": "{{order_id}}",
        "timestamp": "{{timestamp}}"
      }
    }
  }
}
```

**Default channel**

Use `"default"` to provide content for all channels not explicitly specified:

```json theme={null}
{
  "type": "channel",
  "channel": "email",
  "elements": [
    {
      "type": "text",
      "content": "Detailed email content here"
    }
  ]
},
{
  "type": "channel",
  "channel": "default",
  "elements": [
    {
      "type": "text",
      "content": "Content for push, SMS, and other channels"
    }
  ]
}
```

**Channel-specific considerations**

**Email**

* Supports full Elemental elements or raw HTML/MJML
* Can use `raw.subject` for email subject line
* Supports `transformers` array for templating engines

**Push**

* Typically uses `meta.title` for notification title
* Content should be concise due to character limits
* Supports action buttons via action elements

**SMS**

* Very limited character count
* Best for short, essential messages
* No rich formatting support

**Direct message (Slack, Discord, Teams, etc.)**

* Provider-specific formats via `raw` property
* Can use provider-specific block structures
* Supports rich interactive elements per provider
