Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .changeset/ui-app-action-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@getbrevo/cli': minor
---

Add UI app support: author action links and manage their per-account availability (BEX-290).

A UI app is a new *type* of app rather than a separate entity — it shares the app record, credentials, and version lifecycle with OAuth apps, and adds a `ui_app` block to `app-config.json` describing where and how it renders inside Brevo. The first shippable variant is the **action link**: a partner-authored entry point that appears in a CRM record's action menu and opens an external URL in a new tab with record context.

The `ui_app` block is the app snapshot the platform stores, field for field — `extensionType`, `surfacePointList`, `heading`, `subheading`, `redirectLink`, `linkTarget` — so what a partner authors is exactly what the platform stores, serves and renders, with no mapping layer in between. `extensionType` uses the camelCase vocabulary the platform settled on in BEX-350 (`actionLink`, and `iframeExtension`/`legacyComponent` for the types the CLI doesn't author); the older snake_case spellings are not accepted.

`brevo app create` gained a leading "What type of app are you building?" prompt, offering an OAuth integration (the default path) or a UI app. **A UI app is authored entirely through the prompts** — there is no `--type` flag, and no flag for any UI-app field. Every non-interactive run, whether `--json` or piped stdin, creates an OAuth app exactly as before, so existing scripted invocations are unaffected and nothing new becomes scriptable while the feature is still in development. The UI path collects placement and destination instead of OAuth callbacks — which record pages to appear on (`contact`/`company`/`deal`, multi-select, so one action link can serve several), heading, optional subheading, destination URL and link target — and never asks for or defaults a redirect URL, since an action link has no OAuth callback. `redirect_uris` is omitted from the create call entirely for UI apps, which also start from narrower default scopes (`contacts:read`, `contacts:write`). No feature is scaffolded for a UI app; there is no local server to run.

`brevo app upload` now sends the block under the `snapshot` key for UI apps and validates it locally before the request. The validation is deliberately stricter than the wire, because the platform degrades a bad snapshot silently rather than rejecting it: `extensionType` must be `actionLink` (`iframeExtension` and `legacyComponent` are not CLI-authorable, and the pre-BEX-350 `action_link` spelling is rejected); `surfacePointList` must be non-empty, drawn from the twelve-point extension-point registry, action slots only, and free of duplicates; `heading` must be non-empty; `redirectLink` must be https — `http://` is accepted only for `localhost`/`127.0.0.1` so a partner can point at a local dev server; `linkTarget` must be `_blank` or `_self`; and `modalIframeUrl` is rejected outright, since the UI kit keeps it only for an `iframeExtension` item and would otherwise discard it without a word.

Extension-point validation is the most load-bearing part of that list. Slot names follow the grammar `<location>.<place>.<kind>`, the UI kit matches them by exact string equality, and the backend drops any name without a registry row — so a typo, a stale `.region`-era name, or a casing slip produces an empty slot, an HTTP 200, and no error anywhere in the stack. The CLI is the only layer that can tell a partner, so it checks locally against the registry.

The redirect-URL requirement is now OAuth-only. The upload diff covers the snapshot (ignoring key order), so editing only that block is correctly detected as a change instead of reporting "already up to date". For OAuth apps nothing is sent and the payload is unchanged.

New `brevo app deploy <account-id>` and `brevo app remove <account-id>` manage a UI app's availability in a single Brevo account. Both resolve the target app from `--app-id`, the linked `app-config.json`, or an interactive picker, and support `--force` and `--json`. `deploy` refuses until the configuration has been validated by an upload, pointing at `brevo app upload` — detected locally from a missing `version` and mapped from the server's own rejection. `remove` has no such gate and treats "not deployed to this account" as informational, exiting `0`, so teardown scripts stay idempotent.

`brevo app scaffold` inside a UI-app project now refreshes the base config and reports that there are no features to scaffold. It preserves a hand-edited `ui_app` block through a confirmed config refresh, which would otherwise have overwritten `app-config.json` wholesale from server values that don't include it, and no longer reports phantom redirect-URL drift for an app type that has none.

Note two fields the CLI deliberately does **not** author, because the platform has no counterpart: a per-action label (the record action menu labels the entry with the *app name*) and partner-declared context properties (the record context an action receives is an allow-list on the platform's extension-point registry row).

The write path for the snapshot does not exist on the platform yet, so the upload transport (a `snapshot` key on the existing upload endpoint) and the deploy/remove endpoints remain assumptions — marked in code comments and tracked in `RELEASE-CHECKLIST.md`. The field names themselves are confirmed against both of the platform's consumers (BEX-308 / BEX-350).

UI apps are **not live on the Brevo platform yet**. As with public apps, `agent-context/SKILL.md` and `agent-context/AGENTS.md` carry a notice telling AI agents not to create one or drive the deploy lifecycle, with the same internal-Brevo-account exception so dogfooding still works.
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ Because that surface is shipped and unguarded, `agent-context/SKILL.md` and `age
- **This is documentation-level only.** The CLI itself still accepts `--distribution public` without a warning or a guard, by design — a runtime guard is tracked separately (see `RELEASE-CHECKLIST.md`). If one is ever added, it needs the same internal-account escape hatch.
- **When public apps go GA**, work through `RELEASE-CHECKLIST.md` → *Before public-apps GA* to remove the notice everywhere in one pass.

## UI apps are not GA either — same deal (BEX-290)

UI apps (action links that render inside Brevo CRM records) are **not live on the platform**. The CLI ships the surface — the *UI app* choice at `brevo app create`'s app-type prompt, `brevo app deploy <account-id>`, `brevo app remove <account-id>` — and `agent-context/SKILL.md` / `agent-context/AGENTS.md` each carry a **⚠️ UI apps are not available yet** notice reusing the same *Exception — internal Brevo accounts* clause. `README.md` mirrors it.

A UI app is **prompt-only**: there is no `--type` flag and no per-field flags, so non-interactive runs always create an OAuth app. `extensionType` values are camelCase (`actionLink`, `iframeExtension`, `legacyComponent`) and the old snake_case spellings are rejected. See `CLAUDE.md` for why.

**Every clause of the public-apps section above applies verbatim**, including that it does **not** restrict work in this repo — building, testing, and QA-ing the UI-app code paths is expected and unblocked. Never refuse or hedge on a UI-app task here.

The `ui_app` block's **field names are confirmed** against both of the platform's consumers, the manifest read path and the extensibility UI kit (BEX-308 / BEX-350) — it is the stored app snapshot verbatim. What remains **assumed is the transport**: nothing on the platform writes that snapshot yet. See `CLAUDE.md` → *UI apps are not GA either* and `RELEASE-CHECKLIST.md` → *Before UI-apps GA*.

- **When UI apps go GA**, work through `RELEASE-CHECKLIST.md` → *Before UI-apps GA*.

## Public repository

Repo (`github.com/getbrevo/brevo-cli`) and package (`@getbrevo/cli` on the public npm registry) are **public**. Every commit, PR, and issue is world-readable.
Expand Down
Loading