---
name: courier
description: Use when sending notifications across email, SMS, push, chat, and
  in-app channels; building multi-step journeys; designing templates; managing
  user preferences; or orchestrating customer messaging workflows. Agents should
  reach for this skill when users ask to send messages, build automations,
  manage notification content, or integrate messaging into applications.
metadata:
  mintlify-proj: courier
  version: "1.0"
---

# Courier Skill

## Product summary

Courier is a customer messaging orchestration platform that sends and manages notifications across email, SMS, push, Slack, Microsoft Teams, WhatsApp, and in-app channels from one API. Agents use Courier to send transactional messages, build multi-step journeys, design templates, manage user preferences, and track delivery.

**Key files and commands:**
- REST API base: `https://api.courier.com` (or `https://api.eu.courier.com` for EU)
- Authentication: Bearer token in `Authorization` header
- Primary endpoint: `POST /send` for sending messages
- SDKs: Node.js, Python, Ruby, Go, Java, PHP, C#
- CLI: `courier` command with subcommands like `send message`, `profiles create`, `journeys invoke`
- MCP server: Available for AI agent integration

**Primary docs:** https://www.courier.com/docs

## When to use

Reach for this skill when:
- **Sending notifications:** User wants to send email, SMS, push, or in-app messages to one or many recipients
- **Building workflows:** User needs multi-step journeys with delays, branches, digests, or conditional logic
- **Designing templates:** User wants to create reusable notification content across channels
- **Managing preferences:** User needs to let recipients control which notifications they receive and on which channels
- **Tracking delivery:** User wants to monitor message status, delivery rates, opens, or clicks
- **Integrating messaging:** User is adding notifications to an application (signup confirmations, alerts, onboarding sequences)
- **Multi-tenant scenarios:** User needs separate branding, routing, or preferences per customer

## Quick reference

### Core concepts

| Concept | Purpose | Example |
|---------|---------|---------|
| **Template** | Reusable notification content across channels | `nt_01kx4h2jdafq8bk9aftxak4b40` |
| **User profile** | Recipient data (email, phone, custom fields) | `{ user_id: "user_123", email: "..." }` |
| **Journey** | Multi-step workflow with triggers, delays, branches | Onboarding sequence, re-engagement flow |
| **Routing strategy** | Channel order and failover rules | Email → SMS fallback |
| **Subscription topic** | Preference category users control | "marketing", "product_updates" |
| **Broadcast** | One message to a list or audience | Newsletter to all subscribers |
| **Tenant** | Isolated workspace for multi-tenant apps | Customer-specific branding and preferences |

### Essential API calls

| Task | Endpoint | Method |
|------|----------|--------|
| Send a message | `/send` | POST |
| Create/update user profile | `/profiles/{user_id}` | POST |
| Get user profile | `/profiles/{user_id}` | GET |
| Create template | `/notifications` | POST |
| List templates | `/notifications` | GET |
| Publish template | `/notifications/{id}/publish` | POST |
| Invoke journey | `/journeys/{id}/invoke` | POST |
| List journeys | `/journeys` | GET |
| Get message status | `/messages/{message_id}` | GET |
| List messages | `/messages` | GET |
| Set user preferences | `/users/{user_id}/preferences/{topic_id}` | PUT |
| Get user preferences | `/users/{user_id}/preferences` | GET |

### CLI commands

```bash
# Send a message
courier send message --api-key "$KEY" --message.to '{"user_id":"user_123"}' --message.template nt_01kx4h2jdafq8bk9aftxak4b40

# Create a profile
courier profiles create user_123 --api-key "$KEY" --profile '{"email":"user@example.com"}'

# List messages
courier messages list --api-key "$KEY"

# Get a message
courier messages get message_id --api-key "$KEY"

# Invoke a journey
courier journeys invoke journey_id --api-key "$KEY" --data '{"user_id":"user_123"}'
```

### Template ID prefixes

| Prefix | Resource |
|--------|----------|
| `nt_` | Notification template |
| `rs_` | Routing strategy |
| `pt_` | Preference topic |
| `ps_` | Preference section |

## Decision guidance

### When to use Send API vs Journeys vs Broadcasts

| Scenario | Use | Why |
|----------|-----|-----|
| One-off transactional message (receipt, password reset) | Send API + Template | Simple, immediate, no orchestration needed |
| Multi-step sequence (onboarding, re-engagement) | Journey | Built-in delays, branching, and step inspection |
| One message to many users (newsletter, announcement) | Broadcast | Fans out to list/audience, scheduled or immediate |
| Message triggered by user event | Journey | Trigger on API invoke or Segment event |
| Message triggered by your app code | Send API | Your code controls timing and logic |

### When to use routing strategies vs inline routing

| Scenario | Use | Why |
|----------|-----|-----|
| Same channel order across many templates | Routing strategy | Change once, applies to all templates using it |
| One-off channel order for a single send | Inline routing in message | Override strategy for this send only |
| Failover between providers | Routing strategy | Define primary and backup providers once |
| Conditional channel selection | Routing strategy with `if` conditions | Route based on user data or profile |

### When to store profiles vs send inline

| Scenario | Use | Why |
|----------|-----|-----|
| Recurring sends to same user | Store profile | No repeat data, preferences attach to it |
| One-off send to unknown address | Inline in `to` | No profile needed, no preferences apply |
| User signup or address change | Store profile | Sync with your database, enable preferences |
| Bulk send to many users | Store profiles | Enables list/audience sends and preferences |

