Authentication
Every request to the Warmerly API is authenticated with an API key, sent as the
X-Api-Key header.
API access is an Agency-plan feature. Without Agency, creating or listing keys, and any
request made with a key, returns 403 agency_plan_required. Upgrade at
warmerly.com/pricing.
Ways to authenticate
| Method | Used by | Credential | Plan |
|---|---|---|---|
| API key | Your own code, scripts and automation tools | X-Api-Key: wmv_... header | Agency |
| Signed-in session | The Warmerly dashboard in your browser | Session cookie | Any |
| Mobile session | The Warmerly Android app | Authorization: Bearer <session token> | Any |
| OAuth connection | AI assistants such as Claude, ChatGPT and Cursor, through the MCP server | Access token starting wm_at_ | Any |
| Transactional sending key | The transactional email API | Authorization: Bearer wm_tx_... or X-Api-Key: wm_tx_... | Transactional plan |
For your own integrations use an API key. The session cookie and mobile token belong to a
signed-in person and are not meant for scripts. The two key types are not interchangeable:
wmv_ keys are refused by the transactional send API, and wm_tx_ keys are refused everywhere
else.
Getting a key
- Sign in to app.warmerly.com.
- Go to Settings > Platform API keys. On a plan below Agency this page shows an upgrade prompt instead of the form.
- Enter a Key name, click Create key, and copy the secret shown. It is only displayed once and cannot be retrieved again. If you lose it, revoke the key and create a new one.
Keys are prefixed wmv_ and are scoped to your user account (they can access every
workspace/project you're a member of, subject to the project you select via
X-Project-Id). You can also create keys programmatically, see API Keys.
A key carries your full access. There are no per-endpoint scopes and no read-only keys, so a key can do whatever you can do in the workspaces you belong to. Warmerly stores only a SHA-256 hash of the key, which is why the secret cannot be shown again. Each user can hold up to 10 active keys.
Required headers
| Header | Required | Description |
|---|---|---|
X-Api-Key | Always | Your API key secret. |
X-Project-Id | Most endpoints | The UUID of the project to operate on (mailboxes, campaigns, and inbox data all belong to a project). Find it in the dashboard URL or via the Workspaces & Projects endpoints. |
X-Workspace-Id | Optional | Which workspace a workspace-scoped endpoint (suppression, usage, billing, channel limits, inbox-wide reads) should act in. Omit it and the call uses your account's currently active workspace, which for a key that never touches the dashboard means the one selected there, not necessarily the one you meant. If you work across several workspaces, send it explicitly on every workspace-scoped call. |
curl https://app.warmerly.com/api/v1/accounts \
-H "X-Api-Key: wmv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-Project-Id: 8f14e45f-ceea-4c6a-8c8d-6b1a5b5c9c1a"Endpoints that aren't project-scoped (e.g. listing your workspaces, managing API
keys) only need X-Api-Key.
There are three scopes, and mixing them up is the most common integration mistake:
| Scope | Identified by | Examples |
|---|---|---|
| Account | the key itself | /keys, /workspaces, /projects, /alerts |
| Workspace | X-Workspace-Id, else your active workspace | /suppression, /usage/quotas, /billing/me, /accounts/limits, /inbox/* |
| Project | X-Project-Id (required) | /accounts, /campaigns, /email-finder/* |
Campaign-scoped endpoints are the exception that needs neither header: /campaigns/{id}/…
resolves access from the campaign id in the URL, so passing a mismatched X-Project-Id
changes nothing.
Which plan is checked
A key has no workspace of its own, so Warmerly checks the plan of the workspace the request acts
on: the project in X-Project-Id if you send one and you are a member of its workspace, otherwise
the workspace in X-Workspace-Id, otherwise your own workspace. Naming a workspace never grants
anything. You can only use the plan of a workspace you already belong to.
If that workspace is not on Agency (for example after a downgrade), requests return
403 agency_plan_required. The key is not revoked: it works again after you upgrade. The plan
check is cached for 60 seconds, so a downgrade or an upgrade can take up to a minute to show.
A suspended account gets 403 account_suspended on every request, including ones made with a
key.
Rate limits
Each key has its own per-minute rate limit (default 60 requests/minute, configurable
per key up to 120). Exceeding it returns 429 too_many_requests. A few endpoints layer
an additional, tighter limit on top of this. See Errors & Rate Limits for the
full picture, including the exact response shape and per-endpoint limits.
The per-key limit is a fixed window per calendar minute (UTC). Each request counts toward the current minute, and once the count passes the key's limit every further request in that minute returns:
{ "error": { "code": "too_many_requests", "message": "rate_limit_exceeded", "details": null } }Responses carry no Retry-After header, so wait for the next minute to begin before retrying,
and spread bulk jobs out instead of sending them in a burst. You choose a key's limit when you
create it with rateLimitPerMin (1 to 120, default 60); the dashboard form uses the default.
Running out of a monthly allowance (verifications, lead exports, lookups) is a different failure
and returns 402, not 429.
Authenticating AI assistants (MCP)
AI assistants do not use API keys. The MCP server at https://app.warmerly.com/api/mcp uses
OAuth 2.1 with PKCE (S256 only), so you approve a connection once on a consent screen and never
copy a secret. Each connection is scoped to one signed-in user's one workspace, and it is
available on every plan including Free.
- Access tokens last 1 hour. Refresh tokens last 30 days and rotate each time they are used.
- Tool calls are limited to 60 per minute per connection, and tools that send email to a real person have a tighter limit.
- To see or cut off a connection, open Settings > Connected apps and revoke it. Removing a member from a workspace also ends their connections to it.
Setup steps for each assistant are in Use Warmerly with AI (MCP), and the approval screen is explained in How do I connect, check or revoke an AI app?.
Revoking a key
Revoke a key any time from Settings > Platform API keys with the Revoke button on its
row, or with DELETE /keys/{id}. Revoked keys fail every request
immediately with 401 unauthorized, there is no grace period.
Rotating a key
Rotate keys on a schedule, and immediately if one may have leaked. Because you can hold 10 active keys, rotation needs no downtime:
- Create a new key and name it after the integration it replaces.
- Deploy the new key to your application or secret store.
- Check the old key's
lastUsedAtinGET /keys, or the last-used time on the settings page. When it stops advancing, nothing is using it any more. - Revoke the old key. Revocation is immediate and cannot be undone.
Key creation and revocation are recorded in the workspace audit log as api_key.created and
api_key.revoked. See What is the audit log?.
Keeping keys safe
- Keep keys in environment variables or a secrets manager, never in source control, front-end code or a mobile app. Anyone holding a key can act as you.
- Use one key per integration, named after it, so you can revoke one without breaking the others.
- Call the API from a server, not from a browser page, where visitors could read the key.
- If a key leaks, revoke it first and investigate second.
- Revoke keys you no longer use. Revoking is always allowed, even after a downgrade.
Errors
An invalid or revoked key returns 401:
{ "error": { "code": "unauthorized", "message": "unauthorized", "details": null } }A missing or malformed X-Project-Id on a project-scoped endpoint returns 400:
{ "error": { "code": "bad_request", "message": "missing X-Project-Id header", "details": null } }| Status | error.code | Cause |
|---|---|---|
401 | unauthorized | Key missing, unknown or revoked. |
403 | agency_plan_required | The workspace the request acts on is not on Agency. |
403 | account_suspended | The account is suspended. |
400 | bad_request | X-Project-Id missing or not a valid UUID. |
429 | too_many_requests | The key's per-minute limit was exceeded. |
The full list is in Errors & Rate Limits.
Common questions
Why do I get 403 agency_plan_required with a valid key? The workspace the request acts on
is not on the Agency plan. Check the X-Project-Id and X-Workspace-Id you are sending.
Can I see my key again? No. Revoke it and create a new one.
Can I limit a key to certain endpoints? No. Keys have no scopes and carry your access.
Does one key work across workspaces? Yes, for every workspace you are a member of, as long as
the workspace you target is on Agency. Send X-Project-Id or X-Workspace-Id to choose.
How is this different from the transactional sending keys? Sending keys (wm_tx_) are for the
transactional email API and belong to one workspace. See Transactional email.
Summarize with AI

