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

# Delay and schedule a send

> Delay a send by a duration, until a timestamp, or into a delivery window in the user's timezone.

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

The `delay` object holds a message for later. Choose a duration, an exact timestamp, or a recurring window in the recipient's timezone.

## Prerequisites

* <Doc href="/docs/send/overview">A working send</Doc>

## Delay a send

<Steps>
  <Step title="Delay by a duration">
    Set `delay.duration` in milliseconds to hold the message that long from now. Courier sends after exactly that interval (3600000 ms is one hour).

    <CodeGroup>
      ```javascript Node.js highlight={5} theme={null}
      const { requestId } = await client.send.message({
        message: {
          to: { user_id: 'user_123' },
          template: 'nt_01kx4h2jdafq8bk9aftxak4b40',
          delay: { duration: 3600000 },
        },
      });
      ```

      ```python Python highlight={5} theme={null}
      response = client.send.message(
          message={
              "to": {"user_id": "user_123"},
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "delay": {"duration": 3600000},
          },
      )
      ```

      ```bash cURL highlight={8} theme={null}
      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_01kx4h2jdafq8bk9aftxak4b40",
            "delay": { "duration": 3600000 }
          }
        }'
      ```

      ```ruby Ruby highlight={5} theme={null}
      response = courier.send_.message(
        message: {
          to: { user_id: "user_123" },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          delay: { duration: 3600000 }
        }
      )
      ```

      ```go Go highlight={10} theme={null}
      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfUserRecipient: &shared.UserRecipientParam{
      				UserID: courier.String("user_123"),
      			},
      		},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      		Delay: courier.SendMessageParamsMessageDelay{
      			Duration: courier.Int(3600000),
      		},
      	},
      })
      ```

      ```java Java highlight={5} theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(JsonValue.from(java.util.Map.of("user_id", "user_123")))
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .delay(JsonValue.from(java.util.Map.of("duration", 3600000)))
              .build())
          .build();
      client.send().message(params);
      ```

      ```php PHP highlight={5} theme={null}
      $response = $client->send->message(
        message: [
          'to' => ['userID' => 'user_123'],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'delay' => ['duration' => 3600000],
        ],
      );
      ```

      ```csharp C# highlight={7} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Delay = new() { Duration = 3600000 },
          },
      };

      await client.Send.Message(parameters);
      ```

      ```bash CLI highlight={3} theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message '{"to":{"user_id":"user_123"},"template":"nt_01kx4h2jdafq8bk9aftxak4b40","delay":{"duration":3600000}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 in one hour.
      ```
    </CodeGroup>
  </Step>

  <Step title="Schedule for a specific time">
    Set `delay.until` to an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp to send at an exact moment. A date alone (`2026-01-01`) is midnight UTC. Include a time and offset for precision (`2026-01-01T09:00:00-05:00`).

    <CodeGroup>
      ```javascript Node.js highlight={5} theme={null}
      const { requestId } = await client.send.message({
        message: {
          to: { user_id: 'user_123' },
          template: 'nt_01kx4h2jdafq8bk9aftxak4b40',
          delay: { until: '2026-01-01T09:00:00Z' },
        },
      });
      ```

      ```python Python highlight={5} theme={null}
      response = client.send.message(
          message={
              "to": {"user_id": "user_123"},
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "delay": {"until": "2026-01-01T09:00:00Z"},
          },
      )
      ```

      ```bash cURL highlight={8} theme={null}
      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_01kx4h2jdafq8bk9aftxak4b40",
            "delay": { "until": "2026-01-01T09:00:00Z" }
          }
        }'
      ```

      ```ruby Ruby highlight={5} theme={null}
      response = courier.send_.message(
        message: {
          to: { user_id: "user_123" },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          delay: { until: "2026-01-01T09:00:00Z" }
        }
      )
      ```

      ```go Go highlight={10} theme={null}
      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{
      			OfUserRecipient: &shared.UserRecipientParam{
      				UserID: courier.String("user_123"),
      			},
      		},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      		Delay: courier.SendMessageParamsMessageDelay{
      			Until: courier.String("2026-01-01T09:00:00Z"),
      		},
      	},
      })
      ```

      ```java Java highlight={5} theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              .to(JsonValue.from(java.util.Map.of("user_id", "user_123")))
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .delay(JsonValue.from(java.util.Map.of("until", "2026-01-01T09:00:00Z")))
              .build())
          .build();
      client.send().message(params);
      ```

      ```php PHP highlight={5} theme={null}
      $response = $client->send->message(
        message: [
          'to' => ['userID' => 'user_123'],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'delay' => ['until' => '2026-01-01T09:00:00Z'],
        ],
      );
      ```

      ```csharp C# highlight={7} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              To = new UserRecipient { UserID = "user_123" },
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Delay = new() { Until = "2026-01-01T09:00:00Z" },
          },
      };

      await client.Send.Message(parameters);
      ```

      ```bash CLI highlight={3} theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message '{"to":{"user_id":"user_123"},"template":"nt_01kx4h2jdafq8bk9aftxak4b40","delay":{"until":"2026-01-01T09:00:00Z"}}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 on January 1 at 9am UTC.
      ```
    </CodeGroup>
  </Step>

  <Step title="Send within a delivery window">
    Set `delay.until` to a recurring window (opening-hours syntax) and Courier holds the message until the next time inside it. This is how you deliver during business hours or at a good local time.

    It is also how you enforce **quiet hours**, because a window is the inverse of a do-not-disturb period. A 9am to 9pm window in the recipient's timezone says "never between 9pm and 9am". A message arriving outside it waits rather than waking anyone.

    <CodeGroup>
      ```javascript Node.js highlight={9} theme={null}
      const { requestId } = await courier.send.message({
        message: {
          to: {
            user_id: "user_123",
            timezone: "America/New_York",
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          delay: {
            until: "Mo-Fr 09:00-17:00",
          },
        },
      });
      ```

      ```python Python highlight={9} theme={null}
      response = client.send.message(
          message={
              "to": {
                  "user_id": "user_123",
                  "timezone": "America/New_York",
              },
              "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
              "delay": {
                  "until": "Mo-Fr 09:00-17:00",
              },
          },
      )
      ```

      ```bash cURL highlight={12} wrap theme={null}
      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",
              "timezone": "America/New_York"
            },
            "template": "nt_01kx4h2jdafq8bk9aftxak4b40",
            "delay": {
              "until": "Mo-Fr 09:00-17:00"
            }
          }
        }'
      ```

      ```ruby Ruby highlight={9} theme={null}
      response = courier.send_.message(
        message: {
          to: {
            user_id: "user_123",
            timezone: "America/New_York"
          },
          template: "nt_01kx4h2jdafq8bk9aftxak4b40",
          delay: {
            until: "Mo-Fr 09:00-17:00"
          }
        }
      )
      ```

      ```go Go highlight={12} theme={null}
      // This provider addresses the recipient with fields outside the typed
      // UserRecipient model, so pass the recipient as raw JSON.
      to := param.Override[shared.UserRecipientParam](json.RawMessage(`{
        "user_id": "user_123",
        "timezone": "America/New_York"
      }`))

      response, err := client.Send.Message(context.TODO(), courier.SendMessageParams{
      	Message: courier.SendMessageParamsMessage{
      		To: courier.SendMessageParamsMessageToUnion{OfUserRecipient: &to},
      		Template: courier.String("nt_01kx4h2jdafq8bk9aftxak4b40"),
      		// delay: {"until": "Mo-Fr 09:00-17:00"}
      	},
      })
      ```

      ```java Java highlight={10} theme={null}
      SendMessageParams params = SendMessageParams.builder()
          .message(SendMessageParams.Message.builder()
              // This provider addresses the recipient with fields outside the
              // typed UserRecipient model, so pass them as additional properties.
              .to(UserRecipient.builder()
                  .userId("user_123")
                  .putAdditionalProperty("timezone", JsonValue.from("America/New_York"))
                  .build())
              .template("nt_01kx4h2jdafq8bk9aftxak4b40")
              .delay(JsonValue.from(java.util.Map.of("until", "Mo-Fr 09:00-17:00")))
              .build())
          .build();
      SendMessageResponse response = client.send().message(params);
      ```

      ```php PHP highlight={9} theme={null}
      $response = $client->send->message(
        message: [
          'to' => [
            'user_id' => 'user_123',
            'timezone' => 'America/New_York',
          ],
          'template' => 'nt_01kx4h2jdafq8bk9aftxak4b40',
          'delay' => [
            'until' => 'Mo-Fr 09:00-17:00',
          ],
        ],
      );
      ```

      ```csharp C# highlight={18} theme={null}
      SendMessageParams parameters = new()
      {
          Message = new()
          {
              // This provider addresses the recipient with fields outside the
              // typed UserRecipient model, so build it from raw JSON.
              To = UserRecipient.FromRawUnchecked(
                  JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
                      """
                      {
                        "user_id": "user_123",
                        "timezone": "America/New_York"
                      }
                      """
                  )
              ),
              Template = "nt_01kx4h2jdafq8bk9aftxak4b40",
              Delay = new() { Until = "Mo-Fr 09:00-17:00" },
          },
      };

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

      ```bash CLI highlight={5} wrap theme={null}
      courier send message \
        --api-key "$COURIER_API_KEY" \
        --message.to '{"user_id": "user_123", "timezone": "America/New_York"}' \
        --message.template nt_01kx4h2jdafq8bk9aftxak4b40 \
        --message.delay '{"until": "Mo-Fr 09:00-17:00"}'
      ```

      ```text MCP theme={null}
      With Courier MCP, send my nt_01kx4h2jdafq8bk9aftxak4b40 template to user_123 during business hours in their timezone.
      ```
    </CodeGroup>

    Courier resolves the timezone in this order: `delay.timezone` (highest), then `to.timezone` or the profile's `zoneinfo`, then the profile's `timezone`, then UTC. Send an array of recipients with different timezones and each one's window is computed independently, so a single call delivers at 9 AM local for everyone. Windows accept patterns like `Mo-Fr 09:00-17:00`, `Sa-Su 00:00-23:59`, and split schedules (`Mo-Fr 09:00-12:00,14:00-18:00`).

    <Note>
      **`delay.timezone` only applies to a window.**<br />
      For an ISO `until` timestamp, the timezone is already in the timestamp. `delay.timezone` matters only when `until` is an opening-hours expression.
    </Note>
  </Step>

  <Step title="Check the logs">
    Send with a `delay`, then open the message in <AppLink href="https://app.courier.com/logs">Logs</AppLink>. A `Request Delayed` event shows the computed delivery time.
  </Step>
</Steps>
