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

# Message logs

> Delivery history for every send: request, routing, provider response, opens, and clicks.

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

Courier records the delivery lifecycle of every message you send.

Logs answer "what happened to this one send". <Doc href="/docs/monitor/analytics">Analytics</Doc> answer "how is this template performing". Both read the same status data.

## How it works

Each entry in the <AppLink href="https://app.courier.com/logs">Logs</AppLink> shows status, notification, recipient, and the provider channels the message went through. Open a message to see its timeline: each status change, the provider response, and any error.

Filter the list by status, provider, or error to find a specific class of failure.

### Finding one message

Each message has a message id (`requestId`), the same value the <Endpoint method="GET" path="/messages/{message_id}" name="Get message" href="/docs/api-reference/messages/get-message">Messages API</Endpoint> and <Doc href="/docs/monitor/webhooks/outbound">outbound webhooks</Doc> return. Use it to match a log entry to an API response or a webhook event.

### Reading a status

A message's status only ever advances. Two independent systems drive it. Provider confirmation produces `DELIVERED`. Courier tracking produces `OPENED` and `CLICKED`, so engagement can appear before, or without, a delivery confirmation.

For the full status table and the send-time reasons a message never reaches a provider, see <Doc href="/docs/send/statuses">statuses</Doc>.

## Limits & behavior

* **A message stuck at `SENT` is usually not lost.** Courier handed it to the provider but has no delivery confirmation. Usually delivery tracking is not configured for that channel.
* **`DELIVERED` depends on the provider.** Not every provider confirms delivery, so a missing `DELIVERED` is not proof of failure. Treat `OPENED` and `CLICKED` as receipt signals.
* **Logs are per message, not per recipient group.** A broadcast to a list produces one log entry per recipient. Use <Doc href="/docs/monitor/broadcast-performance">broadcast performance</Doc> for the roll-up.
* **Retention depends on your plan.** Developer and Business keep 30 days of message history. Enterprise keeps 365 days. Searches and reads are clipped to that window, so export anything you need to keep longer. A message's own event history is capped at roughly 93 days on every plan, so an older message still lists but returns no timeline.

## FAQ

<AccordionGroup>
  <Accordion title="How do I match a log entry to my send request?">
    Use the message id (`requestId`) from your Send API response. It is the same id shown in the log and carried on the `message:updated` webhook.
  </Accordion>

  <Accordion title="A message is stuck at SENT. Is it lost?">
    Usually not. `SENT` means Courier handed it to the provider without a delivery confirmation. Check the provider's delivery-status setup.
  </Accordion>

  <Accordion title="Why did a message open before it was delivered?">
    Opens and clicks are tracked the instant the recipient acts. `DELIVERED` waits on the provider's asynchronous confirmation, so engagement can arrive first.
  </Accordion>
</AccordionGroup>
