PadiMail Docs
Sending

Send an email

Recipients, bodies, attachments, tags, streams and headers.

NeedsTier 1: IndividualTest keys work on any account; the sandbox (Tier 0) sends to your own team only; Tier 1 sends to anyone.
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.app while 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.

Was this page helpful?

On this page