## Workflow

### 1. Send your first message

1. Get your API key from Settings → API Keys
2. Create a user profile (or send inline):
   ```bash
   curl -X POST https://api.courier.com/profiles/user_123 \
     -H "Authorization: Bearer $COURIER_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"profile":{"email":"user@example.com"}}'
   ```
3. Create a template in Design Studio or via API with content for your channel
4. Publish the template
5. Send the message:
   ```bash
   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_..."}}'
   ```
6. Check delivery in Logs or via `GET /messages/{message_id}`

### 2. Build a journey

1. Go to Journeys in the dashboard or use the Journeys API
2. Add a trigger (API Invoke or Segment event)
3. Add nodes in sequence: send, delay, branch, fetch data, throttle, batch, cancel
4. For each send node, create a journey template inline with content
5. Publish each template, then publish the journey
6. Invoke from your app: `POST /journeys/{id}/invoke` with user data
7. Monitor runs in the journey's Logs tab or via `GET /journeys/{id}/runs`

### 3. Set up preferences

1. Create subscription topics in Preferences Editor (e.g., "marketing", "product_updates")
2. Set each topic's default state (OPTED_IN, OPTED_OUT, or REQUIRED)
3. Link templates to topics in template settings
4. Courier enforces preferences at send time automatically
5. Let users manage choices via hosted preference page or embedded preference center
6. Read/update preferences via API: `GET /users/{user_id}/preferences`, `PUT /users/{user_id}/preferences/{topic_id}`

### 4. Track delivery

1. Send returns a `requestId`
2. Look up the message: `GET /messages/{requestId}`
3. Check status: ENQUEUED, ROUTED, SENT, DELIVERED, OPENED, CLICKED, or failure states
4. View timeline and rendered content in the response
5. Search logs in dashboard by recipient, template, status, or provider
6. Subscribe to webhooks for real-time status updates: `POST /webhooks` (outbound)

## Common gotchas

- **Send returns immediately, not delivery status.** The API returns a `requestId`, not a delivery result. Check logs or webhooks for actual delivery.
- **Preferences are enforced at send time.** If a user is opted out of a topic, the send is blocked with reason `UNSUBSCRIBED`. No routing logic needed in your code.
- **Profiles are not created by sending.** A send looks up an existing profile; it doesn't create one from the `to` object. Create profiles first with `POST /profiles/{user_id}` or pass inline contact details.
- **Templates must be published to send.** Edits go to draft. Publishing makes it the active version. Runs use the published version.
- **Journey templates are scoped to the journey.** They're separate from workspace templates. You can't reference a workspace template from a journey or vice versa.
- **Idempotency keys are per-endpoint.** The same key sent to `/send` and `/profiles/{user_id}` creates two independent records. Generate one per logical request.
- **Payload limit is 6 MB.** Base64 attachments inflate size by ~33%. Host large files externally and pass URLs.
- **Device tokens attach to user_id, not the profile body.** Replacing a profile with `PUT` doesn't remove stored tokens. Mobile SDKs register them automatically.
- **Preferences are tenant-scoped.** An opt-out under tenant A doesn't affect sends under tenant B. Global opt-outs across tenants are off by default.
- **POST merges profiles, PUT replaces.** Use `POST` for partial updates. `PUT` removes any field you leave out.
- **Routing strategy changes apply immediately.** Templates using a strategy pick up changes on the next send with no republish.
- **Delays in journeys cap at 93 days.** Chain multiple delays for longer gaps.
- **Broadcasts fan out asynchronously.** The API returns immediately; delivery happens in the background.

## Verification checklist

Before submitting work:

- [ ] API key is valid and scoped to the correct environment (Test or Production)
- [ ] User profile exists or inline contact details are provided in `to`
- [ ] Template is published (not in draft state)
- [ ] Template is linked to a subscription topic if preferences should apply
- [ ] Routing strategy or inline routing specifies valid channels and providers
- [ ] Providers are configured under Integrations for the channels being used
- [ ] Journey templates are published before publishing the journey
- [ ] Idempotency key is unique per logical request (if retries are needed)
- [ ] Payload is under 6 MB (host large attachments externally)
- [ ] Message status is checked in Logs or via `GET /messages/{message_id}`
- [ ] Preferences are tested: send to an opted-out user and confirm it's blocked
- [ ] Journey runs are inspected in the Logs tab or via `GET /journeys/{id}/runs/{run_id}`

## Resources

**Comprehensive navigation:** https://www.courier.com/docs/llms.txt

**Critical documentation:**
- [Send a message](https://www.courier.com/docs/send/overview) — How sending works, three ways to send, the pipeline
- [Journeys](https://www.courier.com/docs/journeys/overview) — Multi-step workflows, triggers, nodes, and run inspection
- [Templates](https://www.courier.com/docs/design/templates/overview) — Template design, drafts, publishing, and versioning
- [User profiles](https://www.courier.com/docs/recipients/overview) — Storing recipient data, merge vs replace, device tokens
- [Preferences](https://www.courier.com/docs/recipients/preferences/overview) — How preferences resolve, topic defaults, user choices
- [API reference](https://www.courier.com/docs/reference/api-overview) — Authentication, pagination, errors, rate limits, idempotency
- [Logs and analytics](https://www.courier.com/docs/monitor/overview) — Tracking delivery, open rates, and debugging

---

> For additional documentation and navigation, see: https://www.courier.com/docs/llms.txt