Email tools

Cold Email API for Domains, Warm-Up and Replies

What the Outreach2day cold email API covers: keys, workspaces, domain checks and purchase, mailbox lists, warm-up, replies and export to Instantly or Smartlead.

Outreach2dayPublished 8 min read
On this page

A cold email API lets your own code do what you would otherwise click through in a dashboard: check and buy sending domains, list mailboxes, start warm-up, read replies and add mailboxes to your sequencer (the sending tool, such as Instantly or Smartlead). This page lists the Outreach2day REST API endpoints: how to authenticate, what each group does today and what still goes through the app.

Every snippet below uses the real base URL and field names from the public OpenAPI spec at https://public.outreach2day.com/openapi.json. The full reference is at dev.outreach2day.com.

What a Cold Email API Covers

Cold email infrastructure has four layers: sending domains with DNS records, mailboxes on those domains, warm-up for each mailbox, and the tool that sends and reads replies. (Our cold email infrastructure guide explains each layer.) An email infrastructure API is useful when you repeat the same setup for many clients or workspaces, or when you want another system, such as a CRM, a script or an AI agent, to see the state of your mailboxes.

The Outreach2day API covers these jobs:

Job

Endpoints

Workspaces

GET /workspaces, POST /workspaces, GET /workspaces/{workspace_id}

Check domain availability

POST /check_domains

Buy domains

POST /domains/purchase

Connect domains you already own

POST /domains/transfer/checkout, GET /domains/transfer/status

Website redirects

PATCH /domains/redirects

List domains and mailboxes

GET /domains, GET /mailboxes

Edit mailbox names, signatures, forwarding

POST /update_mailboxes

Warm-up

POST /warmup/operations, GET /warmup/operations/{operation_id}, GET /warmup/mailboxes

Replies (unified inbox)

GET /threads, GET /threads/{thread_id}/messages, POST /threads/{thread_id}/reply

Export to Instantly or Smartlead

POST /sequencers, POST /sequencers/{connection_id}/export

A workspace created with POST /workspaces has no subscription or saved card yet: domain purchases in it return 402 until its first order is made in the app.

Cold Email API vs Transactional Email API

Transactional email APIs, such as SendGrid or Mailgun, take a message in an API call and send it from the provider's servers: receipts, password resets, notifications. A cold email API works one level lower. It manages the domains, mailboxes and warm-up, and the email itself goes out from those mailboxes, through Outreach2day or through your sequencer. You do not send cold email by calling a send endpoint per message.

Base URL and Authentication

  • Base URL: https://public.outreach2day.com

  • Auth header: Authorization: Bearer ot2d_...

API keys start with ot2d_. You create them in the app on the API keys page (app.outreach2day.com/api-keys). A key is created in one workspace and is valid for 365 days, but it acts as your user: it can reach every workspace you are a member of, including buying domains there. Keep it as secret as a password. Keep the key in an environment variable, not in code:

export O2D="https://public.outreach2day.com"
export O2D_KEY="ot2d_your_key"

The curl examples in this article and the posts it links to use both variables.

A first call lists the workspaces the key can see:

curl "$O2D/workspaces" \
  -H "Authorization: Bearer $O2D_KEY"

The response is an array of workspaces:

[
  {
    "workspace_id": 123,
    "project_name": "Client A",
    "type": "default",
    "max_mailboxes": 24,
    "description": null,
    "role": "owner"
  }
]

workspace_id is the number every other call needs. Its name differs by endpoint: most take the query parameter workspaceId, GET /mailboxes and GET /domains take workspace_id, and in JSON bodies POST /domains/purchase uses workspace_id while the transfer and redirect endpoints use workspaceId. The examples in this article and in the posts it links to use the exact name each endpoint expects.

A missing key returns 401 with "Unauthorized", a wrong key returns 401 with "Invalid API key", and a workspace the key's owner is not a member of returns 403. Error details are inside detail, for example {"detail": {"error": "PAYMENT_METHOD_REQUIRED", "message": "..."}}.

A Small Python Client

The same calls in Python with requests. The domain, warm-up and export posts reuse this api() helper.

import os
import requests

BASE = "https://public.outreach2day.com"
HEADERS = {"Authorization": f"Bearer {os.environ['O2D_KEY']}"}

def api(method, path, **kwargs):
    r = requests.request(
        method, BASE + path, headers=HEADERS,
        timeout=30, **kwargs,
    )
    r.raise_for_status()
    return r.json()

workspaces = api("GET", "/workspaces")
ws = workspaces[0]["workspace_id"]

mailboxes = api("GET", "/mailboxes",
                params={"workspace_id": ws})
for m in mailboxes:
    print(m["address"], m["status"])

Domains: Check, Buy, Connect

POST /check_domains takes a list of names and returns a status for each: free, taken or not_allowed. GET /domains lists the domains a workspace already has:

curl "$O2D/domains?workspace_id=123" \
  -H "Authorization: Bearer $O2D_KEY"

POST /domains/purchase buys the free ones and charges the card saved on the workspace at once. There is no separate confirm step, so check the list before you send it. The workspace needs a payment method from its first order in the app; without one the call returns 402 with PAYMENT_METHOD_REQUIRED. DNS for bought domains is set up for you.

If you already own domains, POST /domains/transfer/checkout connects them for free: you point the domain's nameservers to the ones GET /domains/transfer/status returns, and DNS is configured once the zone is active.

