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

# Send Multi-Channel Notifications from an AI Agent

> Expose Courier's Send API as an AI agent tool. Add urgency-based routing and channel failover in TypeScript, then connect via the Courier MCP server.

In this tutorial, you'll build a TypeScript function that lets an AI agent send notifications across multiple channels with a single API call. The agent decides what to communicate and how urgent it is. Courier handles channel selection, failover, and delivery.

## Prerequisites

Before you start, make sure you have:

* **Node.js 18+** installed
* A **Courier account** ([sign up free](https://app.courier.com/signup))
* Your **Courier API key** from [Settings > API Keys](https://app.courier.com/settings/api-keys)
* A **test user profile** in Courier with an email address (create one in the [Users page](https://app.courier.com/users) or via the API)

### Which channels work out of the box?

Not every channel requires provider setup. Here's what you can use right away and what needs configuration:

| Channel   | Ready out of the box? | Setup needed                                                                                                                                     |
| --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Inbox** | Yes                   | None. Courier's in-app inbox works immediately.                                                                                                  |
| **Email** | Yes (test mode)       | Courier provides a built-in email provider for testing. For production, [add a provider](/external-integrations/email) like SendGrid or AWS SES. |
| **Push**  | No                    | Requires a push provider (Firebase, APNs) and device tokens on user profiles.                                                                    |
| **SMS**   | No                    | Requires an SMS provider (Twilio, Vonage) and phone numbers on user profiles.                                                                    |

<Warning>
  Channels without a configured provider show as **Unroutable** in the logs. Courier skips them automatically and tries the next channel in the list. Start with `"low"` urgency (inbox only) and add channels as you set up providers.
</Warning>

## Project setup

<Steps>
  <Step title="Initialize the project">
    Create a new directory and install the Courier SDK:

    ```bash theme={null}
    mkdir agent-courier-notifications && cd agent-courier-notifications
    npm init -y
    npm install @trycourier/courier dotenv
    npm install typescript tsx --save-dev
    npx tsc --init
    ```
  </Step>

  <Step title="Configure your API key">
    Create a `.env` file in your project root:

    ```
    COURIER_API_KEY=your_courier_api_key
    ```
  </Step>

  <Step title="Create the project structure">
    ```
    agent-courier-notifications/
    ├── src/
    │   └── send-notification.ts
    ├── .env
    ├── package.json
    └── tsconfig.json
    ```
  </Step>
</Steps>

## Understanding the routing model

Every Courier `send` call accepts a `routing` object with two fields:

```typescript theme={null}
routing: {
  method: "all" | "single",
  channels: ["email", "sms", "push", "inbox"]
}
```

| Method   | Behavior                                                         |
| -------- | ---------------------------------------------------------------- |
| `single` | Try each channel in order, stop at the first successful delivery |
| `all`    | Deliver to every channel in the array simultaneously             |

The channel array is your priority list. For `single`, Courier works through it top to bottom. For `all`, every channel fires at once.

<Tip>
  For a full walkthrough of routing configuration (including the visual designer), see [How to configure multi-channel routing](/tutorials/sending/how-to-configure-multi-channel-routing).
</Tip>

## Pattern 1: Urgency-based routing

The most common pattern for agent-driven notifications. Instead of hardcoding a channel, the agent expresses urgency and a helper function maps it to a routing strategy.

Create `src/send-notification.ts`:

```typescript theme={null}
import Courier from "@trycourier/courier";
import "dotenv/config";

const courier = new Courier();

function routingForUrgency(urgency: string) {
  const strategies: Record<string, { method: "all" | "single"; channels: string[] }> = {
    critical: { method: "all", channels: ["email", "sms", "push", "inbox"] },
    high:     { method: "single", channels: ["push", "email", "inbox"] },
    normal:   { method: "single", channels: ["email", "inbox"] },
    low:      { method: "single", channels: ["inbox"] },
  };
  return strategies[urgency] ?? strategies.normal;
}

export async function sendNotification(
  userId: string,
  title: string,
  body: string,
  urgency: string
) {
  const routing = routingForUrgency(urgency);

  const { requestId } = await courier.send.message({
    message: {
      to: { user_id: userId },
      content: { title, body },
      routing,
    },
  });

  return { requestId, channels: routing.channels, method: routing.method };
}
```

Here's what each urgency level does:

| Urgency    | Method   | Channels                | When to use                                   |
| ---------- | -------- | ----------------------- | --------------------------------------------- |
| `critical` | `all`    | email, SMS, push, inbox | Security alerts, outages, account compromises |
| `high`     | `single` | push → email → inbox    | Time-sensitive operational alerts             |
| `normal`   | `single` | email → inbox           | Reports, digests, routine updates             |
| `low`      | `single` | inbox                   | Informational, non-time-sensitive             |

### Test it

Add a quick test script at the bottom of `send-notification.ts` (or in a separate file):

```typescript theme={null}
async function main() {
  const urgency = process.argv[2] || "low";
  const result = await sendNotification(
    "test_user_123",
    "Weekly report ready",
    "Your weekly usage report is now available.",
    urgency
  );
  console.log("Sent:", result);
}

main().catch(console.error);
```

Start with `low` urgency to confirm your setup works. This sends to inbox only, which requires no provider configuration:

```bash theme={null}
npx tsx src/send-notification.ts low
```

Check [Courier Logs](https://app.courier.com/logs) to confirm the notification was delivered to inbox. Then try higher urgency levels as you configure additional channels:

```bash theme={null}
npx tsx src/send-notification.ts normal    # email + inbox
npx tsx src/send-notification.ts high      # push → email → inbox
npx tsx src/send-notification.ts critical  # all channels at once
```

<Tip>
  If a channel shows as **Unroutable** in the logs, that just means you haven't configured a provider for it yet. With `method: "single"`, Courier automatically skips to the next channel in the list, so your notification still gets delivered through whichever channels are set up.
</Tip>

## Pattern 2: Channel failover

Some users don't have push tokens configured. Some haven't verified their phone number. With `method: "single"`, Courier attempts each channel in order and stops at the first success:

```typescript theme={null}
const { requestId } = await courier.send.message({
  message: {
    to: { user_id: userId },
    content: { title, body },
    routing: {
      method: "single",
      channels: ["push", "email", "sms", "inbox"],
    },
  },
});
```

If push fails (no device token), Courier tries email. If email fails (bad address), it tries SMS. If SMS fails, it falls back to inbox. The agent doesn't need to check user profiles before sending.

<Note>
  The `requestId` returned by the send call lets you check delivery status later using the [Messages API](/api-reference/sent-messages/get-message). This is useful for agents that need to confirm a notification landed before continuing.
</Note>

## Pattern 3: Provider failover

Channel failover handles "which channel." Provider failover handles "which service within a channel." If your primary email provider goes down, Courier can silently retry through a backup.

Add a `channels` object to configure per-channel provider ordering:

```typescript theme={null}
const { requestId } = await courier.send.message({
  message: {
    to: { user_id: userId },
    content: { title, body },
    routing: {
      method: "single",
      channels: ["email", "push", "inbox"],
    },
    channels: {
      email: {
        providers: ["sendgrid", "ses"],
        routing_method: "single",
      },
    },
  },
});
```

This gives you two layers of failover:

1. **Channel level**: email → push → inbox
2. **Provider level** (within email): SendGrid → AWS SES

If SendGrid returns a 5xx, Courier retries through SES automatically.

<Tip>
  You can configure provider failover for any channel, not just email. For example, set up Twilio as a primary SMS provider with Vonage as a backup.
</Tip>

## Pattern 4: Template-based multi-channel

For notifications that need branded designs, channel-specific copy, or complex layouts, use a Courier template instead of inline content. Design the template once in the [template designer](/platform/content/template-designer/template-designer-overview), and Courier renders the appropriate version for each channel.

```typescript theme={null}
const { requestId } = await courier.send.message({
  message: {
    to: { user_id: userId },
    template: "DEPLOYMENT_ALERT",
    data: {
      service: "api-server",
      environment: "production",
      exitCode: 1,
      timestamp: new Date().toISOString(),
    },
    routing: {
      method: "all",
      channels: ["email", "push", "inbox"],
    },
  },
});
```

The `data` object passes dynamic values into your template variables. The email version can include a full HTML layout with error details, the push notification can be a short summary, and the inbox message can include action buttons.

<Note>
  To create the `DEPLOYMENT_ALERT` template, go to [Templates](https://app.courier.com/templates), click **New**, and add channel blocks for each delivery channel. Use `{service}`, `{environment}`, and `{exitCode}` as template variables.
</Note>

## Exposing to your agent

The `sendNotification` function from Pattern 1 works as a tool in any agent framework. Here are three ways to connect it:

### Option 1: Courier MCP server

If your agent runs in Cursor, Claude Code, or another MCP-compatible environment, the [Courier MCP server](https://www.courier.com/docs/tools/mcp) exposes a `send_message` tool that already supports `routing`, `channels`, and `template` fields. No custom code needed.

Add to your `.cursor/mcp.json`:

```json theme={null}
{
  "mcpServers": {
    "courier": {
      "url": "https://mcp.courier.com",
      "headers": {
        "api_key": "your_courier_api_key"
      }
    }
  }
}
```

### Option 2: Courier CLI

For agents that work through shell commands, use `courier send message` with the same JSON structure:

```bash theme={null}
courier send message --message '{
  "to": {"user_id": "test_user_123"},
  "content": {"title": "Deployment failed", "body": "Exit code 1"},
  "routing": {"method": "all", "channels": ["email", "push", "inbox"]}
}'
```

See the [CLI reference](https://www.courier.com/docs/tools/cli) for installation and full command documentation.

### Option 3: Custom tool definition

Wrap `sendNotification` as a tool in your framework of choice. The function signature maps directly to a tool schema: `userId`, `title`, `body`, and `urgency` as parameters, with the urgency descriptions guiding the LLM's selection.

This works with OpenAI function calling, Vercel AI SDK, LangChain, or any framework that supports structured tool definitions.

## Verify delivery

After sending, check delivery status in two ways:

1. **Courier Logs UI**: Go to [app.courier.com/logs](https://app.courier.com/logs) to see the full delivery timeline for each notification, including which channels were attempted, which succeeded, and which providers were used.

2. **Messages API**: Query delivery status programmatically:

```typescript theme={null}
const message = await courier.messages.retrieve("your_request_id");
console.log(message.status); // DELIVERED, SENT, UNDELIVERABLE, etc.
```

In the logs, you'll see one entry per channel attempted. Common statuses:

| Status            | Meaning                                                                                      |
| ----------------- | -------------------------------------------------------------------------------------------- |
| **Delivered**     | The provider accepted and delivered the notification                                         |
| **Sent**          | The provider accepted the notification (delivery confirmation pending)                       |
| **Unroutable**    | No provider is configured for this channel. Expected if you haven't set up that channel yet. |
| **Undeliverable** | A provider is configured but delivery failed (bad address, expired token, etc.)              |

## What's next

<CardGroup cols={2}>
  <Card title="Configure multi-channel routing" icon="list-tree" href="/tutorials/sending/how-to-configure-multi-channel-routing">
    Set up routing rules in the Send API and the visual designer
  </Card>

  <Card title="Agent quickstart" icon="wand-sparkles" href="/tools/agent-quickstart">
    Set up your AI agent with Courier's MCP server, CLI, and Skills
  </Card>

  <Card title="CLI reference" icon="terminal" href="/tools/cli">
    Send and manage notifications from the command line
  </Card>

  <Card title="Send API reference" icon="code" href="/api-reference/send/send-a-message">
    Full API documentation for the Send endpoint
  </Card>
</CardGroup>
