> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.brevo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.brevo.com/_mcp/server.

# Machine-to-machine (M2M)

Machine-to-machine (M2M) is an OAuth 2.0 `client_credentials` grant: your own server holds a `client_id` and `client_secret` and calls the Brevo API **as itself** — there's no end user to redirect and no consent screen. It's the recommended way to authenticate server-to-server integrations going forward, in place of a static API key.

## Why prefer M2M over a static API key

|                                 | API key                                         | Machine-to-machine                                             |
| ------------------------------- | ----------------------------------------------- | -------------------------------------------------------------- |
| **Credential**                  | One static, all-access secret                   | `client_id` + `client_secret`, scoped to only what you request |
| **What's sent on each request** | The same long-lived secret, every time          | A short-lived access token (\~1 hour)                          |
| **Blast radius of a leak**      | Full account access until you notice and rotate | Bounded to \~1 hour, and only to the scopes you granted        |
| **Rotation**                    | Manual, all-or-nothing                          | `brevo app secret rotate --app-id <id>`                        |
| **Setup**                       | Copy from the dashboard                         | `brevo app create --m2m --scopes "..."`                        |

Use M2M for any new backend integration that calls Brevo as itself. Reach for a static [API key](/docs/api-key-authentication) only where you specifically need the simplicity of a single header with no token exchange step.

M2M is a different flow from [OAuth apps](/docs/oauth) (the `authorization_code` grant) — see [Which flavor of OAuth do I need?](/docs/oauth#which-flavor-of-oauth-do-i-need) if your app needs to act on behalf of a Brevo *user* instead of as itself.

## Get started

### Create a Brevo account

[Sign up](https://onboarding.brevo.com/account/register) if you don't have one yet.

### Install the Brevo CLI

See [Installation](/docs/cli-reference#installation). If `brevo app create`'s app-type prompt doesn't offer **Machine to Machine**, update to the latest version (see [Upgrading](/docs/cli-reference#upgrading)).

### Create an M2M app

```bash
brevo app create
```

```txt
? App name: m2m-test
? What distribution type should this app use?   Private  (Used exclusively by your organisation)
? What type of app are you building?   OAuth app       (Authorize against Brevo and call the API on a user's behalf)
? Which OAuth flow does this app use?   Machine to Machine  (Your server calls the API as itself; no user)
  Select every scope this app needs — you can add or remove scopes later with `brevo app scopes update --app-id <id> --scopes <a,b,c>`.
? Which scopes does this app need? contacts:read, contacts:write, crm:read, crm:write

App created
  App name:       m2m-test
  App ID:         d385abac-dc19-4aec-8bfe-610e70e15691
  Client ID:      f2c11389ad043b797eade63baf0c9b4a
  Client secret:  [hidden — run `brevo app credentials --reveal-secret`]
```

Read the credentials back at any time with `brevo app credentials --app-id <app-id>` (add `--reveal-secret` to print the secret).

> **Note**
>
> Pick every scope the app will ever need up front — you can widen or narrow the set later with [`brevo app scopes update`](#manage-the-app-over-its-lifetime), but each change is a full replace of the granted set. See the [Scopes reference](/docs/oauth-scopes) for the full catalog (the same scope names used by OAuth apps).

To script app creation non-interactively:

```bash
brevo app create --name "My App" --distribution private \
  --m2m --scopes "contacts:read,crm:read" --json
```

`--m2m` requires `--scopes`, and `--m2m` cannot combine with `--redirect-uri`, `--ui-app`/`--ui-config`, or `--distribution public` — an M2M app has no user to redirect and is private-distribution only.

### Get an access token

```bash
brevo app token --app-id d385abac-dc19-4aec-8bfe-610e70e15691
```

```txt
Authorization: Bearer eyJhbGciOi...
```

Or request it directly, without the CLI:

```bash
curl --request POST \
  --url https://oauth.brevo.com/realms/partner/oauth/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=client_credentials' \
  --data-urlencode 'client_id=<CLIENT_ID>' \
  --data-urlencode 'client_secret=<CLIENT_SECRET>'
```

```json
{ "access_token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 3600, "scope": "all" }
```

> **Warning**
>
> Always request an explicit `scope` — omitting it currently returns a token scoped to every permission the app has, rather than failing closed. Pass the scopes you actually need, space-separated, e.g. `--data-urlencode 'scope=contacts:read crm:read'` (or `brevo app token --app-id <id> --scope contacts:read,crm:read`).

The token is a Bearer token, valid for **\~1 hour**. There's no refresh token in the `client_credentials` grant — when it expires, request a new one the same way.

### Call the API

Replace the `api-key` header with `Authorization: Bearer <token>`:

```bash
curl https://api.brevo.com/v3/account \
  --header 'Authorization: Bearer <ACCESS_TOKEN>'
```

## Manage the app over its lifetime

Every lifecycle command is keyed by `--app-id`.

```bash
# List every M2M app in the account
brevo app list --type m2m

# Read credentials back (secret hidden unless you ask)
brevo app credentials --app-id <app-id> --reveal-secret

# Zero-downtime secret rotation — the old secret keeps working for a grace window
brevo app secret rotate --app-id <app-id>

# Widen or narrow granted scopes (full replace, not a delta)
brevo app scopes update --app-id <app-id> --scopes contacts:read,crm:read

# Delete the app
brevo app delete --app-id <app-id>
```

See the [CLI reference](/docs/cli-reference) for every flag.

## Limitations (v1)

> **Warning**
>
> * **No access-token revocation.** Deleting the app or rotating the secret stops *new* tokens from being issued, but a token already handed out stays valid until it expires (\~1 hour). Store secrets in a secret manager and rotate promptly if one leaks.
> * **One account per credential.** Multi-account M2M credentials aren't supported yet.
> * **No refresh token.** Request a new token with the same `client_credentials` call when the old one expires — this is standard for the grant, not a Brevo-specific gap.