Both flows, with request bodies and error codes, are in how to buy domains via API.

Mailboxes and Warm-Up

GET /mailboxes?workspace_id=123 lists the mailboxes of a workspace: id, address, names, domain_id, status, signature and forwarding. The response also includes each mailbox's SMTP/IMAP password: do not log it or pass it to an AI assistant. Warm-up state is in GET /warmup/mailboxes.

POST /update_mailboxes writes the same firstName, lastName and signature (all three required) to every mailbox in the list. Leaving out forwardTo clears forwarding. It works on mailboxes in the key's own workspace.

Warm-up runs as operations. You send POST /warmup/operations?workspaceId=123 with an action (enable, pause, disable or remove), a list of mailbox addresses and an idempotencyKey, then poll the operation until it finishes. GET /warmup/mailboxes?workspaceId=123 returns a warmupState per mailbox: not_started, warming, paused, error or disabled. The full flow, with polling and error handling, is in the email warmup API guide.

Replies and Sequencer Export

GET /threads?workspaceId=123 lists reply threads across all mailboxes of a workspace, with cursor pagination (limit up to 100, cursor from next_cursor) and filters such as hasCustomerReply=true. A thread can carry a replyIntent label (the field is left out when a reply has not been classified), for example interested, meeting_request, ooo or unsubscribe. GET /threads/{thread_id}/messages returns the messages of one thread, and POST /threads/{thread_id}/reply answers it.

curl -G "$O2D/threads" \
  -d workspaceId=123 \
  -d hasCustomerReply=true \
  -d limit=20 \
  -H "Authorization: Bearer $O2D_KEY"

If you send from Instantly or Smartlead, the sequencer endpoints connect that account with its API key and add your mailboxes to it. See export mailboxes to Instantly or Smartlead via API.

Limits, Errors and Idempotency

  • Rate limits: 10 requests a second, 300 a minute, 1,000 an hour and 10,000 a day, counted per user and per workspace. Over the limit the API returns 429 with a Retry-After header in seconds.

  • Errors: 400 for invalid input, 401 for a bad key, 402 when a payment is needed or fails, 403 for a workspace you cannot access, 404 for an unknown object, 409 for conflicts and 422 for a body that does not match the schema.

  • Retries: warm-up operations require an idempotencyKey (8–128 characters). Sending the same key again returns the first operation instead of starting a second one, so retrying a warm-up request after a timeout is safe. POST /domains/purchase has no idempotency key: never retry a purchase that timed out before checking GET /domains.

A retry wrapper for 429 only (a 429 is returned before the request does anything, so repeating it is safe). It uses requests, BASE and HEADERS from the client above:

import time

def api_retry(method, path, tries=5, **kwargs):
    for _ in range(tries):
        r = requests.request(
            method, BASE + path, headers=HEADERS,
            timeout=30, **kwargs,
        )
        if r.status_code != 429:
            r.raise_for_status()
            return r.json()
        time.sleep(int(r.headers.get("Retry-After", "1")))
    raise RuntimeError("rate limited")

What Still Goes Through the App

Some steps are not available over the API today:

  • Sign-up and API keys. You create the account and the key in the app.

  • The first payment, for each workspace. Buying mailboxes and adding the first card happen in the app. After that, domain purchases over the API charge the saved card.

  • Ordering new mailboxes. You order mailboxes in the app. The API lists them, edits them and runs their warm-up. The spec also lists POST /v2/mailboxes (and the deprecated POST /mailboxes); they save mailbox records but do not set the mailboxes up, so use the app.

  • Campaigns. The API reference also lists campaign endpoints. They are not enabled for every workspace yet; ask support if you need them.

There is no hosted MCP server for AI assistants yet either. The REST API works from any language or agent framework that can send HTTP requests, and you can build a cold email MCP server over it for Claude or Cursor.

FAQ

Is the API free?

Yes. It is part of every Outreach2day plan; you pay for mailboxes and domains, not for API calls (pricing).

Where is the OpenAPI spec?

At https://public.outreach2day.com/openapi.json. Interactive docs are at https://public.outreach2day.com/docs, and an LLM-friendly summary is at https://public.outreach2day.com/llms.txt.

Can I use x-api-key instead of the Bearer header?

Yes. The API also accepts the key in an x-api-key header for clients that can only send a fixed custom header. Authorization: Bearer is the documented default.

Do API calls count against my sending limits?

API requests have their own rate limits. Replies sent with POST /threads/{thread_id}/reply go out from the mailbox like any other email. How much each mailbox should send a day is a deliverability question; see how to plan mailbox and domain counts for your volume.

Is there an SDK?

No official SDK. You can generate a client from the OpenAPI spec with a tool such as OpenAPI Generator, but some responses are untyped there and the campaign paths are not enabled for every workspace, so plain HTTP as in the examples above is often simpler.

Can I call it from n8n, Make or Zapier?

Any HTTP request step that can send an Authorization: Bearer header can call the API. There is no dedicated Outreach2day app in those tools.

Get Started

Create an account, buy your first mailboxes in the app, then create a key on the API keys page and run the GET /workspaces call above.

Get an API key

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, warm-up, leads, copy and scaling.

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

    How to Buy Domains via API for Cold Email

  • Email tools6 min read

    Email Warmup API: Start, Pause and Monitor

See deliverability issues before they kill performance

Monitor mailbox health in real time, spot degradation early, and keep warmup, protection, and sending in one place.