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

Accounts

API reference16 min read17 sections

An account (also called a mailbox or sender account) is a connected email inbox used for warmup and/or campaign sending, for example a Gmail, Outlook, Zoho, Namecheap, or custom SMTP/IMAP mailbox. Every account belongs to a project and carries its own warmup status, ramp schedule, health score, and sending limits.

All endpoints below require X-Api-Key. Only connecting mailboxes (POST /accounts and POST /accounts/bulk) requires X-Project-Id; GET /accounts uses it when sent. Every /accounts/{id}/... endpoint finds the project from the account id in the URL, and answers 403 forbidden for an account in a workspace you do not belong to and 400 for an id that is not a valid UUID. See Authentication.

API reference

#The account object

Responses never include raw credentials: SMTP/IMAP passwords and OAuth tokens are encrypted at rest and stripped from the API response entirely. The object carries every stored setting of the mailbox, so it has more fields than the abridged example below (for example lastCampaignSendAt, warmupEnabled, warmupStartedAt, warmupActiveDays, campaignSendingPausedAt, slowRampEnabled, managedBy, dkimSelector and trackingDomainVerified). A typical account looks like this:

json
{
  "id": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
  "projectId": "8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a",
  "kind": "real",
  "email": "sales@yourdomain.com",
  "displayName": "Alex Carter",
  "provider": "custom",
  "authMethod": "password",
  "smtpHost": "smtp.yourdomain.com",
  "smtpPort": 587,
  "smtpUser": "sales@yourdomain.com",
  "imapHost": "imap.yourdomain.com",
  "imapPort": 993,
  "imapUser": "sales@yourdomain.com",
  "timezone": "UTC",
  "status": "aging",
  "createdAt": "2026-06-20T09:12:00.000Z",
  "activatedAt": null,
  "lastHealthScore": null,
  "lastSendAt": null,
  "consecutiveFailures": 0,
  "needsReconnect": false,
  "notes": null,
  "dnsLastCheckedAt": null,
  "dnsOverallStatus": null,
  "senderFirstName": null,
  "senderLastName": null,
  "fromName": null,
  "replyToAddress": null,
  "dailyCampaignLimit": 30,
  "minWaitMinutes": 10,
  "trackingDomain": null,
  "warmupEnabled": true,
  "warmupDailyLimit": null,
  "warmupStartedAt": "2026-06-20T09:12:00.000Z"
}

status is one of aging, active, paused, flagged, dead. Newly connected accounts start as aging while warmup ramps up sending volume.

API reference

#List accounts

text
GET /accounts

Returns the accounts for a single project when X-Project-Id is set. If you omit X-Project-Id, this endpoint falls back to returning every account across all projects in your active workspace (the one named in X-Workspace-Id if you belong to it, otherwise your own workspace): useful for a workspace-wide overview. Mailboxes whose hosted subscription has been cancelled are left out.

Add ?view=summary for a lighter list with only id, email, provider, status, todayQuota, lastHealthScore, needsReconnect, campaignSendingPausedAt, managedBy, warmupStartedAt and createdAt.

bash
curl https://app.warmerly.com/api/v1/accounts \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Project-Id: 8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a"
json
{
  "accounts": [
    {
      "id": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
      "email": "sales@yourdomain.com",
      "provider": "custom",
      "status": "aging",
      "todayQuota": 4,
      "sentToday": 2,
      "campaignSendBlocked": false
    }
  ]
}

The default view returns the full account object plus baseQuota and todayQuota (today's warmup target), sentToday, receivedToday and todayPlan (today's warmup numbers) and, when X-Project-Id is set, campaignSendBlocked (true when the campaign engine will never send a campaign from that mailbox). The example above shows only some of those fields.

API reference

#Connect an account

text
POST /accounts

Connects a mailbox using manual SMTP/IMAP credentials. Requires X-Project-Id and a plan that includes the email channel, see the errors below. Fields you send that are not in the table are ignored.

