Less boo. More booked. Halloween offer: 30 days free on any paid plan. Claim 30 days freeEvery new account gets one free mailbox for warmup and replies. No domain to buy. See how it works

Skip to content

Warmup

API reference8 min read8 sections

Warmup endpoints report on the health of your mailboxes and the automated inbox-placement emails Warmerly sends and receives between accounts. Use them to build dashboards, alert on score drops, or list the individual warmup emails sent for an account.

API reference

#Get warmup stats

text
GET /warmup/stats

Returns a point-in-time summary across your accounts: how many are active, aging, or paused, how many warmup emails have gone out today, and the average health score.

X-Project-Id is optional here, omit it to get stats across every project in your active workspace (the one named in X-Workspace-Id if you belong to it, otherwise your own workspace), or pass it to scope the numbers to a single project in that workspace.

bash
curl https://app.warmerly.com/api/v1/warmup/stats \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Project-Id: 8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a"
json
{
  "activeAccounts": 12,
  "agingAccounts": 3,
  "pausedAccounts": 1,
  "todaySent": 84,
  "todayScheduled": 12,
  "todayPlanned": 96,
  "todayFailed": 0,
  "todayTarget": 110,
  "avgHealthScore": 87
}
FieldTypeDescription
activeAccountsintegerAccounts with status = active (past the ramp period).
agingAccountsintegerAccounts still ramping up (status = aging).
pausedAccountsintegerAccounts with status = paused.
todaySentintegerWarmup emails sent today, summed from each mailbox's own day (its local timezone).
todayScheduledintegerWarmup emails still pending for today.
todayPlannedintegerEverything on today's schedule, in any status.
todayFailedintegerWarmup emails that failed today.
todayTargetintegerThe sum of each mailbox's target for today.
avgHealthScoreinteger \| nullAverage last_health_score across accounts that have one.
API reference
text
GET /warmup/trends

Returns a daily time series plus a current-vs-previous-window summary, suitable for charting score and inbox-placement rate over time. Backed by daily health_snapshots rows, aggregated across the accounts you have access to.

X-Project-Id is optional, same as /warmup/stats, pass it to scope to one project. Without it the numbers cover your active workspace.

Query paramTypeDefaultDescription
days7 \| 30 \| 9030Length of the window. Any other value falls back to 30.
bash
curl "https://app.warmerly.com/api/v1/warmup/trends?days=30" \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Project-Id: 8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a"
json
{
  "range": { "days": 30, "from": "2026-06-03", "to": "2026-07-02" },
  "series": [
    { "date": "2026-06-03", "sent": 41, "inbox": 37, "spam": 3, "bounce": 1, "score": 82 },
    { "date": "2026-06-04", "sent": 45, "inbox": 42, "spam": 2, "bounce": 1, "score": 84 }
  ],
  "summary": {
    "avgScore": 87,
    "avgScorePrev": 81,
    "avgScoreDelta": 6,
    "inboxRate": 92,
    "inboxRatePrev": 88,
    "inboxRateDelta": 4,
    "sentTotal": 1240,
    "sentTotalPrev": 1105
  }
}

series is ordered ascending by date (UTC days) and covers the requested window only, ending today. summary compares the requested window against the equal-length window immediately before it (the ...Prev fields); any rate is null when there's no data to divide by. inboxRate is inbox / (inbox + spam + bounce), expressed as a whole-number percentage.

API reference

#List warmup jobs

text
GET /warmup/jobs

Returns individual warmup emails (a "job" is one email sent from one warmed mailbox to another), most recent first. This endpoint doesn't take X-Project-Id. It only returns jobs where one of your own mailboxes in your active workspace (X-Workspace-Id) is the sender or the recipient; filter by account_id to scope results to a specific mailbox. An account_id that is not one of yours returns 404 not_found, and an invalid query returns 400 bad_request (invalid_query).

