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
subjectand at least one ofhtmlortext. - Template email references exactly one published template by
templateSlugortemplateId, with optionaltemplateData. Omit inlinesubject,html, andtext.
Use the Promise SDK
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
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> --yesUse --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:00ZThe 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.