Email tools

Email Warmup API: Start, Pause and Monitor

How to run mailbox warmup through the Outreach2day email warmup API: enable, pause and disable operations, idempotency keys, polling and warmup states.

Max Korolev
Max KorolevFounder & CEO, Outreach2dayUpdated 6 min read
On this page

An email warmup API lets you start, pause and check mailbox warmup from code. That matters once you manage mailboxes in batches: a new client's mailboxes should start warming the day they are ready, a mailbox with problems should stop, and your own tools should know which mailboxes are still warming.

This article covers the three warmup endpoints of the Outreach2day API, with curl and Python examples. It assumes you have an API key and a workspace ID; the cold email API overview explains both. The curl examples use two shell variables:

export O2D="https://public.outreach2day.com"
export OUTREACH2DAY_API_KEY="<your key from the API keys page>"

What Warmup Does and How Long It Takes

Warmup sends a small, growing number of emails between a new mailbox and other mailboxes that open and reply to them. Mailbox providers see a sender with a normal history before any cold email goes out. The email warmup service article explains the mechanism, and the warmup tools comparison lists the options.

Plan for at least two weeks of warmup before a new mailbox sends cold email (see how long to warm up before sending), and keep warmup running while the mailbox sends.

Email Warmup API Endpoints

Endpoint

What it does

POST /warmup/operations?workspaceId=

Starts an operation: enable, pause, disable or remove for a set of mailboxes

GET /warmup/operations/{operation_id}?workspaceId=

Returns the operation's status and a result per mailbox

GET /warmup/mailboxes?workspaceId=

Returns the current warmup state of every mailbox in the workspace

All three use the query parameter workspaceId and the header Authorization: Bearer ot2d_....

Warm Up Inboxes via API in Batches

The request body needs an action, the mailboxes and an idempotencyKey:

curl -X POST "$O2D/warmup/operations?workspaceId=123" \
  -H "Authorization: Bearer $OUTREACH2DAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "enable",
    "mailboxes": [
      "anna@acmemail.com",
      "ben@acmemail.com"
    ],
    "idempotencyKey": "client-a-enable-2026-09-28"
  }'

The response is the operation:

{
  "operationId": "...",
  "status": "requested",
  "action": "enable",
  "workspaceId": 123,
  "requestedCount": 2,
  "prevalidatedCount": 2,
  "acceptedCount": 0,
  "rejectedCount": 0,
  "successCount": 0,
  "failureCount": 0,
  "rejections": [],
  "items": [
    {
      "mailboxId": 501,
      "mailbox": "anna@acmemail.com",
      "status": "requested"
    }
  ]
}

Items carry more fields (timestamps, errorCode, errorMessage); the example is shortened.

Select Mailboxes by Filter Instead of by Address

For a whole domain, you can send a target instead of a list of addresses. With "mode": "all_filtered", the operation applies to every mailbox that matches the filters, for example all mailboxes on one domain:

{
  "action": "enable",
  "target": {
    "mode": "all_filtered",
    "filters": {"domainId": [101]}
  },
  "idempotencyKey": "enable-domain-101-2026-09-28"
}

Filters also accept status, warmupState, tagId and search, and you can leave out mailboxes with excludedMailboxes. The domain IDs come from GET /domains?workspace_id=123.

Idempotency Keys

idempotencyKey is required and must be 8 to 128 characters. If a request with the same key already created an operation in the workspace, the API returns that operation instead of starting a new one. So when a request times out and you are not sure it arrived, send it again with the same key.

The key is matched per workspace, whatever the body says: a second request with the same key but a different action or mailbox list returns the first operation and does nothing. Build one key per batch, for example the client, the action and a batch number or a short hash of the addresses. Do not generate a new random key per attempt, or a retry starts a second operation.

Poll the Operation Until It Finishes

Warmup changes are applied in the background. Poll GET /warmup/operations/{operation_id} while status is requested or processing. Every other status is final:

Final status

Meaning

accepted

The mailboxes were admitted to warmup. For enable this is the normal success result, and successCount stays 0

completed

Every mailbox in the operation succeeded

partial

Part of the batch went through: some mailboxes were rejected or failed

failed, failed_timeout

The operation did not complete

needs_reconciliation

The result is unclear and is being checked

An operation result says the request went through. To see whether a mailbox is actually warming, read GET /warmup/mailboxes (below), where its warmupState becomes warming.

In Python, with the api() helper from the API overview:

import time

WS = 123
op = api("POST", "/warmup/operations",
         params={"workspaceId": WS},
         json={
             "action": "enable",
             "mailboxes": ["anna@acmemail.com"],
             "idempotencyKey": "client-a-enable-001",
         })

RUNNING = {"requested", "processing"}
while op["status"] in RUNNING:
    time.sleep(10)
    op = api("GET",
             f"/warmup/operations/{op['operationId']}",
             params={"workspaceId": WS})

print("operation", op["status"])
for r in op["rejections"]:
    print("rejected", r["mailbox"], r["code"])

Rejected Mailboxes

Mailboxes that cannot take the action are listed in rejections with a code, and the rest of the batch goes ahead:

code

Cause

MAILBOX_NOT_FOUND

The address is not a mailbox in this workspace

MAILBOX_STATUS_NOT_ALLOWED

