# Authentication

> API keys, the read and write scopes, and the separate permission to send emails.

Source: https://docs.maylee.app/authentication

Every request to `https://api.maylee.app/api/v1` carries a Maylee API key as a Bearer token:

```bash
curl https://api.maylee.app/api/v1/mailboxes \
  -H "Authorization: Bearer $MAYLEE_KEY"
```

A key is `mk_` followed by 40 letters and digits. Anything else is rejected before reaching the database. Session cookies are never accepted on this surface, whatever their validity.

## Create a key

1. In the Maylee app, open **Settings**, then **API keys**.
2. In the **Maylee API Key** section, name the key after what it is for ("CRM integration"), not after yourself: that name is what you read the day you need to revoke it.
3. Tick the scopes the integration needs, and **Send emails** only if it really sends email.
4. Create the key and copy the secret. It is shown **once**: only a SHA-256 hash of it is stored, so it can never be displayed again.

Only workspace owners and admins can create and revoke keys. The number of active keys depends on the plan: 2 on Free, 10 on Premium, 50 on Expert. Beyond it, creating a key returns `409` and asks you to revoke one.

> **Keep keys server-side**
>
> A key gives access to every mailbox of the workspace. Never ship it in a browser, a mobile app or a public repository. Revoking it in **Settings, API keys** cuts off every integration that uses it, within a minute at most.

## Scopes

Two scopes, and one separate permission.

| Scope   | Grants                                                                                                                                                                             |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `read`  | List and read emails, bodies and threads, drafts, mailboxes, labels, AI label rules, views, inbox filters, auto-forward rules and their logs.                                      |
| `write` | Mark as read or starred, move to trash, apply and remove labels, bulk operations, create drafts, manage labels, AI label rules, views and inbox filters, classify existing emails. |

A request that lacks a scope gets `403 INSUFFICIENT_SCOPE`, with the missing scope in `details.required` and the key's scopes in `details.granted`. Each endpoint of the [API reference](/api-reference) states the scope it needs.

## The Send emails permission

Making email leave the workspace needs the `write` scope **and** the **Send emails** option of the key (`allowOutboundSend` in the API). The option can only be ticked together with `write`. It gates:

* sending an email with [`POST /drafts/send`](/api-reference/drafts/send-email);
* creating, changing or deleting an [auto-forward rule](/api-reference/auto-forward-rules/create-auto-forward-rule);
* turning on auto-reply on an [AI label rule](/api-reference/ai-label-rules/create-ai-label-rule) (`autoReplyEnabled: true`).

Without it, these requests get `403 OUTBOUND_SEND_NOT_ALLOWED`. Creating a draft for a person to review does **not** need it: nothing leaves the workspace.

## When a key stops working

| Response                  | Meaning                                                                      |
| ------------------------- | ---------------------------------------------------------------------------- |
| `401 INVALID_API_KEY`     | The `Authorization` header is missing, malformed, or the key is unknown.     |
| `401 API_KEY_REVOKED`     | The key was revoked. Create a new one.                                       |
| `401 API_KEY_EXPIRED`     | The key had an expiry date and it has passed. `details.expiredAt` says when. |
| `403 WORKSPACE_SUSPENDED` | The workspace is suspended. Reactivate the subscription.                     |

`401` responses carry no `X-RateLimit-*` headers: the key is resolved before the counters are read.

## MCP server

AI assistants connect to the [MCP server](/mcp) with OAuth, or with the same API keys: see [Connect](/mcp/connect#connect-with-an-api-key).
