Sending Email

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.

Endpoint

Submit a POST request to the send endpoint. Replace {orgId} with your organisation ID, which you can find in Settings → General.

POSThttps://api.mailpipe.dev/api/v1/outbound/{orgId}/send

All requests must include an Authorization: Bearer header with a key that has the mail:send scope. See the Authentication guide for details.

Request Body

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.

Full JSON schema
{
  // 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>"
    }
  ]
}
FieldTypeRequiredDescription
fromstringRequiredSender address. Accepts "Name <email>" format.
tostring[]RequiredOne or more recipient addresses.
subjectstringRequiredEmail subject line.
textBodystringOptionalPlain-text message body.
htmlBodystringOptionalHTML message body.
ccstring[]OptionalCarbon-copy recipients.
bccstring[]OptionalBlind carbon-copy recipients.
replyTostringOptionalAddress for replies.
headersobjectOptionalCustom SMTP headers.
tagsstring[]OptionalLabels for filtering in the dashboard.
attachmentsAttachment[]OptionalBase64-encoded file attachments.

Code Examples

cURL

cURL
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>"
  }'

Node.js (fetch)

Node.js
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"

Node.js (official SDK)

Node.js SDK
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}`);

Python

Python
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"])

Attachments

Attach files by including an attachments array in the request body. Each attachment object requires three fields:

FieldTypeDescription
filenamestringThe file name shown to the recipient (e.g., invoice.pdf)
contentTypestringMIME type (e.g., application/pdf, image/png)
contentstringBase64-encoded file contents
Node.js — encoding a file attachment
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.

Rate Limits

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.

60
requests
Per minute
500
emails
Per hour
10,000
emails
Per day

Rate limit status is returned in every response via these headers:

Response 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.

Response Formats

Success — 201 Created

A successful send returns the new message object with its assigned ID and queued status.

Success response
{
  "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"
}

Validation error — 400 Bad Request

Returned when required fields are missing or contain invalid values.

Error response
{
  "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"
      }
    ]
  }
}

Authentication error — 401 Unauthorized

Auth error response
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

See Error Codes for the full list of possible error responses.

Need Help?

Our team is here to help. Reach out if you have any questions.

Contact Support