> ## 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 test message

> Send one message with the API using a Test key, then find it in the logs.

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

<Steps>
  <Step title="Get your API key">
    Copy your **Test** key from <AppLink href="https://app.courier.com/~/test/platform/api-keys">Settings → API Keys</AppLink>.
  </Step>

  <Step title="Send the message">
    Replace `YOUR_COURIER_API_KEY` with your key and `you@example.com` with your own address.

    <Note>
      In Test, email only delivers to the address you signed up with.
    </Note>

    <CodeGroup>
      ```javascript Node.js theme={null}
      // npm install @trycourier/courier
      import Courier from "@trycourier/courier";

      const client = new Courier({ apiKey: "YOUR_COURIER_API_KEY" });

      const response = await client.send.message({
        message: {
          to: { email: "you@example.com" },
          content: {
            version: "2022-01-01",
            elements: [
              { type: "meta", title: "Hello from Courier!" },
              {
                type: "html",
                content:
                  "<h1>It works!</h1><p>You just sent your first notification.</p>",
              },
            ],
          },
        },
      });

      console.log("Sent! Request ID:", response.requestId);
      ```

      ```python Python theme={null}
      # pip install trycourier
      from courier import Courier

      client = Courier(api_key="YOUR_COURIER_API_KEY")

      response = client.send.message(
          message={
              "to": {"email": "you@example.com"},
              "content": {
                  "version": "2022-01-01",
                  "elements": [
                      {"type": "meta", "title": "Hello from Courier!"},
                      {
                          "type": "html",
                          "content": "<h1>It works!</h1><p>You just sent your first notification.</p>",
                      },
                  ],
              },
          }
      )

      print("Sent! Request ID:", response.request_id)
      ```

      ```bash cURL theme={null}
      curl -X POST https://api.courier.com/send \
        -H "Authorization: Bearer YOUR_COURIER_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "message": {
            "to": { "email": "you@example.com" },
            "content": {
              "version": "2022-01-01",
              "elements": [
                { "type": "meta", "title": "Hello from Courier!" },
                {
                  "type": "html",
                  "content": "<h1>It works!</h1><p>You just sent your first notification.</p>"
                }
              ]
            }
          }
        }'
      ```

      ```ruby Ruby theme={null}
      # gem install courier
      require "courier"

      courier = Courier::Client.new(api_key: "YOUR_COURIER_API_KEY")

      response = courier.send_.message(
        message: {
          to: { email: "you@example.com" },
          content: {
            version: "2022-01-01",
            elements: [
              { type: "meta", title: "Hello from Courier!" },
              {
                type: "html",
                content: "<h1>It works!</h1><p>You just sent your first notification.</p>"
              }
            ]
          }
        }
      )

      puts("Sent! Request ID: #{response.request_id}")
      ```

      ```go Go theme={null}
      // go get github.com/trycourier/courier-go/v4
      client := courier.NewClient(option.WithAPIKey("YOUR_COURIER_API_KEY"))

      // Elemental nodes carry no typed content fields, so pass the document as raw JSON.
      content := param.Override[shared.ElementalContentParam](json.RawMessage(`{
        "version": "2022-01-01",
        "elements": [
          { "type": "meta", "title": "Hello from Courier!" },
          {
            "type": "html",
            "content": "<h1>It works!</h1><p>You just sent your first notification.</p>"
          }
        ]
      }`))

      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfUserRecipient: &shared.UserRecipientParam{
      				Email: courier.String("you@example.com"),
      			},
      		},
      		Content: courier.SendMessageParamsMessageContentUnion{OfElementalContent: &content},
      	},
      })
      if err != nil {
      	panic(err.Error())
      }

      fmt.Printf("Sent! Request ID: %s\n", response.RequestID)
      ```

      ```java Java theme={null}
      CourierClient client = CourierOkHttpClient.builder().apiKey("YOUR_COURIER_API_KEY").build();

      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(UserRecipient.builder().email("you@example.com").build())
              // Elemental nodes carry no typed content fields, so pass the document as raw JSON.
              .content(JsonValue.from(java.util.Map.of(
                  "version", "2022-01-01",
                  "elements", java.util.List.of(
                    java.util.Map.of(
                      "type", "meta",
                      "title", "Hello from Courier!"),
                    java.util.Map.of(
                      "type", "html",
                      "content", "<h1>It works!</h1><p>You just sent your first notification.</p>")))))
              .build())
          .build();
      SendMessageResponse response = client.send().message(params);
      ```

      ```php PHP theme={null}
      $client = new Client(apiKey: 'YOUR_COURIER_API_KEY');

      $response = $client->send->message(
        message: [
          'to' => ['email' => 'you@example.com'],
          'content' => [
            'version' => '2022-01-01',
            'elements' => [
              ['type' => 'meta', 'title' => 'Hello from Courier!'],
              [
                'type' => 'html',
                'content' => '<h1>It works!</h1><p>You just sent your first notification.</p>',
              ],
            ],
          ],
        ],
      );
      ```

      ```csharp C# theme={null}
      CourierClient client = new() { ApiKey = "YOUR_COURIER_API_KEY" };

      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { Email = "you@example.com" },
              // Elemental nodes expose no typed content fields, so build the document from raw JSON.
              Content = ElementalContent.FromRawUnchecked(
                  JsonSerializer.Deserialize<Dictionary<string, JsonElement>>("""
                  {
                    "version": "2022-01-01",
                    "elements": [
                      { "type": "meta", "title": "Hello from Courier!" },
                      {
                        "type": "html",
                        "content": "<h1>It works!</h1><p>You just sent your first notification.</p>"
                      }
                    ]
                  }
                  """)),
          },
      };

      var response = await client.Send.Message(parameters);
      ```

      ```bash CLI theme={null}
      # npm install -g @trycourier/cli
      export COURIER_API_KEY=YOUR_COURIER_API_KEY

      courier send message \
        --message.to '{"email": "you@example.com"}' \
        --message.content '{"version": "2022-01-01", "elements": [{"type": "meta", "title": "Hello from Courier!"}, {"type": "html", "content": "<h1>It works!</h1><p>You just sent your first notification.</p>"}]}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send a test email to my own address with the title Hello from Courier.
      ```
    </CodeGroup>

    The `meta` element's `title` becomes the email subject. The `<h1>` is the heading
    inside the message, so the two are set separately.

    See <Doc href="/docs/design/elemental/overview">Elemental</Doc> for all the elements you can send.

    The response returns a `requestId`:

    ```json theme={null}
    { "requestId": "1-67890abc-d1e2f3a4b5c6" }
    ```
  </Step>

  <Step title="Check the logs">
    Open <AppLink href="https://app.courier.com/~/test/logs">Test Logs</AppLink> to see the delivery timeline.

    Logs are per <Doc href="/docs/workspaces/overview">environment</Doc>, so a missing message usually means a key from the other one.
  </Step>
</Steps>

## FAQ

<AccordionGroup>
  <Accordion title="Do I need to configure a Provider first?">
    Not to email yourself. Every Test environment ships with the Courier Test Email Provider, which delivers to your signup address only.

    Any other recipient, or any other channel, needs a provider of your own. <Guide href="/docs/guides/send-your-first-email">Send your first email</Guide> connects one and verifies a sending domain.
  </Accordion>

  <Accordion title="What are Templates?">
    A <Doc href="/docs/design/templates/overview">Template</Doc> holds a notification's content and behavior in Courier, not in your code. You design it once, then send it by ID and pass only the data.

    This first send uses inline `content` so it needs nothing but a key. Most production sends reference a Template, so wording and design change without a deploy.
  </Accordion>

  <Accordion title="When do I move off Test?">
    Test and Production are isolated, with separate keys, Templates, integrations, and logs. Build against Test, then <Doc href="/docs/workspaces/overview">swap to a Production key</Doc> when you go live. Test sends are billable.
  </Accordion>
</AccordionGroup>