Query paramTypeDescription
statuspending \| sending \| sent \| processed \| failedFilter by job status.
account_iduuidOnly jobs where this account is the sender or recipient.
limitinteger, 1-100Default 50.
bash
curl "https://app.warmerly.com/api/v1/warmup/jobs?account_id=1c2d3e4f-...&status=processed&limit=20" \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{
  "jobs": [
    {
      "id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
      "senderAccountId": "1c2d3e4f-1111-4a2b-8c3d-4e5f6a7b8c9d",
      "recipientAccountId": "2d3e4f5a-2222-4a2b-8c3d-4e5f6a7b8c9d",
      "scheduledAt": "2026-07-02T08:00:00.000Z",
      "sentAt": "2026-07-02T08:00:42.000Z",
      "status": "processed",
      "subject": "Re: quick question about the roadmap",
      "deliveredFolder": "inbox",
      "wasOpened": true,
      "wasReplied": false,
      "emailType": "reply"
    }
  ]
}
FieldTypeDescription
iduuidJob ID.
senderAccountId / recipientAccountIduuidThe two mailboxes involved in the exchange.
scheduledAttimestampWhen the job was scheduled to send.
sentAttimestamp \| nullWhen it actually sent.
statusstringpending, sending, sent, processed, or failed.
subjectstringSubject line used.
deliveredFolderinbox \| spam \| promotions \| unknown \| nullWhere the recipient mailbox filed it, once processed.
wasOpened / wasRepliedbooleanWhether the recipient side opened or replied to it.
emailTypestring \| nullInternal template category (e.g. intro, reply).

This endpoint returns the stored job row, so each job also carries fields such as bodyText, bodyHtml, messageId, error, errorKind, attempts and threadDepth. The example above shows only some of them.

API reference

#Per-account health and job history

Two account-scoped endpoints give you the same data filtered to a single mailbox. Both take the account ID from the URL, not X-Project-Id.

#Get an account's health snapshots

text
GET /accounts/{id}/health
Query paramTypeDefaultDescription
daysinteger, 1-36590How far back to return daily snapshots.
bash
curl "https://app.warmerly.com/api/v1/accounts/1c2d3e4f-.../health?days=30" \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{
  "health": [
    {
      "id": "b3e1c9a0-....",
      "accountId": "1c2d3e4f-....",
      "date": "2026-07-02",
      "sentCount": 22,
      "inboxCount": 20,
      "spamCount": 1,
      "bounceCount": 1,
      "replyCount": 3,
      "openCount": 17,
      "score": 89,
      "trend": "up",
      "spamScore": 0,
      "computedAt": "2026-07-02T00:15:00.000Z"
    }
  ]
}

health is ordered most recent first. trend is up, flat, or down. Each entry also carries inboxRate, bounceRate and replyRate, fractions between 0 and 1 of sentCount.

#Get an account's warmup job history

text
GET /accounts/{id}/warmup-jobs

Returns the last 100 jobs (sent or received) for the account, plus a daily summary of the last 90 days. Returns 404 if the account doesn't exist.

bash
curl "https://app.warmerly.com/api/v1/accounts/1c2d3e4f-.../warmup-jobs" \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{
  "jobs": [
    {
      "id": "9b1deb4d-....",
      "scheduledAt": "2026-07-02T08:00:00.000Z",
      "sentAt": "2026-07-02T08:00:42.000Z",
      "status": "processed",
      "senderAccountId": "1c2d3e4f-....",
      "recipientAccountId": "2d3e4f5a-....",
      "subject": "Re: quick question about the roadmap",
      "deliveredFolder": "inbox",
      "wasOpened": true,
      "wasReplied": false,
      "emailType": "reply"
    }
  ],
  "dailySummary": [
    { "day": "2026-07-02", "sent": 22, "received": 19, "inbox": 20, "promotions": 0, "spam": 1, "bounce": 0 }
  ]
}