The mailbox status does not allow warmup (for example, it is still being set up or is deactivated)

Warmup accepts mailboxes whose status is active, warmup_in_progress, warmup_paused or warmup_error. If every mailbox is rejected, the operation ends as failed.

A request with no valid addresses returns 400.

Free guide19 min read

Send 100,000 cold emails a month

Domains and mailboxes you need, DNS, warmup, lists, copy and follow-ups, with a launch checklist, sent to your inbox.

Unsubscribe in one click.

Pause, Disable or Remove Warmup

The same endpoint handles the other actions:

  • pause stops warmup for now. Use it while you investigate a mailbox, then send enable again.

  • disable turns warmup off for the mailbox.

  • remove removes the mailbox from warmup. It is the only action that also accepts mailboxes whose status blocks the other actions, such as deactivated ones.

curl -X POST "$O2D/warmup/operations?workspaceId=123" \
  -H "Authorization: Bearer $OUTREACH2DAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "pause",
    "mailboxes": ["ben@acmemail.com"],
    "idempotencyKey": "pause-ben-2026-09-28"
  }'

Read the Warmup State of Every Mailbox

GET /warmup/mailboxes returns one row per mailbox:

curl "$O2D/warmup/mailboxes?workspaceId=123" \
  -H "Authorization: Bearer $OUTREACH2DAY_API_KEY"
[
  {
    "mailboxId": 501,
    "mailbox": "anna@acmemail.com",
    "mailboxStatus": "active",
    "warmupState": "warming",
    "warmupEnabled": true,
    "warmupStartedAt": "2026-09-14T09:12:00Z",
    "warmupPausedAt": null,
    "latestOperationId": "...",
    "latestOperationStatus": "completed"
  }
]

The rows have a few more fields; the example is shortened. warmupState is one of not_started, warming, paused, error or disabled. warmupStateReason is set when the last observation of the mailbox is out of date (provider_observation_stale).

A report of mailboxes that have warmed for 14 days or more, or that need attention:

from datetime import datetime, timezone

rows = api("GET", "/warmup/mailboxes",
           params={"workspaceId": 123})
now = datetime.now(timezone.utc)

for m in rows:
    started = m.get("warmupStartedAt")
    days = 0
    if started:
        t = datetime.fromisoformat(
            started.replace("Z", "+00:00"))
        if t.tzinfo is None:
            t = t.replace(tzinfo=timezone.utc)
        days = (now - t).days
    ready = m["warmupState"] == "warming" and days >= 14
    flag = "ready" if ready else m["warmupState"]
    print(m["mailbox"], days, flag)

Fourteen days is the minimum. Before a mailbox sends cold email, also check its DNS with the SPF, DKIM and DMARC test, then add the mailboxes to Instantly or Smartlead if you send from there. To run these calls from Claude with an approval step, see the MCP server for email warmup.

Set it up from your AI assistant. Add https://public.outreach2day.com/mcp to Claude, ChatGPT, Cursor, VS Code or another MCP client and sign in with your Outreach2day account when it asks. To add it to every MCP client on your computer at once, run the installer:

curl -fsSL https://outreach2day.com/install | sh

The assistant starts warmup on new mailboxes, reports each mailbox's warmup state and tells you when a mailbox has had its 14 days. Setup per assistant: Outreach2day MCP.

FAQ

Does the API choose the warmup volume?

No. The API turns warmup on, off or into pause; daily warmup volume is managed by Outreach2day and is not a request field. Each mailbox sends 25+ warmup emails a day to Google recipients, 25 to Outlook and 25 to other providers, so 75+ warmup emails a day in total.

Does the API return a reputation score or inbox rate?

No. It returns warmup state (warmupState, warmupStartedAt, the latest operation). Mailbox analytics are in the app.

Should warmup stay on while I send?

Yes. Keep it running while the mailbox sends cold email; it is included in the mailbox price.

How often should I poll?

Every few seconds for an operation you just started, and once a day or less for GET /warmup/mailboxes. The API allows 10 requests a second, 300 a minute, 1,000 an hour and 10,000 a day per user and per workspace.

Can I start warmup on mailboxes that are not in Outreach2day?

No. Operations only apply to mailboxes in the workspace; other addresses come back as MAILBOX_NOT_FOUND.

Is warmup included in the price?

Yes. Warmup is included in the per-mailbox price on every plan; see pricing.

Get Started

Create an account and order mailboxes in the app, then create a key on the API keys page and start warmup with the request above.

Free guide

Send 100,000 cold emails a month

How to send 100,000 cold emails a month while keeping quality, engagement and conversion rates high: domains, mailboxes, warmup, leads, copy and scaling

Step-by-step playbook · 19 min read
  • Email tools9 min read

    Cold Email API for Domains, Warmup and Replies

  • Email tools8 min read

    MCP Server for Email Warmup With Claude

  • Email tools6 min read

    How to Connect Cursor to Cold Email

  • Free tool

    Email list cleaner

Deliverability call

Talk to our deliverability team

Book a call with the people who run our mailbox infrastructure. We look at your current setup and tell you what to change.

  • Review your domains, DNS records and current inbox placement
  • Size the setup: domains, mailboxes per domain and daily volume per mailbox
  • Plan warmup and the move from your current provider or sequencer

Free call

See open times in your time zone and book on this page.

Or open the booking page