WarmHawk
Docs / API reference / Auth & mailboxes

Auth & mailboxes.

The real, current shape of every /v1 route in this section, as shipped in warmhawk-core-engine. Every example is copy-pasteable against a real instance.

Base URL is https://your-instance/v1. Every route requires Authorization: Bearer <token> except POST /v1/auth/login (which issues the token) and the two OAuth routes (public, protected by a signed state param instead). This page covers auth and the full mailbox lifecycle — create, list, update, delete, and both connection paths (SMTP/IMAP credentials or Google/Microsoft OAuth).

Base URL & auth

https://your-instance/v1 — every route below (and across Leads & campaigns and Queue, domains & webhooks) requires Authorization: Bearer $WARMHAWK_KEY, obtained from POST /v1/auth/login, except that route itself and the two OAuth endpoints below, which are public by design.

POST /v1/auth/login

Authenticates against the engine’s own User table — separate from the Tier 1/2 dashboard’s own login. Rate-limited (10/min) and brute-force locked out per account, both layers active at once.

API details (Tier 0 / self-hosters) · Request▸
POST /v1/auth/login
Content-Type: application/json

{ "email": "you@yourcompany.com", "password": "YOUR_PASSWORD" }
API details (Tier 0 / self-hosters) · Response — 200▸
{ "token": "YOUR_API_KEY" }

401 for invalid credentials (constant response shape whether the account exists or not, to avoid user enumeration). 429 with retryAfterMs once an account is locked from repeated failures.

Mailboxes

RouteWhat it does
GET /v1/mailboxesList every mailbox on the account.
POST /v1/mailboxesCreate a mailbox. Requires email + domainId. With a password, signs in to the SMTP and IMAP servers first. 201 with the created row (credentials stripped); 409 if the address is already connected; 422 if a sign-in is refused.
PATCH /v1/mailboxes/:idUpdate status, dailyCap or senderName. 404 if the id doesn’t exist.
DELETE /v1/mailboxes/:idDisconnect the mailbox. 204 on success, 404 if not found.
GET /v1/oauth/:provider/authorize?mailboxId=Browser redirect to the provider’s consent screen. provider is "google" or "microsoft"; mailboxId must already exist.
GET /v1/oauth/:provider/callback?code=&state=Provider calls this back. Public — no bearer token, verified by the signed state param instead.

POST /v1/mailboxes — body

API details (Tier 0 / self-hosters) · Request body (SMTP/IMAP path)▸
{
  "email": "you@yourcompany.com",       // required
  "domainId": "dom_a1b2c3",              // required — create the Domain first
  "provider": "SMTP_CUSTOM",             // optional, defaults to SMTP_CUSTOM
  "dailyCap": 25,                        // optional, defaults to 25
  "smtpHost": "smtp.yourdomain.com",
  "smtpPort": 587,
  "imapHost": "imap.yourdomain.com",
  "imapPort": 993,
  "authUsername": "you@yourcompany.com",
  "authPassword": "YOUR_SMTP_PASSWORD",  // encrypted at rest, never returned
  "senderName": "Sam Patel"              // optional — the From name; up to 80 characters
}

For Google Workspace / Microsoft 365, POST without authPassword, then complete the OAuth flow against the returned mailbox’s id — see Connecting mailboxes for the full walkthrough with both connection paths end to end.

POST /v1/mailboxes — sign-in check

When the body has an authPassword and an smtpHost or imapHost, the engine signs in to each of those servers once, side by side, before anything is saved. A wrong password is answered while you’re still holding it, not as a failed send hours later. Each check gives up after 10 seconds without an answer. The username is authUsername, or email when it’s left out. Hosts under the reserved test TLDs .test, .example and .invalid are never dialed.

StatusWhen
201Both servers accepted the sign-in (or there was nothing to check). The mailbox is saved.
409That address is already connected. Answered before any server is dialed, so retrying a create is safe.
422A server refused the sign-in, or couldn’t be reached. Nothing is saved. When both fail, the SMTP refusal is the one reported.
API details (Tier 0 / self-hosters) · Response — 409▸
{ "error": "This mailbox is already connected." }
API details (Tier 0 / self-hosters) · Response — 422 (SMTP password refused)▸
{
  "error": "The mail server didn't accept that username and password. Google Workspace and Microsoft 365 usually need an app password here — or use Connect with Google or Connect with Microsoft instead."
}

The error is a sentence written for the person who typed the password, so it’s safe to show as is. The provider’s own reply (for example 535 5.7.8) goes to the engine’s log, not the response. Other 422 sentences you can get:

  • IMAP password refused: “The IMAP server didn’t accept that username and password, so WarmHawk couldn’t read replies. Check the IMAP host — it usually takes the same password as SMTP.”
  • Host doesn’t exist: “We couldn’t find a mail server called smtp.yourdomian.com. Check the SMTP host.”
  • Nothing answers: “We couldn’t connect to mail.yourdomain.com on port 2525. Check the SMTP host and port — most mail servers use 587 or 465.” (For IMAP: “…most mail servers use 993.”)
  • Anything else: “The SMTP server at mail.yourdomain.com didn’t accept the sign-in. Check the SMTP host, port, username and password.”

Continue to Leads & campaigns for lead ingest and campaign lifecycle routes, or Queue, domains & webhooks for send throttling and domain health.