# Authentication

> How to authenticate requests to the Warmerly API using an X-Api-Key header, where to generate and revoke a key, and which endpoints also require X-Project-Id.

Source: https://warmerly.com/docs/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](https://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](https://warmerly.com/docs/ai) | Access token starting `wm_at_` | Any |

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.

## Getting a key

1. Sign in to [app.warmerly.com](https://app.warmerly.com/login).
2. Go to **Settings > Platform API keys**. On a plan below Agency this page shows an upgrade prompt instead of the form.
3. 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](https://warmerly.com/docs/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](https://warmerly.com/docs/workspaces) 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 own workspace (your oldest active one), because an API key has no browser selection to fall back on. A workspace you are not a member of is ignored the same way. If you work across several workspaces, send it explicitly on every workspace-scoped call. |

```bash
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`, `/alerts` |
| Workspace | `X-Workspace-Id`, else your own workspace | `/projects`, `/suppression`, `/usage/quotas`, `/billing/me`, `/accounts/limits`, `/warmup/stats`, `/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 for access. The Agency plan check still follows the headers you send (see below), so for a campaign in a workspace other than your own, send `X-Project-Id` or `X-Workspace-Id` too. `/accounts/{id}/…` endpoints work the same way.

## 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](https://warmerly.com/docs/errors) 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:

```json
{ "error": { "code": "too_many_requests", "message": "rate_limit_exceeded", "details": null } }
```

This per-key `429` carries 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)](https://warmerly.com/docs/ai), and the approval screen is explained in [How do I connect, check or revoke an AI app?](https://warmerly.com/docs/help/manage-connected-ai-apps).

## 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}`](https://warmerly.com/docs/keys#revoke-a-key). 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:

1. Create a new key and name it after the integration it replaces.
2. Deploy the new key to your application or secret store.
3. Check the old key's `lastUsedAt` in [`GET /keys`](https://warmerly.com/docs/keys#list-keys), or the last-used time on the settings page. When it stops advancing, nothing is using it any more.
4. 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?](https://warmerly.com/docs/help/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 not plan-gated, so it still works from the dashboard after a downgrade. A revoke sent with the key itself is refused with `403 agency_plan_required` while the workspace is off Agency.

## Errors

An invalid or revoked key returns `401`:

```json
{ "error": { "code": "unauthorized", "message": "unauthorized", "details": null } }
```

A missing or malformed `X-Project-Id` on a project-scoped endpoint returns `400`:

```json
{ "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](https://warmerly.com/docs/errors).

## 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.

## More in Get started

- [Getting started with Warmerly: from signup to your first send](https://warmerly.com/docs/help/getting-started-checklist): A checklist for the whole Warmerly journey: create your account, use the free mailbox, connect your own…
- [Glossary of Warmerly terms](https://warmerly.com/docs/help/glossary): Plain-English definitions of the words used in the Warmerly app: mailbox, OneMail, free (sandbox) mailbox,…
