A mailbox is one sending identity, on one domain.
Every mailbox WarmHawk sends from belongs to exactly one Domain row and is connected one of two ways: plain SMTP/IMAP credentials, or an OAuth consent flow for Google Workspace / Microsoft 365.
Connecting a mailbox is two steps: register the sending domain (POST /v1/domains), then create the mailbox against it (POST /v1/mailboxes) — either with SMTP/IMAP credentials directly, or by creating a credential-less mailbox row first and completing OAuth consent against GET /v1/oauth/:provider/authorize?mailboxId=<id> for Google or Microsoft.
On Tier 1 or Tier 2? Skip the API steps. In your dashboard, open Mailboxes, enter the address, and click Connect with Google or Connect with Microsoft. WarmHawk Connect handles the sign-in, so there is no OAuth app to build. The one step your company does once is below.
Google Workspace: trust WarmHawk once
Google blocks apps that ask for full Gmail access until your Workspace admin trusts them. It takes about a minute, once per company:
- In your WarmHawk dashboard, open Mailboxes and copy the client ID shown under Google Workspace: trust WarmHawk once.
- Open admin.google.com as a super admin.
- Go to Security → Access and data control → API controls → Manage third-party app access.
- Click Configure new app, search for the client ID, and select WarmHawk.
- Choose your whole organization, then Trusted → Configure.
Then connect each mailbox from the Mailboxes page. If Google’s consent screen shows a checkbox for Gmail access, tick it — without it the mailbox can’t send, and WarmHawk will ask you to try again.
- Personal @gmail.com? It can’t be trusted by an admin. Use the SMTP/IMAP form with an app password instead.
- Microsoft 365? Click Connect with Microsoft. Your Microsoft 365 admin approves WarmHawk once for the whole company. If you aren’t the admin, Microsoft shows “Need admin approval”: copy the approval link from the Mailboxes page and send it to them.
- Rather use your own OAuth app? Register it under Settings in your dashboard. When one is set, it is used instead of Connect, and nothing passes through warmhawk.com.
1. Register the sending domain
API details (Tier 0 / self-hosters) · POST /v1/domains▸
curl -X POST https://app.yourcompany.com/v1/domains \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domainName": "yourcompany.com" }'List existing domains with GET /v1/domains, or update a domain’s redirect URL with PATCH /v1/domains/:id. Checking SPF/DKIM/DMARC and blocklist status for a domain is covered in Sending safely & domain health.
2a. Connect via SMTP/IMAP
For any provider that isn’t Google Workspace or Microsoft 365 (or those, if you prefer app-password auth over OAuth), pass credentials directly. Every field except email and domainId is optional at the type level, but a real SMTP/IMAP mailbox needs the connection fields filled in:
API details (Tier 0 / self-hosters) · POST /v1/mailboxes▸
curl -X POST https://app.yourcompany.com/v1/mailboxes \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "you@yourcompany.com",
"domainId": "dom_a1b2c3",
"provider": "SMTP_CUSTOM",
"smtpHost": "smtp.yourdomain.com",
"smtpPort": 587,
"imapHost": "imap.yourdomain.com",
"imapPort": 993,
"authUsername": "you@yourcompany.com",
"authPassword": "YOUR_SMTP_PASSWORD",
"senderName": "Sam Patel",
"dailyCap": 25
}'The response (201) returns the created Mailbox row with authPassword stripped out — it’s encrypted at rest and never round-tripped back to any caller, ever.
Before saving, WarmHawk signs in to the SMTP and IMAP servers once with that password. If either refuses, you get a 422 with a sentence saying what to fix, and nothing is saved — Google Workspace and Microsoft 365 usually need an app password here, not the account password. An address that’s already connected gets a 409. Every status is listed in the API reference.
senderName is the From name leads see and what {{senderName}} fills in campaigns. Up to 80 characters. The API still accepts a mailbox without one, but its mail then goes out from the bare address, so the dashboard asks for it.
2b. Connect via Google Workspace or Microsoft 365 OAuth
Create the mailbox row first — without authPassword — so you have a mailboxId to bind the OAuth flow to:
API details (Tier 0 / self-hosters) · POST /v1/mailboxes (credential-less, OAuth to follow)▸
curl -X POST https://app.yourcompany.com/v1/mailboxes \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "email": "you@yourcompany.com", "domainId": "dom_a1b2c3", "provider": "GOOGLE_WORKSPACE" }'Then send the browser (not a background curl call — this is a real consent redirect) to:
API details (Tier 0 / self-hosters) · GET /v1/oauth/:provider/authorize?mailboxId=<id>▸
https://app.yourcompany.com/v1/oauth/google/authorize?mailboxId=mbx_a1b2c3
# or: https://app.yourcompany.com/v1/oauth/microsoft/authorize?mailboxId=mbx_a1b2c3That redirects to Google’s or Microsoft’s own consent screen, signed with a short-lived state parameter binding it back to that mailboxId. On success, the provider calls GET /v1/oauth/:provider/callback?code=&state= — a public endpoint with no bearer auth (protected by the signed state instead, since the provider itself is the caller) — which stores an AES-256-GCM-encrypted refresh token against the mailbox and redirects the browser to the dashboard’s /dashboard/mailboxes page (or back with an ?oauth_error= query param if consent was denied or the token exchange failed).
On connect, WarmHawk copies the name the provider already shows for the mailbox — Gmail’s send-as name, or the Microsoft 365 account’s display name over WarmHawk Connect — into senderName, unless one is already set. If the provider has none, the dashboard asks for it after the redirect.
Managing a mailbox afterward
API details (Tier 0 / self-hosters) · GET /v1/mailboxes — list every connected mailbox▸
curl https://app.yourcompany.com/v1/mailboxes -H "Authorization: Bearer YOUR_API_KEY"API details (Tier 0 / self-hosters) · PATCH /v1/mailboxes/:id — adjust status, dailyCap or senderName▸
curl -X PATCH https://app.yourcompany.com/v1/mailboxes/mbx_a1b2c3 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "dailyCap": 40, "senderName": "Sam Patel" }'API details (Tier 0 / self-hosters) · DELETE /v1/mailboxes/:id — disconnect it▸
curl -X DELETE https://app.yourcompany.com/v1/mailboxes/mbx_a1b2c3 \
-H "Authorization: Bearer YOUR_API_KEY"A mailbox that stops authenticating (an expired OAuth token, a rotated app password) shows up as failed sends in the queue, not as a separate alert channel today — see Sending safely & domain health for how to read queue state and what the bounce circuit breaker does if a broken mailbox keeps failing.
Questions
Connecting mailboxes: questions worth answering up front
Do I need to create a domain before a mailbox?+
Yes. POST /v1/mailboxes requires a domainId, so create the Domain (POST /v1/domains) first — a mailbox always belongs to exactly one sending domain.
What happens to my SMTP/IMAP password after I send it?+
WarmHawk signs in to your SMTP and IMAP servers once to check it, then encrypts it server-side (AES-256-GCM) before it is persisted. It is never echoed back in any API response — not even to the authenticated caller who just set it.
Why does my Google Workspace admin have to trust WarmHawk?+
Google blocks third-party apps that ask for full Gmail access until a Workspace admin marks them trusted. It is a one-time step per company, in admin.google.com, and every mailbox on that Workspace can connect afterward. Personal @gmail.com addresses can't be trusted this way, so connect those with an app password over SMTP/IMAP.
Can I connect more than one mailbox per domain?+
Yes, and it is the normal setup — the send queue rotates weighted across every active mailbox on a campaign rather than hammering one inbox.
What does dailyCap actually limit?+
The maximum sends the queue will schedule through that specific mailbox in a rolling day, independent of any other mailbox's cap — it defaults to a conservative 25 if you don't set one.