FieldTypeRequiredDescription
emailstringYesThe mailbox's email address.
displayNamestringNoFriendly name shown in the dashboard.
providerstringYesOne of gmail, outlook, zoho, namecheap, custom.
smtp.hoststringYesSMTP server hostname.
smtp.portintegerYesSMTP server port.
smtp.userstringYesSMTP username.
smtp.passstringYesSMTP password or app password. Encrypted at rest, never returned.
imap.hoststringYesIMAP server hostname.
imap.portintegerYesIMAP server port.
imap.userstringYesIMAP username.
imap.passstringYesIMAP password or app password. Encrypted at rest, never returned.
timezonestringNoIANA timezone for scheduling sends. Defaults to UTC.
bash
curl -X POST https://app.warmerly.com/api/v1/accounts \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "X-Project-Id: 8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sales@yourdomain.com",
    "displayName": "Alex Carter",
    "provider": "custom",
    "smtp": { "host": "smtp.yourdomain.com", "port": 587, "user": "sales@yourdomain.com", "pass": "app-password" },
    "imap": { "host": "imap.yourdomain.com", "port": 993, "user": "sales@yourdomain.com", "pass": "app-password" },
    "timezone": "Europe/London"
  }'

Returns 201 with the created account:

json
{
  "account": {
    "id": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
    "projectId": "8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a",
    "kind": "real",
    "email": "sales@yourdomain.com",
    "displayName": "Alex Carter",
    "provider": "custom",
    "authMethod": "password",
    "smtpHost": "smtp.yourdomain.com",
    "smtpPort": 587,
    "smtpUser": "sales@yourdomain.com",
    "imapHost": "imap.yourdomain.com",
    "imapPort": 993,
    "imapUser": "sales@yourdomain.com",
    "timezone": "Europe/London",
    "status": "aging",
    "warmupEnabled": true,
    "warmupStartedAt": "2026-06-20T09:12:00.000Z"
  }
}

The response is the full account object (abridged above). On creation, Warmerly starts warmup (warmupStartedAt is set) and kicks off a DNS pre-flight check (SPF/DKIM/DMARC/MX) in the background; check dnsOverallStatus on a subsequent GET to see the result. Connecting an address that is already in the same project returns 409 conflict. SMTP or IMAP hosts that point at private or internal addresses are refused with 400 bad_request.

API reference

#Get an account

text
GET /accounts/{id}
bash
curl https://app.warmerly.com/api/v1/accounts/3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10 \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{
  "account": {
    "id": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
    "email": "sales@yourdomain.com",
    "provider": "custom",
    "status": "aging",
    "lastHealthScore": 87,
    "todayQuota": 4,
    "spamPlacementScore": null
  }
}

