Send an email
Recipients, bodies, attachments, tags, streams and headers.
const { id } = await pm.emails.send({
from: "Acme Store <hello@acme.ng>",
to: ["ada@example.com"],
cc: ["accounts@acme.ng"],
reply_to: "support@acme.ng",
subject: "Your receipt #1042",
html: "<p>Thanks for your order.</p>",
text: "Thanks for your order.",
tags: [{ name: "category", value: "receipt" }],
headers: { "X-Entity-Ref-ID": "1042" },
});The response, { "id": "em_…" }, means the email was accepted. Delivery happens a few seconds later; follow it with webhooks or GET /v1/emails/{id}.
Who it's from
from must be one of:
- an address on a verified domain, such as
hello@acme.ng; onboarding@sandbox.padimail.appwhile you're in the sandbox.
Anything else returns 400 invalid_from_address. A display name is fine: Acme Store <hello@acme.ng>.
Recipients
to, cc, bcc and reply_to each take one address or an array. An email can have at most 50 recipients across to, cc and bcc. To send the same message to many people separately, use batch sending. Suppressed addresses are skipped, and the email's events say so.
The body
Give html and/or text, or a template, or (with the Node SDK) a React Email element. If you give only html, we generate the text version for you.
Attachments
import { readFile } from "node:fs/promises";
await pm.emails.send({
from: "Acme Store <hello@acme.ng>",
to: "ada@example.com",
subject: "Your invoice",
text: "Your invoice is attached.",
attachments: [
{ filename: "invoice.pdf", content: (await readFile("invoice.pdf")).toString("base64"), content_type: "application/pdf" },
{ filename: "terms.pdf", path: "https://acme.ng/terms.pdf" },
],
});Give content (base64) or path (an HTTPS URL we fetch). The whole message can be up to 40 MB. Attachments can't be used with batch sending or on shared sending domains.
Tags
Up to 10 { name, value } pairs of letters, digits, _ and -. Tags come back on every webhook event and let you filter the email list: GET /v1/emails?tag=category:receipt.
Streams
stream is transactional (receipts, codes, alerts) or marketing (newsletters, promotions). It defaults to transactional for your own domains. Keep the two apart: complaints about marketing mail then don't hurt your login codes. Unsubscribes apply to the marketing stream.
Look an email up
const email = await pm.emails.get(id);
console.log(email.status, email.recipients.map((r) => `${r.address}: ${r.status}`));
const { data: events } = await pm.emails.events(id);curl https://api.padimail.app/v1/emails/em_01JB.../events -H "Authorization: Bearer $PADIMAIL_API_KEY"The list, GET /v1/emails, covers the last 90 days, newest first, and filters by status, to, from, domain_id, tag, stream, source and dates. The content itself (GET /v1/emails/{id}/content) is kept for your plan's retention period.