dailySummary covers the trailing 90 days, newest day first: sent counts jobs the account sent, received counts jobs delivered to it, inbox, promotions and spam count only processed jobs the account sent that landed in each folder, and bounce counts jobs it sent that failed with a permanent error.

API reference

#Pause and resume warmup on an account

Warmup only runs against accounts with status = active or aging. Pausing an account stops it from being scheduled as a sender or recipient in future warmup jobs.

text
POST /accounts/{id}/pause
POST /accounts/{id}/resume

Both take no request body and return 404 if the account doesn't exist. pause is refused with 409 (sandbox_warmup_locked) for the free sandbox mailbox.

bash
curl -X POST https://app.warmerly.com/api/v1/accounts/1c2d3e4f-.../pause \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{ "ok": true }

resume sets the account back to active, clears needsReconnect and disconnectReason, resets consecutiveFailures to 0 and clears any campaign-sending pause. If the account is flagged as needing a reconnect, it first tests the stored login and answers 409 with code reconnect_required when that fails. Resuming a dead or flagged account can return 402 plan_capacity when the workspace is at its mailbox limit:

bash
curl -X POST https://app.warmerly.com/api/v1/accounts/1c2d3e4f-.../resume \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{ "ok": true }
API reference

#Mailbox health alerts

text
GET /alerts

The last 50 mailbox-health alerts that are still open for your user, newest first. Alerts whose fault has since been seen to clear are left out. These are the same events that trigger a health email, so polling this is the pull equivalent of webhooks for anyone who would rather not run an endpoint.

json
{
  "alerts": [
    {
      "id": "c3f1a2b4-5d6e-4f70-8a91-b2c3d4e5f607",
      "accountId": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
      "kind": "score_drop",
      "severity": "warning",
      "title": "Warmup health dropped for jane@yourdomain.com",
      "body": "Health score fell from 82 to 61 over the last 24 hours.",
      "scoreBefore": 82,
      "scoreAfter": 61,
      "sentAt": "2026-09-11T06:02:00.000Z",
      "createdAt": "2026-09-11T06:01:58.000Z"
    }
  ]
}
kindMeaning
score_dropHealth score fell sharply.
score_floorHealth score is below the acceptable floor.
oauth_disconnectedA Google/Microsoft grant was revoked or expired, reconnect needed.
auth_failSMTP/IMAP authentication is failing.
dns_changedThe domain's SPF/DKIM/DMARC result changed since the last check.
domain_blocklistedThe sending domain appeared on a public blocklist.
ip_blocklistedThe mail server's IP appeared on a public blocklist.
spam_flagThe mailbox or workspace was flagged for spam-like outcomes.
delivery_unobservableMail is going out but nothing can be classified, so health cannot be measured at all. This is distinct from a bad score: there is no score.

severity is info, warning or critical. sentAt is when the notification went out (null if it has not been sent), as opposed to createdAt, when the condition was detected. Each alert is the stored row, so it also carries userId, fingerprint and resolvedAt (always null here).

API reference

#Is warmup actually running?

text
GET /warmup/health

Whether Warmerly's warmup workers are alive. A plain authenticated caller gets the derived flag:

json
{ "ok": true, "healthy": true }

healthy is true when the freshest worker heartbeat is under 10 minutes old. It is a platform-level answer, not a statement about your mailboxes: healthy: true with nothing sending means the problem is your campaign or mailbox configuration, and GET /campaigns/{id}/readiness will say which. Staff callers additionally get lastTick per worker.

POST /warmup/run exists but is ops-only: it triggers a planner/dispatcher pass across every workspace on the platform, so it returns 404 for a customer key. Warmup runs continuously on its own; there is nothing to trigger.

API reference

#Errors

An account ID that doesn't exist returns 404 on any of the endpoints above:

json
{ "error": { "code": "not_found", "message": "not_found", "details": null } }

Missing or invalid X-Api-Key returns 401, following the same shape described in Authentication.

Was this page helpful?