PadiMail Docs
API reference

Emails

Send, batch, schedule, look up and cancel emails.

All requests go to https://api.padimail.app with Authorization: Bearer <your key>. Try these in the interactive reference with a pm_test_ key.

Send an email

POST/v1/emails

Body

FieldTypeRequiredNotes
fromstringYesA verified domain address, one of your sender handles, or onboarding@sandbox.padimail.app. A display name is allowed: Acme Store <hello@acme.ng>.
tostring (email) | string (email)[]YesOne address or an array. At most 50 across to, cc and bcc.
ccstring (email) | string (email)[]No
bccstring (email) | string (email)[]No
reply_tostring (email) | string (email)[]NoWhere replies go.
subjectstringNoRequired unless the template has one.
htmlstringNoGive html and/or text, or template. With only html, we generate text.
textstringNoThe plain-text body.
template{ id, variables }NoA template ID or alias, and its variables. Uses the published version.
stream"transactional" | "marketing"Notransactional for receipts, codes and alerts; marketing for newsletters. Defaults by sender.
headersobjectNoExtra headers. Some can't be overridden on shared domains.
attachmentsobject[]No{ filename, content (base64) or path (HTTPS URL), content_type }. 40 MB in total. Not on shared domains.
tagsobject[]No{ name, value } pairs of letters, digits, _ and -, for filtering and webhooks. At most 10.
scheduled_atstring (date-time)NoSend later: 1 minute to 30 days ahead, ISO 8601.
quota_behavior"queue" | "reject"NoOver a limit: queue waits (the default), reject fails with 429 or 402. Use reject for OTPs.
fallbackbooleanNoFree plan only: false keeps this email off your connected Gmail.

Send an Idempotency-Key header to make retries safe. The Node SDK adds one for you.

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.send({
  from: "Acme Store <hello@acme.ng>",
  to: ["ada@example.com"],
  subject: "Your receipt #1042",
  html: "<p>Thanks for your order.</p>",
  tags: [{ name: "category", value: "receipt" }],
});

Response 200

{ "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S" }

List emails

GET/v1/emails

Query parameters

FieldTypeRequiredNotes
limitintegerNoPage size, 1 to 100. Default 50.
afterstringNoReturn items after this ID (the next value of the previous page).
beforestringNoReturn items before this ID.
created_afterstring (date-time)NoISO 8601.
created_beforestring (date-time)NoISO 8601.
status"queued" | "scheduled" | "deferred" | "scanning" | "held" | "sending" | "sent" | "delivered" | "delivery_delayed" | "bounced" | "complained" | "suppressed" | "rejected" | "failed" | "canceled"No
tostring (email)NoAn exact recipient address.
fromstringNo
domain_idstringNo
tagstringNoname:value.
stream"transactional" | "marketing"No
source"api" | "smtp" | "dashboard" | "paste_send" | "broadcast" | "automation"NoWhere the email came from: api, smtp, dashboard and so on.

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.list({ status: "delivered", limit: 20 });

Response 200

{
  "data": [
    {
      "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
      "object": "email",
      "created_at": "2026-10-27T13:05:09Z",
      "mode": "live",
      "from": "string",
      "to": ["string"],
      "subject": "string",
      "stream": "transactional",
      "source": "api",
      "sender_kind": "custom_domain",
      "sent_via": "padimail",
      "status": "queued",
      "scheduled_at": "2026-10-27T13:05:09Z",
      "deferred_reason": "daily_limit",
      "deferred_until": "2026-10-27T13:05:09Z",
      "tags": [{ "name": "string", "value": "string" }],
      "recipient_count": 0
    }
  ],
  "has_more": false,
  "next": "string"
}

Send up to 100 emails

POST/v1/emails/batch

Query parameters

FieldTypeRequiredNotes
validation"strict" | "permissive"Nostrict (default) fails the batch on one invalid email; permissive sends the valid ones and lists the rest in errors.

Body: an array of 1 to 100 objects with these fields

FieldTypeRequiredNotes
fromstringYesA verified domain address, one of your sender handles, or onboarding@sandbox.padimail.app. A display name is allowed: Acme Store <hello@acme.ng>.
tostring (email) | string (email)[]YesOne address or an array. At most 50 across to, cc and bcc.
ccstring (email) | string (email)[]No
bccstring (email) | string (email)[]No
reply_tostring (email) | string (email)[]NoWhere replies go.
subjectstringNoRequired unless the template has one.
htmlstringNoGive html and/or text, or template. With only html, we generate text.
textstringNoThe plain-text body.
template{ id, variables }NoA template ID or alias, and its variables. Uses the published version.
stream"transactional" | "marketing"Notransactional for receipts, codes and alerts; marketing for newsletters. Defaults by sender.
headersobjectNoExtra headers. Some can't be overridden on shared domains.
tagsobject[]No{ name, value } pairs of letters, digits, _ and -, for filtering and webhooks. At most 10.
quota_behavior"queue" | "reject"NoOver a limit: queue waits (the default), reject fails with 429 or 402. Use reject for OTPs.
fallbackbooleanNoFree plan only: false keeps this email off your connected Gmail.

