> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apollodeploy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schedule an email

> Request future delivery, inspect scheduled state, and cancel before dispatch

Set `scheduledAt` on a send request when the message must not enter provider delivery immediately.
The dashboard lists the accepted message as `scheduled` until dispatch.

## Explicit schedule

Send an ISO 8601 timestamp with a timezone offset or `Z`.
The timestamp must be at least 30 seconds and no more than 30 days in the future.

```json theme={"dark"}
{
  "from": "billing@mail.company.com",
  "to": "alex@example.com",
  "subject": "Your renewal reminder",
  "text": "Your subscription renews tomorrow.",
  "scheduledAt": "2026-09-10T08:00:00Z",
  "idempotencyKey": "renewal-reminder-sub-1042-2026-09"
}
```

<Tip>
  Generate the timestamp in UTC and display the chosen local time to the person scheduling it.
  This avoids daylight-saving and server-timezone mistakes.
</Tip>

## After acceptance

Signal returns the message ID, `scheduled` state, and scheduled time.
The message appears in **Emails** immediately even though it has not been sent.
At the requested time Signal moves it into the normal delivery pipeline.

## Cancel

Cancel while the message still has `scheduled` status.
Once dispatch starts, cancellation is no longer a reliable control.
A successful cancellation changes the state to `cancelled`.

## Safe rescheduling

Treat rescheduling as cancel-then-create:

1. Cancel the original scheduled message.
2. Confirm it is cancelled.
3. Create a new send with the new timestamp and a new logical idempotency key.
4. Store the replacement Signal ID.

## Common validation failures

* Timestamp is less than 30 seconds ahead.
* Timestamp is more than 30 days ahead.
* Timestamp has no unambiguous timezone.
* The From domain is no longer verified.
* The same idempotency key already belongs to the original logical send.

<Note>
  The API also documents optimal-time scheduling fields for eligible non-transactional categories.
  Use the API reference for that contract; the dashboard workflow described here uses an explicit time.
</Note>

## SDK example

Start with the [basic send example](/signal/send-with/sdk-examples), then add the scheduling field
for your SDK.

Add the scheduling value to the normal `SendEmailRequest`. `scheduledAt` must be a future RFC 3339
timestamp produced by your application.

<CodeGroup dropdown>
  ```typescript TypeScript theme={"dark"}
  scheduledAt: scheduledAt,
  ```

  ```python Python theme={"dark"}
  scheduled_at=scheduled_at,
  ```

  ```go Go theme={"dark"}
  ScheduledAt: signal.String(scheduledAt),
  ```

  ```ruby Ruby theme={"dark"}
  scheduled_at: scheduled_at
  ```

  ```ruby Rails theme={"dark"}
  scheduled_at: scheduled_at
  ```

  ```php PHP / Laravel theme={"dark"}
  $request->scheduledAt = $scheduledAt;
  ```

  ```java Java theme={"dark"}
  request.scheduledAt = scheduledAt;
  ```

  ```kotlin Kotlin theme={"dark"}
  scheduledAt = scheduledAt,
  ```

  ```csharp .NET theme={"dark"}
  ScheduledAt = scheduledAt,
  ```

  ```rust Rust theme={"dark"}
  scheduled_at: Some(scheduled_at.to_string()),
  ```

  ```elixir Elixir theme={"dark"}
  scheduled_at: scheduled_at
  ```

  ```swift Swift theme={"dark"}
  scheduledAt: scheduledAt
  ```

  ```zig Zig theme={"dark"}
  .scheduledAt = scheduled_at,
  ```
</CodeGroup>
