Skip to navigation

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, distribution type (private or public; choosing public also asks you to agree to the Brevo Developer Terms), app type, OAuth flow (private OAuth apps only), 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 — a regular (user-consent) app, or a machine-to-machine (M2M) app with --m2m. A private app works right away; a public app needs Brevo’s review before other accounts can authorize it (see Private and public apps).

brevo app create [--name <name>] [--distribution <type>] [--accept-developer-terms] [--redirect-uri <url>] [--m2m --scopes <list>]
FlagDescription
--name <name>App name
--distribution <type>private or public (public requires CLI 2.5.0 or later). Interactive runs ask; non-interactive runs default to private. Fixed after creation.
--accept-developer-termsConfirm you have read and agree to the Brevo Developer Terms. Required to create a public app non-interactively.
--redirect-uri <url>Redirect URI — repeatable to add multiple URIs
--m2mCreate a machine-to-machine OAuth app (private only; needs --scopes)
--scopes <list>Comma-separated scopes for the M2M app (with --m2m)
--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.
  • Public apps (--distribution public): the CLI opens the Brevo Developer Terms and asks you to confirm you agree (default No; declining cancels the creation). Non-interactive runs are refused unless you pass --accept-developer-terms. A public app is always a consent-based OAuth app using PKCE, with no client secret; --m2m and Brevo Functions are private-only, and UI apps can’t be public for now.
  • No logo at creation. To set a logo, edit logo_uri in app-config.json, then run brevo app upload.
  • --m2m is create-only and writes no project files — an M2M app has no app-config.json, so brevo app upload and brevo app scaffold don’t apply to it. --m2m without --scopes is refused, as is --scopes without --m2m, --m2m combined with --redirect-uri/--ui-app/--ui-config, or --m2m --distribution public — an M2M app has no user to redirect and is private-distribution only. See Machine-to-machine (M2M) for the full walkthrough.

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
# Public app, non-interactive (agrees to the Brevo Developer Terms)
brevo app create --name "My App" --distribution public \
--redirect-uri https://myapp.com/callback \
--accept-developer-terms --json
# Machine-to-machine app
brevo app create --name "My App" --distribution private --m2m --scopes "contacts:read,crm:read" --json

brevo app list

List all apps in your account.

brevo app list [--type <type>]
FlagDescription
--type <type>Show only apps of one type (oauth|ui|function|m2m)
--jsonOutput result as JSON

Examples:

brevo app list --json
# Only machine-to-machine apps
brevo app list --type m2m

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 token

Mint a short-lived M2M access token for an app. M2M-only — the app picker (when --app-id is omitted) shows only M2M apps.

brevo app token [--app-id <id>] [--scope <list>]
FlagDescription
--app-id <id>App ID. Omit to select interactively (M2M apps only).
--scope <list>Comma-separated scopes to request (default: the app’s full granted set)
--jsonOutput result as JSON

Behavior:

  • The token is printed in full with no reveal gate — unlike the client secret, it’s short-lived, never written to disk, and handing it back is the whole point of the command.
  • Requesting a scope the app wasn’t granted returns a targeted error pointing you at brevo app credentials.

Examples:

brevo app token --app-id d385abac-dc19-4aec-8bfe-610e70e15691
brevo app token --app-id d385abac-dc19-4aec-8bfe-610e70e15691 --scope contacts:read,crm:read
brevo app token --app-id d385abac-dc19-4aec-8bfe-610e70e15691 --json

See Machine-to-machine (M2M) for the full token walkthrough.


brevo app secret rotate

Rotate an M2M app’s client secret. M2M-only.

brevo app secret rotate [--app-id <id>] [--yes]
FlagDescription
--app-id <id>App ID. Omit to select interactively (M2M apps only).
--yesSkip confirmation prompt (for CI)
--jsonOutput result as JSON

Behavior:

  • Destructive and immediate: the old secret stops working right away — any caller still using it starts failing. A confirmation prompt gates the action; --yes (or --json) skips it.
  • The new secret is printed in full — a reveal gate would make the command unable to return the value it exists to produce in CI. Store it in a secret manager immediately; this command won’t show it again.

Examples:

brevo app secret rotate --app-id d385abac-dc19-4aec-8bfe-610e70e15691
brevo app secret rotate --app-id d385abac-dc19-4aec-8bfe-610e70e15691 --yes

