Install
openclaw skills install @sendmux.ai/sendmux-send-emailSend one or many emails through Sendmux using approved mailbox or agent credentials, idempotency keys, attachments, MCP, CLI, SDKs, or HTTP.
openclaw skills install @sendmux.ai/sendmux-send-emailThis ClawHub skill connects OpenClaw agents to Sendmux. Some workflows require a Sendmux account and an appropriate Sendmux API key or agent token. Sendmux account usage is external to ClawHub; do not ask users to paste secrets into chat.
Use this skill when the user is ready to send outbound email through Sendmux or needs code/commands for sending.
smx_mbx_ key, owner-approved Sending-resource smx_agent_ token, or REST OAuth grant with Sending access and email.send for the Sending HTTP API. SMTP requires a send-capable API key; OAuth access tokens are not SMTP or IMAP passwords.For an agent-profile before/after approval answer, cover the complete lifecycle:
| State | Required explanation |
|---|---|
| Before owner acceptance and sending approval | The durable credential is read/receive-only, sends fail closed, and the self-registered inbox is capped at 500 MiB. |
| After sending approval | Storage rises to at least 5 GiB. The CLI automatically exchanges for a one-hour delegated email.send token and caches it only until near expiry; the sender supplies one stable --idempotency-key per logical send. |
| After sending revocation | Sending becomes unavailable again, but revocation does not itself change the current inbox storage allocation. |
Sending approval supplies the required authority; it does not guarantee that a later request will pass validation or be accepted for delivery.
| User task | Efficient default |
|---|---|
| One outbound email | POST /emails/send, MCP sending_send_email, CLI sending:send, or SDK sendingSendEmail. |
| More than one independent outbound email | Batch by default: POST /emails/send/batch, MCP sending_send_email_batch, CLI sending:send:batch, or SDK sendingSendEmailBatch. |
| Sending API attachment file | Upload a real file with sending_create_attachment_upload, CLI --attach, or SDK file helpers; reserve sending_upload_attachment inline content for tiny agent-authored bytes; send by attachment_id. |
| Replying while working inside one mailbox | Use mailbox send from sendmux-mailbox-agent when the workflow is mailbox-centred. |
| Existing app only supports SMTP | Use SMTP only because the existing tool requires it. For new agent or app integrations, prefer the HTTP Sending API. |
When explaining a batch send, cover:
| Part | Requirement |
|---|---|
| Authority | A send-capable smx_mbx_ key, owner-approved Sending-resource smx_agent_ token, or REST OAuth grant with email.send. |
| Payload | A messages[] body with up to 100 independently confirmed messages. |
| Completion | Inspect every result item because a batch can partially succeed. |
Credential validation proves only that the credential can read Sending connection metadata; it does not approve or authorise a later message. A complete validation answer has three parts:
sendmux sending:get-connection --profile work --json checks a Sending connection without sending email.email.send works for the HTTP Sending API; OAuth access tokens are not SMTP or IMAP passwords.For OAuth profile setup, refresh and logout, follow sendmux-cli. SDK clients accept accessToken instead of apiKey, including a token provider; your application owns storage and refresh coordination.
Single send body:
{
"from": { "email": "sender@example.com", "name": "Sender Name" },
"to": { "email": "recipient@example.com", "name": "Recipient Name" },
"subject": "Subject line",
"html_body": "<p>Hello.</p>",
"text_body": "Hello."
}
Required fields: from, to, subject, html_body.
Useful optional fields:
text_body: plain text alternative.cc, bcc: arrays of recipients, max 49 each and subject to 50 total to, cc, and bcc recipients.reply_to: one address object.return_path: envelope sender for bounce handling.custom_headers: custom X-* headers.attachments: up to 10 items. Prefer uploaded refs { "attachment_id": "att_..." }. Inline compatibility form uses filename plus base64 content, optional type, and encoding: "base64".For real local files, do not ask the model to produce base64. Route attachment-heavy work to sendmux-attachments; use CLI --attach, SDK file helpers, or delegated sending_create_attachment_upload, then pass attachment_id refs. For a presigned Sending upload, the upload intent's returned max_size_bytes is the size authority; the final sent message is capped at 25 MB. Mailbox blob_id refs are for mailbox sends, not Sending API sends.
For a direct HTTP batch call, keep authority and retry metadata outside the JSON body:
POST /emails/send/batch HTTP/1.1
Authorization: Bearer $SENDMUX_MBX_KEY
Content-Type: application/json
Idempotency-Key: $IDEMPOTENCY_KEY
$SENDMUX_MBX_KEY must resolve to a send-capable smx_mbx_ key. An owner-approved Sending-resource smx_agent_ token is also accepted. The request body is:
{
"messages": [
{
"from": { "email": "sender@example.com" },
"to": { "email": "alice@example.com" },
"subject": "Hello Alice",
"html_body": "<p>Hi Alice.</p>"
},
{
"from": { "email": "sender@example.com" },
"to": { "email": "bob@example.com" },
"subject": "Hello Bob",
"html_body": "<p>Hi Bob.</p>"
}
]
}
Add Idempotency-Key to every send that may be retried. Use one stable key per logical email or batch.
409 idempotency_conflict.Use MCP when the user's agent already has the Sending server connected:
sending_send_email.sending_send_email_batch.sending_upload_attachment with content_base64 only when the decoded content is at most 32,768 bytes.sending_create_attachment_upload with the filename, content type, and exact local size_bytes; compare the size with returned max_size_bytes.attachment_id from the successful external upload response. The intent returns upload_id and transfer metadata, not attachment_id; never substitute its upload_id or a Mailbox blob_id.attachment_id to sending_send_email with one stable Idempotency-Key for this logical send when the client exposes it. If MCP does not expose it clearly, use CLI, SDK, or direct HTTP for a retry-sensitive send.sending_get_attachment.For attachments through MCP, use sendmux-attachments so the agent chooses presigned upload or tiny inline base64 correctly; MCP does not receive a local file_path.
For a self-registered agent, use its profile instead of copying credentials:
sendmux sending:send \
--profile my-agent \
--idempotency-key "$IDEMPOTENCY_KEY" \
--body-file ./sendmux-email.json \
--json
The owner must already have accepted the invitation and approved sending. The CLI obtains and caches the short-lived delegated token; the durable read credential is not used directly by the Sending API.
One email:
SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux sending:send \
--idempotency-key "$IDEMPOTENCY_KEY" \
--body '{
"from": { "email": "sender@example.com", "name": "Sender Name" },
"to": { "email": "recipient@example.com", "name": "Recipient Name" },
"subject": "Subject line",
"html_body": "<p>Hello.</p>",
"text_body": "Hello."
}' \
--json
Batch:
SENDMUX_API_KEY="$SENDMUX_MBX_KEY" sendmux sending:send:batch \
--idempotency-key "$IDEMPOTENCY_KEY" \
--body-file ./sendmux-batch.json \
--json
Use --attach ./file.pdf for local files; the CLI uploads bytes first and injects attachment_id refs before sending. Use --body-file for larger JSON payloads or already-prepared Sending API attachment objects.
One email:
import { createSendingClient, sendingSendEmail } from "@sendmux/sending";
const client = createSendingClient({ apiKey: process.env.SENDMUX_API_KEY! });
const response = await sendingSendEmail({
client,
throwOnError: true,
headers: { "Idempotency-Key": idempotencyKey },
body: {
from: { email: "sender@example.com", name: "Sender Name" },
to: { email: "recipient@example.com", name: "Recipient Name" },
subject: "Subject line",
html_body: "<p>Hello.</p>",
text_body: "Hello.",
},
});
console.log(response.data.data.message_id, response.data.data.status);
Batch:
import { createSendingClient, sendingSendEmailBatch } from "@sendmux/sending";
const client = createSendingClient({ apiKey: process.env.SENDMUX_API_KEY! });
const response = await sendingSendEmailBatch({
client,
throwOnError: true,
headers: { "Idempotency-Key": idempotencyKey },
body: {
messages,
},
});
for (const result of response.data.data.results) {
console.log(result.index, result.status, result.message_id, result.error);
}
Use direct HTTP only when MCP, CLI, or SDK is unavailable:
curl --silent --show-error --fail-with-body \
--config - \
--data-binary @sendmux-email.json <<CURL_CONFIG
request = "POST"
url = "https://smtp.sendmux.ai/api/v1/emails/send"
header = "Authorization: Bearer $SENDMUX_MBX_KEY"
header = "Content-Type: application/json"
header = "Idempotency-Key: $IDEMPOTENCY_KEY"
CURL_CONFIG
The stdin-fed config keeps the bearer and idempotency value out of the child process arguments. Do not print or persist that config.
Single send success returns a 200 success envelope with:
data.message_id: eml_...data.status: queuedBatch success returns a 200 success envelope with:
data.summary.totaldata.summary.queueddata.summary.faileddata.results[] containing index, status, message_id, and optional errorHandle these errors deliberately:
401: missing, invalid, or revoked key.402: insufficient credits.403: key lacks email.send, is not allowed for the sender, or uses the wrong surface.409: idempotency conflict.413: request body exceeds 25 MB.422: validation failed; read error.errors.429 or 503: retry according to response headers.sendmux-getting-started.sendmux-attachments.sendmux-mailbox-agent.sendmux-cli.sendmux-token-efficient-usage.