Provision cold email mailboxes by API
Create cold email domains and up to 100 mailboxes per call with the EmailPal REST API: SPF, DKIM and DMARC written automatically, SMTP/IMAP credentials by export.
The short answer
Two calls provision sending infrastructure: POST /api/public/v1/domains registers up to 50 domains and writes SPF, DKIM, DMARC and MX, then POST /api/public/v1/mailboxes creates up to 100 mailboxes per call on a ready domain. Both return 202 with a job id to poll. Starter is $69/mo with 50 mailboxes included, extra mailboxes are $1/mo, warming is $0.60 per inbox, and the API budget is 1,000 requests an hour. SMTP and IMAP passwords come only from an admin-scoped export.
Buy or connect domains, wait for DNS to verify, create mailboxes in bulk, poll the job, then export credentials as CSV with an admin key. Domain purchases draw from a prepaid balance and are all-or-nothing per batch. Send an Idempotency-Key on every create so a retry after a timeout cannot buy twice. Mailbox and domain creation are asynchronous, so poll /jobs or use webhooks rather than looping. The limits are real: one account and one plan ceiling (3x the included mailboxes), hosted SMTP and IMAP only, no OAuth to Google or Microsoft mailboxes, and 1,000 requests an hour.
How it works, step by step
Step 1
Create a write key, and an admin key if you need credentials
Create keys under API keys in the dashboard. Provisioning needs the write scope. GET /mailboxes/export and POST /account/topup need admin. The secret is shown once; EmailPal stores a hash.
Step 2
Check the account before you size a batch
GET /account returns the plan, mailbox and domain usage against the limits, the warming cost per month and domain_balance_cents. Read it first, because a 402 halfway through a large batch leaves you working out which items landed.
GET /api/public/v1/accountbashcurl https://www.emailpal.io/api/public/v1/account \ -H "Authorization: Bearer $EMAILPAL_API_KEY"Step 3
Check availability and price
POST /domains/search takes exact names or a brand seed. It reserves nothing and charges nothing, but it counts against the hourly request budget. price_cents is what the purchase will take off the balance.
POST /api/public/v1/domains/searchjson{ "seed": "northworks", "tlds": ["com", "co"], "limit": 10 }Step 4
Buy the domains with an Idempotency-Key
POST /domains with mode purchase prices the whole batch, takes it off the prepaid domain balance and only then queues registrations. If the balance cannot cover all of it, nothing is bought and the 402 names the shortfall. Use mode connect for domains you already own.
POST /api/public/v1/domainsbashcurl -X POST https://www.emailpal.io/api/public/v1/domains \ -H "Authorization: Bearer $EMAILPAL_API_KEY" \ -H "Idempotency-Key: 3f6c9e1a-2b7d-4c58-9a10-5e8d7f2b1c44" \ -H "Content-Type: application/json" \ -d '{ "mode": "purchase", "domains": ["outbound-atlas.com", "atlas-mail.co"], "years": 1, "tags": ["tenant:44"] }'Step 5
Poll the jobs until each domain is ready
Each queued domain carries a job_id. Poll GET /jobs/{id} and branch on terminal, or list GET /jobs?type=domain.purchase once instead of polling each id. Registration and DNS verification take minutes. A domain must be ready or active before it can take mailboxes.
GET /api/public/v1/jobs/{id}bashcurl https://www.emailpal.io/api/public/v1/jobs/job_4h29xk \ -H "Authorization: Bearer $EMAILPAL_API_KEY"Step 6
Create mailboxes in bulk
POST /mailboxes takes domain_id and one of count, personas or mailboxes (named local parts). Up to 100 per call. The response lists the addresses immediately, before the mail server has them, and a job_id to poll.
POST /api/public/v1/mailboxesbashcurl -X POST https://www.emailpal.io/api/public/v1/mailboxes \ -H "Authorization: Bearer $EMAILPAL_API_KEY" \ -H "Idempotency-Key: 8d1e4b72-6a3c-4f09-b5d2-0c9a7e3f1a68" \ -H "Content-Type: application/json" \ -d '{ "domain_id": "dom_91af22", "count": 5, "pattern": "first.last", "warming_profile": "standard", "tags": ["tenant:44"] }'Step 7
Poll the job, then export credentials
Once the provisioning job succeeds, GET /mailboxes/export returns SMTP and IMAP credentials as CSV. It needs an admin key and is written to the audit log on every call. It only includes mailboxes that have finished provisioning and are warming, active or paused.
GET /api/public/v1/mailboxes/exportbashcurl "https://www.emailpal.io/api/public/v1/mailboxes/export?format=generic&domain_id=dom_91af22" \ -H "Authorization: Bearer $EMAILPAL_ADMIN_KEY"
01
What the create responses look like
A domain purchase returns 202 with a queued array (domain, domain_id, job_id, price_cents), a rejected array with a reason per name, charged_cents and the new balance_cents. A mailbox create returns 202 with a job_id, the addresses, overage_billable and warming_withheld. Neither response contains a password.
{
"job_id": "job_5m71pq",
"poll": "/api/public/v1/jobs/job_5m71pq",
"mailboxes": [
{ "id": "mbx_2c81f0", "email": "ada.lovelace@outbound-atlas.com" },
{ "id": "mbx_2c81f1", "email": "grace.hopper@outbound-atlas.com" }
],
"overage_billable": false,
"warming_withheld": false,
"note": "Provisioning now. Credentials are available from the export endpoint once the job finishes."
}02
DNS: SPF, DKIM, DMARC and MX are written for you
On domains EmailPal registers, and on connected domains once DNS is delegated or a Cloudflare token is attached, the platform writes SPF with a hard fail, two DKIM selectors (rsa._domainkey at RSA-2048 and ed._domainkey at Ed25519), DMARC at p=reject and MX, then re-verifies them against public resolvers. The SPF value is v=spf1 include:_spf.emailpal.io -all.
GET /domains/{id} returns every record with expected and observed values and a status, so a script can tell a verified record from a mismatch. When connecting a domain you own, dns_method is nameservers (default, delegate the zone to EmailPal), manual (you add the records at your own DNS host, including after a DKIM rotation) or cloudflare_token (EmailPal writes into your Cloudflare zone with a token you scope). A connected domain sits in needs_dns until public DNS agrees; that is expected, not a failure.
03
SMTP and IMAP credentials
Mailbox reads return the SMTP and IMAP host and port for each mailbox, never the password. The password comes from GET /mailboxes/export, which returns text/csv with smtp_username, smtp_password, imap_username, imap_password, hosts, ports and security modes. The username is the full email address. The format parameter accepts generic plus one format per sending tool, such as smartlead, instantly, woodpecker and salesforge.
Because export needs the admin scope, keep that key on a server you control and out of any worker that only creates mailboxes. A key that can create mailboxes cannot read their passwords.
04
Async jobs, polling and webhooks
Anything that touches a registrar, public DNS or the mail server runs as a job. Poll GET /jobs/{id} and loop on terminal, which is true for succeeded, failed, dead and cancelled. A failed job with a retry_after is still going to be retried on its own. POST /jobs/{id} with action retry or cancel is available on a write key, and retrying is safe because jobs that touch money carry their own idempotency key.
Polling spends your hourly budget. A hundred job ids polled every few seconds will hit the limit, so list jobs with a type or status filter, or register a webhook. Webhook events include domain.ready, domain.failed, mailbox.provisioned, mailbox.failed and job.succeeded. Deliveries carry an EmailPal-Signature header and are at-least-once, so dedupe on the event id.
05
Idempotency
Send an Idempotency-Key header of 8 to 255 characters on POST /domains and POST /mailboxes. The same key with the same body replays the stored response and adds an Idempotent-Replay: true header. The same key with a different body returns 409 idempotency_key_reused. If the first request is still running you get 409 idempotency_in_progress. Records expire after 24 hours.
Error responses are not stored. A failed attempt releases the key, so retrying after a 500 or after topping up a balance runs again instead of replaying the old failure. The header is optional; without it, a retry after a timeout is a new request.
06
Rate limits and errors
The hourly request budget is 1,000 on every current plan, counted in a fixed clock-hour window. Every response carries X-RateLimit-Limit. A rejected request returns 429 rate_limited with limit, used and resetAt in the body plus X-RateLimit-Reset and Retry-After headers. Failed requests count against the budget. EmailPal can set a per-account override, but the plan default is 1,000.
Errors share one envelope, error.code and error.message, and you should branch on the code. The ones a provisioning client meets are plan_limit (402, the batch passes the mailbox or domain ceiling, with limit and used), insufficient_balance (402, with requiredCents, balanceCents and shortfallCents), domain_not_ready (409, with the domain status), address_taken (409), invalid_local_part (400), insufficient_scope (403, with granted and required) and registrar_unavailable (502, availability unknown, not unavailable).
{
"error": {
"code": "insufficient_balance",
"message": "That batch comes to $41.97 and the balance is $24.15. Top up $17.82 and try again.",
"requiredCents": 4197,
"balanceCents": 2415,
"shortfallCents": 1782
}
}07
Money: balance, overage and warming
Domains are bought from a prepaid domain balance, separate from the subscription card. POST /account/topup, which needs an admin key, returns a hosted payment page for $20 to $5,000; nothing is charged by the call itself and the balance rises when the processor confirms payment. Registrations that fail after the charge are refunded to the balance. Premium names are rejected rather than bought.
Mailboxes beyond the plan allowance are billed at $1/mo each, not refused, until the hard ceiling of three times the included count. Warming defaults to on at $0.60 per inbox per month. On an account with no active subscription, mailboxes are still created but warming is withheld, and the response says so in warming_withheld.
08
What the Terms allow if you embed this in a product
Section 9 of the Terms (effective 6 October 2026) allows you to embed mailbox and domain provisioning in your own product and supply it to your own end customers through the REST API or MCP server, under your own account and your own name. You are responsible for each end user’s compliance with the acceptable use policy, API keys must not be shared between organisations, and allowances are capped at what you purchased, so do not promise your customers more than you have bought. Reselling dashboard access, white-labelling or using the EmailPal brand needs written agreement. This page is a summary, not legal advice.
Limits, prices and names, in one table
The figures this page relies on, so you do not have to hunt for them.
| Domains per create call | 50 |
| Mailboxes per create call | 100count, personas or named local parts |
| Create responses | 202 Acceptedjob_id to poll on GET /jobs/{id} |
| Hourly API budget | 1,000 requestsEvery current plan; fixed clock-hour window; 429 rate_limited with Retry-After |
| Idempotency-Key | 8 to 255 characters, 24 hours |
| Starter plan | $69/mo50 mailboxes, 50 domain slots |
| Growth plan | $189/mo200 mailboxes, 200 domain slots |
| Scale plan | $499/mo600 mailboxes, 600 domain slots |
| Extra mailbox | $1/moHard ceiling at 3x the included count |
| Warming | $0.60 per inbox per month |
| Domain balance top-up | $20 to $5,000Admin key; returns a payment page, charges nothing itself |
| Mailboxes per domain | 2 to 5 recommendedNot enforced on domains you own |
| Warming ramp | ~2 weeks standard, ~1 week accelerated |
| Scopes | read, write, adminExport of passwords and balance top-up need admin |
Checked against the product and the API on . Plans and limits change, so confirm in the docs before you build on them.
This is not for you if
- You need to connect existing Google Workspace or Microsoft 365 mailboxes by OAuth. EmailPal provisions its own hosted SMTP and IMAP mailboxes and has no OAuth sign-in to Google or Microsoft.
- You need a separate sub-account, API budget or billing relationship per end customer. There is one account, one plan ceiling and one hourly budget; tags only label what belongs to whom.
- You need a password on the creation response. Passwords come only from the admin-scoped export.
- You need to hand your customers their own EmailPal API keys. Keys must not be shared between organisations.
- You need to place thousands of requests an hour. The plan default is 1,000, and polling spends it.
Common questions
How many mailboxes can I create in one API call?
Up to 100 per POST /mailboxes call, as a count, a list of personas or a list of named local parts. Send either personas or mailboxes, not both. Domains are capped at 50 per POST /domains call.
Does the API return SMTP and IMAP credentials?
Mailbox responses return the SMTP and IMAP host and port. The password is only in GET /mailboxes/export, which returns CSV, needs an admin-scoped key and is recorded in the audit log. The export includes only mailboxes that have finished provisioning.
Are SPF, DKIM and DMARC set up automatically?
Yes, on domains EmailPal registers and on connected domains once DNS is delegated or a Cloudflare token is attached. EmailPal writes SPF with a hard fail, DKIM at RSA-2048 and Ed25519, DMARC at p=reject and MX, then re-verifies against public resolvers. On a manual domain you add the records yourself from GET /domains/{id}.
What happens if my request times out during a domain purchase?
With an Idempotency-Key header, retrying the same body within 24 hours replays the first response instead of buying again. Without the header, the retry is a new request and can be a second purchase, so send the header on every POST /domains.
What is the API rate limit?
The plan default is 1,000 requests an hour on every current plan, in a fixed clock-hour window. Every response carries X-RateLimit-Limit. Past the limit you get 429 rate_limited with Retry-After and X-RateLimit-Reset. Failed requests count, and polling jobs spends the budget.
Can I resell mailboxes provisioned through the API to my own customers?
Section 9 of the Terms lets you embed provisioning in your own product and supply it to your own end customers through the REST API or MCP server, under your own account and name. You are responsible for their compliance with the acceptable use policy, and white-labelling or using the EmailPal brand needs written agreement.
Build it on infrastructure that holds up
Domains, mailboxes, warming and verification over one REST API and MCP server, with the sequencer, Unibox and CRM on every plan.
No card to start — cancel any time.