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

> The JSON format behind every template, and the elements Courier renders on each channel.

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

You compose a notification from a list of elements. Courier renders each one for email, push, SMS, inbox, and direct message.

A document has two top-level fields, and a send passes it as `message.content`:

```json theme={null}
{
  "message": {
    "to": { "email": "sarah@acme-corp.com" },
    "content": {
      "version": "2022-01-01",
      "elements": [
        // CourierElement[]
      ]
    }
  }
}
```

<ParamField path="version" type="string" required>
  The Elemental format version. The only supported value is `"2022-01-01"`.
</ParamField>

<ParamField path="elements" type="CourierElement[]" required>
  The elements that make up the notification. Every type is documented below.
</ParamField>

**Two ways to write content**

**ElementalContentSugar** is a shorthand for simple notifications. Pass `title` and `body` instead of a full element tree, and omit `version` and `elements`. Courier converts it to full Elemental internally: `title` becomes a `meta` element, `body` becomes a `text` element.

<CodeGroup>
  ```javascript Node.js highlight={5-6} theme={null}
  const { requestId } = await client.send.message({
    message: {
      to: { email: "sarah@acme-corp.com" },
      content: {
        title: "Welcome!",
        body: "Thanks for signing up, {{name}}",
      },
      data: { name: "Sarah Bennett" },
    },
  });
  ```

  ```python Python highlight={5-6} theme={null}
  response = client.send.message(
      message={
          "to": {"email": "sarah@acme-corp.com"},
          "content": {
              "title": "Welcome!",
              "body": "Thanks for signing up, {{name}}",
          },
          "data": {"name": "Sarah Bennett"},
      },
  )
  ```

  ```bash cURL highlight={8-9} 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": {
          "title": "Welcome!",
          "body": "Thanks for signing up, {{name}}"
        },
        "data": { "name": "Sarah Bennett" }
      }
    }'
  ```

  ```ruby Ruby highlight={5-6} theme={null}
  response = courier.send_.message(
    message: {
      to: { email: "sarah@acme-corp.com" },
      content: {
        title: "Welcome!",
        body: "Thanks for signing up, {{name}}"
      },
      data: { name: "Sarah Bennett" }
    }
  )
  ```

  ```go Go highlight={10-11} theme={null}
  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{
  			OfElementalContentSugar: &shared.ElementalContentSugarParam{
  				Title: "Welcome!",
  				Body:  "Thanks for signing up, {{name}}",
  			},
  		},
  		Data: map[string]any{"name": "Sarah Bennett"},
  	},
  })
  ```

  ```java Java highlight={5-6} 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(
              "title", "Welcome!",
              "body", "Thanks for signing up, {{name}}")))
          .data(JsonValue.from(java.util.Map.of("name", "Sarah Bennett")))
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={5-6} theme={null}
  $response = $client->send->message(
    message: [
      'to' => ['email' => 'sarah@acme-corp.com'],
      'content' => [
        'title' => 'Welcome!',
        'body' => 'Thanks for signing up, {{name}}',
      ],
      'data' => ['name' => 'Sarah Bennett'],
    ],
  );
  ```

  ```csharp C# highlight={8-9} theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { Email = "sarah@acme-corp.com" },
          Content = new ElementalContentSugar()
          {
              Title = "Welcome!",
              Body = "Thanks for signing up, {{name}}",
          },
          Data = new Dictionary<string, JsonElement>()
          {
              { "name", JsonSerializer.SerializeToElement("Sarah Bennett") }
          },
      },
  };

  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 '{"title": "Welcome!", "body": "Thanks for signing up, {{name}}"}' \
    --message.data '{"name": "Sarah Bennett"}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send an email to sarah@acme-corp.com with the title Welcome and a short thank-you body.
  ```
</CodeGroup>

Use the sugar format for a title and body with no conditionals, loops, or layout. Use **full Elemental** for everything else: multi-channel customization, columns and groups, `if`/`loop`/`ref`, locales, and styling.

**Element nesting**

Elemental is a tree. The root `elements` array holds elements, and the container types hold their own `elements` arrays:

