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

# Create your first journey

> Build a one-node journey, invoke it from your code, and read the run.

export const Tags = ({items}) => {
  const routes = {
    Email: "/integrations/email/overview",
    SMS: "/integrations/sms/overview",
    Push: "/integrations/push/overview",
    Inbox: "/in-app/overview",
    Chat: "/integrations/direct-message/overview",
    Templates: "/design/templates/overview",
    Variables: "/design/templates/variables",
    Elemental: "/design/elemental/overview",
    Brands: "/design/brands",
    Translations: "/design/elemental/locales",
    Routing: "/send/routing",
    Preferences: "/recipients/preferences/overview",
    Journeys: "/journeys/overview",
    Broadcasts: "/broadcasts/overview",
    Tenants: "/tenants/overview",
    Logs: "/monitor/overview",
    Webhooks: "/monitor/webhooks/outbound",
    Lists: "/recipients/lists-and-audiences/overview",
    Users: "/recipients/overview",
    Digests: "/journeys/nodes/digest",
    Environments: "/workspaces/overview",
    MCP: "/resources/mcp"
  };
  const icons = {
    Email: "envelope",
    SMS: "comment",
    Push: "mobile",
    Inbox: "inbox",
    Chat: "comments",
    Templates: "pen-ruler",
    Variables: "pen-ruler",
    Elemental: "pen-ruler",
    Brands: "pen-ruler",
    Translations: "pen-ruler",
    Routing: "paper-plane",
    Preferences: "users",
    Journeys: "route",
    Broadcasts: "bullhorn",
    Tenants: "building",
    Logs: "chart-simple",
    Webhooks: "chart-simple",
    Lists: "users",
    Users: "users",
    Digests: "route",
    Environments: "briefcase",
    MCP: "toolbox"
  };
  const base = "https://d3gk2c5xim1je2.cloudfront.net/fontawesome/v7.2.0/regular/";
  const names = String(items || "").split(",").map(entry => entry.trim()).filter(Boolean);
  return <div className="cx-tags">
      {names.map(name => {
    const href = routes[name];
    const icon = icons[name];
    const url = icon ? "url(" + base + icon + ".svg)" : null;
    const style = url ? {
      "--cx-tag-icon": url
    } : null;
    if (!href) {
      return <span className="cx-tag" data-icon={icon} style={style} key={name}>
              {name}
            </span>;
    }
    return <a className="cx-tag" data-icon={icon} style={style} href={href} key={name}>
            {name}
          </a>;
  })}
    </div>;
};

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

<Tags items="Email, Journeys" />

Build the smallest journey that works, start a run from your code, then open the run and read what it did.

One send node is enough to see the whole loop. Delays, branches, and digests are the same graph with more nodes on it.

## Prerequisites

* <Doc href="/docs/integrations/email/overview">A connected email provider</Doc>
* <AppLink href="https://app.courier.com/settings/api-keys">A Courier API key</AppLink>

## Build it

<Steps>
  <Step title="Create the journey">
    Open <AppLink href="https://app.courier.com/orchestration/journeys">Journeys</AppLink>, select **New Journey**, and name it. Choose the **API Invoke** trigger, which is what lets your code start each run.
  </Step>

  <Step title="Add one send node">
    Drop a **send** node on the canvas, pick email, and select **+ Create** to write its content inline. Each send node owns one Template scoped to this journey, so there is nothing to create separately.

    Address the recipient by `user_id`. The profile supplies the email address, which means preferences apply and the address can change without touching this journey.
  </Step>

  <Step title="Publish">
    Select **Publish**. That locks the draft as a version and makes it the one new runs use. Copy the journey ID from the editor.
  </Step>
</Steps>

## Invoke it

<Doc href="/docs/journeys/invoke">Invoke a Journey</Doc> carries the call in every language. The response returns a `runId`, which is the handle for everything below.

Send an `Idempotency-Key` if the call sits anywhere a retry could reach it, because a repeated key returns the stored result instead of starting a second run.

## Read the run

**Run inspection is a console view, and there is no public endpoint for a run's trace.** Take the `runId` from the invoke response, open the journey's **Logs** tab, and search for it.

The detail view draws each node's outcome on the graph. Click a node to see its step context: the input data, the profile, any conditions evaluated, and the output. <Doc href="/docs/monitor/journey-metrics">Journey metrics and inspection</Doc> covers the run states and what each view shows.

This is the step worth doing slowly. A journey that sends nothing looks identical to one that was never invoked until you open the run and see which node it stopped at.

## Verify

<Steps>
  <Step title="The run completed">
    Invoke for a test user, then confirm the run reads as complete rather than sitting on the send node.
  </Step>

  <Step title="The message arrived">
    Check the inbox for the address on that user's profile, then open <Doc href="/docs/monitor/overview">message logs</Doc> and confirm the send is recorded against the journey.
  </Step>

  <Step title="A second invoke starts a second run">
    Invoke again without an `Idempotency-Key` and confirm two runs appear. Repeat with the same key and confirm only one does.
  </Step>
</Steps>

## What to add next

<CardGroup cols={2}>
  <Card title="Onboarding sequence" icon="clock" href="/docs/guides/build-an-onboarding-sequence">
    Delays and a branch, so only stalled users get nudged.
  </Card>

  <Card title="Nodes" icon="diagram-project" href="/docs/journeys/nodes">
    Every node type, one page each.
  </Card>
</CardGroup>

## Limits & behavior

* **Editing a send node's content after publishing changes nothing on its own.** The edit saves as a template draft, the journey keeps sending the version it was published with, and nothing marks the difference. Publish the journey again.
* **A run in flight finishes on the version it started with.** A publish does not migrate it.
* **A draft does not run.** Invoking a journey that was never published starts nothing.
