CLI reference
Complete reference for the Brevo CLI — install, authenticate, and manage OAuth apps
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
yarn
Homebrew
Requirements: Node.js >= 20.15.0 (Homebrew installs Node automatically as a dependency).
Verify:
Authentication commands
brevo login
Authenticate with your Brevo account via browser.
Opens oauth-cli.brevo.com in your browser. After signing in, credentials are saved to ~/.brevo/credentials.json.
Example:
brevo logout
Clear stored credentials.
Example:
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.
Example:
App commands
brevo app init
Guided setup — authenticate, create an app, and optionally scaffold starter code, in one flow.
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:
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.
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 withbrevo 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.jsonalready exists in the current directory. Runbrevo app scaffoldthere instead to add a feature to that project. - Under
--json, a target directory that already existed reportsscaffoldSkipped(alsoscaffold_skipped) instead ofscaffolded— see the--jsonnote on why both keys appear.
Examples:
brevo app list
List all OAuth apps in your account.
Example:
brevo app credentials
Show credentials for an app.
Examples:
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.
Behavior:
- Always reads
app-config.jsonfrom 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 inapp-config.jsonfirst, then runbrevo 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
0withAlready 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
--yesor--json), pushes the whole file, and writes the server-confirmed state — including the resolvedversion— back toapp-config.json. distribution_typeis immutable afterapp create. Ifapp-config.jsondisagrees with the server,uploadrefuses locally before showing the diff or pushing anything — create a new app withbrevo app createto change distribution.- If
auth.scopesstill contains the deprecatedallscope,uploadrefuses and points tobrevo 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 asinvalid_scopeerrors at authorization time — runbrevo app available-scopesfirst to confirm the spelling. - Errors if
app-config.jsonis missing, invalid, or lacksapp_id.
Examples:
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.
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 localapp-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 uploadimmediately changes what every account the app is installed in renders. Installing again isn’t how you push an update — seebrevo app upload.
Example:
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 available-scopes
List every OAuth scope your app can request, grouped by category. Reads the live catalog from Brevo’s OAuth server.
Examples:
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 one — bootstraps: sets the directory up for an app you already have. This is the migration path off the removed
brevo app update --app-id.
Bootstrap mode — recovering a project:
- With
--app-id <id>, fetches that app and writesapp-config.jsonplus 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 exits0. - Under
--jsonor off a TTY with no--app-id, it raises the same “noapp-config.json” error rather than opening a picker — scripts must pass--app-idexplicitly. - 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-idnaming 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).
--overwriteskips 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:
brevo app start
Start a scaffolded feature server locally.
Run from inside your scaffolded project directory (where app-config.json is). Install dependencies first:
Examples:
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.
Example:
Function commands
Brevo Functions are serverless functions, running on Brevo’s infrastructure, that calculate a contact attribute. They’re currently in closed beta — join 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.
Behavior:
- Interactive only — requires a terminal; refuses under
--jsonor 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 deploy
Deploy a draft Brevo Function — one generated by an earlier brevo function init session that wasn’t deployed at the time.
Behavior:
brevo function list --draftshows 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--idexplicitly. - Runs the same sample-contact preview, naming, and deploy confirmation as
brevo function initbefore 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 list
List the Brevo Functions in your account.
Example:
brevo function get
Show one function’s details — status, description, and formula.
Example:
brevo function activate
Activate a function so it runs and updates its linked contact attribute again.
Example:
brevo function deactivate
Deactivate a function without deleting it — it stops running but stays in your account.
Example:
brevo function delete
Delete a deployed Brevo Function. This action cannot be undone.
Example:
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.
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
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:
Every --json document carries each camelCase key together with its snake_case twin — appId / 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.
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
yarn
Homebrew
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.