Send an Idempotency-Key header to make retries safe. The Node SDK adds one for you.

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.batch([
  {
    from: "Acme Store <hello@acme.ng>",
    to: ["ada@example.com"],
    subject: "Your receipt #1042",
    html: "<p>Thanks, Ada.</p>",
  },
  {
    from: "Acme Store <hello@acme.ng>",
    to: ["chidi@example.com"],
    subject: "Your receipt #1043",
    html: "<p>Thanks, Chidi.</p>",
  },
]);

Response 200

{
  "data": [
    { "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S" },
    { "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5T" }
  ]
}

Get an email

GET/v1/emails/{id}

Path parameters

FieldTypeRequiredNotes
idstringYesThe email's ID, em_….

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.get("em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S");

Response 200

{
  "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
  "object": "email",
  "created_at": "2026-10-27T13:05:09Z",
  "mode": "live",
  "from": "string",
  "to": ["string"],
  "subject": "string",
  "stream": "transactional",
  "source": "api",
  "sender_kind": "custom_domain",
  "sent_via": "padimail",
  "status": "queued",
  "scheduled_at": "2026-10-27T13:05:09Z",
  "deferred_reason": "daily_limit",
  "deferred_until": "2026-10-27T13:05:09Z",
  "tags": [{ "name": "string", "value": "string" }],
  "recipient_count": 0,
  "reply_to": ["string"],
  "domain_id": "dom_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
  "api_key_id": "string",
  "template_id": "string",
  "template_version": 0,
  "has_attachments": false,
  "size_bytes": 0,
  "content_expires_at": "2026-10-27T13:05:09Z",
  "content_available": false,
  "canceled_at": "2026-10-27T13:05:09Z",
  "recipients": [
    {
      "id": "rcp_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
      "address": "ada@example.com",
      "kind": "to",
      "status": "queued",
      "sent_at": "2026-10-27T13:05:09Z",
      "delivered_at": "2026-10-27T13:05:09Z",
      "opened_at": "2026-10-27T13:05:09Z",
      "clicked_at": "2026-10-27T13:05:09Z",
      "bounced_at": "2026-10-27T13:05:09Z",
      "complained_at": "2026-10-27T13:05:09Z",
      "bounce_type": "hard",
      "status_detail": "string",
      "open_count": 0,
      "click_count": 0
    }
  ]
}

Reschedule a scheduled email

PATCH/v1/emails/{id}

Path parameters

FieldTypeRequiredNotes
idstringYesThe email's ID, em_….

Body

FieldTypeRequiredNotes
scheduled_atstring (date-time)YesThe new send time. Only while the email is scheduled.

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.update("em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S", { scheduled_at: "2026-10-28T09:00:00+01:00" });

Response 200

{
  "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
  "object": "email",
  "created_at": "2026-10-27T13:05:09Z",
  "mode": "live",
  "from": "string",
  "to": ["string"],
  "subject": "string",
  "stream": "transactional",
  "source": "api",
  "sender_kind": "custom_domain",
  "sent_via": "padimail",
  "status": "queued",
  "scheduled_at": "2026-10-27T13:05:09Z",
  "deferred_reason": "daily_limit",
  "deferred_until": "2026-10-27T13:05:09Z",
  "tags": [{ "name": "string", "value": "string" }],
  "recipient_count": 0,
  "reply_to": ["string"],
  "domain_id": "dom_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
  "api_key_id": "string",
  "template_id": "string",
  "template_version": 0,
  "has_attachments": false,
  "size_bytes": 0,
  "content_expires_at": "2026-10-27T13:05:09Z",
  "content_available": false,
  "canceled_at": "2026-10-27T13:05:09Z",
  "recipients": [
    {
      "id": "rcp_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
      "address": "ada@example.com",
      "kind": "to",
      "status": "queued",
      "sent_at": "2026-10-27T13:05:09Z",
      "delivered_at": "2026-10-27T13:05:09Z",
      "opened_at": "2026-10-27T13:05:09Z",
      "clicked_at": "2026-10-27T13:05:09Z",
      "bounced_at": "2026-10-27T13:05:09Z",
      "complained_at": "2026-10-27T13:05:09Z",
      "bounce_type": "hard",
      "status_detail": "string",
      "open_count": 0,
      "click_count": 0
    }
  ]
}

An email's events, oldest first

GET/v1/emails/{id}/events

Path parameters

FieldTypeRequiredNotes
idstringYesThe email's ID, em_….

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.events("em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S");

Response 200

{
  "data": [
    {
      "id": "evt_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
      "type": "queued",
      "occurred_at": "2026-10-27T13:05:09Z",
      "email_id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
      "recipient_id": "rcp_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
      "to": "string",
      "data": {}
    }
  ]
}

An email's content while it is retained

GET/v1/emails/{id}/content

Path parameters

FieldTypeRequiredNotes
idstringYesThe email's ID, em_….

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.content("em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S");

Response 200

{ "html": "string", "text": "string", "headers": {} }

Cancel a scheduled, waiting or held email

POST/v1/emails/{id}/cancel

Path parameters

FieldTypeRequiredNotes
idstringYesThe email's ID, em_….

Example

import { PadiMail } from "@padimail/node";

const pm = new PadiMail(process.env.PADIMAIL_API_KEY);

const result = await pm.emails.cancel("em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S");

Response 200

{
  "id": "em_01JBX7Q3F6ZK0Q9W1Y2R3T4V5S",
  "object": "email",
  "status": "canceled"
}
Was this page helpful?

On this page