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

# Send a message

> Send a message with one API call, and choose between a template, a broadcast, or a journey.

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

A send is one API call. Name who it goes to and what it says. Courier renders it, applies preferences, routes, and delivers.

<CodeGroup>
  ```javascript Node.js theme={null}
  const { requestId } = await client.send.message({
    message: {
      to: { user_id: 'user_123' },
      template: 'nt_01kx4h2jdafq8bk9aftxak4b40',
      data: { name: 'Sarah Bennett' },
    },
  });
  ```

  ```python Python theme={null}
  response = client.send.message(
      message={
          "to": {"user_id": "user_123"},
          "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
          "data": {"name": "Sarah Bennett"},
      },
  )
  ```

  ```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": { "user_id": "user_123" },
        "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
        "data": { "name": "Sarah Bennett" }
      }
    }'
  ```

  ```ruby Ruby theme={null}
  response = courier.send_.message(
    message: {
      to: { user_id: "user_123" },
      template: "nt_01kx4h2jdafq8bk9aftxak4b40",
      data: { name: "Sarah Bennett" }
    }
  )
  ```

  ```go Go 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"),
  		Data: map[string]any{
  			"name": "Sarah Bennett",
  		},
  	},
  })
  ```

  ```java Java theme={null}
  SendMessageParams params = SendMessageParams.builder()
      .message(SendMessageParams.Message.builder()
          .to(JsonValue.from(java.util.Map.of("user_id", "user_123")))
          .template("nt_01kx4h2jdafq8bk9aftxak4b40")
          .data(JsonValue.from(java.util.Map.of("name", "Sarah Bennett")))
          .build())
      .build();
  client.send().message(params);
  ```

  ```php PHP theme={null}
  $response = $client->send->message(
    message: [
      'to' => ['userID' => 'user_123'],
      'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
      'data' => ['name' => 'Sarah Bennett'],
    ],
  );
  ```

  ```csharp C# theme={null}
  SendMessageParams parameters = new()
  {
      Message = new()
      {
          To = new UserRecipient { UserID = "user_123" },
          Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
          Data = new Dictionary<string, JsonElement>()
          {
              { "name", JsonSerializer.SerializeToElement("Sarah Bennett") },
          },
      },
  };

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

  ```bash CLI theme={null}
  courier send message \
    --api-key "$COURIER_API_KEY" \
    --message '{"to":{"user_id":"user_123"},"template":"nt_01kx4h2jdafq8bk9aftxak4b40","data":{"name":"Sarah Bennett"}}'
  ```

  ```text MCP theme={null}
  With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 with their name.
  ```
</CodeGroup>

The response returns a `requestId`, not a delivery result. Find it in <AppLink href="https://app.courier.com/logs">Logs</AppLink> to confirm the message rendered and delivered.

## Prerequisites

* <AppLink href="https://app.courier.com/settings/api-keys">A Courier API key</AppLink>, and <Doc href="/docs/sdk-libraries/sdks-overview">the SDK for your language</Doc> or curl
* <Doc href="/docs/design/templates/overview">A template</Doc>, for the `nt_` id in the call above
* <Doc href="/docs/recipients/overview">A saved user</Doc>, or their contact details inline in `to`
* <Doc href="/docs/integrations/overview">A connected provider</Doc> for the channel it goes out on. Test email and the <Doc href="/docs/in-app/overview">inbox</Doc> need none.

## Three ways to send

All three send the same content: a <Doc href="/docs/design/templates/overview">template</Doc> you design once. What differs is what starts the send.

### 1. Templates

**Use a template when your application triggers the send.** A receipt, a password reset, an alert. Your app calls the API with a recipient and a template, like the example above. The template holds what the message says on every channel, plus its routing and brand settings. Your app supplies `data`, and the template's <Doc href="/docs/design/templates/variables">variables</Doc> render each recipient's own version.

That split keeps a send small. Content changes ship by publishing the template, with no deploy.

<Card title="Design a Template" icon="pen-ruler" href="/docs/design/templates/overview" horizontal arrow="true">
  Build it in the visual editor, or manage it as JSON over the Templates API.
</Card>

### 2. Broadcasts

**Use a broadcast when you choose the moment.** One message to a whole <Doc href="/docs/recipients/lists-and-audiences/overview">list or audience</Doc>, sent now or on a schedule. You pick the group, and Courier fans it out.

<Card title="Send a Broadcast" icon="bullhorn" href="/docs/broadcasts/overview" horizontal arrow="true">
  Reach a saved list or audience in one send.
</Card>

### 3. Journeys

**Use a journey when the send happens later, or depends on what the user does.** A multi-step flow that runs per user, with delays, branching, and enrichment between steps. A user event or an API call starts it, and the journey decides what comes next.

<Card title="Build a Journey" icon="route" href="/docs/journeys/overview" horizontal arrow="true">
  Lay out steps, delays, and branches in the visual builder.
</Card>

|            | Templates              | Broadcasts                 | Journeys                           |
| ---------- | ---------------------- | -------------------------- | ---------------------------------- |
| Shape      | One message            | One message to a group     | Multi-step flow, per user          |
| Started by | Your API call          | You, now or scheduled      | A user event or API call           |
| Timing     | Immediate, or a delay  | Now or scheduled           | Delays and waits between steps     |
| Logic      | In your code           | None                       | Built-in branching                 |
| Best for   | Transactional messages | Newsletters, announcements | Onboarding, digests, re-engagement |

## How it works

Every send is one `message` object posted to <Endpoint method="POST" path="/send" name="Send a message" href="/docs/api-reference/send/send-a-message" />, whichever SDK you call it from. It has four parts:

```json theme={null}
{
  "message": {
    "to": { ... },
    "template": "...",
    "data": { ... },
    "routing": { ... }
  }
}
```

| Part       | What it does                                                                                                                                                                      | Example                           |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `to`       | Who gets it: a `user_id` for a stored <Doc href="/docs/recipients/overview">profile</Doc>, or contact details inline                                                                   | `{ "user_id": "user_123" }`       |
| `template` | What it says: a <Doc href="/docs/design/templates/overview">template</Doc> by its `nt_` ID, or an <Doc href="/docs/design/templates/overview#send-it-by-id-or-alias">alias</Doc> you assign | `"nt_01kx4h2jdafq8bk9aftxak4b40"` |
| `data`     | The <Doc href="/docs/design/templates/variables">variables</Doc> it fills in                                                                                                           | `{ "name": "Sarah Bennett" }`     |
| `routing`  | Where it goes. Leave it out and the template's own <Doc href="/docs/send/routing">routing</Doc> applies                                                                                | `{ "method": "single" }`          |

Pass an array in `to` to reach several recipients, or contact details inline for a one-off: `{ "email": "sarah@acme-corp.com" }`.

A message with no reusable design can carry its `content` inline instead of a `template`. That content is <Doc href="/docs/design/elemental/overview">Elemental</Doc>, the same format a template renders.

Courier enforces each recipient's <Doc href="/docs/recipients/preferences/overview">preferences</Doc> at send time. Opt-outs and channel choices apply without extra logic in your code.

### The pipeline

```mermaid theme={null}
flowchart LR
    A["Your app"] --> B["Courier API"]
    B --> C["Provider"]
    C --> D["Your user"]
