CLI reference

Complete reference for the Brevo CLI — install, authenticate, and manage OAuth apps

View as Markdown

npm version

The Brevo CLI (@getbrevo/cli) lets you create and manage OAuth apps, scaffold starter code, and run a local test server — all from the terminal.

Installation

$npm install -g @getbrevo/cli

Requirements: Node.js >= 20.15.0 (Homebrew installs Node automatically as a dependency).

Verify:

$brevo --version

Authentication commands

brevo login

Authenticate with your Brevo account via browser.

$brevo login

Opens oauth-cli.brevo.com in your browser. After signing in, credentials are saved to ~/.brevo/credentials.json.

FlagDescription
--browserForce browser login (default)
--jsonOutput result as JSON

Example:

$brevo login

brevo logout

Clear stored credentials.

$brevo logout [--force]
FlagDescription
--forceSkip confirmation prompt (required in non-interactive environments)
--jsonOutput result as JSON

Example:

$brevo logout --force

Logout clears cached app credentials (client ID / client secret). Run brevo app credentials --reveal-secret before logging out if you need to save them.


brevo whoami

Show the currently authenticated user.

$brevo whoami
FlagDescription
--jsonOutput result as JSON

Example:

$brevo whoami
$# Authenticated as you@example.com (Acme Corp)

App commands

brevo app init

Guided setup — authenticate, create an app, and optionally scaffold starter code, in one flow.

$brevo app init

No flags. Prompts for: app name, logo URL, distribution type, app type, OAuth callback URL, output directory, and whether to scaffold the Test OAuth App feature.

Example:

$brevo app init

Use this for first-time setup. For scripted or automated flows, use brevo app create with flags — non-interactive runs stay base-project-only, with no prompts.


brevo app create

Create a new OAuth app.

