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

> Render ordered or unordered lists, nested up to five levels, from your data.

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 list element renders an ordered (numbered) or unordered (bulleted) list. Lists nest up to 5 levels deep. Parent and nested lists can each use their own loop. The type value is `"list"`, and each child must be a `"list-item"` element.

**When to use:**

* Display ordered sequences (steps, rankings)
* Show unordered items (features, benefits, options)
* Create nested hierarchical structures
* Display dynamic lists from data (products, items, etc.)

**Basic example**

<CodeGroup>
  ```json Elemental theme={null}
  {
    "type": "list",
    "list_type": "unordered",
    "elements": [
      {
        "type": "list-item",
        "elements": [
          { "type": "string", "content": "First item" }
        ]
      },
      {
        "type": "list-item",
        "elements": [
          { "type": "string", "content": "Second item" }
        ]
      }
    ]
  }
  ```

  ```javascript Node.js highlight={9-32} theme={null}
  const { requestId } = await client.send.message({
    message: {
      to: {
        email: "sarah@acme-corp.com",
      },
      content: {
        version: "2022-01-01",
        elements: [
          {
            type: "list",
            list_type: "unordered",
            elements: [
              {
                type: "list-item",
                elements: [
                  {
                    type: "string",
                    content: "First item",
                  },
                ],
              },
              {
                type: "list-item",
                elements: [
                  {
                    type: "string",
                    content: "Second item",
                  },
                ],
              },
            ],
          },
        ],
      },
    },
  });
  ```

  ```python Python highlight={9-32} theme={null}
  response = client.send.message(
      message={
        "to": {
          "email": "sarah@acme-corp.com",
        },
        "content": {
          "version": "2022-01-01",
          "elements": [
            {
              "type": "list",
              "list_type": "unordered",
              "elements": [
                {
                  "type": "list-item",
                  "elements": [
                    {
                      "type": "string",
                      "content": "First item",
                    },
                  ],
                },
                {
                  "type": "list-item",
                  "elements": [
                    {
                      "type": "string",
                      "content": "Second item",
                    },
                  ],
                },
              ],
            },
          ],
        },
      },
  )
  ```

  ```bash cURL highlight={12-35} 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": "list",
                "list_type": "unordered",
                "elements": [
                  {
                    "type": "list-item",
                    "elements": [
                      {
                        "type": "string",
                        "content": "First item"
                      }
                    ]
                  },
                  {
                    "type": "list-item",
                    "elements": [
                      {
                        "type": "string",
                        "content": "Second item"
                      }
                    ]
                  }
                ]
              }
            ]
          }
        }
      }'
  ```

  ```ruby Ruby highlight={9-32} theme={null}
  response = courier.send_.message(
    message: {
      to: {
        email: "sarah@acme-corp.com"
      },
      content: {
        version: "2022-01-01",
        elements: [
          {
            type: "list",
            list_type: "unordered",
            elements: [
              {
                type: "list-item",
                elements: [
                  {
                    type: "string",
                    content: "First item"
                  }
                ]
              },
              {
                type: "list-item",
                elements: [
                  {
                    type: "string",
                    content: "Second item"
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  )
  ```

  ```go Go highlight={5-28} 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": "list",
        "list_type": "unordered",
        "elements": [
          {
            "type": "list-item",
            "elements": [
              {
                "type": "string",
                "content": "First item"
              }
            ]
          },
          {
            "type": "list-item",
            "elements": [
              {
                "type": "string",
                "content": "Second item"
              }
            ]
          }
        ]
      }
    ]
  }`))

  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-22} 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", "list",
                      "list_type", "unordered",
                      "elements", java.util.List.of(
                        java.util.Map.of(
                          "type", "list-item",
                          "elements", java.util.List.of(
                            java.util.Map.of(
                              "type", "string",
                              "content", "First item"))),
                        java.util.Map.of(
                          "type", "list-item",
                          "elements", java.util.List.of(
                            java.util.Map.of(
                              "type", "string",
                              "content", "Second item")))))))))
          .build())
      .build();
  SendMessageResponse response = client.send().message(params);
  ```

  ```php PHP highlight={9-32} theme={null}
  $response = $client->send->message(
    message: [
      'to' => [
        'email' => 'sarah@acme-corp.com',
      ],
      'content' => [
        'version' => '2022-01-01',
        'elements' => [
          [
            'type' => 'list',
            'list_type' => 'unordered',
            'elements' => [
              [
                'type' => 'list-item',
                'elements' => [
                  [
                    'type' => 'string',
                    'content' => 'First item',
                  ],
                ],
              ],
              [
                'type' => 'list-item',
                'elements' => [
                  [
                    'type' => 'string',
                    'content' => 'Second item',
                  ],
                ],
              ],
            ],
          ],
        ],
      ],
    ],
  );
  ```

  ```csharp C# highlight={12-35} 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": "list",
                    "list_type": "unordered",
                    "elements": [
                      {
                        "type": "list-item",
                        "elements": [
                          {
                            "type": "string",
                            "content": "First item"
                          }
                        ]
                      },
                      {
                        "type": "list-item",
                        "elements": [
                          {
                            "type": "string",
                            "content": "Second item"
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
              """)),
      },
  };

  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": "list", "list_type": "unordered", "elements": [{"type": "list-item", "elements": [{"type": "string", "content": "First item"}]}, {"type": "list-item", "elements": [{"type": "string", "content": "Second item"}]}]}]}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send an email to sarah@acme-corp.com listing every item in their order.
  ```
</CodeGroup>

**Fields**

<ParamField path="list_type" type="string" required>
  One of `"ordered"` (numbered) or `"unordered"` (bulleted).
</ParamField>

<ParamField path="elements" type="ListItemElement[]" required>
  An array of list item elements. Each must be of type `"list-item"`. See the **List item fields** section below.
</ParamField>

<ParamField path="imgSrc" type="string">
  Renders bullets as a custom image, for unordered lists only. The image URL becomes the bullet.
</ParamField>

<ParamField path="imgHref" type="string">
  URL for the bullet image, if used. Makes the bullet image clickable.
</ParamField>

<ParamField path="loop" type="string">
  An expression that generates the list from data. See <Doc href="/docs/design/elemental/control-flow#loop">Control flow</Doc>.
</ParamField>

<ParamField path="if" type="string | object[]">
  A condition that determines whether the list renders. Accepts a string expression or a structured condition array. See <Doc href="/docs/design/elemental/control-flow#if">Control flow</Doc>.
</ParamField>

<ParamField path="channels" type="string[]">
  An array of channel names. The list renders only for the listed channels. See <Doc href="/docs/design/elemental/control-flow#channels">Control flow</Doc>.
</ParamField>

**List item fields**

Each item in the `elements` array must be a list item element with the following properties:

<ParamField path="type" type="string" required>
  Must be `"list-item"`.
</ParamField>

<ParamField path="elements" type="CourierElement[]" required>
  Content of the list item. Can include:

  * `string` elements for text
  * `link` elements for clickable links
  * `img` elements for inline images
  * Nested `list` elements for sub-lists
</ParamField>

<ParamField path="background_color" type="string">
  Background color for the list item. Any valid CSS color value.
</ParamField>

<ParamField path="loop" type="string">
  An expression that repeats the list item. See <Doc href="/docs/design/elemental/control-flow#loop">Control flow</Doc>.
</ParamField>

<ParamField path="if" type="string | object[]">
  A condition that determines whether the list item renders. Accepts a string expression or a structured condition array. See <Doc href="/docs/design/elemental/control-flow#if">Control flow</Doc>.
</ParamField>

**Examples and variants**

**Unordered list**

Simple bulleted list:

```json theme={null}
{
  "type": "list",
  "list_type": "unordered",
  "elements": [
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Feature 1" }
      ]
    },
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Feature 2" }
      ]
    },
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Feature 3" }
      ]
    }
  ]
}
```

**Ordered list**

Numbered list:

```json theme={null}
{
  "type": "list",
  "list_type": "ordered",
  "elements": [
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Step 1: Sign up" }
      ]
    },
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Step 2: Verify email" }
      ]
    },
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Step 3: Get started" }
      ]
    }
  ]
}
```

**Nested lists**

Lists can be nested up to 5 levels deep:

```json theme={null}
{
  "type": "list",
  "list_type": "unordered",
  "elements": [
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Fruits" },
        {
          "type": "list",
          "list_type": "ordered",
          "elements": [
            {
              "type": "list-item",
              "elements": [
                { "type": "string", "content": "Apple" }
              ]
            },
            {
              "type": "list-item",
              "elements": [
                { "type": "string", "content": "Banana" }
              ]
            }
          ]
        }
      ]
    },
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Vegetables" },
        {
          "type": "list",
          "list_type": "ordered",
          "elements": [
            {
              "type": "list-item",
              "elements": [
                { "type": "string", "content": "Carrot" }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

**List items with links**

Include clickable links in list items:

```json theme={null}
{
  "type": "list",
  "list_type": "unordered",
  "elements": [
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "View our " },
        { "type": "link", "content": "documentation", "href": "https://example.com/docs" },
        { "type": "string", "content": " for more info" }
      ]
    }
  ]
}
```

**Dynamic lists with loops**

Generate lists from data:

```json theme={null}
{
  "type": "list",
  "list_type": "unordered",
  "elements": [
    {
      "type": "list-item",
      "loop": "data.products",
      "elements": [
        { "type": "string", "content": "{{$.item.name}} - ${{$.item.price}}" }
      ]
    }
  ]
}
```

**Nested dynamic lists**

Dynamic lists with nested loops:

```json theme={null}
{
  "type": "list",
  "list_type": "unordered",
  "elements": [
    {
      "type": "list-item",
      "loop": "data.categories",
      "elements": [
        { "type": "string", "content": "{{$.item.name}}" },
        {
          "type": "list",
          "list_type": "ordered",
          "elements": [
            {
              "type": "list-item",
              "loop": "$.item.products",
              "elements": [
                { "type": "string", "content": "{{$.item}}" }
              ]
            }
          ]
        }
      ]
    }
  ]
}
```

**Custom bullet images**

Use custom images for bullets:

```json theme={null}
{
  "type": "list",
  "list_type": "unordered",
  "imgSrc": "https://example.com/custom-bullet.png",
  "imgHref": "https://example.com",
  "elements": [
    {
      "type": "list-item",
      "elements": [
        { "type": "string", "content": "Item with custom bullet" }
      ]
    }
  ]
}
```

<Note>
  Lists can be nested up to 5 levels deep. Nested lists can have different list types (ordered/unordered) than their parent lists.
</Note>

**Channel support**

* **Email**: ✅ Full support with proper list rendering
* **Push**: ✅ Supported (may render as plain text in some cases)
* **SMS**: ⚠️ Limited support (may render as plain text)
* **Inbox**: ✅ Full support
