Send transactional and marketing emails
Use the Mailpipe API to send emails programmatically from your application. Supports HTML and plain-text bodies, CC/BCC, file attachments, and custom headers.
Submit a POST request to the send endpoint. Replace {orgId} with your organisation ID, which you can find in Settings → General.
https://api.mailpipe.dev/api/v1/outbound/{orgId}/sendAll requests must include an Authorization: Bearer header with a key that has the mail:send scope. See the Authentication guide for details.
The request body must be JSON. At minimum you must supply from, at least one recipient in to, a subject, and either textBody or htmlBody.
{
// Required
"from": "Sender Name <sender@yourdomain.com>",
"to": ["recipient@example.com"], // array of addresses
"subject": "Your subject line",
// At least one body field is required
"textBody": "Plain-text version of the email.",
"htmlBody": "<h1>HTML version</h1><p>Rich content here.</p>",
// Optional recipients
"cc": ["cc-recipient@example.com"],
"bcc": ["bcc-recipient@example.com"],
// Optional metadata
"replyTo": "replies@yourdomain.com",
"headers": {
"X-Custom-Header": "custom-value"
},
"tags": ["transactional", "welcome"],
// Optional attachments (see Attachments section)
"attachments": [
{
"filename": "invoice.pdf",
"contentType": "application/pdf",
"content": "<base64-encoded-content>"
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
from | string | Required | Sender address. Accepts "Name <email>" format. |
to | string[] | Required | One or more recipient addresses. |
subject | string | Required | Email subject line. |
textBody | string | Optional | Plain-text message body. |
htmlBody | string | Optional | HTML message body. |
cc | string[] | Optional | Carbon-copy recipients. |
bcc | string[] | Optional | Blind carbon-copy recipients. |
replyTo | string | Optional | Address for replies. |
headers | object | Optional | Custom SMTP headers. |
tags | string[] | Optional | Labels for filtering in the dashboard. |
attachments | Attachment[] | Optional | Base64-encoded file attachments. |
curl -X POST "https://api.mailpipe.dev/api/v1/outbound/org_xxxxxxxx/send" \
-H "Authorization: Bearer mp_live_your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <noreply@acme.com>",
"to": ["customer@example.com"],
"subject": "Your order has shipped",
"textBody": "Hi! Your order #12345 is on its way.",
"htmlBody": "<p>Hi!</p><p>Your order <strong>#12345</strong> is on its way.</p>"
}'const response = await fetch(
`https://api.mailpipe.dev/api/v1/outbound/${process.env.MAILPIPE_ORG_ID}/send`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MAILPIPE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: 'Acme <noreply@acme.com>',
to: ['customer@example.com'],
cc: ['manager@acme.com'],
subject: 'Your order has shipped',
textBody: 'Hi! Your order #12345 is on its way.',
htmlBody: '<p>Hi!</p><p>Your order <strong>#12345</strong> is on its way.</p>',
tags: ['transactional', 'shipping'],
}),
}
);
const result = await response.json();
console.log(result.id); // e.g. "msg_abc123"import { MailpipeClient } from '@mailpipe/sdk';
const client = new MailpipeClient({
apiKey: process.env.MAILPIPE_API_KEY,
});
const message = await client.outbound.send({
from: 'Acme <noreply@acme.com>',
to: ['customer@example.com'],
subject: 'Your order has shipped',
textBody: 'Hi! Your order #12345 is on its way.',
htmlBody: '<p>Hi!</p><p>Your order <strong>#12345</strong> is on its way.</p>',
});
console.log(`Sent: ${message.id}`);import os, requests
resp = requests.post(
f"https://api.mailpipe.dev/api/v1/outbound/{os.environ['MAILPIPE_ORG_ID']}/send",
headers={
"Authorization": f"Bearer {os.environ['MAILPIPE_API_KEY']}",
"Content-Type": "application/json",
},
json={
"from": "Acme <noreply@acme.com>",
"to": ["customer@example.com"],
"subject": "Your order has shipped",
"textBody": "Hi! Your order #12345 is on its way.",
"htmlBody": "<p>Hi!</p><p>Your order <strong>#12345</strong> is on its way.</p>",
},
)
resp.raise_for_status()
print(resp.json()["id"])Attach files by including an attachments array in the request body. Each attachment object requires three fields:
| Field | Type | Description |
|---|---|---|
filename | string | The file name shown to the recipient (e.g., invoice.pdf) |
contentType | string | MIME type (e.g., application/pdf, image/png) |
content | string | Base64-encoded file contents |
import fs from 'fs';
const pdfBuffer = fs.readFileSync('./invoice.pdf');
const base64Content = pdfBuffer.toString('base64');
await client.outbound.send({
from: 'billing@acme.com',
to: ['customer@example.com'],
subject: 'Your invoice is attached',
textBody: 'Please find your invoice attached.',
attachments: [
{
filename: 'invoice-2025-01.pdf',
contentType: 'application/pdf',
content: base64Content,
},
],
});The maximum total message size (including all attachments) is 25 MB. For larger files, host the file externally and include a download link in the email body.
Sending is subject to per-account rate limits to protect deliverability for all customers. The defaults are shown below; contact support to request higher limits.
Rate limit status is returned in every response via these headers:
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 47 X-RateLimit-Reset: 1706181600 # Unix timestamp when the window resets
When the limit is exceeded you receive a 429 Too Many Requests response. Implement exponential back-off and retry after the X-RateLimit-Reset timestamp. See the Rate Limits reference for full details.
201 CreatedA successful send returns the new message object with its assigned ID and queued status.
{
"id": "msg_01hx8yq3vdkj1p4nze7rbc56wf",
"status": "queued",
"from": "Acme <noreply@acme.com>",
"to": ["customer@example.com"],
"subject": "Your order has shipped",
"created_at": "2025-01-15T10:30:00.000Z"
}400 Bad RequestReturned when required fields are missing or contain invalid values.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "to",
"message": "At least one recipient is required"
},
{
"field": "from",
"message": "Sender address must belong to a verified domain"
}
]
}
}401 Unauthorized{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
}See Error Codes for the full list of possible error responses.