Introduction
Send OTPs, receipts and notifications from your app, and see what happened to every email.
PadiMail sends email for your app. You call the API, use the Node SDK or point your SMTP library at our relay, and we deliver the email and tell you what happened to it: delivered, opened, bounced or marked as spam.
Node quickstart
Send your first email from Node.js in 5 minutes.
cURL quickstart
Try the API from your terminal.
Python quickstart
Send from Python with requests.
SMTP relay
Keep Nodemailer, Laravel or Django; change the host.
Switch from Resend
Change two environment variables and keep your code.
API reference
Every endpoint, with examples you can run.
How it works
- Sign up and create a test key. A
pm_test_…key works straight away. It never delivers real email, but every email shows up in the dashboard with simulated events, so you can build your whole integration before verifying anything. - Verify your phone to get live keys. In the sandbox (Tier 0) you can send from
onboarding@sandbox.padimail.appto your own team's addresses, up to 50 emails a day. - Verify your identity and add your domain (Tier 1) to send to anyone from
you@yourcompany.ng.
The basics
| Base URL | https://api.padimail.app/v1 |
| Authentication | Authorization: Bearer pm_live_… (or pm_test_…) |
| Format | JSON in and out, snake_case fields, UTF-8 |
| Times | ISO 8601. We return UTC (Z) |
| IDs | Prefixed strings such as em_01JB… and dom_01JB…. Treat them as opaque |
| Errors | One shape for every error, with a code you can branch on. See Errors |
Keep your API key on your server
Never put an API key in a browser, mobile app or Flutter client: anyone who opens the app can read it and send email as you. Call PadiMail from your server, and have your app call your server.
When you're over a limit
Every account has daily and monthly limits that depend on its tier and plan. Reaching one doesn't lose email: by default the email is accepted with status: "deferred" and sends as soon as the limit opens up. For login codes and OTPs, where a late email is useless, ask for an error instead with quota_behavior: "reject". See Scheduling and limits.