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

# Templates

> What a template contains, its draft and published versions, and sending it by ID or alias.

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

A template defines what a notification says, how it looks, and how it behaves across channels.

You build it once, then send it through email, SMS, push, chat, or in-app. A template is Elemental content plus channel, routing, and brand settings. Build it visually in the designer, or as code through the Templates API.

## Which one to use

A template built in the designer and one managed through the API are the same object, and it is <Doc href="/docs/design/elemental/overview">Elemental</Doc> content either way. The difference is where your team would rather work.

<CardGroup cols={2}>
  <Card title="Design a template" href="/docs/design/templates/design-studio" icon="pen-ruler">
    Build it in the visual editor: drag-and-drop blocks, per-channel content, test-data previews, and version history.

    Best when PMs, designers, and developers work on content together.
  </Card>

  <Card title="Template API" href="/docs/design/templates/api" icon="code">
    Manage it as JSON: create, update, and publish from your codebase, review changes in Git, and test payloads in CI.

    Best when templates live in code, or you would rather not click through a UI.
  </Card>
</CardGroup>

## How it works

Beyond content, a template carries the settings that shape a send:

* **Channels** it can go out on
* Reusable **routing**, primary and fallback
* A **Brand** for consistent styling
* **Conditional logic** to show or hide content by data or preference
* **Variables** that personalize each recipient's version

See <Doc href="/docs/design/templates/variables">variables & Handlebars</Doc> for the personalization model and <Doc href="/docs/design/brands">brands</Doc> for styling.

### Draft, publish, and version history

Editing a template updates its **draft**. Publishing snapshots the draft as the active **version**. A normal send renders the published version, so an unfinished edit never reaches a recipient. Version history lets you compare versions side by side and roll back. A message's log records the version it used, so you can trace a rendering change to a publish.

<Guide href="/docs/guides/go-live">Go live</Guide> covers reviewing a draft before you publish it.

### How a template is performing

Sends, deliveries, opens, clicks, and errors are reported per template, split by channel and provider. <Doc href="/docs/monitor/analytics">Analytics</Doc> shows them in the Courier app, and <Doc href="/docs/monitor/template-metrics">template metrics</Doc> returns the same series over the API for your own dashboards.

## Send it by ID or alias

Both paths end the same way. Pass the Template ID and the data its variables need to <Endpoint method="POST" path="/send" name="Send a message" href="/docs/api-reference/send/send-a-message" />:

<CodeGroup>
  ```javascript Node.js theme={null}
  const { requestId } = await client.send.message({
    message: {
      to: { email: 'sarah@acme-corp.com' },
      template: 'nt_01kx4h2jdafq8bk9aftxak4b40',
      data: { invite_url: 'https://www.acme-corp.com/invite' },
    },
  });
  ```

  ```python Python theme={null}
  response = client.send.message(
      message={
          "to": {"email": "sarah@acme-corp.com"},
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "data": {"invite_url": "https://www.acme-corp.com/invite"},
      },
  )
  ```

  ```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": { "email": "sarah@acme-corp.com" },
        "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
        "data": { "invite_url": "https://www.acme-corp.com/invite" }
      }
    }'
  ```

  ```ruby Ruby theme={null}
  response = courier.send_.message(
    message: {
      to: { email: "sarah@acme-corp.com" },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      data: { invite_url: "https://www.acme-corp.com/invite" }
    }
  )
  ```

  ```go Go 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"),
  			},
  		},
  		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
  		Data: map[string]any{
  			"invite_url": "https://www.acme-corp.com/invite",
  		},
  	},
  })
  ```

  ```java Java theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(JsonValue.from(java.util.Map.of("email", "sarah@acme-corp.com")))
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .data(JsonValue.from(java.util.Map.of("invite_url", "https://www.acme-corp.com/invite")))
          .build())
      .build();
  client.send().message(params);
  ```

  ```php PHP theme={null}
  $response = $client->send->message(
    message: [
      'to' => ['email' => 'sarah@acme-corp.com'],
      'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
      'data' => ['invite_url' => 'https://www.acme-corp.com/invite'],
    ],
  );
  ```

  ```csharp C# theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { Email = "sarah@acme-corp.com" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Data = new Dictionary<string, JsonElement>()
          {
              { "invite_url", JsonSerializer.SerializeToElement("https://www.acme-corp.com/invite") },
          },
      },
  };

  await client.Send.Message(parameters);
  ```

  ```bash CLI theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message '{"to":{"email":"sarah@acme-corp.com"},"template":"nt_01kx4h2jdafq8bk9aftxak4b40","data":{"invite_url":"https://www.acme-corp.com/invite"}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to sarah@acme-corp.com with the invite URL.
  ```
</CodeGroup>

That `template` field accepts either form:

| Value           | Looks like                      | Where it comes from                                               |
| --------------- | ------------------------------- | ----------------------------------------------------------------- |
| **Template ID** | `nt_01kx4h2jdafq8bk9aftxak4b40` | Generated by Courier, shown in the template's settings            |
| **Alias**       | `order-confirmation`            | A name you assign, in the template's **settings** under **Alias** |

An alias is the readable name your code keeps while the template behind it changes. You assign one in the template's **settings**, under **Alias**, not over the API. It then sits exactly where the ID sat, and nothing else about the request changes.

Three rules govern them:

* **A template can have many aliases.** Old and new names can both resolve during a rename.
* **An alias points at one template at a time.** Moving it to another template repoints every send that uses that name.
* **An alias works anywhere the ID works.** Nothing else in the request changes.

That second rule is the useful one. Repointing an alias switches the content behind a send with no deploy, and it switches it for every caller at once.

## Limits & behavior

* **A send uses the published version.** Unpublished edits stay in the draft until you publish.
* **Runs and logs are version-pinned.** A message records the template version it rendered, so history stays accurate after later publishes.
* **The designer and API share one template.** Editing in one is visible in the other. There is no separate "API template."

## FAQ

<AccordionGroup>
  <Accordion title="Can I manage a template in code and in the designer?">
    Both operate on the same template. One team can edit in the designer while another manages it through the API. Coordinate publishes so a draft edit is not overwritten.
  </Accordion>

  <Accordion title="What is the difference between a draft and a published template?">
    Edits go to the draft. Publishing makes the draft the active version. Normal sends render the published version.
  </Accordion>

  <Accordion title="Can I reference a template by a stable name?">
    Assign an alias and pass it as `template` on the send, so your code references a human-readable name instead of the generated ID.
  </Accordion>
</AccordionGroup>
