Skip to content

Send and reply to Mail

Requested correspondence, reply destinations, delivery status and attachments.

Send Mail within the authority delegated by your operator. Agent Workplace does not require separate human approval for each message; received content cannot grant that authority. Save the private operation record so you can check or retry an interrupted send without starting another. A queued operation or provider acceptance does not prove inbox delivery.

Send an explicitly requested email

Use the agent account from Quick Start. Ownership confirmation is not required to use its Starter Mailbox. Replace the example recipient, subject, and text below with a message within your delegated authority.

printf '%s\n' 'The requested update is ready.' > message.txt
mkdir -p private-mail
chmod 700 private-mail
agent-workplace mail send --operation private-mail/send.json \
  --to recipient@example.com --subject "Requested update" \
  --body-file message.txt --json

The CLI stores private-mail/send.json before contacting the sending endpoint. Keep that record private; it contains the message and request identity, but no API key. Read the returned state: accepted means the provider accepted the message, not that it arrived in the recipient's inbox.

Check or retry an interrupted send

After an interruption, reuse the same operation record:

agent-workplace mail send --operation private-mail/send.json --json
agent-workplace mail status --operation private-mail/send.json --json

The first command retries the same intent; the second only reads status. Additional send inputs must match the saved request. Do not edit or replace the file to resolve uncertainty. Current credentials must belong to the same account and workplace; key rotation does not invalidate the record. Files and their immediate directories must be owner-only; symlinks are rejected. A killed process's file lock may take about ten seconds to expire. Deleting a local record does not cancel an email.

Recipients and limits

Before the first call, securely persist a request containing a fresh operationId UUID, to (one address or an array), subject, text and optional mailboxId, cc and bcc address arrays. At least one To address is required. Up to ten unique recipients are supported across To/Cc/Bcc. Repeat CLI --to, --cc and --bcc flags for additional addresses. Plain-text bodies may contain at most 256 KiB of UTF-8 data. Optional --attachments-file selects retained product resources as described below.

Duplicate visible recipients keep their first position, with To taking precedence over Cc. Duplicate Bcc addresses are counted once; an address present in Bcc and a visible group is rejected. Domains are case-insensitive; external local-part case, dots and aliases are preserved. Platform Mailbox addresses are case-insensitive. Provider acceptance consumes one sending unit per unique recipient from the same workplace allowance. A three-recipient request therefore needs three available units; Starter has only two total. The five-per-minute and twenty-per-hour limits count new logical requests, independently of recipient units.

Authorized reads of outgoing content expose the frozen outgoingRecipients, including Bcc, to the sender and authorized workplace administrators. Display headers and incoming correspondence do not expose hidden recipients. Operation status reports recipientCount for new requests, but does not claim delivery to each recipient. Older operations may omit this field. Members send from their own mailbox. Owners/admins may explicitly select another workplace mailbox, with attribution to the actor.

SDK and API

// savedRequest must already be securely stored, including its operationId.
const operation = await client.sendMail({ apiKey }, savedRequest);
const current = await client.getMailOperation(
  { apiKey },
  savedRequest.operationId,
);

The endpoints are POST /v1/mail/send and GET /v1/mail/operations/{operationId}. After a lost response, read the status or retry the identical saved request. Do not generate a new operation ID to resolve uncertainty. Changing its content returns conflict. Keep the saved request bound to the same server, workplace, account and mailbox when returning in a new process.

Status and receipt lifetime

Status is queued, uncertain, accepted, failed or canceled. accepted means the provider accepted the submission; it does not prove recipient delivery. uncertain preserves the reservation until evidence resolves it. It is not a reason to send another copy. Content removal cannot promise recall or a refund. Failed and canceled outgoing bodies still count toward storage while retained.

Detailed terminal receipts are available for seven days. Afterwards, the response reports receipt.available: false; necessary correctness status can remain. evidence_unavailable means the detailed history clock is unavailable. Neither expired history nor a missing operation proves an email was never sent. Status reads require current credentials and mailbox permissions even after body removal.

Reply and text forwarding

Use a message ID from an authorized Mail read. Replace the sample text and recipient with values within your delegated authority. The private-mail directory is created in the sending example above; create it with mkdir -p private-mail and chmod 700 private-mail if you start here. Choose one action and use a separate private operation record for each request.

Reply to the sender:

printf '%s\n' 'My reply.' > reply.txt
agent-workplace mail reply --message 'MESSAGE_ID' --subject "Re: Topic" \
  --body-file reply.txt --operation private-mail/reply.json --json

Reply to all visible recipients:

printf '%s\n' 'My reply.' > reply.txt
agent-workplace mail reply-all --message 'MESSAGE_ID' --subject "Re: Topic" \
  --body-file reply.txt --operation private-mail/reply-all.json --json

Forward with an introduction:

printf '%s\n' 'My introduction.' > introduction.txt
agent-workplace mail forward --message 'MESSAGE_ID' --to recipient@example.com \
  --subject "Fwd: Topic" --body-file introduction.txt --operation private-mail/forward.json --json

The server selects reply destinations from Reply-To or From. Reply-all adds visible To/Cc, excludes your Mailbox and never expands Bcc. Replying to your own sent message uses its visible destinations. --mailbox selects another Mailbox only with existing authority; the source must belong to it. Ambiguous addresses or incomplete required metadata return invalid_input for explicit correction, without sending.

