Outbound
Send and reply to email from a specific mailbox using the mailbox-scoped outbound API.
The outbound API is the mailbox-scoped send surface used by agentic mailbox clients. It handles thread projection, canary token detection, deduplication, and autonomy level gating per mailbox.
Outbound vs. agent send
Molted provides two sending surfaces:
| Endpoint | Best for |
|---|---|
POST /v1/agent/request-send | Tenant-level sends from any agent. Full policy trace and scheduled send support. |
POST /v1/agent/outbound/send | Mailbox-scoped sends. Thread projection, canary detection, per-mailbox autonomy enforcement. |
Use /v1/agent/outbound/send when your agent acts on behalf of a specific mailbox and you want thread continuity and per-mailbox autonomy controls. Use /v1/agent/request-send for general-purpose tenant-level sends.
See Sending Email for full documentation on the agent send endpoint.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /v1/agent/outbound/send | Send an email from a mailbox |
| POST | /v1/agent/outbound/reply | Reply to an existing thread |
| POST | /v1/agent/outbound/schedule-followup | Schedule a followup if no reply is received |
All endpoints require Bearer authentication and the automation API key scope.
Send
Send a new email from a mailbox. Creates a new thread if no matching thread exists.
POST https://api.molted.email/v1/agent/outbound/sendcurl -X POST "https://api.molted.email/v1/agent/outbound/send?tenantId=TENANT_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipientEmail": "alice@example.com",
"templateId": "welcome",
"dedupeKey": "welcome-alice-1",
"payload": { "name": "Alice" },
"mailboxId": "mbx_abc123"
}'Request body
| Field | Type | Required | Description |
|---|---|---|---|
recipientEmail | string | Yes | Recipient email address |
templateId | string | Yes | Template ID to use for the send |
dedupeKey | string | Yes | Unique key to prevent duplicate sends. Same key on a second call returns the original result. |
payload | object | Yes | Template variables |
mailboxId | string | No | Send from a specific mailbox. Enables per-mailbox autonomy level enforcement. |
threadId | string | No | Associate with an existing thread |
subject | string | No | Override the email subject |
sendReason | string | No | Reason for sending, recorded in the decision trace |
idempotencyKey | string | No | Alternative deduplication key |
Response
{
"status": "sent",
"requestId": "req_abc123",
"threadId": "thr_abc123"
}Response status values:
| Status | Meaning |
|---|---|
sent | Queued for delivery |
blocked | Blocked by the policy engine |
pending_approval | Held by the mailbox's autonomy level |
duplicate | Same dedupeKey was already used |
A blocked or pending_approval status is not an error - it means the policy engine or autonomy level is working correctly. Always check the status field.
CLI
# The send CLI uses /v1/agent/request-send by default
# To use the outbound endpoint, see: molted threads reply
molted send --to alice@example.com --template welcome \
--dedupe-key "welcome-alice-1" --payload '{"name": "Alice"}' \
--mailbox mbx_abc123Reply
Reply to an existing thread. The reply is sent from the mailbox that owns the thread.
POST https://api.molted.email/v1/agent/outbound/replycurl -X POST "https://api.molted.email/v1/agent/outbound/reply?tenantId=TENANT_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"threadId": "thr_abc123",
"templateId": "_default",
"dedupeKey": "reply-alice-1",
"payload": { "body": "Thanks for reaching out - we will get back to you shortly." }
}'Request body
| Field | Type | Required | Description |
|---|---|---|---|
threadId | string | Yes | Thread to reply to |
templateId | string | Yes | Template ID. Use _default for plain-text replies. |
dedupeKey | string | Yes | Unique key to prevent duplicate replies |
payload | object | Yes | Template variables |
sendReason | string | No | Reason for replying |
idempotencyKey | string | No | Alternative deduplication key |
CLI
# Shorthand with plain text body
molted threads reply thr_abc123 --body "Thanks for reaching out!"
# With a specific template and payload
molted threads reply thr_abc123 \
--template-id follow-up \
--payload '{"name": "Alice"}' \
--dedupe-key "reply-alice-2"Schedule followup
Schedule a followup email that sends automatically if the contact does not reply within a specified delay.
POST https://api.molted.email/v1/agent/outbound/schedule-followupcurl -X POST "https://api.molted.email/v1/agent/outbound/schedule-followup?tenantId=TENANT_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contactEmail": "alice@example.com",
"threadRequestId": "req_abc123",
"delayMinutes": 4320,
"cancelOnReply": true
}'Request body
| Field | Type | Required | Description |
|---|---|---|---|
contactEmail | string | Yes | Contact email address |
threadRequestId | string | Yes | The requestId from the initial send response |
delayMinutes | integer | Yes | Delay in minutes before the followup sends (1-43200, max 30 days) |
cancelOnReply | boolean | No | Cancel the followup automatically if the contact replies first |
triggerConditions | object | No | Additional trigger conditions |
CLI
# Schedule followup 72 hours after initial send
molted threads followup req_abc123 \
--contact-email alice@example.com \
--delay-hours 72 \
--cancel-on-replyDeduplication
Every outbound send and reply requires a dedupeKey. The same key on a second call returns the original result without sending again. Use deterministic keys based on your workflow, contact, and step:
{workflow}-{contact-id}-{step}
# e.g. "onboard-alice-day1", "renewal-alice-2026q1"If you use an auto-generated key (like the CLI does with --dedupe-key auto), each call is treated as a new send.
Autonomy levels
Outbound sends through a mailbox are subject to that mailbox's autonomy level:
- L1 (Assisted) - Every send requires human approval. Response status:
pending_approval. - L2 (Guarded Auto) - First message to a new contact requires approval. Subsequent messages send automatically.
- L3 (Fully Autonomous) - All policy-approved sends execute immediately.
When a send is held for approval, use the approval queues API to review and release it.