brevo app scopes update

Update an M2M app’s granted OAuth scopes. M2M-only.

brevo app scopes update [--app-id <id>] [--scopes <list>] [--yes]
FlagDescription
--app-id <id>App ID. Omit to select interactively (M2M apps only).
--scopes <list>Comma-separated scope list — the full desired set, not a delta. Interactive runs without it open a scope picker pre-filled with the current set.
--yesSkip confirmation prompt (for CI)
--jsonOutput result as JSON

Behavior:

  • The request is always a plain replace with --scopes’s value — there’s no way to add or remove a single scope without restating the full set.
  • Shows the added/removed diff before confirming. A no-op (nothing added or removed) exits cleanly reporting changed: false.
  • Under --json or off a TTY, --scopes is required.

Examples:

brevo app scopes update --app-id d385abac-dc19-4aec-8bfe-610e70e15691 --scopes contacts:read,crm:read
brevo app scopes update --app-id d385abac-dc19-4aec-8bfe-610e70e15691 --scopes contacts:read --yes

To discover valid scope names, see brevo app available-scopes.


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 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.
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

App-review commands

These commands apply to public apps only and require CLI 2.5.0 or later. See Private and public apps for the full review process.

brevo app submit

Submit a public app for review.

brevo app submit [--app-id <id>] [--json] [--accept-submission-terms]
FlagDescription
--app-id <id>Target app ID. Uses app-config.json if omitted.
--jsonPrint the submission form URL as JSON instead of opening a browser
--accept-submission-termsConfirm the submission statement explicitly and skip the confirmation prompt. Required for --json and piped runs.

Behavior:

  • Refuses a private app, and an app that has never been uploaded (run brevo app upload first).
  • Refuses if required fields are missing, listing them by their API names (for example logoLink, oauth.scopes), which can differ from app-config.json keys, and if your local app-config.json differs from the app on Brevo, showing the difference. Run brevo app upload, then submit again.
  • Refuses with Review submission is currently unavailable when no submission form is available, for example while the app is already submitted or under review. Check brevo app status.
  • Shows the app configuration and asks you to confirm the submission statement (default No). Declining cancels the submission.
  • Opens the submission form in your browser. The app is submitted only when you complete and send that form. Brevo emails you the decision.
  • Under --json with --accept-submission-terms, prints { "app_id": "…", "form_url": "…" } instead of opening a browser.

Examples:

brevo app submit
brevo app submit --app-id e22eb778-a2a8-488a-a5e8-466b6dad9385 --json --accept-submission-terms

brevo app status

Show a public app’s review status.

brevo app status [--app-id <id>] [--json]
FlagDescription
--app-id <id>Target app ID. Uses app-config.json if omitted.
--jsonOutput { "state": "…", "message": "…" }

Possible states: draft, submitted, in_review, approved, rejected, changes_requested, or unknown (no status yet; check that the app is public and has been uploaded). Reviewer feedback is sent by email, not shown here.

brevo app withdraw

Withdraw a public app from submission. If the app was approved, the approved version is no longer available to Brevo accounts until you submit it again and it is approved.

brevo app withdraw [--app-id <id>] [--force] [--json]
FlagDescription
--app-id <id>Target app ID. Uses app-config.json if omitted.
--forceSkip the confirmation prompt (required in non-interactive environments, even with --json)
--jsonOutput { "withdrawn": true, "appId": "…", "app_id": "…" } (see the --json note on why both keys appear; read app_id)

If the app was never submitted, the command says so and exits successfully without changing anything (under --json: "withdrawn": false with "reason": "NOT_SUBMITTED").


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.

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:

{
"error": {
"name": "ApiError",
"message": "App not found",
"exitCode": 5,
"exit_code": 5,
"code": "APP_NOT_FOUND",
"statusCode": 404,
"status_code": 404
}
}
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 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.

{
"app_id": "e22eb778-a2a8-488a-a5e8-466b6dad9385",
"app_name": "my-app",
"version": "1",
"logo_uri": "https://example.com/logo.png",
"distribution_type": "private",
"app_type": "oauth",
"auth": {
"scopes": ["contacts:read", "contacts:write", "crm:read", "crm:write"],
"redirect_uris": ["http://localhost:3009/auth/callback"]
}
}
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_typestringprivate or public (see Private and public apps). Immutable after app create — brevo 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.