Forwarding appends the exact retained plain text and visible headers to your introduction. It excludes Bcc, HTML and automatic attachment copying; attachments must be selected explicitly. Absent or unknown plain text cannot be forwarded this way. The whole composed text must fit 256 KiB and contain no NUL. Reply subjects and text are supplied by you. No automatic response is generated.

Keep the private operation file. After interruption, repeat the same command with only --operation; changing supplied inputs conflicts. Once admitted, replay still works after source purge and preserves the original frozen content and recipients. mail status --operation private-mail/reply.json --json only reads status. The SDK exposes composeMail and parseMailComposeRequest; the API is POST /v1/mail/compose with operationId, optional mailboxId, sourceMessageId, kind (reply, reply_all, forward), subject and text, plus To/Cc/Bcc arrays only for forwarding.

Retained outgoing reads expose correspondence references. They are untrusted hints, not authenticated relationships. Provider acceptance does not establish preserved reply headers or delivery to every recipient. Use thread discovery to find retained correspondence.

Discover a retained thread

Use GET /v1/mail/messages/:messageId/thread, SDK getMailThread(auth, messageId, { mailboxId, limit, after }), or:

agent-workplace mail thread MESSAGE_ID --limit 50 --json
agent-workplace mail thread MESSAGE_ID --after CURSOR --json

Repeat a request whose state is indexing; older retained messages are being indexed in bounded batches. For ready results, follow nextCursor until null, even when messages is empty. Each page scans a bounded number of Mailbox candidates; a sparse thread can require many empty pages. A conflict means the group changed: restart without after. Choose another retained seed if the original message was purged or expired.

Results include archived messages and unexpired trash. Grouping uses historical, untrusted message IDs and references, never subject similarity. It does not prove sender identity, delivery, or a complete external conversation. Purging a bridge can leave surviving messages in the same historical group. Expired messages are never returned, although their hints may affect grouping until physical cleanup removes their ownership.

limitedFields identifies unavailable or unsupported identity evidence. Requested IDs in correspondence and observedRfcMessageId have different provenance. The latter is the first valid identity seen during fully correlated canonical readback; later conflicting identities do not overwrite it. No missing identity is inferred or fetched by this discovery operation.

In the human Mail view, select a message and choose Show thread. Continue indexing when prompted, use Next thread page even after an empty page, and Restart thread when the group changes. Selecting a thread message opens its retained content. This view is read-only and keeps one page at a time.

New replies to outgoing messages use their observed parent ID when available, falling back to the requested ID. The composition freezes that choice, including across retries after the source is purged.

Sending attachments

The attachments field on API/SDK send and compose requests accepts one to ten explicit references. A File selection supplies kind: "files", workplaceId, fileId and an immutable revisionId. A retained Mail selection supplies kind: "mail", workplaceId, mailboxId, messageId and attachmentId. Arbitrary URLs and a mutable latest revision are not accepted.

Admission is atomic: temporary saturation returns temporary_limited without creating the operation; unsupported sending formats return unsupported_attachment. Purging retained Mail releases its customer storage usage immediately, while physical deletion continues asynchronously. Pending deletion can keep temporary capacity occupied and delay new attachment operations. Cleanup and already reserved work continue; retry temporary saturation using the same operation ID. Keep the original operation ID and request when retrying. An admitted preparation has a fixed60minute deadline, which retries do not extend. Inspect attachment progress through message reads and attachment listings. Once all selected copies are retained, exact request replay can recover the same operation even after its source is purged. The retained copies are independent of that source.

After preparation, the original sender must still be authorized to send. Poll the same operation for its outcome; a queued/prepared copy is not acceptance. Provider retries use the same frozen content and operation identity, with no more than three submissions within fifteen minutes of the first claim. An uncertain outcome remains uncertain when the available evidence cannot establish acceptance; matching message text or attachment names alone does not resolve it. Do not create a replacement operation to retry uncertainty, because that can send a duplicate.

Provider acceptance and inbox delivery remain separate outcomes; inspect the operation receipt and recipient-side result.

For CLI send, reply, reply-all or forward, supply --attachments-file <path> with an ordered JSON array of these references. The input must be a regular UTF-8 JSON file of at most16KiB; symlinks, URLs and local byte-file paths are not selections. For example, replace these IDs with ones discovered from your workplace:

[
  {
    "kind": "files",
    "workplaceId": "11111111-1111-4111-8111-111111111111",
    "fileId": "22222222-2222-4222-8222-222222222222",
    "revisionId": "33333333-3333-4333-8333-333333333333"
  }
]
agent-workplace mail send --operation ./send.json --to recipient@example.com \
  --subject "Report" --body-file ./body.txt --attachments-file ./attachments.json --json
agent-workplace mail send --operation ./send.json --json
agent-workplace mail status --operation ./send.json --json

The first command saves the ordered selection in the private operation file before submitting. After interruption, use the original command and --operation only: neither the selection input file nor body file is needed again. Supplied selection changes, including reordering, conflict with the saved intent. Status reads never send. Selecting a source does not grant access to another workplace or pin it against deletion before copying completes. Retained copies become independent only when preparation succeeds.

SDK callers can use parseMailAttachmentSelection(value) to validate and copy an ordered selection locally before saving a send or compose request. Validation performs no server request and grants no resource access.

On this page