# Authentication

> Authenticate API requests with a Bearer API key or OAuth access token, choose full or sending scope, restrict keys to a domain and handle auth errors.

Every request to the Emailit API must carry a credential in the `Authorization` header. This page covers the two kinds of credentials (API keys and OAuth access tokens), what each scope can do, and every authentication error you can get back.

## API keys

An API key belongs to one workspace, and every request made with it acts on that workspace. Keys look like this:

```text
secret_Xq7pL2mN9vB4kR8tW1yZ6cH3jF5dS0aG
```

That's `secret_` followed by 32 letters and digits. Keys created before the `secret_` format have no prefix, and they keep working.

Create keys in the dashboard under **Email API → API Keys**, or with [Create an API key](/docs/api-reference/api-keys/create/). The secret is shown only once, when you create or [regenerate](/docs/api-reference/api-keys/regenerate/) the key, so store it right away. See [API keys](/docs/developers/api-keys/) for managing them.

The same keys work as the SMTP password for the [SMTP relay](/docs/smtp/settings/).

## Send the key with each request

Use the `Bearer` scheme in the `Authorization` header. The API doesn't accept keys in the query string or request body.

**cURL**

```bash
curl https://api.emailit.com/v2/domains \
  -H "Authorization: Bearer $EMAILIT_API_KEY"
```

**Node.js**

```javascript
const response = await fetch('https://api.emailit.com/v2/domains', {
  headers: { Authorization: `Bearer ${process.env.EMAILIT_API_KEY}` },
});
const domains = await response.json();
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://api.emailit.com/v2/domains",
    headers={"Authorization": f"Bearer {os.environ['EMAILIT_API_KEY']}"},
)
domains = response.json()
```

The SDKs set this header for you when you pass the key to the client.

## Scopes

Each key has one of two scopes. You choose it when you create the key, and you can't change it later.

| Scope | Can call | Use it for |
| --- | --- | --- |
| `full` | Every endpoint in the API. This is the default. | Back-office tools, scripts and integrations that manage domains, contacts, templates or webhooks. |
| `sending` | Only the send endpoints listed below. | Application servers that only send email. |

A `sending` key can call these endpoints and nothing else:

| Endpoint | Description |
| --- | --- |
| `POST /emails` | [Send an email](/docs/api-reference/emails/send/) |
| `POST /emails/{id}` | [Update a scheduled email](/docs/api-reference/emails/update/) |
| `POST /emails/{id}/cancel` | [Cancel an email](/docs/api-reference/emails/cancel/) |
| `POST /emails/{id}/retry` | [Retry an email](/docs/api-reference/emails/retry/) |
| `POST /emails/{id}/forward` | [Forward an email](/docs/api-reference/emails/forward/) |

Reading emails (list, retrieve, raw, body, metadata, attachments and status) needs a `full` key. When a `sending` key calls any other endpoint, the API returns `403` with `Permission denied: full` (or `Permission denied: read` for the email read endpoints).

[All endpoints](/docs/api-reference/endpoints/) lists the scope of every endpoint.

## Restrict a key to one domain

A `sending` key can also be locked to one sending domain. Pass the domain's ID as `sending_domain_id` when you [create the key](/docs/api-reference/api-keys/create/). A restricted key can only send from addresses on that domain. Any other `from` domain returns `403`:

```json
{
  "error": "Domain not authorized",
  "message": "API key is not authorized to send from this domain"
}
```

Domain restrictions apply to `sending` keys only. A `full` key always has access to every domain in the workspace.

## OAuth access tokens

Apps that act on behalf of an Emailit user, such as MCP clients and third-party integrations, don't ask for an API key. They use OAuth 2.1 instead: the user signs in to Emailit, chooses the workspaces the app can use (all of them or only selected ones) and approves the `sending` or `full` scope, and the app receives an access token. The user can change or revoke that access under [Connected apps](/docs/account/connected-apps/).

Send access tokens in the same header as API keys:

```http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
```

An access token is valid for 15 minutes and acts on the grant's default workspace, with the granted scope and the user's role in that workspace. Apps refresh it with the refresh token. See [OAuth apps](/docs/developers/oauth-apps/) to build one.

## Authentication errors

Authentication runs before anything else, so these errors can come back from any endpoint.

| Status | `message` or `error` | Cause | Fix |
| --- | --- | --- | --- |
| `401` | `API key required` | The `Authorization` header is missing or doesn't start with `Bearer `. | Send `Authorization: Bearer <key>`. |
| `401` | `Valid API key required` | The header has the `Bearer` prefix but no token. | Check that the variable holding your key isn't empty. |
| `401` | `Invalid API key` | The key doesn't exist, was deleted, or was regenerated (the old secret stops working), or an OAuth token expired. | Use a current key, or refresh the OAuth token. |
| `403` | `Workspace is suspended` | The workspace is suspended. | Contact [support](/contact/). |
| `403` | `Permission denied: full` | A `sending` key called an endpoint that needs `full`. | Use a `full` key. |
| `403` | `Domain not authorized` | A domain-restricted key sent from another domain. | Send from the key's domain or use another key. |
| `403` | `unverified_workspace_recipient` | The workspace isn't verified yet and a recipient isn't a workspace member. | See [Unverified workspaces](#unverified-workspaces). |
| `503` | `Authentication service unavailable` | A temporary problem on our side. | Retry with backoff. |

**401**

```json
{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid API key"
}
```

**403 Scope**

```json
{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Permission denied: full"
}
```

**403 Suspended**

```json
{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Workspace is suspended"
}
```

**403 Unverified**

```json
{
  "code": "unverified_workspace_recipient",
  "error": "Workspace not verified",
  "message": "Unverified workspaces can only send to workspace members' account emails. Blocked recipient: ada@example.com.",
  "blocked_recipients": ["ada@example.com"]
}
```

## Unverified workspaces

New workspaces start unverified. Until Emailit approves [production access](/docs/workspaces/production-access/), the API only sends to the account email addresses of the workspace's members. Sending, retrying or forwarding to anyone else returns `403` with the code `unverified_workspace_recipient` and the list of `blocked_recipients`, and campaigns can't be sent at all. Your API keys work normally for everything else.

## Keep your keys secret

An API key gives access to your workspace, so treat it like a password.

- Call the API from your server only. Never put a key in browser JavaScript, a mobile app or any other code that runs on someone else's device.
- Keep keys out of source control. Load them from environment variables or a secret manager.
- Create one key per application and environment, and name it after where it's used, so you can revoke one without breaking the others.
- Give each key the least access it needs: a `sending` key, restricted to one domain, is enough for most applications.
- Check `last_used_at` in [List API keys](/docs/api-reference/api-keys/list/) and delete keys you no longer use.
- If a key leaks, [regenerate](/docs/api-reference/api-keys/regenerate/) it or [delete](/docs/api-reference/api-keys/delete/) it right away. The old secret stops working immediately.

## Related

  - [API keys](/docs/developers/api-keys/): Create, restrict and rotate keys in the dashboard.
  - [Errors](/docs/api-reference/errors/): Every error format and status code.
  - [OAuth apps](/docs/developers/oauth-apps/): Let users connect your app to their workspace.
  - [Production access](/docs/workspaces/production-access/): Get your workspace verified to send to anyone.

---
Source: https://emailit.com/docs/api-reference/authentication/