$brevo app create [--name <name>] [--distribution <type>] [--redirect-uri <url>] [--logo-uri <url>]
FlagDescription
--name <name>App name
--distribution <type>Distribution type — private (default)
--redirect-uri <url>Redirect URI — repeatable to add multiple URIs
--logo-uri <url>App logo URL (http:// or https://). Optional. When omitted in an interactive shell, the command prompts for it; leave blank to skip.
--jsonOutput result as JSON

Behavior:

  • Writes only the base project (app-config.json, .gitignore, AGENTS.md, CLAUDE.md, README.md) first, then — interactively — asks “Scaffold the Test OAuth App?” (default yes) before writing the OAuth test server code. Decline to stay base-only and add it later with brevo app scaffold.
  • Non-interactive runs (--json, or piped stdin) always stay base-only — the feature prompt is skipped, not defaulted to yes.
  • Hard-errors if app-config.json already exists in the current directory. Run brevo app scaffold there instead to add a feature to that project.
  • Under --json, a target directory that already existed reports scaffoldSkipped (also scaffold_skipped) instead of scaffolded — see the --json note on why both keys appear.

Examples:

$# Interactive
$brevo app create
$
$# Non-interactive — base project only; follow up with `brevo app scaffold`
$brevo app create --name "My App" --distribution private --redirect-uri http://localhost:3009/auth/callback
$
$# Multiple redirect URIs
$brevo app create --name "My App" \
> --redirect-uri http://localhost:3009/auth/callback \
> --redirect-uri https://staging.myapp.com/auth/callback
$
$# With a logo
$brevo app create --name "My App" --distribution private --logo-uri https://example.com/logo.png

brevo app list

List all OAuth apps in your account.

$brevo app list
FlagDescription
--jsonOutput result as JSON

Example:

$brevo app list --json

brevo app credentials

Show credentials for an app.

$brevo app credentials [--app-id <id>] [--reveal-secret]
FlagDescription
--app-id <id>Target app ID. Omit to select interactively.
--reveal-secretShow the client secret (hidden by default). Prompts for confirmation.
--jsonOutput result as JSON

Examples:

$# Show credentials (secret hidden)
$brevo app credentials --app-id e22eb778-a2a8-488a-a5e8-466b6dad9385
$
$# Reveal client secret
$brevo app credentials --app-id e22eb778-a2a8-488a-a5e8-466b6dad9385 --reveal-secret

brevo app upload

Renamed from brevo app update in v2.1.0. The command no longer takes --app-id, --name, --redirect-uri, --scope, or --logo-uri flags — edit the field in app-config.json, then run brevo app upload to push the change. Running brevo app update (with or without its old flags) prints this migration message and exits with code 1 — nothing is forwarded or uploaded.

Push app-config.json to Brevo, validated and synced with the server.

$brevo app upload [--yes] [--json]
FlagDescription
--yesSkip confirmation prompt
--jsonOutput result as JSON

Behavior:

  • Always reads app-config.json from the current directory and pushes the whole file — there’s no flag to edit a single field remotely. To change the app’s name, redirect URLs, scopes, or logo, edit the corresponding field in app-config.json first, then run brevo app upload.
  • Always fetches the app’s current state from Brevo and shows a local-vs-server diff before doing anything else — including under --yes (which only skips the confirmation prompt) and --json.
  • No differences: exits 0 with Already up to date at version <version>. and makes no network push — there’s nothing to write back.
  • A difference: prints the diff, asks to confirm (unless --yes or --json), pushes the whole file, and writes the server-confirmed state — including the resolved version — back to app-config.json.
  • distribution_type is immutable after app create. If app-config.json disagrees with the server, upload refuses locally before showing the diff or pushing anything — create a new app with brevo app create to change distribution.
  • If auth.scopes still contains the deprecated all scope, upload refuses and points to brevo app available-scopes — replace it with specific scopes first.
  • Scope values are format-validated locally (^[A-Za-z0-9][A-Za-z0-9:_.-]*$) but not cross-checked against the IdP catalog. Typos surface as invalid_scope errors at authorization time — run brevo app available-scopes first to confirm the spelling.
  • Errors if app-config.json is missing, invalid, or lacks app_id.

Examples:

$# Edit app-config.json, then push the change
$brevo app upload
$
$# Skip the confirmation prompt
$brevo app upload --yes
$
$# Machine-readable output
$brevo app upload --json

Lost your project folder? Recover it with brevo app scaffold --app-id <id>, then edit app-config.json and upload.

For a UI app (e.g. an action link), upload diffs the ui_app block placement by placement instead — every changed value as before → after, added placements tagged (new), dropped ones trailing (removed). Because a UI app’s configuration is shared by every account it’s installed in, the command also warns that the app may already be installed and asks “Proceed with upload and update every account this app is installed in?” before pushing. --yes skips that question but still prints the warning.


brevo app install

Install a UI app into a Brevo account.

$brevo app install [account-id] [--app-id <id>] [--force]
FlagDescription
[account-id]Optional. A regular account installs into itself; a corporate account without one is prompted to pick a sub-account — pass it explicitly in scripts
--app-id <id>Target app ID. Omit to select interactively — the picker offers only UI apps
--forceSkip confirmation prompt

Behavior:

  • Prints the app’s configuration as stored on the server (its version, extension_type, and every placement) before asking to confirm — that snapshot, not your local app-config.json, is what the account will render.
  • Refuses an app that isn’t a UI app — there’s nothing for an OAuth app to render.
  • No separate publish step: a successful brevo app upload immediately changes what every account the app is installed in renders. Installing again isn’t how you push an update — see brevo app upload.

Example:

$brevo app install --app-id e22eb778-a2a8-488a-a5e8-466b6dad9385

brevo app uninstall

Uninstall a UI app from a Brevo account. Takes the same arguments as brevo app install[account-id], --app-id, --force.

$brevo app uninstall [account-id] [--app-id <id>] [--force]

brevo app available-scopes

List every OAuth scope your app can request, grouped by category. Reads the live catalog from Brevo’s OAuth server.

$brevo app available-scopes [--json] [--web]
FlagDescription
--jsonOutput the catalog as JSON instead of a printed table
--webOpen a local browser page at 127.0.0.1:<port> with search and refresh

Examples:

$# Print the catalog grouped by category
$brevo app available-scopes
$
$# Machine-readable output for scripting
$brevo app available-scopes --json | jq '.scopes[].name'
$
$# Searchable browser view
$brevo app available-scopes --web

brevo app scaffold

Two modes, chosen by whether the current directory already has an app-config.json:

  • With one — adds a feature (the OAuth test server) to the app linked there.
  • Without onebootstraps: sets the directory up for an app you already have. This is the migration path off the removed brevo app update --app-id.
$brevo app scaffold [--app-id <id>] [--overwrite] [--json]
FlagDescription
--app-id <id>Set an empty directory up for an app you already have (bootstrap mode)
--overwriteOverwrite existing feature files instead of merging (skips the conflict prompt)
--jsonOutput result as JSON

Bootstrap mode — recovering a project:

  • With --app-id <id>, fetches that app and writes app-config.json plus the base files (.gitignore, AGENTS.md, CLAUDE.md, README.md), then continues into the feature prompt below.
  • Without --app-id, interactively: asks “Set up a project for an app you already have?” (default yes), then shows a picker of your apps — so recovery doesn’t require the app’s ID in hand. Declining exits 0.
  • Under --json or off a TTY with no --app-id, it raises the same “no app-config.json” error rather than opening a picker — scripts must pass --app-id explicitly.
  • Also asks for an output directory (default: the app name as a slug) and creates it — answer . to stay in the current directory.
  • Refuses, before any network call: bootstrapping into a directory that’s already inside an existing app project; --app-id naming a different app than the one the current directory is already linked to; and an output directory that’s already a different app’s project.

Feature mode — adding to a linked project:

  • Reads the linked app from app-config.json, diffs it against the server, and on drift asks to update the local file to match before writing.
  • If the feature’s files already exist, asks Overwrite / Merge / Cancel (default Merge). --overwrite skips that prompt.

Generates 6 files for the OAuth test server — src/oauth/server.js, handler.js, token-store.js, .env.example, .env.local, package.json — with .env.local pre-populated with your credentials.

Examples:

$# Add the OAuth test server to the app linked in this directory
$brevo app scaffold
$
$# Recover a project for an app you already have
$brevo app scaffold --app-id e22eb778-a2a8-488a-a5e8-466b6dad9385
$
$# Force a full rewrite of feature files
$brevo app scaffold --overwrite
$
$# Machine-readable output
$brevo app scaffold --json

brevo app start

Start a scaffolded feature server locally.

$brevo app start oauth [--port <port>]
ArgumentDescription
oauthStart the OAuth test server
FlagDescription
--port <port>Override the default port (default: read from app-config.json, fallback 3009)

Run from inside your scaffolded project directory (where app-config.json is). Install dependencies first:

$npm --prefix src/oauth install
$# or: yarn --cwd src/oauth install

Examples:

$# Start on default port
$brevo app start oauth
$
$# Start on a custom port
$brevo app start oauth --port 3000

If you change the port, add the matching redirect URL to auth.redirect_uris in app-config.json, then run brevo app upload to push the change.

Auto-registration. When the resolved port has no matching http://localhost:<port>/... entry on the app, the CLI prompts to register it automatically — approving pushes the new URL to Brevo and updates app-config.json. In non-TTY/CI mode it hard-fails instead of silently mutating the remote app; add the URL to auth.redirect_uris in app-config.json and run brevo app upload to register it first.


brevo app delete

Delete an OAuth app. This action cannot be undone.

$brevo app delete [--app-id <id>] [--force]
FlagDescription
--app-id <id>Target app ID. Omit to select interactively.
--forceSkip confirmation prompt
--jsonOutput result as JSON

Example:

$brevo app delete --app-id e22eb778-a2a8-488a-a5e8-466b6dad9385

Function commands

Brevo Functions are serverless functions, running on Brevo’s infrastructure, that calculate a contact attribute. They’re currently in closed betajoin the waitlist if your account doesn’t have access yet.

Every command below also works under the shorter brevo fn alias — brevo fn list, brevo fn init, and so on.

brevo function init

Create a new Brevo Function, interactively.

$brevo function init
FlagDescription
--jsonOutput result as JSON

Behavior:

  • Interactive only — requires a terminal; refuses under --json or piped input.
  • If you have more than one Brevo Function app, prompts you to pick which one the function belongs to.
  • Asks “How would you like to create your function?”Generate using AI (describe what it should do, then the CLI streams through generation stages) or Use a predefined template.
  • Previews the result against sample contacts from your account before you name or deploy anything.
  • On the AI path, offers to iterate — describe changes and preview again — until you choose Deploy.
  • Prompts for a name (must be unique among your functions), then warns “This will activate the function and run it with real-time data” before the final confirmation.

Example:

$brevo function init
$# or:
$brevo fn init

brevo function deploy

Deploy a draft Brevo Function — one generated by an earlier brevo function init session that wasn’t deployed at the time.

$brevo function deploy [--id <draft-id>] [--app-id <id>] [--json]
FlagDescription
--id <id>Draft ID to deploy. Shows a picker if omitted (interactive only)
--app-id <id>Brevo Function app to link the deployed function to. Prompts for one if omitted
--jsonOutput result as JSON

Behavior:

  • brevo function list --draft shows draft IDs — drafts expire, so don’t leave one unfinished for too long.
  • Off a TTY (or under --json) with no --id, the draft picker can’t be shown — pass --id explicitly.
  • Runs the same sample-contact preview, naming, and deploy confirmation as brevo function init before activating.
  • If deploy succeeds but linking to the app fails, the function is still deployed — the command reports the link failure separately rather than rolling back.

Example:

$brevo function deploy --id draft-001 --app-id my-app-id --json

brevo function list

List the Brevo Functions in your account.

$brevo function list [--draft] [--json]
FlagDescription
--draftList draft functions (not yet deployed) instead of deployed ones
--jsonOutput result as JSON

Example:

$brevo function list
$brevo function list --draft

brevo function get

Show one function’s details — status, description, and formula.

$brevo function get [--id <id>] [--json]
FlagDescription
--id <id>Function ID. Shows a picker if omitted
--jsonOutput result as JSON

Example:

$brevo function get --id fn-001

brevo function activate

Activate a function so it runs and updates its linked contact attribute again.

$brevo function activate [--id <id>] [--json]
FlagDescription
--id <id>Function ID. Shows a picker if omitted
--jsonOutput result as JSON

Example:

$brevo function activate --id fn-001

brevo function deactivate

Deactivate a function without deleting it — it stops running but stays in your account.

$brevo function deactivate [--id <id>] [--json]
FlagDescription
--id <id>Function ID. Shows a picker if omitted
--jsonOutput result as JSON

Example:

$brevo function deactivate --id fn-001

brevo function delete

Delete a deployed Brevo Function. This action cannot be undone.

$brevo function delete [--id <id>] [--force] [--json]
FlagDescription
--id <id>Function ID. Shows a picker if omitted
--forceSkip confirmation prompt
--jsonOutput result as JSON

Example:

$brevo function delete --id fn-001 --force

Claude Code skill

The CLI ships a Claude Code skill so you can drive brevo commands — creating apps, managing scopes, running the local OAuth test server — straight from Claude Code, with the CLI’s conventions and guardrails loaded as context.

$brevo skill:cli install
CommandDescription
brevo skill:cli install [--json]Install the brevo-cli Claude Code skill
brevo skill:cli uninstall [--json]Remove the brevo-cli skill

Once installed, every brevo invocation auto-refreshes the skill when the CLI ships a newer version, so the guidance stays in sync with your installed CLI.

The skill format is specific to Claude Code. Other AI agents (Cursor, Copilot CLI, Gemini, Codex) shouldn’t run brevo skill:cli install — refer to the agent-context/AGENTS.md file bundled with the package instead.


Exit codes

CodeMeaning
0Success
1General error
2Aborted (Ctrl+C or SIGTERM)
3Authentication failure (401)
4Network error (API unreachable)
5Not found (404)

JSON errors

Under --json, a command that fails writes one JSON document to stdout describing the failure — the human-readable message still goes to stderr:

1{
2 "error": {
3 "name": "ApiError",
4 "message": "App not found",
5 "exitCode": 5,
6 "exit_code": 5,
7 "code": "APP_NOT_FOUND",
8 "statusCode": 404,
9 "status_code": 404
10 }
11}
FieldDescription
nameError class — CliError, ApiError, AuthExpiredError, or AbortError
messageSame text printed to stderr
exit_codeProcess exit code. Deprecated: exitCode is the same value, kept for compatibility
codePresent on an ApiError when Brevo’s API classified the failure, e.g. APP_NOT_FOUND
status_codePresent on an ApiError — the HTTP status. Deprecated: statusCode is the same value, kept for compatibility

Every --json document carries each camelCase key together with its snake_case twinappId / app_id, clientId / client_id, upToDate / up_to_date, and so on (brevo app create --json’s redirectUri is returned as redirect_uris on both spellings). Nothing is removed yet, so an existing jq .appId keeps working — but the camelCase keys are deprecated and will be removed in the next major version. Write new scripts and examples against the snake_case names.


app-config.json schema

The scaffold writes app-config.json to the project root. brevo app upload pushes this file to Brevo.

1{
2 "app_id": "e22eb778-a2a8-488a-a5e8-466b6dad9385",
3 "app_name": "my-app",
4 "version": "1",
5 "logo_uri": "https://example.com/logo.png",
6 "distribution_type": "private",
7 "app_type": "oauth",
8 "auth": {
9 "scopes": ["contacts:read", "contacts:write", "crm:read", "crm:write"],
10 "redirect_uris": ["http://localhost:3009/auth/callback"]
11 }
12}
FieldTypeDescription
app_idstringApp ID assigned by Brevo — do not edit
app_namestringDisplay name of your app
versionstringRead-only. Tracks the app-store API’s version for this app; updated automatically after each brevo app upload.
logo_uristringOptional http:// or https:// URL to your app’s logo. Empty when not set. Edit this field, then run brevo app upload to push it.
distribution_typestringAlways private. Immutable after app createbrevo app upload refuses a changed value.
app_typestringOptional label written by brevo app create / brevo app scaffold: oauth, ui, or function. Informational only, never sent to Brevo — the presence of ui_app / brevo_function is what actually determines the app type.
auth.scopesstring[]OAuth scopes your app requests. New apps default to ["contacts:read", "contacts:write", "crm:read", "crm:write"]. See Scopes for the full catalog.
auth.redirect_urisstring[]Registered redirect URLs — must match exactly during authorization

Migrated from an older CLI? Before v2.1.0, this file used auth.redirectUrls (now auth.redirect_uris) and a top-level distribution, with an interim auth.type (both now distribution_type). Older keys are still read — the new key wins if both are present — and the file is rewritten to the current shape the next time a command writes it (brevo app upload, app start’s redirect-URL registration, app credentials, or app scaffold). cliVersion and minCliVersion are no longer written.

Downgrading to an older CLI? It reads only redirectUrls, so a file already migrated to redirect_uris looks to it like it has no redirect URLs. Harmless for most commands, but brevo app start may then offer to register a single local callback URL as the app’s entire redirect_uris, dropping every real one. Upgrade the older CLI, or keep both keys during a transition — if it already happened, restore the list and run brevo app upload.

Every key in app-config.json is snake_case. Files written by earlier CLI releases used appId, appName, logoUri, appType, and auth.redirectUris. The CLI still reads those and rewrites the file with the snake_case keys the next time it writes it — including a brevo app upload or brevo app scaffold run that has nothing else to change. If your own scripts read app-config.json, switch them to the new names. Downgrading to an older CLI has the same caveat as the redirectUrls rename above: it reads only the camelCase keys, so a migrated file looks empty to it.

The scaffolded OAuth test server reads auth.scopes from app-config.json and joins it with spaces for the scope= query parameter. If the file is missing or unreadable, it falls back to an internal default that won’t match your app’s registered scopes — keep app-config.json in your project root and unmodified.


Upgrading

$npm install -g @getbrevo/cli@latest

The CLI checks for new versions after each command and prints a banner when one is available. Pass --no-update-notifier or set BREVO_NO_UPDATE_NOTIFIER=1 to suppress it.