```

The send is asynchronous, so the API responds before delivery happens. Courier records every step, so you can follow a message from request to delivery in <Doc href="/docs/monitor/overview">message logs</Doc>. Transient provider errors retry, and an unavailable provider or channel fails over to the next one.

### Idempotency

Send an `Idempotency-Key` header to make a retry safe. A repeated request with the same key returns the original response instead of sending again. A retry after a network failure cannot duplicate the send.

<Tip>
  Add `routing` to pin the channels for this request, or `channels` and `providers` to adjust delivery settings. See <Doc href="/docs/send/overrides">channel settings & overrides</Doc> and <Doc href="/docs/send/routing">how routing & failover work</Doc>.
</Tip>

## Limits & behavior

* **A send is asynchronous.** The API returns a `requestId`, not a delivery result. Track the outcome in the logs or via <Doc href="/docs/monitor/webhooks/outbound">outbound webhooks</Doc>.
* **Preferences are enforced automatically.** A recipient's opt-outs and channel choices apply at send time with no extra code.
* **Idempotency is opt-in.** Send an `Idempotency-Key` header to dedupe retries. Without it, each call sends.

## FAQ

<AccordionGroup>
  <Accordion title="Can I send to multiple users at once?">
    Pass an array of recipients in `to`, or send to a <Doc href="/docs/recipients/lists-and-audiences/overview">list or audience</Doc>. For a large group, a list or audience send fans out for you (see <Guide href="/docs/guides/announce-to-a-list">Send to lists & audiences</Guide>).
  </Accordion>

  <Accordion title="Does the API tell me if delivery succeeded?">
    Not directly. It returns a `requestId` and the send runs asynchronously. Check delivery in <Doc href="/docs/monitor/overview">message logs</Doc> or receive a `message:updated` <Doc href="/docs/monitor/webhooks/outbound">webhook</Doc>.
  </Accordion>

  <Accordion title="How do I prevent duplicate sends?">
    Send an `Idempotency-Key` header. A second request with the same key returns the original result without sending again.
  </Accordion>
</AccordionGroup>