* `group`: conditions and loops
* `channel`: channel-specific content
* `columns`: holds `column` elements
* `list`: holds `list-item` elements

Nesting makes conditional groups, per-channel branches, and multi-column layouts possible.

```json theme={null}
{
  "version": "2022-01-01",
  "elements": [
    {
      "type": "channel",
      "channel": "email",
      "elements": [
        { "type": "meta", "title": "Order Confirmation" },
        {
          "type": "group",
          "if": "data.items.length > 0",
          "elements": [
            {
              "type": "columns",
              "elements": [
                {
                  "type": "column",
                  "width": "50%",
                  "elements": [{ "type": "image", "src": "{{item.image}}" }]
                },
                {
                  "type": "column",
                  "width": "50%",
                  "elements": [{ "type": "text", "content": "{{item.name}}" }]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

**Common properties**

Every element has a required `type` string. Each section below gives its value, for example `"text"` for the text element.

Most elements also accept these shared properties:

* `channels`: an array of channel names. The element renders only for the listed channels.
* `if`: a condition that controls whether the element renders. Accepts a string expression or a <Doc href="/docs/design/elemental/control-flow#if">structured condition array</Doc>.
* `loop`: an expression that repeats the element for each item in an array.
* `ref`: a reference identifier for the element.

<Doc href="/docs/design/elemental/control-flow">Control flow properties</Doc> below covers all four. Many elements also accept a `locales` object for multi-language content, covered in <Doc href="/docs/design/elemental/locales">Locales</Doc>. Where a property is central to an element, such as `if` and `loop` on group and list, it is listed in that element's fields below.

Text fields support <Doc href="/docs/design/templates/variables">Handlebars</Doc> variables. They resolve against `message.data`, for example `data.first_name`.

## The elements

| Element                                                        | Use it to                                               |
| -------------------------------------------------------------- | ------------------------------------------------------- |
| <Doc href="/docs/design/elemental/elements/text">`text`</Doc>       | Format copy: bold, italic, links, alignment, colour.    |
| <Doc href="/docs/design/elemental/elements/action">`action`</Doc>   | Add a button or a link.                                 |
| <Doc href="/docs/design/elemental/elements/channel">`channel`</Doc> | Wrap content for one channel, and override its routing. |
| <Doc href="/docs/design/elemental/elements/meta">`meta`</Doc>       | Set the subject line and other metadata.                |
| <Doc href="/docs/design/elemental/elements/image">`image`</Doc>     | Place an image, sized, aligned, optionally a link.      |
| <Doc href="/docs/design/elemental/elements/divider">`divider`</Doc> | Draw a horizontal rule.                                 |
| <Doc href="/docs/design/elemental/elements/group">`group`</Doc>     | Nest elements so one control-flow rule covers them all. |
| <Doc href="/docs/design/elemental/elements/columns">`columns`</Doc> | Lay elements side by side.                              |
| <Doc href="/docs/design/elemental/elements/list">`list`</Doc>       | Render repeating items from your data.                  |
| <Doc href="/docs/design/elemental/elements/quote">`quote`</Doc>     | Call out a passage.                                     |
| <Doc href="/docs/design/elemental/elements/comment">`comment`</Doc> | Leave an internal remark that never renders.            |
| <Doc href="/docs/design/elemental/elements/html">`html`</Doc>       | Embed raw HTML for email.                               |
| <Doc href="/docs/design/elemental/elements/jsonnet">`jsonnet`</Doc> | Generate content programmatically.                      |

Every element also takes `if`, `loop`, `ref`, and `channels`. Those four are documented once, in <Doc href="/docs/design/elemental/control-flow">control flow</Doc>.

## Sending a document and storing one differ

A send takes the document above as `message.content` and renders it for every channel it routes to.

**Storing the same document on a Template needs its top-level elements wrapped in a `channel` element.** A send accepts a bare element list. The write API does not, and its rejection names a schema path rather than the rule you broke. <Doc href="/docs/design/templates/api">Templates API</Doc> covers the round trip.
