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
subjectandhtml;textis an optional fallback. - 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",
});
if (scheduled.error) throw new Error("Could not schedule email");
await samva.scheduledMessages.cancel({ id: scheduled.data.id });Use the Effect SDK
import { Effect } from "effect";
import { FetchHttpClient } from "effect/unstable/http";
import { createClient } from "samva/effect";
const program = Effect.gen(function* () {
const samva = yield* createClient({ apiKey: process.env.SAMVA_API_KEY! });
const scheduled = yield* samva.scheduledMessages.create({
payload: {
send: {
to: [{ email: "ada@example.com" }],
channel: "email",
email: { subject: "Reminder", html: "<p>See you tomorrow.</p>" },
},
scheduledFor: "2026-08-01T09:00:00Z",
},
});
return yield* samva.scheduledMessages.get(scheduled.id);
}).pipe(Effect.provide(FetchHttpClient.layer));
await Effect.runPromise(program);Use the REST API
curl -X POST https://api.samva.app/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 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 get <scheduled-message-id>
samva scheduled cancel <scheduled-message-id>Use --template-slug <slug> --template-data '<json>' instead of the inline content flags to send a
published template. samva scheduled create --dry-run validates and prints the request without
calling the API.
Use MCP
Ask the agent to call samva_scheduled_messages_schedule_email with to, one valid content source,
and scheduledFor. It can inspect the result with samva_scheduled_messages_get or
samva_scheduled_messages_list, and cancel it with samva_scheduled_messages_cancel.
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.