Compose and send

Send new messages, reply or forward in context, and save drafts while tracking approvals and delivery waits.

Start with an existing mailbox and a client allowed to send from it. A mailbox-scoped key needs send; saving or editing drafts also needs update. The mailbox chooses the sending address through its default From identity. Agents and keys explains how to limit access and require approval.

Send a new message

Supply recipients, a subject, and at least one of text or html. Choose a stable idempotency key for this intentional send and keep it if you retry the same request.

send-message.ts
import { createClient } from "samva";

const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const id = "mbx_...";

const receipt = await samva.mailboxes.sendMessage({
  id,
  to: ["customer@example.com"],
  subject: "Your support request",
  text: "We are looking into your request.",
  "idempotency-key": "support-request-123-first-response",
});
console.log(receipt.status, receipt.actionId, receipt.waitReason);

Optional cc, bcc, and replyTo address lists let you include other recipients or choose where answers return. See send a message for the request.

Reply or forward in context

Use a message ID from reading the conversation. reply addresses the message's Reply-To, or its sender when Reply-To is absent, and sets the subject and threading headers. replyAll also includes the other recipients and excludes the mailbox's own addresses.

reply-and-forward.ts
const messageId = "msg_...";

await samva.mailboxes.reply({
  id,
  messageId,
  text: "Thanks, we have the details we need.",
  "idempotency-key": "support-request-123-reply",
});

await samva.mailboxes.forward({
  id,
  messageId,
  to: ["specialist@example.com"],
  text: "Could you help with this request?",
  "idempotency-key": "support-request-123-forward",
});

Forwarding quotes the original message and carries its attachments; any attachments you supply are added to them. See reply, reply all, and forward for the precise inputs.

Save a draft before sending

Drafts belong to their author. Listing drafts returns summaries without bodies; getDraft reads the full draft. A new-message draft can be incomplete while you work.

save-and-send-draft.ts
const draft = await samva.mailboxes.createDraft({
  id,
  to: ["customer@example.com"],
  subject: "Your support request",
  text: "We are checking the details.",
});

await samva.mailboxes.updateDraft({
  id,
  draftId: draft.id,
  text: "The details are confirmed. Your request is complete.",
});

const draftReceipt = await samva.mailboxes.sendDraft({
  id,
  draftId: draft.id,
  "idempotency-key": "support-request-123-final-response",
});
console.log(draftReceipt.status, draftReceipt.actionId);

To save a reply draft, pass inReplyToMessageId to createDraft; it must name a message in that mailbox. The reply stays in its conversation. A draft without a reply target sends as a new conversation. Sending requires recipients, a subject, and a body, including for reply drafts.

Each field in updateDraft replaces that field; omitted fields stay. null clears an optional field, and an empty to list removes recipients. See create a draft and update a draft.

A saved draft is editable and deletable. An approvalPending draft is held unchanged for the reviewer: editing returns 409, and it cannot be deleted or sent again. An authorized reviewer can read the held draft. Denying the send returns it to its author as an editable draft; changing and sending it requires a fresh approval. A sent draft cannot be edited or resent. Follow Approve sends for the review workflow.

Attach a file

Send and draft requests accept attachments. Each attachment has a filename, contentType, size, and exactly one source: base64 content or the mediaId of a ready upload belonging to your organization. A mailbox-only key can complete or attach only uploads created with that same key. An upload created with a setup key or another agent's key returns 404; create a new upload with the sending key instead. Declared size and content type must match the uploaded file. A message's attachment ID or download URL is not an outbound media ID.

attach-file.ts
await samva.mailboxes.sendMessage({
  id,
  to: ["customer@example.com"],
  subject: "Your receipt",
  text: "Your receipt is attached.",
  attachments: [
    { filename: "receipt.txt", content: "SGVsbG8=", contentType: "text/plain", size: 5 },
  ],
  "idempotency-key": "support-request-123-receipt",
});

For larger files, upload and complete organization-owned media before attaching its ID. The email attachments guide owns size limits and upload instructions.

Send receipts

New messages, replies, forwards, and draft sends return the same receipt synchronously. Read status before deciding what happens next:

StatusMeaningNext step
sentSamva accepted the message for sending. Delivery can still be waiting.Inspect waitReason and follow delivery events.
pending_approvalThe principal's send mode or a mailbox policy holds the send for a person's decision.Follow actionId through the approval workflow.

sent does not confirm arrival in the recipient's inbox. waitReason is null when the receipt has no reported delivery wait; a non-null value explains a wait, such as rate, daily-quota, domain-health, or content-review. reviewExpectedAt is a nullable estimate for a content review decision, present only while waitReason is content-review, and is not a promised delivery time. See email delivery for delivery outcomes.

The receipt also carries actionId, threadId, createdAt, and nullable messageId. messageId is null while approval is pending; use it to inspect the accepted message when it is present. skippedRecipients lists excluded addresses with their reason; an empty list means no exclusions are reported. Check it even when status is sent, because acceptance does not mean every requested address is included. The send response reference defines the complete shape.

Retry without creating another send

Mailbox sends have a 24-hour idempotency window. Within that window, repeat the same operation with the same body and Idempotency-Key after a lost response. After retention expires, the same request can create another send; check the action and message history before retrying an old request. Reusing a key with a different body returns 409. An in-progress conflict asks you to repeat the same request later; a held send replays its current approval receipt. Do not submit another send to resolve an approval hold or delivery wait.

A recorded failed action returns a conflict instructing a new idempotency key. Treat that as a new intentional submission: inspect the action and message history before proceeding if the outcome is unclear. Follow Approve sends to approve or deny held work and Mailbox events to observe the result.

Related documentation

On this page