@getbrevo/cli 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +59 -6
- package/agent-context/AGENTS.md +20 -10
- package/agent-context/SKILL.md +49 -13
- package/dist/bin/files/AGENTS.md.tmpl +2 -2
- package/dist/bin/files/CLAUDE.md.tmpl +1 -1
- package/dist/bin/files/README.md.tmpl +3 -3
- package/dist/bin/files/app-config.json.tmpl +11 -6
- package/dist/bin/index.js +109 -31
- package/dist/bin/index.js.map +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @getbrevo/cli
|
|
2
2
|
|
|
3
|
+
## 2.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4379784: Add `function` as a first-class app type in the CLI's app-type registry. A Brevo Function app is now detected by the presence of `brevo_function` in both the local config and the server record, and participates in the same resolution, capability, and recoverability checks as `oauth` and `ui` apps. The capability matrix grants a private Function app no OAuth or UI capabilities (it ships its own `brevo function deploy` flow); public distribution adds `review-lifecycle`. A new `app_type` field is written to `app-config.json` on `brevo app create` and `brevo app scaffold` as informational metadata (`"oauth"`, `"ui"`, or `"function"`); it is never sent to the server and legacy configs without it continue to work unchanged. Additional fixes: `fetchFunctionList` now paginates past 50 functions, `sseStream` refreshes the OAuth token before connecting, `brevo function deploy` appears in help output, and `resolveFunctionId` replaces a duplicated IIFE in the shared action runner.
|
|
8
|
+
|
|
9
|
+
Refuse a `brevo app upload` whose `app_type` contradicts the blocks it sits next to. `app_type` stays informational — the presence of `ui_app` / `brevo_function` / `auth` is still the only discriminator, and `isUiAppConfig()` is still the only place it is read — but a file labelled `"oauth"` that carries a `ui_app` block is a hand-edit that half-landed, and `upload` now says so before any round trip, naming both the declared and the detected type and how to reconcile them. A config that omits `app_type` (every file written by an earlier release) is unaffected: the check is skipped entirely rather than defaulted. The label is never sent to Brevo.
|
|
10
|
+
|
|
11
|
+
Normalize every key in `app-config.json` to snake_case, matching the wire contract. The file was meant to be snake_case throughout but several keys were camelCase; they are renamed as follows:
|
|
12
|
+
- `appId` → `app_id`
|
|
13
|
+
- `appName` → `app_name`
|
|
14
|
+
- `logoUri` → `logo_uri`
|
|
15
|
+
- `appType` → `app_type`
|
|
16
|
+
- `auth.redirectUris` → `auth.redirect_uris`
|
|
17
|
+
|
|
18
|
+
`version`, `distribution_type`, `auth.scopes`, `ui_app` and `brevo_function` are unchanged, and nothing sent to or received from the Brevo API changes.
|
|
19
|
+
|
|
20
|
+
**Backward compatibility is preserved.** Every command still reads the camelCase spellings written by earlier releases (and the older `auth.redirectUrls`), through one shared normalizer in the config reader. When a file carries both spellings of a key with different values the snake_case one wins and a one-line notice is printed to stderr.
|
|
21
|
+
|
|
22
|
+
**Legacy files are migrated on write.** `brevo app create`, `brevo app upload`, `brevo app scaffold` and `brevo app start` now write snake_case keys only, and the camelCase copies are dropped. `brevo app upload` and `brevo app scaffold` also rewrite an in-sync legacy file on their "nothing to change" paths, so running either once is enough to migrate a project. Values are never changed by the migration.
|
|
23
|
+
|
|
24
|
+
The only `--json` output that echoes config key names is `brevo app scaffold`'s `diffs[].field`, which now reports `app_name`, `redirect_uris` and `logo_uri` instead of `appName`, `redirectUris` and `logoUri`. The `appId` / `appName` / `logoUri` keys in other commands' `--json` output are unchanged.
|
|
25
|
+
|
|
26
|
+
Every `--json` document now carries each camelCase key together with its snake_case twin: `appId` and `app_id`, `clientId` and `client_id`, `upToDate` and `up_to_date`, and in the error envelope `exitCode` / `exit_code` and `statusCode` / `status_code`. `brevo app create --json`'s `redirectUri` and `brevo app credentials --json`'s `redirectUris` are twinned as `redirect_uris`, the wire name. Array documents (`brevo app list --json`) alias each element. Nested objects are left as they are — `ui_app`, the upload diff's `current` / `next`, Function records — since they were already snake_case or are the user's own data. Nothing is removed: every existing `jq .appId` keeps working. This is the deprecation step toward one spelling for machine-readable output, matching `app-config.json` and the API; the camelCase keys will be removed in the next major release, and new scripts should read the snake_case ones.
|
|
27
|
+
|
|
28
|
+
## 2.2.2
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- 820b67e: `brevo app create` can now create an `actionLink` UI app non-interactively, either via `--ui-config <file>` (a JSON file: `{ extension_type, record_page, surface_point_name, label, more_info?, redirect_link }`) or via `--ui-app --record-page <slug> --placement <surface_point_name> --label <text> --url <url> [--more-info <text>]`. Previously a non-interactive run (`--json`, piped stdin, or a script) always created an OAuth app instead, with no error, even when a UI app was intended. Both new paths reuse the interactive wizard's own registry validation and reject an unknown `--record-page`/`--placement` with the valid options listed in the error. Only `extension_type: "actionLink"` is supported non-interactively today; `iframeExtension`/`legacyComponent` are refused with a clear message. A plain non-interactive run with neither flag still creates an OAuth app, unchanged.
|
|
33
|
+
|
|
3
34
|
## 2.2.1
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
# Brevo Developer CLI
|
|
2
2
|
|
|
3
|
-
Command-line tool to
|
|
3
|
+
Command-line tool to build [Brevo](https://www.brevo.com/) integrations from your terminal — OAuth apps, UI apps that render inside Brevo, and Brevo Functions.
|
|
4
4
|
|
|
5
5
|
> 📖 Full command and option documentation: **[Brevo CLI reference](https://developers.brevo.com/docs/cli-reference)**
|
|
6
6
|
|
|
7
7
|
> [!WARNING]
|
|
8
|
-
> **Upgrade to the latest released version.** All versions from **1.1.1** up to (but not including) **2.0.0** should be migrated to at least **2.
|
|
8
|
+
> **Upgrade to the latest released version.** All versions from **1.1.1** up to (but not including) **2.0.0** should be migrated to at least **2.3.0**. The `2.0.0` release introduced **breaking changes**, so some CLI commands may not work as expected on older versions. **`2.3.0`** also carries further `app-config.json` migrations — legacy `auth.redirectUrls` → `auth.redirect_uris`, and the camelCase keys `appId` / `appName` / `logoUri` / `appType` / `auth.redirectUris` → their snake_case spellings — that are applied automatically the next time the CLI writes your config (`brevo app upload`, `brevo app start`, …). Older configs keep working: both spellings are still read, and the migration never changes a value.
|
|
9
|
+
>
|
|
10
|
+
> **`2.3.0` also begins a `--json` key deprecation.** Every JSON document now carries each camelCase key alongside its snake_case twin (`appId` **and** `app_id`, `clientId` **and** `client_id`, `exitCode` **and** `exit_code`, …). Nothing is removed yet — the camelCase spellings go away in the next major release, so point new scripts at the snake_case ones.
|
|
9
11
|
>
|
|
10
12
|
> Upgrade with `npm install -g @getbrevo/cli@latest` (or `yarn global add @getbrevo/cli@latest`, or `brew upgrade brevo`), then confirm with `brevo --version`.
|
|
11
13
|
|
|
14
|
+
## What's new in 2.3.0
|
|
15
|
+
|
|
16
|
+
- **UI apps and action links.** `brevo app create` can build a **UI app** — an app that Brevo
|
|
17
|
+
renders directly on a CRM record page. The integration type it authors is an **action link**: a
|
|
18
|
+
menu entry or card CTA button that opens a URL you host, with the record's data passed along as
|
|
19
|
+
query parameters. `brevo app install` / `brevo app uninstall` put it into an account, and
|
|
20
|
+
`brevo app upload` pushes placement changes live to every account it is installed in. See
|
|
21
|
+
[UI apps](#ui-apps) for the full shape of a placement.
|
|
22
|
+
- **Brevo Functions.** A new `brevo function` command group for serverless functions that run on
|
|
23
|
+
Brevo's own infrastructure. **Available on an invite basis** — see [Brevo Functions](#brevo-functions).
|
|
24
|
+
- **`app-config.json` is snake_case throughout**, and `--json` output now carries both spellings of
|
|
25
|
+
every key. Both are covered by the upgrade note above.
|
|
26
|
+
|
|
27
|
+
Full detail for every release is in [CHANGELOG.md](CHANGELOG.md).
|
|
28
|
+
|
|
12
29
|
## Requirements
|
|
13
30
|
|
|
14
31
|
- **Node.js** 20.15.0 or newer (required to run the CLI)
|
|
@@ -93,7 +110,7 @@ Run `brevo --help` or `brevo <command> --help` for full command and option lists
|
|
|
93
110
|
| `brevo logout` | Clear stored credentials (`--force` to skip confirmation) |
|
|
94
111
|
| `brevo whoami` | Show the authenticated user |
|
|
95
112
|
| `brevo app init` | Guided setup — login, create app, and scaffold in one go |
|
|
96
|
-
| `brevo app create` | Create an app — an OAuth app (`--name`, `--distribution private`, repeatable `--redirect-uri`, `--logo-uri`), or a UI app via the interactive prompts (there is no `--type` flag; non-interactive runs always create an OAuth app) |
|
|
113
|
+
| `brevo app create` | Create an app — an OAuth app (`--name`, `--distribution private`, repeatable `--redirect-uri`, `--logo-uri`), or a UI app or Brevo Function via the interactive prompts (there is no `--type` flag; non-interactive runs always create an OAuth app) |
|
|
97
114
|
| `brevo app list` | List apps in your account (each row names its type) |
|
|
98
115
|
| `brevo app credentials` | Show client ID and secret (`--app-id`, `--reveal-secret`) |
|
|
99
116
|
| `brevo app upload` | Push `app-config.json` to Brevo after showing a local-vs-server diff — field by field, including every `ui_app` placement (`--yes`) |
|
|
@@ -103,6 +120,13 @@ Run `brevo --help` or `brevo <command> --help` for full command and option lists
|
|
|
103
120
|
| `brevo app install` | Install a UI app into a Brevo account, after showing the configuration and version it will install (`[account-id]` optional — a regular account installs into itself; a corporate account is prompted to pick a sub-account, so pass the ID explicitly in scripts; `--app-id`, `--force`) |
|
|
104
121
|
| `brevo app uninstall` | Uninstall a UI app from a Brevo account (same arguments as `install`) |
|
|
105
122
|
| `brevo app available-scopes` | List the OAuth scopes the IdP supports (`--web` opens the catalog in a browser) |
|
|
123
|
+
| `brevo function init` | Create a Brevo Function — from a template or generated from a description, previewed on sample contacts before it deploys (interactive only) |
|
|
124
|
+
| `brevo function list` | List the functions in your account (`--draft` for drafts only) |
|
|
125
|
+
| `brevo function get` | Show one function's details (`--id`, or pick from a list) |
|
|
126
|
+
| `brevo function deploy` | Deploy a draft, optionally linking it to an app (`--id`, `--app-id`) |
|
|
127
|
+
| `brevo function activate` | Activate a function so it processes live data (`--id`) |
|
|
128
|
+
| `brevo function deactivate` | Stop a function from processing (`--id`) |
|
|
129
|
+
| `brevo function delete` | Permanently delete a deployed function (`--id`, `--force`) |
|
|
106
130
|
|
|
107
131
|
Most commands require a successful `brevo login` first, except authentication/help flows (`brevo login`, `brevo logout`, `brevo app init`, `--help`). Every command accepts `--json` for machine-readable output.
|
|
108
132
|
|
|
@@ -110,9 +134,10 @@ The table above is the complete command surface of a published release. Features
|
|
|
110
134
|
|
|
111
135
|
### UI apps
|
|
112
136
|
|
|
113
|
-
`brevo app create`'s interactive prompt can build
|
|
114
|
-
that renders directly inside a Brevo CRM record
|
|
115
|
-
`--json` and piped runs always create an OAuth
|
|
137
|
+
`brevo app create`'s interactive prompt can build three kinds of app: an OAuth app, a **UI app**
|
|
138
|
+
that renders directly inside a Brevo CRM record, or a [Brevo Function](#brevo-functions)
|
|
139
|
+
(interactive-only — there is no `--type` flag, so `--json` and piped runs always create an OAuth
|
|
140
|
+
app).
|
|
116
141
|
|
|
117
142
|
Today the prompt authors one integration type, an **action link** (`extension_type: "actionLink"`).
|
|
118
143
|
In short: it's a clickable menu entry or card CTA button that Brevo renders on a record page — no
|
|
@@ -157,6 +182,34 @@ commands show you what you are about to change:
|
|
|
157
182
|
To change what an installed app renders: edit `app-config.json`, run `brevo app upload`, and the
|
|
158
183
|
accounts it is installed in pick the change up — no re-install needed.
|
|
159
184
|
|
|
185
|
+
### Brevo Functions
|
|
186
|
+
|
|
187
|
+
> [!NOTE]
|
|
188
|
+
> Brevo Functions are rolling out **on an invite basis**. The commands ship in every release and
|
|
189
|
+
> `brevo --help` always lists them, but they only work on accounts that have been given access — so
|
|
190
|
+
> if they fail on your account, that is why. Ask your Brevo contact to have it enabled.
|
|
191
|
+
|
|
192
|
+
A **Brevo Function** is a serverless function that runs on Brevo's own infrastructure and processes
|
|
193
|
+
your account's data in real time — no server of yours to host or deploy to. It is a third app type
|
|
194
|
+
alongside OAuth and UI apps: `brevo app create` offers it in the app-type prompt, and a Function app
|
|
195
|
+
is recorded in `app-config.json` by a `brevo_function` block (`app_type: "function"`).
|
|
196
|
+
|
|
197
|
+
The `function` group (aliased to `fn`) is the whole lifecycle:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
brevo function init # create one — template, or generated from a description
|
|
201
|
+
brevo function list --draft # drafts waiting to be deployed
|
|
202
|
+
brevo function deploy --id draft-001 # deploy a draft, optionally --app-id to link it to an app
|
|
203
|
+
brevo function list # everything deployed, with its status
|
|
204
|
+
brevo function deactivate --id fn-001 # stop it processing, without deleting it
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`brevo function init` is interactive only — it previews the function against sample contacts from
|
|
208
|
+
your account and lets you iterate on it before anything is deployed, which is not something `--json`
|
|
209
|
+
or a piped run can do. Deploying activates the function against **real, live data**, so both `init`
|
|
210
|
+
and `deploy` confirm that before they go ahead. Every other command supports `--json`, and the ones
|
|
211
|
+
that act on a single function take `--id`, so a script never has to reach the interactive picker.
|
|
212
|
+
|
|
160
213
|
### Browser login
|
|
161
214
|
|
|
162
215
|
`brevo login` defaults to a browser-based sign-in. The CLI starts a temporary loopback server, opens your browser to the Brevo CLI login service, and stores the returned tokens in `~/.brevo/credentials.json`. Access tokens refresh automatically on expiry.
|
package/agent-context/AGENTS.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!-- BREVO_CLI_AGENTS_BEGIN -->
|
|
2
2
|
# Brevo CLI
|
|
3
3
|
|
|
4
|
-
This project uses the [Brevo Developer CLI](https://www.npmjs.com/package/@getbrevo/cli) (`brevo` binary) to manage Brevo OAuth
|
|
4
|
+
This project uses the [Brevo Developer CLI](https://www.npmjs.com/package/@getbrevo/cli) (`brevo` binary) to manage Brevo applications (OAuth apps, UI apps, and Brevo Functions).
|
|
5
5
|
|
|
6
6
|
## `brevo --help` is the source of truth
|
|
7
7
|
|
|
@@ -62,11 +62,12 @@ Don't fall back to raw HTTP against `api.brevo.com` — the `brevo` binary is th
|
|
|
62
62
|
|
|
63
63
|
## When to use it
|
|
64
64
|
|
|
65
|
-
- Create, list, upload changes to, or delete Brevo apps — OAuth apps,
|
|
65
|
+
- Create, list, upload changes to, or delete Brevo apps — OAuth apps, UI apps (action links rendering inside Brevo CRM records), or Brevo Functions (serverless functions on Brevo's infrastructure)
|
|
66
66
|
- Scaffold a starter OAuth integration
|
|
67
67
|
- Run a local OAuth test server (`brevo app start oauth`)
|
|
68
68
|
- Inspect or rotate app credentials
|
|
69
69
|
- Install or uninstall a UI app in a Brevo account (`brevo app install` / `brevo app uninstall`)
|
|
70
|
+
- Create, list, deploy, activate, deactivate, or delete Brevo Functions (`brevo function` / `brevo fn`)
|
|
70
71
|
|
|
71
72
|
## Common commands
|
|
72
73
|
|
|
@@ -77,9 +78,9 @@ Don't fall back to raw HTTP against `api.brevo.com` — the `brevo` binary is th
|
|
|
77
78
|
| `brevo whoami` | Show the authenticated account (`--json`) |
|
|
78
79
|
| `brevo app init` | Guided setup (login, create, scaffold) |
|
|
79
80
|
| `brevo app list` | List apps (`--json`). Each row names its type. |
|
|
80
|
-
| `brevo app create` | Create an app (`--name`, `--distribution private`, `--redirect-uri`, `--logo-uri`, `--json`). **Pass `--distribution private`** — run `brevo app create --help` for the values this build accepts and don't pass one it doesn't list. Defaults to scopes `contacts:read`, `contacts:write`, `crm:read`, `crm:write`. Interactively it asks for the name before the OAuth prompts, and asks the **app type** (OAuth vs UI app) as the last question before the flow splits — there is no `--type` flag,
|
|
81
|
+
| `brevo app create` | Create an app (`--name`, `--distribution private`, `--redirect-uri`, `--logo-uri`, `--json`). **Pass `--distribution private`** — run `brevo app create --help` for the values this build accepts and don't pass one it doesn't list. Defaults to scopes `contacts:read`, `contacts:write`, `crm:read`, `crm:write`. Interactively it asks for the name before the OAuth prompts, and asks the **app type** (OAuth vs UI app) as the last question before the flow splits — there is still no `--type` flag, but a UI app is also reachable non-interactively via `--ui-app --record-page <slug> --placement <surface_point_name> --label <text> --url <url> [--more-info <text>]`, or `--ui-config <file>` (JSON: `{ extension_type, record_page, surface_point_name, label, more_info?, redirect_link }`) — both `extension_type: "actionLink"` only today. A non-interactive run with neither flag still creates an OAuth app, as before. **Errors immediately if `app-config.json` already exists in the working directory** — move elsewhere or use `brevo app scaffold` there instead. Otherwise resolves (creates/`cd`s into) a target directory, creates the app, and writes the basic project structure (`app-config.json` + `.gitignore`/`AGENTS.md`/`CLAUDE.md`/`README.md`). It scaffolds a feature (OAuth test server) only when the interactive confirm (*"Scaffold the Test OAuth App?"*, default yes) is answered yes; non-interactive runs (`--json` or piped) stay base-only — add the feature afterward with `brevo app scaffold`. |
|
|
81
82
|
| `brevo app upload` | Push `app-config.json` to Brevo (`--yes`, `--json`). No edit flags — change name/redirect URLs/scopes/logo/version by editing `app-config.json` directly, then run `upload`. **`distribution_type` is immutable** — set at `app create` time; if the local value differs from the server, `upload` errors before pushing (restore the local value, or create a new app). Always fetches the remote app first and shows a local-vs-server diff (even under `--yes`/`--json`); exits 0 with no network push if nothing differs. **For a UI app the diff covers the `ui_app` block placement by placement** — every changed value as `before → after`, added placements tagged `(new)`, dropped ones trailing `(removed)`, matched by slot slug so a reordered `surface_point_list` is not a change — and the command then warns that the app may already be installed in Brevo accounts and asks *"Proceed with upload and update every account this app is installed in?"*. There is no separate publish step: a successful upload changes what every account the app is installed in renders, immediately and with no re-install. `--yes` skips the question but still prints the warning; `--json` prints neither and stays a single parseable document. |
|
|
82
|
-
| `brevo app credentials` | Show client ID / secret (`--app-id`, `--reveal-secret`, `--json`). **`--app-id` is required when scripting** — see the picker note below. **OAuth apps only** — a UI app has no OAuth credentials, so the command refuses it with exit `1` and points at `brevo app list` for the app's type. Also backfills a missing top-level `version` / `distribution_type` into cwd's `app-config.json` when its `
|
|
83
|
+
| `brevo app credentials` | Show client ID / secret (`--app-id`, `--reveal-secret`, `--json`). **`--app-id` is required when scripting** — see the picker note below. **OAuth apps only** — a UI app has no OAuth credentials, so the command refuses it with exit `1` and points at `brevo app list` for the app's type. Also backfills a missing top-level `version` / `distribution_type` into cwd's `app-config.json` when its `app_id` matches (fill-only-when-missing, silent). |
|
|
83
84
|
| `brevo app delete` | Delete an app (`--app-id`, `--force`, `--json`). **`--app-id` is required when scripting** — see the picker note below. |
|
|
84
85
|
| `brevo app scaffold` | Add a feature to the app in the current directory (`--app-id`, `--overwrite`, `--json`). Requires an `app-config.json` in cwd **unless `--app-id <id>` is passed or you accept its bootstrap offer**; reads the linked app from it. `--app-id` in a directory with no config fetches that app and writes `app-config.json` + the base files first — the only way to get a config for an app that already exists, and the migration path off the removed `brevo app update --app-id`. Interactively, omitting `--app-id` in a config-less directory prints *"No app-config.json in this directory…"*, asks **"Set up a project for an app you already have?"** (default yes) and on yes shows an app picker; answering no exits `0` with the remaining routes on screen. Every interactive bootstrap (picker or `--app-id`) then asks `Output directory:` defaulted to `./<slugified app name>`, creates it and `cd`s into it — answer `.` to stay in the current directory — and the *Next steps* box opens with `cd <dir>`. Under `--json` or off a TTY there is no offer — it errors, listing the three ways out. It refuses, before any network call: if the directory is already linked to a different app — either the one the command ran in or the one it was pointed at (naming the app it is already linked to is a no-op), if the directory is **inside** an existing app project (a nested second config would make a later `brevo app upload` from there push the wrong app). Diffs the local config against the server and, on drift, updates `app-config.json` to match (on consent) before writing the feature files — including when a bootstrap is pointed at a directory that already holds a project, where answering **Merge** to the directory question does *not* skip that refresh. When feature files already exist, prompts Overwrite / Merge / Cancel (default Merge — existing files kept); `--overwrite` forces overwrite and skips the prompt. The scaffolded OAuth flow is the confidential-client flow: the token exchange is authenticated with the `CLIENT_SECRET` written into the generated `.env.local`. |
|
|
85
86
|
| `brevo app start oauth` | Run the scaffolded OAuth test server (`--port`) |
|
|
@@ -88,18 +89,27 @@ Don't fall back to raw HTTP against `api.brevo.com` — the `brevo` binary is th
|
|
|
88
89
|
| `brevo app available-scopes` | List OAuth scopes supported by the IdP (`--json`, `--web`) |
|
|
89
90
|
| `brevo skill:cli install` | Install the brevo-cli Claude Code skill (Claude-only; auto-refreshes on every `brevo` run) |
|
|
90
91
|
| `brevo skill:cli uninstall` | Remove the brevo-cli skill from `~/.claude/skills/` (Claude-only) |
|
|
92
|
+
| `brevo function list` | List deployed Brevo Functions (`--draft` for drafts, `--json`) |
|
|
93
|
+
| `brevo function get` | Show function details (`--id <id>`, `--json`). Picker when `--id` omitted on a TTY; refused under `--json`/off a TTY. |
|
|
94
|
+
| `brevo function init` | Create a new function via AI generation or template selection. **Interactive only** — refused under `--json` or piped input. |
|
|
95
|
+
| `brevo function deploy` | Deploy a draft function (`--id <draft-id>`, `--app-id <id>`, `--json`). Links function to an app via `--app-id`. |
|
|
96
|
+
| `brevo function activate` | Activate a function (`--id <id>`, `--json`). Same picker behavior as `function get`. |
|
|
97
|
+
| `brevo function deactivate` | Deactivate a function (`--id <id>`, `--json`). Same picker behavior as `function get`. |
|
|
98
|
+
| `brevo function delete` | Delete a function (`--id <id>`, `--force`, `--json`). Same picker behavior as `function get`. |
|
|
91
99
|
|
|
92
100
|
Run `brevo --help` or `brevo <command> --help` for the full set.
|
|
93
101
|
|
|
94
102
|
## Conventions
|
|
95
103
|
|
|
104
|
+
- **`app-config.json` keys are snake_case (`app_id`, `app_name`, `logo_uri`, `app_type`, `version`, `distribution_type`, `auth.scopes`, `auth.redirect_uris`, `ui_app`, `brevo_function`).** Earlier CLI releases wrote `appId` / `appName` / `logoUri` / `appType` / `auth.redirectUris`. The CLI still reads those, but every write — including a `brevo app upload` or `brevo app scaffold` that has nothing else to change — rewrites the file with the snake_case keys and prints a one-line notice. **If the user's own scripts, CI steps or code read `app-config.json`, tell them to switch to the snake_case names**; do not write the camelCase names into a new or edited file. Commands' `--json` output is a separate contract and is unchanged (`brevo app create --json` still returns `appId`). **`app_type` is a label, not the discriminator** — `brevo app create` / `brevo app scaffold` write it (`"oauth"`, `"ui"` or `"function"`) so the file says in one word what it is, but the presence of the `ui_app` / `brevo_function` / `auth` blocks is what actually decides the type. It is never sent to Brevo. `brevo app upload` checks the two agree and refuses a config whose label contradicts its blocks, so if you add or remove a `ui_app` block by hand, update `app_type` to match — or delete it, since it is optional and configs written by older releases have no such key.
|
|
105
|
+
- **`--json` output keys come in both spellings during a transition; read the snake_case one.** Every `--json` document (and each element of an array document) carries each camelCase key together with its snake_case twin — `appId` and `app_id`, `clientId` and `client_id`, `upToDate` and `up_to_date`, and in the error envelope `exitCode` / `exit_code`, `statusCode` / `status_code`. `app create --json`'s `redirectUri` is twinned as `redirect_uris` (the wire name). Nested objects (`ui_app`, `current` / `next`, Function records) are not aliased — they were already snake_case. **Prefer the snake_case key in anything new you write**; the camelCase spellings are deprecated and will be removed in the next major release. Existing scripts that read `appId` keep working until then.
|
|
96
106
|
- **Every command supports `--json`** — prefer this when parsing output programmatically. It applies to failures too: a failing `--json` run writes a single `{"error": {...}}` document to stdout (see *JSON errors* below) while the human message goes to stderr.
|
|
97
|
-
- **
|
|
107
|
+
- **Three app types, one command surface.** `app-config.json` describes one of: an **OAuth app** — a populated `auth` block (`auth.scopes` / `auth.redirect_uris`) and no `ui_app` or `brevo_function`; a **UI app** — a `ui_app` block and an **empty** `auth: {}` (no callbacks, scopes, or credentials); or a **Brevo Function** — `brevo_function: {}` (a static discriminator, no authored content) and no `auth` block. The presence of `ui_app` or `brevo_function` is the discriminator; never mix types in one file. All three types share the top-level `app_id` / `app_name` / `logo_uri` / `version` / `distribution_type` (every key in the file is snake_case; the CLI still reads the camelCase spellings earlier releases wrote and rewrites the file in snake_case on its next write) and the same `create`/`upload`/`list`/`delete` commands; only UI apps take `install`/`uninstall`; Function apps have their own `brevo function` command group (`list`/`get`/`init`/`deploy`/`activate`/`deactivate`/`delete`, alias `brevo fn`) for managing the functions themselves. A Function app's `app_type` field is `"function"` (informational — the actual discriminator is the `brevo_function` block). `brevo function init` is **interactive only** (refused under `--json` or piped input). `brevo app scaffold` inside a Function-app project has no feature to scaffold, and `brevo app start` does not apply. **A UI app has one stored configuration, shared by every account it is installed in** — `brevo app upload` is therefore how you change what an installed app renders (edit `app-config.json`, upload, done), never uninstall-then-reinstall; both commands show what is about to change for that reason. The `ui_app` block holds `extension_type` at its root (`actionLink`, `iframeExtension`, `legacyComponent` — camelCase only) and a `surface_point_list` of placement entries, each carrying `surface_point_name` (the dot-notation slug from the platform's registry, e.g. `contactDetails.header.menu` — not the `<location>.<place>.<kind>` extension-point name like `contactDetails.headerMenu.action`, which is dotted too but a different string), `label`, optional `more_info`, `redirect_link` (record context arrives as query parameters), optional `context`, optional `size` (e.g. `{ "width": "280px", "height": "160px" }` — each axis a positive-integer `px` length or `1%`–`100%` of the host slot, shrink-only, both axes optional; `brevo app create` seeds it from the slot's registry default when the platform declares one, same mechanism as `context` — the entry's own value is what uploads), and — `iframeExtension` only — `modal_iframe_url`. Do **not** write `link_target` or `extension_point_name` into the file: both are wire/server-stamped (`app upload` injects `link_target` itself). Write only the keys documented here — `brevo app upload` validates the whole file and rejects anything it doesn't recognise, including the pre-GA `heading`/`subheading` names (now `label`/`more_info`, per entry). A UI-app project is **configuration only** — no feature to scaffold and no `src/oauth/`, since an action link has no local server (`brevo app scaffold` inside one says so and exits `0`; `brevo app start` does not apply) — and the base docs the scaffold writes (`AGENTS.md` / `CLAUDE.md` / `README.md`) describe whichever type the app is, so a UI app's copies cover the `ui_app` block and the `upload` → `install` flow instead of an OAuth server.
|
|
98
108
|
- **`brevo app create` refuses to run inside an already-linked directory.** If `app-config.json` exists in cwd, it throws immediately (no confirm, no override) — the error points at moving elsewhere or running `brevo app scaffold` there.
|
|
99
109
|
- **`brevo app create` resolves its target directory before creating the app**, then writes the **basic project structure only** (`app-config.json` + `.gitignore`/`AGENTS.md`/`CLAUDE.md`/`README.md`) — the OAuth server code is a *feature*, not part of the base. Interactive mode prompts for the target directory (default `./<slugified-app-name>`, `cd`s into it) before the API call, how to handle an existing one (overwrite / merge / choose a different path), and — after the app is created — whether to scaffold a feature (*"Scaffold the Test OAuth App?"*, default **yes**). There is no follow-up "which feature?" question while the CLI ships one: a list of one is not asked, and the confirm names it instead. A second feature would bring the picker back. Non-interactive runs stay base-only: `--json` (and piped, non-TTY) create the app and write the base files but never scaffold a feature — run `brevo app scaffold` afterward for the OAuth code. Under `--json` the same default directory is used and `cd`d into if it doesn't already exist; if it already exists, both directory setup and scaffolding are skipped (the app is still created). The JSON response always includes `directory` (absolute path) alongside the app fields, plus either `scaffolded` (base file count, on success) or `scaffoldSkipped` (a message, when the directory already existed).
|
|
100
110
|
- **`brevo app scaffold` adds a feature to an already-created project, or sets an empty directory up for an app that already exists.** It **requires** an `app-config.json` in cwd unless `--app-id` is passed or its bootstrap offer is accepted, and only the bootstrap mode ever creates a directory (the feature-add mode always writes into the project it was run in). **`--app-id <id>` bootstraps a project for an app that already exists**: it fetches the app, writes `app-config.json` + the base files, and then continues into the feature flow. That is the only command that produces a config for an existing app (`app create` creates a new one, `app upload` only reads the linked project), which makes it the migration path off the removed `brevo app update --app-id`. **Interactively, `--app-id` is optional**: in a config-less directory the command explains there is no app here, asks *"Set up a project for an app you already have?"* (default **yes**), and on yes runs the same app picker `app delete` uses — because a user who has lost their project folder has the app but not necessarily its ID. Declining is a normal outcome that exits `0` after printing the remaining routes; the offer is skipped entirely under `--json` or off a TTY, where the no-config error (naming all three ways out: `cd` into a project, `--app-id`, `brevo app create`) is raised instead, so scripts behave exactly as before. **An interactive bootstrap also asks where to put the project** — `Output directory:`, defaulted to `./<slugified app name>`, the same prompt (and the same overwrite / merge / choose-a-different-path follow-up on an existing directory) `app create` uses; it creates the directory, `cd`s the CLI process into it, writes and reports the project, then asks *"Scaffold the Test OAuth App?"* (default yes; declining leaves the project and exits `0`), and opens *Next steps* with `cd <dir>` since the user's shell stayed behind. Answering `.` keeps the current directory and drops that step. This too is interactive-only: under `--json` or off a TTY the files go into the current directory as they always have, which is what makes `scaffold --app-id` safe to script. In bootstrap mode the config is written from the server's copy of the app, since there is nothing local to read it from. Bootstrapping is refused, before any network call or write, in two cases: a directory already linked to a **different** app (passing the app it is already linked to changes nothing), and a directory **inside** an existing app project — `readProjectConfig` reads cwd only and never walks up, so without that check a stray `cd` would nest a second `app-config.json` inside the first and a later `app upload` from there would push the wrong app silently. The different-app check applies to the answer to `Output directory:` as well as to cwd, and there it is the only thing standing between you and a project whose `app-config.json` and `src/oauth/.env.local` name two different apps. **A target directory that already holds a project for the same app makes the bootstrap a refresh**: its config is diffed against the server and rewritten only on consent, and the directory question's **Merge** answer does not suppress that. The two answers address different things — Merge means "don't clobber my own files" and is implemented by skipping any path that already exists, which `app-config.json` always does here, so letting it govern the base write meant the command fetched the app, discarded every field, wrote nothing, and still printed its success box. No drift leaves `app-config.json` as it is with a one-line notice; the feature is still offered either way. It otherwise reads the linked app id from that config (no picker — the picker is only for the config-less bootstrap), diffs the local config against the server, and if fields drifted it shows them and asks consent to update `app-config.json` (and the other base files) to match before writing the feature files. When any feature file already exists it prompts **Overwrite / Merge / Cancel** (default **Merge** — existing, e.g. hand-edited, files are kept and only missing files added; Cancel aborts without writing). The `--overwrite` flag forces a full overwrite of feature files and skips that prompt (works interactively and under `--json`). **Under `--json` it never prompts**: a config diff comes back as `{ "cancelled": true, "reason": "...", "diffs": [...] }`; otherwise it writes the feature (merging existing files unless `--overwrite` is passed) and returns `{ "scaffolded": <n>, "directory": "..." }`.
|
|
101
|
-
- **`app-config.json`** in the working directory pins the linked app — `brevo app upload` and `brevo app start` read from it. `upload` is the *only* command that pushes config changes, and it has no `--app-id` override (it always resolves the app from cwd's `app-config.json`, hard-erroring if that file is missing/invalid/lacks `
|
|
102
|
-
- **Commands that pick
|
|
111
|
+
- **`app-config.json`** in the working directory pins the linked app — `brevo app upload` and `brevo app start` read from it. `upload` is the *only* command that pushes config changes, and it has no `--app-id` override (it always resolves the app from cwd's `app-config.json`, hard-erroring if that file is missing/invalid/lacks `app_id`); `brevo app start` accepts `--app-id` to target a different app. The top-level `logo_uri` string is pushed as `logo_uri`; leave it empty to keep the API value untouched. The top-level `version` string is round-tripped as `version` on the wire — `upload` sends the local value (falling back to the server's current value if locally absent) and writes back whatever the server confirms. `brevo app credentials` additionally backfills a missing top-level `version` / `distribution_type` into cwd's `app-config.json` when its `app_id` matches the inspected app — fill-only-when-missing (never overwrites an existing local value), silent in all modes — so legacy projects that are never `upload`ed still converge to the current shape.
|
|
112
|
+
- **Commands that pick interactively refuse to do so when scripted.** `brevo app credentials` and `brevo app delete` fall back to an interactive app picker when `--app-id` is absent; `brevo function get`, `activate`, `deactivate`, and `delete` fall back to a function picker when `--id` is absent. Under `--json` **or** off a TTY the picker is refused up front — before any network call — with a `CliError` naming the exact command to run and exiting `1`. **Always pass `--app-id` (for app commands) or `--id` (for function commands) when scripting.** The refusal exists because the picker renders its choice list to stdout, which would otherwise corrupt the single-JSON-document contract below and leak IDs into whatever is parsing it. `brevo app delete` and `brevo function delete` are the ones that matter most: they are destructive, so a script that relied on the picker was never doing what its author thought.
|
|
103
113
|
- **There is no `brevo app update`.** It was removed and replaced by `brevo app upload`, with no shim and no flag-for-flag equivalent — change an app's name, redirect URLs, scopes or logo by editing `app-config.json`, then run `brevo app upload`. That is what to replace it with wherever you find it: a user's script, a CI job, a README, or your own recollection. Invoking it — with any of the old flags (`--name`, `--redirect-uri`, `--scope`, `--logo-uri`, `--app-id`), with `--help`, or as `brevo app help update` — prints a message naming `upload` and exits `1` **without uploading anything**, so a `1` from `brevo app update` means the command is gone, not that an upload failed. It is absent from every help screen, and needs no login to reach.
|
|
104
114
|
- **Credentials** live at `~/.brevo/credentials.json`. Never commit this file or any `.env.local`.
|
|
105
115
|
- **Non-interactive auth:** `BREVO_API_KEY=xkeysib-... brevo login`. The legacy `--api-key` flag was removed because it leaks into shell history.
|
|
@@ -115,15 +125,15 @@ Run `brevo --help` or `brevo <command> --help` for the full set.
|
|
|
115
125
|
Under `--json`, a failing command writes **one** JSON document to stdout describing the failure; the human-readable message still goes to stderr. The `error` key is the discriminator — no success payload has one:
|
|
116
126
|
|
|
117
127
|
```json
|
|
118
|
-
{ "error": { "name": "CliError", "message": "Not authenticated. Run: brevo login", "exitCode": 1 } }
|
|
128
|
+
{ "error": { "name": "CliError", "message": "Not authenticated. Run: brevo login", "exitCode": 1, "exit_code": 1 } }
|
|
119
129
|
```
|
|
120
130
|
|
|
121
131
|
| Field | Always present | Meaning |
|
|
122
132
|
| --- | --- | --- |
|
|
123
133
|
| `name` | yes | Error class — `CliError`, `ApiError`, `AuthExpiredError`, `AbortError` |
|
|
124
134
|
| `message` | yes | Same text written to stderr |
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
135
|
+
| `exit_code` | yes | Matches the process exit code (also present as the deprecated `exitCode`) |
|
|
136
|
+
| `status_code` | `ApiError` only | HTTP status behind the failure (also present as the deprecated `statusCode`) |
|
|
127
137
|
| `code` | `ApiError`, when classified | `AUTH_INVALID`, `AUTH_EXPIRED`, `ACCESS_DENIED`, `APP_NOT_FOUND`, `REDIRECT_INVALID`, `PORT_IN_USE`, `NETWORK_ERROR`, `RATE_LIMITED`, `APP_LIMIT_REACHED`, `REGISTRY_ERROR`, `AUTH_GATEWAY` |
|
|
128
138
|
|
|
129
139
|
Stdout is always **exactly one** parseable document. Commands that already describe their own failure keep their shape rather than emitting this envelope — `brevo whoami --json` returns `{"authenticated": false, "reason": "no_key"}` (exit `1`). Check for `error` first, then fall back to the command's own shape.
|
package/agent-context/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: brevo-cli
|
|
3
|
-
description: Use when working in a project that uses the Brevo Developer CLI (the `brevo` binary from `@getbrevo/cli`) — managing OAuth apps, scaffolding integrations, running the local OAuth test server, or invoking any `brevo …` command. Activates on: brevo, brevo cli, brevo app, app-config.json, OAuth Brevo, BREVO_API_KEY, getbrevo.
|
|
3
|
+
description: Use when working in a project that uses the Brevo Developer CLI (the `brevo` binary from `@getbrevo/cli`) — managing OAuth apps, UI apps, and Brevo Functions, scaffolding integrations, running the local OAuth test server, or invoking any `brevo …` command. Activates on: brevo, brevo cli, brevo app, brevo function, app-config.json, OAuth Brevo, BREVO_API_KEY, getbrevo.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Brevo CLI
|
|
7
7
|
|
|
8
|
-
This project uses the Brevo Developer CLI to create and manage Brevo OAuth
|
|
8
|
+
This project uses the Brevo Developer CLI to create and manage Brevo applications (OAuth apps, UI apps, and Brevo Functions). Treat the `brevo` binary as the canonical entry point — don't shell out to `curl https://api.brevo.com/...` for things the CLI already covers.
|
|
9
9
|
|
|
10
10
|
## `brevo --help` is the source of truth
|
|
11
11
|
|
|
@@ -52,11 +52,19 @@ Don't fall back to raw HTTP against `api.brevo.com` — the `brevo` binary is th
|
|
|
52
52
|
- "Authenticate" → `brevo login` (or `BREVO_API_KEY=xkeysib-... brevo login` for CI)
|
|
53
53
|
- "Who am I logged in as?" → `brevo whoami --json`
|
|
54
54
|
- "Show / pick an app" → `brevo app list --json`
|
|
55
|
-
- "Create an app" → `brevo app create --name "<name>" --distribution private --redirect-uri <url> --json` (add `--logo-uri <https://…>` to set the app logo at creation time; new apps default to scopes `contacts:read`, `contacts:write`, `crm:read`, `crm:write`). **Use `--distribution private`** — check `brevo app create --help` for the values your account accepts, and don't pass one it doesn't list. **Fails immediately if run from a directory that already has `app-config.json`** — `cd` elsewhere first, or use `brevo app scaffold` in that directory instead. Otherwise resolves (creates/`cd`s into) its target directory, creates the app, and writes the **basic project structure** (`app-config.json` + `.gitignore`/`AGENTS.md`/`CLAUDE.md`/`README.md`). It scaffolds a feature (the OAuth test server) **only** when the interactive confirm (*"Scaffold the Test OAuth App?"*, default yes) is answered yes — there is no separate "which feature?" question while the CLI ships one; **non-interactive runs (`--json` or piped) stay base-only** — run `brevo app scaffold` afterward to add the OAuth code. Under `--json`, the response's `directory` field is where it landed and `scaffolded` is the base file count; check for `scaffoldSkipped` instead of `scaffolded` if that directory already existed (both directory setup and scaffolding are skipped together in that case, but the app is still created).
|
|
56
|
-
- "Create a UI app" (an action link that renders inside Brevo CRM records) → run `brevo app create` **interactively** and pick *UI app* at the *"What type of app are you building?"* prompt. There is **no `--type` flag
|
|
55
|
+
- "Create an app" → `brevo app create --name "<name>" --distribution private --redirect-uri <url> --json` (add `--logo-uri <https://…>` to set the app logo at creation time; new apps default to scopes `contacts:read`, `contacts:write`, `crm:read`, `crm:write`). **Use `--distribution private`** — check `brevo app create --help` for the values your account accepts, and don't pass one it doesn't list. **Fails immediately if run from a directory that already has `app-config.json`** — `cd` elsewhere first, or use `brevo app scaffold` in that directory instead. Otherwise resolves (creates/`cd`s into) its target directory, creates the app, and writes the **basic project structure** (`app-config.json` + `.gitignore`/`AGENTS.md`/`CLAUDE.md`/`README.md`). It scaffolds a feature (the OAuth test server) **only** when the interactive confirm (*"Scaffold the Test OAuth App?"*, default yes) is answered yes — there is no separate "which feature?" question while the CLI ships one; **non-interactive runs (`--json` or piped) stay base-only** — run `brevo app scaffold` afterward to add the OAuth code. Under `--json`, the response's `directory` field is where it landed and `scaffolded` is the base file count; check for `scaffold_skipped` (also `scaffoldSkipped`) instead of `scaffolded` if that directory already existed (both directory setup and scaffolding are skipped together in that case, but the app is still created).
|
|
56
|
+
- "Create a UI app" (an action link that renders inside Brevo CRM records) → run `brevo app create` **interactively** and pick *UI app* at the *"What type of app are you building?"* prompt, **or non-interactively** with `--ui-app --record-page <slug> --placement <surface_point_name> --label <text> --url <url> [--more-info <text>]`, or `--ui-config <file>` (a JSON file: `{ extension_type, record_page, surface_point_name, label, more_info?, redirect_link }`). There is still **no `--type` flag** — a plain non-interactive run with neither `--ui-config` nor `--ui-app` creates an OAuth app, exactly as before. Both non-interactive routes only support `extension_type: "actionLink"` today; an unknown `--record-page`/`--placement` fails with the valid options listed in the error. Interactively, the flow asks for the integration type, one record page, one placement on it, a label, optional supporting text, and the destination URL — every route authors exactly **one** placement; add more by hand as further `surface_point_list` entries in `app-config.json` (each with its own `label` and `redirect_link`), then `brevo app upload`.
|
|
57
|
+
- "Create a Function app" (a serverless function running on Brevo's infrastructure) → run `brevo app create` **interactively** and pick *Brevo Function* at the *"What type of app are you building?"* prompt. Private distribution only. Non-interactive runs (`--json` or piped) create an OAuth app as before — there is no `--type` flag for Function apps. The created `app-config.json` carries `brevo_function: {}` (a static discriminator, not authored content) and `app_type: "function"` (informational). A Function app has no `auth` block and no `ui_app` block.
|
|
58
|
+
- "Create a new function" → `brevo function init` (alias `brevo fn init`). **Interactive only** — refused under `--json` or piped input. Prompts for the parent Function app, then offers AI generation or template selection to create the function. Once generated, the function can be iterated on, previewed, and deployed.
|
|
59
|
+
- "Deploy a draft function" → `brevo function deploy --id <draft-id> [--app-id <id>] --json`. Deploys a draft function to production. `--app-id` links the deployed function to an app. Interactively, omitting `--id` shows a draft picker; under `--json` or off a TTY, `--id` is required. Omitting `--app-id` interactively shows an app picker (Function apps only).
|
|
60
|
+
- "List functions" → `brevo function list --json` (add `--draft` for draft functions only). Lists all deployed Brevo Functions in the account, or drafts with `--draft`.
|
|
61
|
+
- "Show function details" → `brevo function get --id <id> --json`. Interactively, omitting `--id` shows a picker; under `--json` or off a TTY, `--id` is required.
|
|
62
|
+
- "Activate a function" → `brevo function activate --id <id> --json`. Same picker behavior as `get`.
|
|
63
|
+
- "Deactivate a function" → `brevo function deactivate --id <id> --json`. Same picker behavior as `get`.
|
|
64
|
+
- "Delete a function" → `brevo function delete --id <id> --force --json`. Same picker behavior as `get`. `--force` skips the confirmation prompt.
|
|
57
65
|
- "Install a UI app into an account" → `brevo app install [account-id] --app-id <id> --force --json`. **UI apps only** — an OAuth app has nothing to install (it becomes usable when a user authorizes it) and the CLI refuses with exit `1`. The `[account-id]` positional is optional: omitted, a plain account installs into itself (no prompt, so `--json`/CI works) and a corporate account picks a sub-account interactively (non-interactive corporate runs must pass it explicitly). The app must have been validated with `brevo app upload` first — installing before an upload is refused locally. Interactively, omitting `--app-id` outside a linked project opens an app picker that lists **only UI apps**; with no UI app to offer, the command errors (exit `1`) naming `brevo app create`. That picker needs a terminal: under `--json` or off a TTY, omitting `--app-id` outside a linked project is refused with exit `1` — so always pass `--app-id` when scripting from an unlinked directory. Before it acts, `install` prints the configuration it will install **as stored on the server** — app ID, name, `version`, extension type and every placement — because that is what the account will render, not whatever the local `app-config.json` now says; under `--json` the same facts come back as `version` and `ui_app` on the result. If the linked project's `ui_app` block has drifted from the stored one, it warns and names `brevo app upload`, then installs anyway (exit `0`) — the stored configuration is a legitimate thing to install, so this is a notice, not a refusal.
|
|
58
66
|
- "Uninstall a UI app from an account" → `brevo app uninstall [account-id] --app-id <id> --force --json`. Same target resolution as `install`. Uninstalling an app that isn't installed is **informational, exit `0`** — not an error.
|
|
59
|
-
- "Update app metadata" → edit the relevant field(s) in `app-config.json` (`
|
|
67
|
+
- "Update app metadata" → edit the relevant field(s) in `app-config.json` (`app_name`, `auth.redirect_uris`, `auth.scopes`, `logo_uri`, `version`) — every key in the file is snake_case (older projects may still carry the camelCase spellings `appId` / `appName` / `logoUri` / `appType` / `auth.redirectUris`, or the even older `auth.redirectUrls`; the CLI reads all of them and rewrites the file with the snake_case keys on its next write, including a `brevo app upload` or `brevo app scaffold` run that otherwise has nothing to change), then run `brevo app upload --json` (no `--app-id`/`--name`/`--redirect-uri`/`--scope`/`--logo-uri` flags exist — `upload` always pushes the whole file, resolved only from cwd's `app-config.json`). **`distribution_type` is immutable** — it's set at `app create` time and cannot be changed via `upload`; if the local value differs from the server, `upload` errors and tells you to restore it (create a new app to get a different distribution). For a **UI app**, `upload` is also the way to change what an installed app renders — there is no re-install and no publish step: the diff prints every `ui_app` placement field by field (`before → after`, plus `(new)` / `(removed)` placements), then warns that the app may already be installed in Brevo accounts and asks *"Proceed with upload and update every account this app is installed in?"*. `--yes` skips the question but still prints the warning; `--json` prints neither and stays a single parseable document. **There is no `brevo app update`** — it was removed, with no shim and no flag-for-flag equivalent; if you find it in a user's script, CI job, README, or your own recollection, replace it with the edit-then-`upload` flow above. Invoking it — with any of the old flags, with `--help`, or as `brevo app help update` — prints a message naming `brevo app upload` and exits `1` **without uploading anything**, so a `1` from `brevo app update` means the command is gone, not that an upload failed.
|
|
60
68
|
- "Get client credentials" → `brevo app credentials --app-id <id> --json` (add `--reveal-secret` to print the secret). **`--app-id` is not optional here** — without it the command wants an interactive app picker, and under `--json`/off a TTY it refuses with exit `1` rather than prompting. **OAuth apps only** — a UI app has no OAuth credentials (no client ID, secret, scopes, or callbacks), so the command refuses it with exit `1` and points at `brevo app list` for the app's type.
|
|
61
69
|
- "Set up a project for an app that already exists" → `brevo app scaffold --app-id <id>` (`brevo app list` gives the IDs). Fetches the app and writes `app-config.json` + the base files, then adds a feature as below. Interactively it asks which directory to write into first (default `./<slugified app name>`); under `--json`/off a TTY it uses the current one. **This is the only way to get an `app-config.json` for an existing app** — `brevo app create` makes a *new* app, and `brevo app upload` only ever reads the project in the current directory. It refuses if the directory is already linked to a *different* app; pointing it at the app the directory is already linked to is a no-op. **Interactively you can omit `--app-id`**: plain `brevo app scaffold` in a directory with no `app-config.json` says so, asks *"Set up a project for an app you already have?"* (default yes), and on yes lists the account's apps so you can pick one — no need to look the ID up first. **Every interactive bootstrap (picker or `--app-id`) then asks `Output directory:`, defaulted to `./<the app's name slugified>`** — the same question `brevo app create` asks — creates that directory and `cd`s into it, so the files don't land in whatever folder you happened to be standing in; answer `.` to use the current directory instead. It then writes the project (`app-config.json` + the base files), shows what it wrote, and asks *"Scaffold the Test OAuth App?"* (default yes) — declining is normal and leaves the project in place, exit `0`. The *Next steps* box opens with `cd <dir>`, because the CLI can only move its own process, never your shell. **Under `--json` or off a TTY there is no directory question and the files go into the current directory**, so scripted `brevo app scaffold --app-id <id>` runs are unchanged — `mkdir` and `cd` yourself first if you want them somewhere specific. Answering **no** is a normal outcome, not an error: it exits `0` after printing the remaining routes (`brevo app create` here, or `cd` into an existing project). **Always pass `--app-id` when scripting** — the offer needs a terminal, so under `--json` or off a TTY the command errors instead of prompting. **If the directory you point it at already holds a project, the bootstrap is a refresh, not a fresh write**: the config found there is diffed against the server and rewritten only on consent (*"…will update app-config.json to match the server. Continue?"*, default yes) — answering **Merge** at the directory question does not suppress that, because merging keeps the file that exists and so would skip the very file a bootstrap is for. No drift means `app-config.json` is left alone with a one-line notice, and the feature still gets added. A directory holding a project for a **different** app is refused outright, naming both apps. Two refusals apply to both forms, before any write: that different-app case, and the directory must not be **inside** an existing project (a nested second `app-config.json` would make a later `brevo app upload` push the wrong app).
|
|
62
70
|
- "Add a feature (e.g. the OAuth test server) to an existing project" → `brevo app scaffold` (run **inside** the project directory; it reads the linked app from `app-config.json`, so `--app-id` is only needed to bootstrap a directory that has none). Not needed right after `app create` if you already accepted the feature prompt there. If feature files already exist it prompts Overwrite / Merge / Cancel (default Merge); pass `--overwrite` to force a full overwrite without prompting. **The scaffolded OAuth flow is the confidential-client flow:** `/auth/callback` authenticates the token exchange with the `CLIENT_SECRET` written into the scaffolded `.env.local`.
|
|
@@ -67,17 +75,19 @@ Don't fall back to raw HTTP against `api.brevo.com` — the `brevo` binary is th
|
|
|
67
75
|
|
|
68
76
|
## Hard rules
|
|
69
77
|
|
|
78
|
+
- **`app-config.json` keys are snake_case (`app_id`, `app_name`, `logo_uri`, `app_type`, `version`, `distribution_type`, `auth.scopes`, `auth.redirect_uris`, `ui_app`, `brevo_function`).** Earlier CLI releases wrote `appId` / `appName` / `logoUri` / `appType` / `auth.redirectUris`. The CLI still reads those, but every write — including a `brevo app upload` or `brevo app scaffold` that has nothing else to change — rewrites the file with the snake_case keys and prints a one-line notice. **If the user's own scripts, CI steps or code read `app-config.json`, tell them to switch to the snake_case names**; do not write the camelCase names into a new or edited file. Commands' `--json` output is a separate contract and is unchanged (`brevo app create --json` still returns `appId`). **`app_type` is a label, not the discriminator** — `brevo app create` / `brevo app scaffold` write it (`"oauth"`, `"ui"` or `"function"`) so the file says in one word what it is, but the presence of the `ui_app` / `brevo_function` / `auth` blocks is what actually decides the type. It is never sent to Brevo. `brevo app upload` checks the two agree and refuses a config whose label contradicts its blocks, so if you add or remove a `ui_app` block by hand, update `app_type` to match — or delete it, since it is optional and configs written by older releases have no such key.
|
|
79
|
+
- **`--json` output keys come in both spellings during a transition; read the snake_case one.** Every `--json` document (and each element of an array document) carries each camelCase key together with its snake_case twin — `appId` and `app_id`, `clientId` and `client_id`, `upToDate` and `up_to_date`, and in the error envelope `exitCode` / `exit_code`, `statusCode` / `status_code`. `app create --json`'s `redirectUri` is twinned as `redirect_uris` (the wire name). Nested objects (`ui_app`, `current` / `next`, Function records) are not aliased — they were already snake_case. **Prefer the snake_case key in anything new you write**; the camelCase spellings are deprecated and will be removed in the next major release. Existing scripts that read `appId` keep working until then.
|
|
70
80
|
1. **Always pass `--json`** when you intend to parse output. Every command supports it, **on success and on failure alike** — a failing `--json` run writes a single `{"error": {...}}` document to stdout (see *JSON errors* below), so you can read the reason instead of only seeing a non-zero exit.
|
|
71
81
|
2. **Never print, log, or commit** API keys (`xkeysib-…`), client secrets, refresh tokens, or contents of `~/.brevo/credentials.json` / `.env.local`. Redact before sharing diagnostics.
|
|
72
82
|
3. **Don't use `--api-key`** — the flag was removed. Use the `BREVO_API_KEY` env var.
|
|
73
83
|
4. **`brevo app create` refuses to run inside an already-linked directory** (`app-config.json` present) — `cd` elsewhere or use `brevo app scaffold` there instead. **`brevo app scaffold` requires an `app-config.json` in the current directory unless you pass `--app-id` or answer its bootstrap offer** (it adds a feature to an already-created project); with none present and no `--app-id` it offers to set the directory up for an existing app when interactive, and otherwise errors, listing the three ways out (`cd` into a project, `--app-id` to set this directory up for an app you already have, or `brevo app create` for a new one). With `--app-id <id>` in a directory that has no config it fetches that app, asks (interactively only) which directory to write into — default `./<slugified app name>`, `.` to stay put — writes `app-config.json` + the base files there, and continues; that is the migration path off the removed `brevo app update --app-id`, and off a TTY or under `--json` it writes into the current directory with no prompt. If the directory it is told to write into already holds a project for the **same** app, it diffs that config against the server and rewrites it only on consent — the directory prompt's **Merge** answer does not silently skip the refresh, and no drift leaves `app-config.json` untouched with a notice. It refuses (before any network call) if the directory is already linked to a *different* app — whether that is the directory the command ran in or the one it was pointed at — or if the directory is **inside** an existing app project (a nested second `app-config.json` would make a later `brevo app upload` from there push the wrong app — `cd` to the project root or outside it). It reads the linked app from that config, diffs the config against the server, and if fields drifted it tells you and (on consent) rewrites `app-config.json` to match before writing the feature files. When any feature file already exists it prompts **Overwrite / Merge / Cancel** (default **Merge** — existing, e.g. hand-edited, files are kept; Cancel aborts). Pass `--overwrite` to force a full overwrite and skip that prompt (works interactively and under `--json`). **Under `--json` it never prompts**: a config diff comes back as `{ "cancelled": true, "reason": "...", "diffs": [...] }`; otherwise it scaffolds the feature (merging existing files unless `--overwrite` is passed) and returns `{ "scaffolded": <n>, "directory": "..." }`.
|
|
74
84
|
5. **Prefer flag-driven over interactive** in agent contexts: `--name`, `--app-id`, `--force`, `--yes` so the command doesn't block on prompts.
|
|
75
85
|
6. **Write only the `app-config.json` keys this file documents.** `brevo app upload` validates the whole file and rejects keys it doesn't recognise, so an invented block fails at upload rather than doing anything useful.
|
|
76
|
-
7. **Never mix
|
|
86
|
+
7. **Never mix app types in one `app-config.json`.** The presence of `ui_app` or `brevo_function` is the app-type discriminator: an OAuth app has a populated `auth` block and no `ui_app` or `brevo_function`; a UI app has a `ui_app` block and an **empty** `auth: {}` (it has no OAuth callback, scopes, or credentials); a Function app has `brevo_function: {}` and no `auth` block. See *UI apps* and *Brevo Functions* below for each type's shape.
|
|
77
87
|
|
|
78
88
|
## UI apps
|
|
79
89
|
|
|
80
|
-
A **UI app** is an action link that renders inside Brevo CRM record pages (contact, company, deal). It is
|
|
90
|
+
A **UI app** is an action link that renders inside Brevo CRM record pages (contact, company, deal). It is one of three app types (alongside OAuth apps and Brevo Functions) — created by the same `brevo app create` (interactive only, see the decision tree), pushed by the same `brevo app upload`, and made visible in an account with `brevo app install`.
|
|
81
91
|
|
|
82
92
|
A UI-app project is **configuration only**: there is no feature to scaffold and no `src/oauth/`, because an action link has no local server to run — `brevo app scaffold` inside one says so and exits `0`, and `brevo app start` does not apply. The base docs the scaffold writes (`AGENTS.md` / `CLAUDE.md` / `README.md`) describe whichever type the app is, so a UI app's copies document the `ui_app` block and the `upload` → `install` flow rather than an OAuth server.
|
|
83
93
|
|
|
@@ -99,15 +109,41 @@ Install semantics worth knowing: only UI apps install into an account; the app m
|
|
|
99
109
|
|
|
100
110
|
**An installed UI app tracks the server's configuration, not the account's copy of it.** There is one stored snapshot per app, so `brevo app upload` changes what every account it is installed in renders, immediately and with no re-install — which is why `upload` warns and asks before pushing a UI app, and why `install` shows the stored configuration it is about to make visible. Tell a user to edit `app-config.json` and run `brevo app upload`; never tell them to uninstall and re-install to pick up a change.
|
|
101
111
|
|
|
112
|
+
## Brevo Functions
|
|
113
|
+
|
|
114
|
+
A **Brevo Function** is a serverless function that runs on Brevo's infrastructure. It is the third app type alongside OAuth apps and UI apps — created by `brevo app create` (interactive only, pick *Brevo Function* at the app-type prompt), and managed with the `brevo function` command group (alias `brevo fn`).
|
|
115
|
+
|
|
116
|
+
The `brevo_function` block in `app-config.json`:
|
|
117
|
+
|
|
118
|
+
- `brevo_function: {}` — a **static discriminator**, not authored content. Its presence marks the app as a Function app; there are no fields to write inside it. The server does not stamp additional keys onto it.
|
|
119
|
+
- `app_type: "function"` — an informational top-level field (`'oauth' | 'ui' | 'function'`). The actual discriminator is the `brevo_function` block, not this field.
|
|
120
|
+
- A Function app has **no `auth` block** (no OAuth callbacks, scopes, or credentials) and **no `ui_app` block**.
|
|
121
|
+
|
|
122
|
+
A Function-app project is **configuration only** at the `app-config.json` level — the functions themselves are created and deployed through the `brevo function` commands, not by editing the config file. `brevo app scaffold` inside a Function-app project has no feature to scaffold (no `src/oauth/`), and `brevo app start` does not apply.
|
|
123
|
+
|
|
124
|
+
### `brevo function` subcommands
|
|
125
|
+
|
|
126
|
+
All subcommands support `--json`. Commands that pick a function interactively (`get`, `activate`, `deactivate`, `delete`) show a picker when `--id` is omitted on a TTY, and refuse under `--json` or off a TTY — same convention as `brevo app credentials` and `brevo app delete`. **Always pass `--id` when scripting.**
|
|
127
|
+
|
|
128
|
+
| Command | Purpose |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `brevo function list` | List deployed functions (`--draft` for drafts, `--json`) |
|
|
131
|
+
| `brevo function get` | Show function details (`--id <id>`, `--json`) |
|
|
132
|
+
| `brevo function init` | Create a new function via AI generation or template selection. **Interactive only** — refused under `--json` or piped input. Prompts for the parent Function app, creation method (AI or template), and iterates until deployed. |
|
|
133
|
+
| `brevo function deploy` | Deploy a draft function (`--id <draft-id>`, `--app-id <id>`, `--json`). Links the deployed function to an app via `--app-id`. Interactively, omitting `--id` shows a draft picker; `--app-id` omitted shows a Function-app picker. |
|
|
134
|
+
| `brevo function activate` | Activate a function (`--id <id>`, `--json`) |
|
|
135
|
+
| `brevo function deactivate` | Deactivate a function (`--id <id>`, `--json`) |
|
|
136
|
+
| `brevo function delete` | Delete a function (`--id <id>`, `--force`, `--json`). `--force` skips confirmation. |
|
|
137
|
+
|
|
102
138
|
## Locating the linked app
|
|
103
139
|
|
|
104
|
-
If `app-config.json` exists in the working directory, it pins the app — `brevo app upload` and `brevo app start` use it automatically. `brevo app start` accepts an `--app-id` override to target a different app; `upload` does **not** — it only ever reads cwd's `app-config.json`, hard-erroring if that file is missing, invalid, or lacks `
|
|
140
|
+
If `app-config.json` exists in the working directory, it pins the app — `brevo app upload` and `brevo app start` use it automatically. `brevo app start` accepts an `--app-id` override to target a different app; `upload` does **not** — it only ever reads cwd's `app-config.json`, hard-erroring if that file is missing, invalid, or lacks `app_id`.
|
|
105
141
|
|
|
106
|
-
`app-config.json` carries an optional top-level `
|
|
142
|
+
`app-config.json` carries an optional top-level `logo_uri` string. When set, `brevo app upload` pushes it as `logo_uri`; when empty / absent, the field is left untouched on the API.
|
|
107
143
|
|
|
108
144
|
`app-config.json` also carries a top-level `version` string, shown by `brevo app create`/`brevo app list`. `brevo app upload` sends it on the wire as `version` (falling back to the server's current value if locally absent) and writes back whatever version the server confirms after a successful upload.
|
|
109
145
|
|
|
110
|
-
`brevo app credentials` also backfills a legacy `app-config.json` toward the current shape: when the file exists in cwd and its `
|
|
146
|
+
`brevo app credentials` also backfills a legacy `app-config.json` toward the current shape: when the file exists in cwd and its `app_id` matches the app being inspected, any missing top-level `version` / `distribution_type` is filled in from the server (fill-only-when-missing — an existing local value is never overwritten). This runs silently in all modes; human output prints a one-line note when something was written. It's how projects that are never `upload`ed still converge.
|
|
111
147
|
|
|
112
148
|
## Scopes
|
|
113
149
|
|
|
@@ -135,13 +171,13 @@ Writing `app-config.json` for an app whose remote scopes contain `'all'` never p
|
|
|
135
171
|
Under `--json`, a command that fails writes **one** JSON document to stdout describing the failure, and the human-readable message still goes to stderr. The `error` key is the discriminator — no success payload has one:
|
|
136
172
|
|
|
137
173
|
```json
|
|
138
|
-
{ "error": { "name": "CliError", "message": "Not authenticated. Run: brevo login", "exitCode": 1 } }
|
|
174
|
+
{ "error": { "name": "CliError", "message": "Not authenticated. Run: brevo login", "exitCode": 1, "exit_code": 1 } }
|
|
139
175
|
```
|
|
140
176
|
|
|
141
|
-
`name` is the error class (`CliError`, `ApiError`, `AuthExpiredError`, `AbortError`), `message` is the same text printed to stderr, and `exitCode` matches the process exit code. An `ApiError` adds `statusCode` (the HTTP status) and, when the API classified the failure, `code` — one of `AUTH_INVALID`, `AUTH_EXPIRED`, `ACCESS_DENIED`, `APP_NOT_FOUND`, `REDIRECT_INVALID`, `PORT_IN_USE`, `NETWORK_ERROR`, `RATE_LIMITED`, `APP_LIMIT_REACHED`, `REGISTRY_ERROR`, `AUTH_GATEWAY`:
|
|
177
|
+
`name` is the error class (`CliError`, `ApiError`, `AuthExpiredError`, `AbortError`), `message` is the same text printed to stderr, and `exit_code` (also present as the deprecated `exitCode`) matches the process exit code. An `ApiError` adds `status_code` / `statusCode` (the HTTP status) and, when the API classified the failure, `code` — one of `AUTH_INVALID`, `AUTH_EXPIRED`, `ACCESS_DENIED`, `APP_NOT_FOUND`, `REDIRECT_INVALID`, `PORT_IN_USE`, `NETWORK_ERROR`, `RATE_LIMITED`, `APP_LIMIT_REACHED`, `REGISTRY_ERROR`, `AUTH_GATEWAY`:
|
|
142
178
|
|
|
143
179
|
```json
|
|
144
|
-
{ "error": { "name": "ApiError", "message": "App not found", "exitCode": 5, "code": "APP_NOT_FOUND", "statusCode": 404 } }
|
|
180
|
+
{ "error": { "name": "ApiError", "message": "App not found", "exitCode": 5, "exit_code": 5, "code": "APP_NOT_FOUND", "statusCode": 404, "status_code": 404 } }
|
|
145
181
|
```
|
|
146
182
|
|
|
147
183
|
Two things to rely on: stdout is always **exactly one** parseable document, and commands that describe their own failure keep doing so instead of emitting this envelope — `brevo whoami --json` still returns `{"authenticated": false, "reason": "no_key"}` (exit `1`). Check for `error` first, then fall back to the command's own shape.
|
|
@@ -82,8 +82,8 @@ Declares where the app appears inside Brevo's CRM (which record page, which slot
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
## The `ui_app` contract
|
|
85
|
-
- The presence of `ui_app` is the app-type discriminator.
|
|
86
|
-
- `auth` must be exactly `{}` — a UI app uses no OAuth, and `brevo app upload` refuses one carrying `scopes` or `
|
|
85
|
+
- The presence of `ui_app` is the app-type discriminator. `app_type` is a label the CLI writes alongside it (`"oauth"`, `"ui"` or `"function"`) so the file says in one word what it is; nothing branches on it. `brevo app upload` does check it still agrees with the blocks and refuses a contradiction — if you add or remove a `ui_app` block by hand, update `app_type` to match (or delete it; it is optional, and older configs have no such key).
|
|
86
|
+
- `auth` must be exactly `{}` — a UI app uses no OAuth, and `brevo app upload` refuses one carrying `scopes` or `redirect_uris`.
|
|
87
87
|
- `extension_type` is the only field at the `ui_app` root: `actionLink` (opens `redirect_link` in a new tab) or `iframeExtension` (embeds `modal_iframe_url` in a modal). camelCase only — `action_link` is rejected, not aliased.
|
|
88
88
|
- Everything else lives **per entry** in `surface_point_list`, so each placement carries its own text and destination:
|
|
89
89
|
- `surface_point_name` (required) — the registry slug in dot notation, e.g. `contactDetails.header.menu`. Not the dotted extension-point name a spec quotes (`contactDetails.headerMenu.action`).
|
|
@@ -97,7 +97,7 @@ brevo app uninstall # remove it from that account again
|
|
|
97
97
|
`brevo app upload` has no edit flags — it always pushes the whole of `app-config.json`. `install` and `uninstall` both take an optional `[account-id]`; omitted, the target resolves from the authenticated account (a corporate account is asked which sub-account).
|
|
98
98
|
|
|
99
99
|
## The `ui_app` block
|
|
100
|
-
The presence of `ui_app` is the app-type discriminator — it is what makes this a UI app rather than an OAuth one. That is why `auth` is `{}`, and it must stay empty: `brevo app upload` refuses a UI app carrying `scopes` or `
|
|
100
|
+
The presence of `ui_app` is the app-type discriminator — it is what makes this a UI app rather than an OAuth one. That is why `auth` is `{}`, and it must stay empty: `brevo app upload` refuses a UI app carrying `scopes` or `redirect_uris`.
|
|
101
101
|
|
|
102
102
|
`extension_type` is the only field at the root, because an app is one kind of extension rather than several:
|
|
103
103
|
|
|
@@ -62,17 +62,17 @@ Brevo OAuth integration for testing and implementing app authorization flows, sc
|
|
|
62
62
|
brevo app upload
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
For example, to rename the app or add a redirect URL, edit `
|
|
65
|
+
For example, to rename the app or add a redirect URL, edit `app_name` or `auth.redirect_uris` in `app-config.json`:
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
# 1. Edit app-config.json (e.g. change "
|
|
68
|
+
# 1. Edit app-config.json (e.g. change "app_name", or append to "auth.redirect_uris")
|
|
69
69
|
# 2. Push the change
|
|
70
70
|
brevo app upload
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
`brevo app upload` always fetches the current server state first and shows a diff before pushing — review it before confirming (or pass `--yes` to skip the prompt).
|
|
74
74
|
|
|
75
|
-
To avoid unintentionally accumulating stale redirect URLs over time, periodically review `auth.
|
|
75
|
+
To avoid unintentionally accumulating stale redirect URLs over time, periodically review `auth.redirect_uris` in `app-config.json` and remove obsolete entries before running `brevo app upload`.
|
|
76
76
|
|
|
77
77
|
`brevo app upload` has no `--app-id` flag — it always resolves the app from this directory's `app-config.json`. To manage a different app, `cd` into that app's project directory (or use `brevo app credentials --app-id <id>` to inspect it without editing).
|
|
78
78
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
2
|
+
"app_id": "{{APP_ID}}",
|
|
3
|
+
"app_name": "{{APP_NAME}}",
|
|
4
4
|
"version": "{{APP_VERSION}}",
|
|
5
|
-
"
|
|
5
|
+
"logo_uri": "{{LOGO_URI}}",
|
|
6
6
|
"distribution_type": "{{DISTRIBUTION}}",
|
|
7
|
+
"app_type": "{{APP_TYPE}}",
|
|
7
8
|
{{#if ui_app}}
|
|
8
9
|
"ui_app": {{UI_APP_JSON}},
|
|
10
|
+
"auth": {}
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if brevo_function}}
|
|
13
|
+
"brevo_function": {}
|
|
9
14
|
{{/if}}
|
|
10
|
-
"auth": {
|
|
11
15
|
{{#if oauth}}
|
|
16
|
+
"auth": {
|
|
12
17
|
"scopes": {{SCOPES_JSON}},
|
|
13
|
-
"
|
|
14
|
-
{{/if}}
|
|
18
|
+
"redirect_uris": {{REDIRECT_URLS_JSON}}
|
|
15
19
|
}
|
|
20
|
+
{{/if}}
|
|
16
21
|
}
|