Email

Schedule an email

Schedule one email for an absolute future instant, then inspect or cancel it before dispatch begins.

Use a scheduled message when the recipients and content are already known and the email should send later. This is a separate resource from an immediate send. messages.send has no scheduling field.

Choose the time and content

scheduledFor must be a future absolute ISO-8601 instant with Z or a numeric offset, for example 2026-08-01T09:00:00Z or 2026-08-01T14:30:00+05:30. A local date-time without an offset is rejected.

timezone is optional validated IANA metadata for display and audit. It never converts or reinterprets scheduledFor.

Choose exactly one content source:

  • Inline email requires subject and at least one of html or text.
  • Template email references exactly one published template by templateSlug or templateId, with optional templateData. Omit inline subject, html, and text.

Use the Promise SDK

schedule-email.ts
import { createClient } from "samva";

const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const scheduled = await samva.scheduledMessages.create({
  send: {
    to: [{ email: "ada@example.com" }],
    channel: "email",
    email: {
      subject: "Your appointment is tomorrow",
      html: "<p>We will see you at 9:00.</p>",
      text: "We will see you at 9:00.",
    },
  },
  scheduledFor: "2026-08-01T09:00:00Z",
  timezone: "America/New_York",
  idempotencyKey: "appointment-ada-2026-08-01",
});

await samva.scheduledMessages.cancel({ id: scheduled.id });

Use the Effect SDK

schedule-email-effect.ts
import { Effect } from "effect";
import * as Client from "samva/effect/client";
import * as ScheduledMessages from "samva/effect/scheduled-messages";

const program = Effect.gen(function* () {
  const scheduled = yield* ScheduledMessages.create({
    send: {
      to: [{ email: "ada@example.com" }],
      channel: "email",
      email: { subject: "Reminder", html: "<p>See you tomorrow.</p>" },
    },
    scheduledFor: new Date("2026-08-01T09:00:00Z"),
    timezone: "America/New_York",
    idempotencyKey: "reminder-ada-2026-08-01",
  });

  return yield* ScheduledMessages.get({ id: scheduled.id });
}).pipe(Effect.provide(Client.layerFetch({ apiKey: process.env.SAMVA_API_KEY! })));

await Effect.runPromise(program);

Use the REST API

curl -X POST https://api.samva.dev/v1/messages/scheduled \
  -H "X-API-Key: $SAMVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "send": {
      "to": [{ "email": "ada@example.com" }],
      "channel": "email",
      "email": { "subject": "Reminder", "html": "<p>See you tomorrow.</p>" }
    },
    "scheduledFor": "2026-08-01T09:00:00Z",
    "timezone": "America/New_York",
    "idempotencyKey": "reminder-ada-2026-08-01"
  }'

Use GET /v1/messages/scheduled/{id} to inspect the schedule and POST /v1/messages/scheduled/{id}/cancel to cancel it. See the generated scheduled messages reference for schemas and response fields.

Use the CLI

samva scheduled-messages create --to ada@example.com \
  --subject "Reminder" --html "<p>See you tomorrow.</p>" \
  --at 2026-08-01T09:00:00Z --timezone America/New_York \
  --idempotency-key reminder-ada-2026-08-01
samva scheduled-messages get <scheduled-message-id>
samva scheduled-messages cancel <scheduled-message-id> --yes

Use --template-slug <slug> --template-data '<json>' instead of the inline content flags to send a published template. samva scheduled-messages create --dry-run validates and prints the request without calling the API.

Use MCP

Ask the agent to call scheduled_messages_schedule_email with to, one valid content source, and scheduledFor. It can inspect the result with scheduled_messages_get or scheduled_messages_list, cancel it with scheduled_messages_cancel, or explicitly resume a review-paused email with scheduled_messages_resume.

Resume after email review

If review is no longer clear when dispatch begins, the scheduled message pauses before any provider call. It does not replay automatically after approval. Resume it explicitly and choose what an overdue schedule should do:

samva scheduled-messages resume <scheduled-message-id> --send-now
samva scheduled-messages resume <scheduled-message-id> --at 2026-10-01T09:00:00Z

The SDK and REST endpoint accept { choice: "send_now" } or { choice: "new_time", scheduledFor } at POST /v1/messages/scheduled/{id}/resume. A new time must be a future absolute instant.

Cancellation, errors, and usage

A scheduled email can be cancelled only while its status is pending. Once dispatch begins, cancellation returns a conflict. At dispatch, it follows the normal email send path and usage semantics. Validation failures do not create a schedule; provider and delivery outcomes appear on the resulting message after dispatch. Handle authentication, validation, not-found, conflict, and usage-limit errors as described in the error reference.

On this page