Returns the full account object (abridged above) plus trackingCnameTarget, sandboxSendingBlock (a sentence, only for the free sandbox mailbox), sendingFlag, todayQuota (today's warmup target), capacityExplain (why the warmup quota is what it is), spamPlacementScore and workspaceCapOverride. Returns 404 if the account doesn't exist.

API reference

#Update an account

text
PATCH /accounts/{id}

Updates settings on an existing account. Only send the fields you want to change. A few of the most commonly used fields:

FieldTypeDescription
displayNamestringFriendly name shown in the dashboard.
timezonestringIANA timezone for scheduling sends.
notesstringFreeform notes shown in the dashboard.
dailyCampaignLimitinteger (0-1000)Max campaign emails sent per day from this account.
minWaitMinutesinteger (0-1440)Minimum wait between campaign emails from this account.
slowRampEnabledbooleanTurns the slower campaign volume ramp on or off.
dailyInboxPlacementTestLimitinteger (0-100)Daily limit on inbox placement tests for this account.
warmupDailyLimitinteger (1-500) or nullPer-mailbox ceiling on warmup emails per day. null removes it.
senderFirstName / senderLastNamestring or nullOverrides the sender identity used in campaign merge fields.
fromNamestring (max 80) or nullThe name recipients see in the From header, for example your company. Empty or null falls back to first and last name. Does not change {{senderFirstName}}.
signatureHtmlstring or nullSignature for outgoing campaign mail.
replyToAddressvalid email, "" or nullReply-to address for outgoing campaign mail. "" clears it.
trackingDomainstring or nullA custom tracking hostname. It must be a bare hostname, and changing it resets its verification until the next DNS check.
dkimSelectorstring or nullThe DKIM selector to check first in DNS checks.
smtpUser / imapUservalid email or nullLogin identity override, for accounts connected with OAuth only (400 otherwise).
projectIdstring (UUID)Moves the mailbox to another project, including one in another workspace. Needs owner or admin on both sides.

These are the only fields PATCH accepts. Any other field in the body is ignored without an error, so check the returned account to confirm a change landed. On the free sandbox mailbox (managedBy: "warmerly_sandbox") dailyCampaignLimit, minWaitMinutes, slowRampEnabled and dailyInboxPlacementTestLimit are refused with 400.

bash
curl -X PATCH https://app.warmerly.com/api/v1/accounts/3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10 \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "dailyCampaignLimit": 45, "notes": "Ramping faster for Q3 push" }'
json
{
  "account": {
    "id": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
    "dailyCampaignLimit": 45,
    "notes": "Ramping faster for Q3 push"
  }
}

The response is the full account object (abridged above) with trackingCnameTarget added.

API reference

#Disconnect an account

text
DELETE /accounts/{id}

Permanently disconnects and removes the account, including its warmup history. For a Warmerly-hosted mailbox it also suspends the mail account on Warmerly's mail server. An optional JSON body can carry reason and note (strings), which are recorded for the audit log.

bash
curl -X DELETE https://app.warmerly.com/api/v1/accounts/3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10 \
  -H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
json
{ "ok": true }

Returns 404 if the account doesn't exist.

API reference

#Connect several accounts at once

text
POST /accounts/bulk

Requires X-Project-Id and a plan that includes the email channel (403 channel_not_included otherwise). Takes { "accounts": [...] }, where each item has email, provider (gmail | outlook | zoho | namecheap | custom), smtp and imap objects, and optional displayName and timezone (default UTC), the same fields as POST /accounts. SMTP or IMAP hosts that point at private or internal addresses refuse the whole batch with 400 bad_request.

Capacity is all-or-nothing: if the batch would breach the plan's mailbox limit the whole request is refused with 402 plan_capacity rather than importing the first few. A billing-locked workspace is refused with 402 billing_locked. Past that gate each account is inserted independently, so one bad row does not lose the others:

json
{
  "results": [
    { "index": 0, "email": "a@yourdomain.com", "ok": true, "id": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10" },
    { "index": 1, "email": "b@yourdomain.com", "ok": false, "error": "duplicate key value violates unique constraint" }
  ]
}

A failed row carries the database's error text in error, so treat it as debugging context, not something to parse. Each connected mailbox starts with status: "aging" and warmup enabled (warmupStartedAt is set). Unlike POST /accounts, the bulk route does not start the DNS pre-flight check itself.

API reference

#Check settings before you connect

text
POST /accounts/detect
POST /accounts/test-connection

detect takes { "email": "you@yourdomain.com" } (3 to 320 characters) and returns { "detection": { … } }: Warmerly's best guess at the provider, from the domain's live MX records, with the SMTP/IMAP hosts and ports the dashboard's connect wizard would prefill. A domain it cannot place comes back as an unsupported kind with a human-readable note rather than an error.

test-connection takes full smtp and imap objects (host, port from 1 to 65535, user, pass, all required) and reports whether each side authenticated, without saving anything. Use it to surface a credential problem before creating an account that would immediately pause. Hosts that point at private or internal addresses are refused with 400 bad_request. On a failure, message is a plain-language reason; on success it is omitted.

json
{ "smtp": { "ok": true }, "imap": { "ok": false, "message": "Invalid credentials" } }
API reference

#Domain health (records + blocklists in one call)

text
GET  /accounts/{id}/domain-health
POST /accounts/{id}/domain-health

The single call behind the domain-health card in the dashboard, and the one to reach for first: it returns the record checks and the blocklist checks for the sending domain and its sending IP together, with per-record advisories rather than a bare pass/fail. GET reads the latest stored results; it does not run a new check. Any of dns, domainBlocklist and ipBlocklist is null when there is no stored result for it yet.

json
{
  "domain": "yourdomain.com",
  "providerManaged": false,
  "dns": {
    "spfStatus": "pass",
    "dkimStatus": "none",
    "dmarcStatus": "pass",
    "mxStatus": "pass",
    "findings": [{ "record": "dkim", "severity": "warn", "title": "No DKIM record found" }],
    "checkedAt": "2026-09-12T08:14:00.000Z"
  },
  "domainBlocklist": { "subject": "yourdomain.com", "status": "clean", "listings": [], "zonesQueried": ["dbl.spamhaus.org"], "zonesErrored": [], "checkedAt": "2026-09-12T08:14:00.000Z" },
  "ipBlocklist": { "subject": "203.0.113.10", "via": "smtp.yourprovider.com", "status": "clean", "listings": [], "zonesQueried": [], "zonesErrored": [], "checkedAt": "2026-09-12T08:14:00.000Z" },
  "ipSkippedReason": null
}
FieldDescription
providerManagedtrue for gmail.com, outlook.com and the like: the records belong to the provider, so a DKIM reading of them is unreliable and nothing here is your responsibility to fix.
dns.findingsAdvisories beyond presence, SPF's all qualifier and its 10-lookup budget, DKIM key length and test mode, DMARC policy/pct/rua.
domainBlocklist / ipBlocklistListings on public blocklists, with the zones actually queried and any that errored, so a partial answer is visible as partial.
ipSkippedReasonWhy there is no IP section (a provider-shared host, an unresolvable SMTP host). A hole with no reason reads as something silently broken.

POST re-runs the DNS check and returns the same payload. It is rate-limited internally: a blocklist re-check inside the minimum interval (6 hours) reuses the stored result rather than re-querying third-party blocklist zones, so holding down a refresh button cannot multiply Warmerly's query volume across them.

The two endpoints below remain, and are the narrower views of the same data.

API reference

#DNS and domain checks

text
GET  /accounts/{id}/dns
POST /accounts/{id}/dns

GET returns the last 10 DNS checks for the account, newest first, plus latest as a convenience (the first item of history, or null). POST re-runs the check now and returns { "result": { … } }, or 400 with the error text if the check could not run.

json
{
  "latest": {
    "id": "b1e7c2a4-0c3e-4c0a-9a6f-2b7d8e5f1a33",
    "accountId": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
    "domain": "yourdomain.com",
    "checkedAt": "2026-09-11T08:14:00.000Z",
    "spfStatus": "pass",
    "spfRecord": "v=spf1 include:_spf.google.com ~all",
    "dkimStatus": "none",
    "dkimSelector": null,
    "dkimRecord": null,
    "dmarcStatus": "pass",
    "dmarcRecord": "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com",
    "mxStatus": "pass",
    "mxRecords": ["aspmx.l.google.com"],
    "findings": [],
    "overallStatus": "warn"
  },
  "history": [ { "id": "b1e7c2a4-0c3e-4c0a-9a6f-2b7d8e5f1a33", "…": "same shape as latest" } ]
}

Each record status (SPF, DKIM, DMARC and MX) is pass, none, fail or error; overallStatus is pass, warn or fail. mxStatus is null on older checks. Two things to know before you treat a result as a verdict:

  • error means DNS did not answer (timeout or resolver problem), not that the record is wrong. Re-check rather than re-publishing records.
  • dkimStatus: "none" can mean "present but under a selector Warmerly could not guess", the check tries a list of common selector names and cannot discover an arbitrary one. Set dkimSelector on the account (see Update an account) and re-check.

SPF, DKIM, DMARC and MX are checked here, and if the account has a tracking domain its CNAME is re-verified on the same run. Everything each record does, and the order to publish them in, is in the DNS records checklist.

API reference

#Placement tests

text
GET  /accounts/{id}/placement
POST /accounts/{id}/placement

GET lists this account's 10 most recent placement tests, newest first, with a summary:

json
{
  "tests": [
    {
      "id": "6d2f0b1a-9c4e-4f77-bb31-2a9e7c4d8e55",
      "accountId": "3a6f9e2e-4b7a-4e9a-9b1d-6e3a2f8c1a10",
      "startedAt": "2026-09-10T09:00:00.000Z",
      "completedAt": "2026-09-10T09:01:04.000Z",
      "status": "done",
      "score": 82,
      "details": { }
    }
  ],
  "summary": {
    "latestScore": 82,
    "latestStatus": "done",
    "latestStartedAt": "2026-09-10T09:00:00.000Z",
    "latestDegraded": false,
    "trendDelta": 6
  }
}

status is pending, running, done or failed. trendDelta is the score change against the previous comparable test, or null when there is nothing fair to compare with. latestDegraded is true when the latest test was measured against Warmerly's own warmup mailboxes rather than real seed inboxes. Its score is not a placement result and is not comparable with a clean one. Tests recorded before this flag existed also read as degraded.

POST sends live email and spends quota. It dispatches probe messages from this mailbox to seed addresses and debits one of your monthly placement tests (counted per user, as in Usage & limits). Out of allowance returns 402 with error.code quota_exhausted and details carrying used, limit, upgradeUrl and, when credits can cover it, a topUp offer. The charge is taken before the run and handed back if the run fails, which returns 400 bad_request. The response is { "test": { … } }. The call waits at least 30 seconds for delivery before responding, so expect it to take that long or more.

API reference

#Blocklist status

text
GET  /accounts/{id}/blocklist
POST /accounts/{id}/blocklist

Whether the account's sending domain appears on public DNS blocklists. GET returns the last 10 checks; POST runs one now, and returns a recent result with cached: true rather than re-querying the zones if one was taken moments ago.

json
{ "domain": "yourdomain.com", "applicable": true, "latest": null, "history": [] }

applicable: false means the domain belongs to a free consumer provider (gmail.com and friends), which is never checked. A shared provider's reputation is not yours to read.

API reference

#Activity and warmup history

text
GET /accounts/{id}/stats
GET /accounts/{id}/events
GET /accounts/{id}/log
GET /accounts/{id}/warmup-jobs

stats is today-and-7-day counters for one mailbox. "Today" is the UTC day:

json
{ "sentToday": 24, "pendingToday": 6, "inboxToday": 22, "spamToday": 2, "openRate": 0.415, "replyRate": 0.123, "todayPlan": null, "firstWarmupSend": null }

openRate and replyRate here are warmup-network rates over 7 days, returned as fractions between 0 and 1 (not percentages), not campaign performance. The two are different metrics with similar names, explained in How warmup works. Either can be null when there is nothing to divide by. todayPlan is the mailbox's warmup plan for its local day and firstWarmupSend describes the first warmup email during the first 7 days after warmup starts (null afterwards).

events returns up to 200 lifecycle events, newest first, as { "events": [...] } with { id, accountId, type, payload, createdAt } each. type is one of smtp_fail, imap_fail, flagged, auto_paused, auto_resumed, manual_pause, aged_in, marked_dead, oauth_refresh_fail, oauth_connected and oauth_reconnected.

log pages through the individual warmup messages this mailbox sent, newest first, as { "jobs": [...], "nextCursor": "..." }: ?limit= (1-100, default 50) and ?cursor= (pass the previous response's nextCursor, a timestamp). nextCursor is null only when a page comes back empty, so keep paging until jobs is empty.

warmup-jobs returns the 100 most recent warmup messages this mailbox sent or received, newest first by scheduled time, as { "jobs": [...], "dailySummary": [...] }, with no paging.

API reference

#Pause, resume and warmup

text
POST /accounts/{id}/pause
POST /accounts/{id}/resume
POST /accounts/{id}/warmup/toggle
POST /accounts/{id}/send-test
POST /accounts/{id}/oauth/reconnect

pause sets the mailbox's status to paused and resume sets it back to active; both return { "ok": true }. resume also clears a campaign-sending pause. If the mailbox is flagged as needing a reconnect, resume first tests the stored login and answers 409 with code reconnect_required when it fails. Resuming a dead or flagged mailbox can return 402 plan_capacity when the workspace is at its mailbox limit.

warmup/toggle takes { "enabled": true | false } (required) and is the supported way to start or stop warmup for one mailbox; it returns { "account": { … } }. Enabling it for the first time also stamps the warmup start date the ramp is measured from.

The free mailbox every new account is given (managedBy: "warmerly_sandbox") keeps warming for as long as you have it: pause, and warmup/toggle with { "enabled": false }, answer 409 with code sandbox_warmup_locked for it. Delete it (DELETE /accounts/{id}) if you no longer want it.

send-test takes { "to": "you@example.com" } (a valid email) and sends one real message through this mailbox, with a fixed subject and body, the fastest way to prove SMTP works end to end. It returns { "ok": true }. A failed send returns 500 with { "error": { "message": ... } } where message is smtp_auth_failed, smtp_timeout, smtp_connection_failed or send_failed (this response has no error.code).

oauth/reconnect is for mailboxes connected with Google or Microsoft sign-in. It returns { "auth_url": "..." }, a consent URL to open in a browser; completing it reconnects the mailbox (for example a Microsoft 365 mailbox that authenticates but fails to send, see Troubleshooting). It returns 400 for a mailbox that was not connected with OAuth.

API reference

#Channel account limits

text
GET /accounts/limits

No X-Project-Id; the OneMail part follows your active workspace (X-Workspace-Id). Reports connected-vs-included counts for the per-account channels (LinkedIn and WhatsApp, counted for your user) with an overage price in USD, plus the OneMail hosted-mailbox add-on: its current quantity, per-mailbox price in minor units, the tier discount, and the full price ladder.

json
{
  "limits": {
    "linkedin": { "used": 1, "included": 0, "overageUsd": 19 },
    "whatsapp": { "used": 0, "included": 2, "planIncluded": 2, "slots": 0, "overageUsd": 6 },
    "onemail": { "used": 4, "included": null, "unitAmountMinor": 199, "currency": "usd", "billableQuantity": 4, "recurringAmountMinor": 796, "tier": "growth", "discountPercent": 33 }
  }
}

whatsapp.included is the whole ceiling: planIncluded from the plan plus slots bought on top. linkedin.overageUsd and whatsapp.overageUsd are the USD price of one more slot, the same figure the slot checkout uses; they are always in USD, whatever currency your plan bills in. The onemail object has more fields than shown: packSlots, packSlotsFree, minimumQuantity, tierPrices and upgrade.

included: null on onemail is not a missing value: hosted mailboxes have no cap at all, they are billed per unit and never consume a plan mailbox slot, so never render that one as a progress bar. For monthly allowances and plan capacity see Usage & limits.

API reference

#OAuth-connected accounts

Outlook.com and Microsoft 365 accounts can also be connected via OAuth instead of raw SMTP/IMAP credentials. That is a browser sign-in flow built for the dashboard's Add mailbox → Microsoft wizard, and it is not part of this reference. Gmail and Google Workspace connect with SMTP/IMAP and a Google app password (smtp.gmail.com:465, imap.gmail.com:993); mailboxes connected with Google OAuth before 2026-09-15 keep working. Once connected (by either method), the resulting account is fully manageable through this API.

API reference

#Errors

Attempting to connect an account on a plan without the email channel returns 403:

json
{
  "error": {
    "code": "channel_not_included",
    "message": "Your plan does not include email features. Upgrade at /pricing.",
    "details": { "upgradeUrl": "/pricing" }
  }
}

An invalid request body (e.g. malformed smtp/imap object) returns 400:

json
{ "error": { "code": "bad_request", "message": "invalid_body", "details": { /* zod field errors */ } } }

Connecting beyond your plan's mailbox limit returns 402 with error.code plan_capacity, and a billing-locked workspace returns 402 billing_locked. A missing account on GET, PATCH, or DELETE returns 404:

json
{ "error": { "code": "not_found", "message": "not_found", "details": null } }
Was this page helpful?