Brandapp SDK Install
This is NOT the SDK you know. Read node_modules/@reopt-ai/brandapp-sdk/docs/ before writing code (the package ships docs at top-level docs/, not dist/docs/). Heed deprecation notices — 2.0 renamed every env var without aliases; 3.0 rewrote the webhook contract and blocks clientSecret in the browser; 4.0 moved to Better Auth 1.7 (no client plugin, new callback path). docs/migration.md stops at 2.x → 3.0.0, so read the package CHANGELOG.md for 3.1–4.0.
When to apply
A consumer project adopting @reopt-ai/brandapp-sdk for the first time. Triggers: "install", "init", "setup", "bootstrap", "apply SDK", "brandapp integration" against @reopt-ai/brandapp-sdk.
Step 1 — Pin agent rules into AGENTS.md / CLAUDE.md
Source of truth for the rules block: the module's own agent-rules file, once it ships one. @reopt-ai/brandapp-sdk does not ship one as of 4.2.0, so use the fallback agent-rules.md bundled with this skill.
Append to the consumer's AGENTS.md (fall back to CLAUDE.md if AGENTS.md is absent — never both). Wrap the content between:
<!-- BEGIN:reopt/brandapp-sdk-agent-rules -->
…content from source above…
<!-- END:reopt/brandapp-sdk-agent-rules -->
Idempotent: if the markers already exist, replace only the content between them. Never touch text outside the markers — the consumer's own rules live there.
Step 2 — Consumer-side setup (this skill owns; docs cannot)
These are properties of the consumer project, not the module. They will not appear in the module's docs/.
Public npm registry — no token or scoped .npmrc entry is required. Inspect the project .npmrc and npm config get @reopt-ai:registry; if the scope still resolves to GitHub Packages, remove only the legacy project entry @reopt-ai:registry=https://npm.pkg.github.com before installing. Preserve unrelated registry/auth settings, and ask before changing user/global npm config.
Env namespace (2.0+) — 3 tiers, no aliases for renamed vars:
| Prefix |
Owner |
Purpose |
BRANDAPP_* |
Consumer |
Credentials (BRANDAPP_CLIENT_ID/SECRET, BRANDAPP_ID, BRANDAPP_WEBHOOK_SECRET) |
REOPT_* |
Platform |
Host overrides only (REOPT_BASE_URL, REOPT_ID_BASE_URL) |
BRANDAPP_SDK_* |
Consumer |
Behavior toggles (BRANDAPP_SDK_DEBUG, BRANDAPP_SDK_LOG_FORMAT) |
NEXT_PUBLIC_BRANDAPP_* |
Browser |
Public subset (e.g. NEXT_PUBLIC_BRANDAPP_EAV_HASH) |
Pre-2.0 REOPT_CLIENT_* / REOPT_BRANDAPP_ID / REOPT_WEBHOOK_SECRET / REOPT_SDK_* are gone. Migrate .env before bumping.
3.0 — no clientSecret in the browser. NEXT_PUBLIC_BRANDAPP_CLIENT_SECRET is forbidden; createReoptSDK / createBrandappProvider throw CONFIG_BROWSER_SECRET if a clientSecret reaches a browser. Client-side SDK: mint a short-lived scoped token server-side (POST /api/v1/brandapp/{id}/token/mint) and construct with { brandappId, token } (token-only config — clientId/clientSecret optional when token is set). Server-side clientId+clientSecret is unchanged.
Peer deps — every peer is declared optional, so npm will not install one for you. Using Auth means adding better-auth@^1.7.1 yourself: 4.0 is built on 1.7 and does not work with 1.6 or below, so bump the SDK and better-auth in the same change. Others as needed: @ai-sdk/provider >=4.0.0, @tanstack/react-query >=5.0.0, hono >=4.0.0, react >=18, @reopt-ai/opt-editor >=1.0.0. The 4.1 @reopt-ai/brandapp-sdk/analytics subpath (startBrandappAnalytics(sdk)) peers on @reopt-ai/data-sdk >=0.1.1 — that exact package name, even though npm marks it deprecated in favour of data-sdk-client; do not substitute the client package, and install it only when that subpath is used.
4.0 auth migration order (do this before bumping) — Better Auth 1.7 serves the Reopt callback at ${BETTER_AUTH_URL}/api/auth/callback/reopt (1.6 used /api/auth/oauth2/callback/reopt). Reopt's own system brandapp clients have both registered; a self-registered redirect URI must add the new path in the studio first (exact match), or sign-in breaks on upgrade. Then rewrite call sites: delete createReoptOAuthClient() (1.7 has no client plugin), signIn.oauth2({ providerId }) → signInWithReopt(authClient, { callbackURL }), oauth2.link() → linkReoptAccount(authClient).
Optional dev-mode bootstrap — npx @reopt-ai/cli brandapp init scaffolds the offline dev server (see reopt-brandapp skill). It does not create .env.local, lib/sdk.ts, lib/auth.ts, auth route handler, or webhook route — those remain this skill's responsibility.
Step 3 — Route to module docs
For everything else (code generation, API surface, version-specific behavior, error handling), route to module docs. Do not duplicate API surface here — read the file. Module docs are pinned to the installed version; this skill is not.
Paths are relative to node_modules/@reopt-ai/brandapp-sdk/. docs/api-reference.md is the combined surface (SDK init, user-scoped auth, EAV, webhooks, service token, React hooks); the rest are topic files. Two surfaces have no docs/ file — route them to the package root instead, as marked below.
| Task signal |
Read |
SDK init (createReoptSDK / createLazySDK, lib/sdk.ts; token-only client config), user-scoped sdk.auth + cross-subdomain session helpers, EAV (defineSchema, linkedTo, backfill, record-list select projection in 3.6; 4.2 version + ifVersion optimistic concurrency, increments atomic number deltas, expiresAt TTL, the 10k-row in-memory filter cap → 422 QUERY_TOO_BROAD), webhooks (createWebhookHandler, timestamp-first verifySignature, contact/workflow + subscription lifecycle events), service token, React hooks, AI |
docs/api-reference.md |
Better Auth wiring + Reopt OAuth (4.0 / better-auth 1.7) — createReoptBetterAuth, createReoptOAuth (tokenEndpointAuthMethod, providerLogout), createReoptAdapter, and the browser helpers signInWithReopt / linkReoptAccount |
README.md + CHANGELOG.md [4.0.0] — no docs/ file covers this wiring. The Auth section of api-reference.md is the user-token API, a different surface; do not infer the sign-in flow from it |
Env vars + 3-tier namespace, host split (brandapp.reopt.ai / id.reopt.ai) |
docs/environment.md |
Error classes / codes (auth/AI, CONFIG_BROWSER_SECRET, QUERY_TOO_LARGE, Files 402/409/413/415; RequiredTermsError is an older-server compatibility path) |
docs/errors.md |
Plans catalog + hosted checkout (createCheckout / getCheckout / cancel): hosted page collects required-terms consent; live Paddle works, priced live Toss returns LIVE_MODE_UNSUPPORTED; cancellation result + subscription webhook semantics |
docs/api-reference.md + declaration JSDoc resolved from the installed @reopt-ai/brandapp-sdk/plans export (the 3.6 docs retain a stale pre-hosted-consent catch example) |
Marketing site / CMS (toMetadata, toSitemapItems, toRssFeed, verifySession, optimizeUrl; cms is read-only from 1.8+) |
docs/cms.md |
OIDC Single Logout (3.2+): back-channel receive (createBackchannelLogoutHandler, verifyBackchannelLogoutToken) + RP-initiated (buildEndSessionUrl), dev.mintLogoutToken for tests — @reopt-ai/brandapp-sdk/logout |
docs/logout.md |
| Files (folders, upload/list, rename/move, text preview, usage meter, React hooks) |
docs/files.md |
Terms + consent (sdk.terms): list / listWithMeta (Basic Auth, callable signed-out), listMyConsents / consent / withdraw (Bearer). Terms are brand-owned — the same set binds Brandapp and brandfront; currentVersion.contentRich is an EditorSpec renderable by opt-editor's StaticRenderer |
docs/api-reference.md § Terms Client |
Feedback two-way thread (sdk.feedback, 3.4+): create / list / get |
docs/api-reference.md § Feedback Client |
Push device tokens (sdk.push): registerDeviceToken / unregisterDeviceToken / listDevices — Bearer, self-scoped, no raw tokens returned |
declaration JSDoc from the installed @reopt-ai/brandapp-sdk/push export — docs/ has only a one-line entry-point row for this surface |
Analytics bridge (4.1): sdk.analytics.getConfig() + startBrandappAnalytics(sdk) from @reopt-ai/brandapp-sdk/analytics — initialises reopt-data collection with the brand's baseUrl / writeKey, returns null when the brand has no Data project |
CHANGELOG.md [4.1.0] + declaration JSDoc from the installed @reopt-ai/brandapp-sdk/analytics export — neither README.md nor docs/ mentions it |
AI SDK provider (createBrandappProvider, @reopt-ai/brandapp-sdk/ai-provider) |
docs/api-reference.md § AI SDK Provider |
Dev server (createDevServer, instrumentation.ts, offline development) |
docs/dev-server.md |
| Version migration / breaking changes |
docs/migration.md for ≤ 3.0.0 — it has no section past 2.x → 3.0.0, so read CHANGELOG.md for 3.1–4.2 (hosted checkout, Files, EAV select, Better Auth 1.7, analytics bridge, EAV concurrency/TTL) |
| Testing the integration |
docs/testing.md |
Safety
- Do not add GitHub Packages auth for
@reopt-ai/*; the packages are public on npm. Remove only the legacy project-level scope override and preserve unrelated registry settings.
- Never put
NODE_TLS_REJECT_UNAUTHORIZED=0 in .env — script-scope only.
BRANDAPP_ID is the brandappId (app), not the brandId (brand).
BETTER_AUTH_URL must match the browser-facing origin exactly.
createReoptAdapter / createReoptOAuth / createReoptBetterAuth throw in browser runtimes — keep behind import "server-only".
- Keep
REOPT_ID_BASE_URL (the discovered issuer) stable across deploys — 4.0 namespaces accounts by issuer, so changing it makes existing users resolve as new accounts.
- Do not enable
providerLogout to "fix" a logout: most Reopt clients may not initiate OP logout and the redirect fails. It needs enableEndSession + a registered post-logout URI on the client; otherwise use @reopt-ai/brandapp-sdk/logout.
- Never pass
clientSecret (or the webhook secret) into client bundles — 3.0 throws CONFIG_BROWSER_SECRET; use a server-minted { token } client-side.
expiresAt (4.2) is an absolute ISO timestamp the client computes — clock skew shifts the TTL; an expired record vanishes from reads, count, and unique checks before the hourly physical delete, so do not rely on it for audit history.
- The in-memory dev server refuses to start under
NODE_ENV=production (3.0); only override with REOPT_DEV_SERVER_ALLOW_PRODUCTION=1 for deliberate offline tests, never a real deploy.
Verify
npx tsc --noEmit passes.
- (Auth)
curl -I http://localhost:3000/api/auth/ok → 200.
- (Any module)
await sdk.eav.entities.list() succeeds from a server component. 401 → credentials. 404 → BRANDAPP_ID.
- (Optional) mirror
apps/brandapp-playground/app/health/ for a 12-probe deploy check.
1---2name: brandapp-sdk-install3description: Install @reopt-ai/brandapp-sdk in a consumer project. Sets up Auth, OAuth, EAV (incl. 4.2 optimistic concurrency / atomic increments / TTL), Plans checkout, Files, analytics bridge, API routes, and env config. Triggers on "brandapp-sdk install", "brandapp-sdk init", "brandapp sdk setup", "brandapp sdk bootstrap", "apply SDK", "brandapp integration", "brandapp files setup", "brandapp checkout setup".4---56# Brandapp SDK Install78> This is NOT the SDK you know. Read `node_modules/@reopt-ai/brandapp-sdk/docs/` before writing code (the package ships docs at top-level `docs/`, not `dist/docs/`). Heed deprecation notices — 2.0 renamed every env var without aliases; 3.0 rewrote the webhook contract and blocks `clientSecret` in the browser; **4.0 moved to Better Auth 1.7** (no client plugin, new callback path). `docs/migration.md` stops at `2.x → 3.0.0`, so read the package `CHANGELOG.md` for 3.1–4.0.910## When to apply1112A consumer project adopting `@reopt-ai/brandapp-sdk` for the first time. Triggers: "install", "init", "setup", "bootstrap", "apply SDK", "brandapp integration" against `@reopt-ai/brandapp-sdk`.1314## Step 1 — Pin agent rules into AGENTS.md / CLAUDE.md1516Source of truth for the rules block: the module's own agent-rules file, once it ships one. `@reopt-ai/brandapp-sdk` does **not** ship one as of 4.2.0, so use the fallback `agent-rules.md` bundled with this skill.1718Append to the consumer's `AGENTS.md` (fall back to `CLAUDE.md` if `AGENTS.md` is absent — never both). Wrap the content between:1920```21<!-- BEGIN:reopt/brandapp-sdk-agent-rules -->22…content from source above…23<!-- END:reopt/brandapp-sdk-agent-rules -->24```2526**Idempotent:** if the markers already exist, replace only the content between them. **Never touch text outside the markers** — the consumer's own rules live there.2728## Step 2 — Consumer-side setup (this skill owns; docs cannot)2930These are properties of the consumer project, not the module. They will not appear in the module's `docs/`.31321. **Public npm registry** — no token or scoped `.npmrc` entry is required. Inspect the project `.npmrc` and `npm config get @reopt-ai:registry`; if the scope still resolves to GitHub Packages, remove only the legacy project entry `@reopt-ai:registry=https://npm.pkg.github.com` before installing. Preserve unrelated registry/auth settings, and ask before changing user/global npm config.33342. **Env namespace (2.0+)** — 3 tiers, no aliases for renamed vars:3536 | Prefix | Owner | Purpose |37 |---|---|---|38 | `BRANDAPP_*` | Consumer | Credentials (`BRANDAPP_CLIENT_ID/SECRET`, `BRANDAPP_ID`, `BRANDAPP_WEBHOOK_SECRET`) |39 | `REOPT_*` | Platform | Host overrides only (`REOPT_BASE_URL`, `REOPT_ID_BASE_URL`) |40 | `BRANDAPP_SDK_*` | Consumer | Behavior toggles (`BRANDAPP_SDK_DEBUG`, `BRANDAPP_SDK_LOG_FORMAT`) |41 | `NEXT_PUBLIC_BRANDAPP_*` | Browser | Public subset (e.g. `NEXT_PUBLIC_BRANDAPP_EAV_HASH`) |4243 Pre-2.0 `REOPT_CLIENT_*` / `REOPT_BRANDAPP_ID` / `REOPT_WEBHOOK_SECRET` / `REOPT_SDK_*` are gone. Migrate `.env` before bumping.4445 **3.0 — no `clientSecret` in the browser.** `NEXT_PUBLIC_BRANDAPP_CLIENT_SECRET` is forbidden; `createReoptSDK` / `createBrandappProvider` throw `CONFIG_BROWSER_SECRET` if a `clientSecret` reaches a browser. Client-side SDK: mint a short-lived scoped token server-side (`POST /api/v1/brandapp/{id}/token/mint`) and construct with `{ brandappId, token }` (token-only config — `clientId`/`clientSecret` optional when `token` is set). Server-side `clientId`+`clientSecret` is unchanged.46473. **Peer deps** — every peer is declared optional, so npm will not install one for you. Using Auth means adding **`better-auth@^1.7.1`** yourself: 4.0 is built on 1.7 and does **not** work with 1.6 or below, so bump the SDK and `better-auth` in the same change. Others as needed: `@ai-sdk/provider >=4.0.0`, `@tanstack/react-query >=5.0.0`, `hono >=4.0.0`, `react >=18`, `@reopt-ai/opt-editor >=1.0.0`. The 4.1 `@reopt-ai/brandapp-sdk/analytics` subpath (`startBrandappAnalytics(sdk)`) peers on **`@reopt-ai/data-sdk >=0.1.1`** — that exact package name, even though npm marks it deprecated in favour of `data-sdk-client`; do not substitute the client package, and install it only when that subpath is used.48494. **4.0 auth migration order (do this before bumping)** — Better Auth 1.7 serves the Reopt callback at `${BETTER_AUTH_URL}/api/auth/callback/reopt` (1.6 used `/api/auth/oauth2/callback/reopt`). Reopt's own system brandapp clients have both registered; a **self-registered** redirect URI must add the new path in the studio **first** (exact match), or sign-in breaks on upgrade. Then rewrite call sites: delete `createReoptOAuthClient()` (1.7 has no client plugin), `signIn.oauth2({ providerId })` → `signInWithReopt(authClient, { callbackURL })`, `oauth2.link()` → `linkReoptAccount(authClient)`.50515. **Optional dev-mode bootstrap** — `npx @reopt-ai/cli brandapp init` scaffolds the offline dev server (see `reopt-brandapp` skill). It does **not** create `.env.local`, `lib/sdk.ts`, `lib/auth.ts`, auth route handler, or webhook route — those remain this skill's responsibility.5253## Step 3 — Route to module docs5455For everything else (code generation, API surface, version-specific behavior, error handling), route to module docs. Do **not** duplicate API surface here — read the file. Module docs are pinned to the installed version; this skill is not.5657Paths are relative to `node_modules/@reopt-ai/brandapp-sdk/`. `docs/api-reference.md` is the combined surface (SDK init, user-scoped auth, EAV, webhooks, service token, React hooks); the rest are topic files. **Two surfaces have no `docs/` file — route them to the package root instead**, as marked below.5859| Task signal | Read |60|---|---|61| SDK init (`createReoptSDK` / `createLazySDK`, `lib/sdk.ts`; token-only client config), user-scoped `sdk.auth` + cross-subdomain session helpers, EAV (`defineSchema`, `linkedTo`, `backfill`, record-list `select` projection in 3.6; 4.2 `version` + `ifVersion` optimistic concurrency, `increments` atomic number deltas, `expiresAt` TTL, the 10k-row in-memory filter cap → 422 `QUERY_TOO_BROAD`), webhooks (`createWebhookHandler`, timestamp-first `verifySignature`, contact/workflow + subscription lifecycle events), service token, React hooks, AI | `docs/api-reference.md` |62| **Better Auth wiring + Reopt OAuth (4.0 / better-auth 1.7)** — `createReoptBetterAuth`, `createReoptOAuth` (`tokenEndpointAuthMethod`, `providerLogout`), `createReoptAdapter`, and the browser helpers `signInWithReopt` / `linkReoptAccount` | **`README.md`** + **`CHANGELOG.md` `[4.0.0]`** — no `docs/` file covers this wiring. The Auth section of `api-reference.md` is the user-token API, a different surface; do not infer the sign-in flow from it |63| Env vars + 3-tier namespace, host split (`brandapp.reopt.ai` / `id.reopt.ai`) | `docs/environment.md` |64| Error classes / codes (auth/AI, `CONFIG_BROWSER_SECRET`, `QUERY_TOO_LARGE`, Files 402/409/413/415; `RequiredTermsError` is an older-server compatibility path) | `docs/errors.md` |65| Plans catalog + hosted checkout (`createCheckout` / `getCheckout` / `cancel`): hosted page collects required-terms consent; live Paddle works, priced live Toss returns `LIVE_MODE_UNSUPPORTED`; cancellation result + subscription webhook semantics | `docs/api-reference.md` + declaration JSDoc resolved from the installed `@reopt-ai/brandapp-sdk/plans` export (the 3.6 docs retain a stale pre-hosted-consent catch example) |66| Marketing site / CMS (`toMetadata`, `toSitemapItems`, `toRssFeed`, `verifySession`, `optimizeUrl`; `cms` is read-only from 1.8+) | `docs/cms.md` |67| OIDC Single Logout (3.2+): back-channel receive (`createBackchannelLogoutHandler`, `verifyBackchannelLogoutToken`) + RP-initiated (`buildEndSessionUrl`), `dev.mintLogoutToken` for tests — `@reopt-ai/brandapp-sdk/logout` | `docs/logout.md` |68| Files (folders, upload/list, rename/move, text preview, usage meter, React hooks) | `docs/files.md` |69| Terms + consent (`sdk.terms`): `list` / `listWithMeta` (Basic Auth, callable signed-out), `listMyConsents` / `consent` / `withdraw` (Bearer). Terms are **brand-owned** — the same set binds Brandapp and brandfront; `currentVersion.contentRich` is an `EditorSpec` renderable by opt-editor's StaticRenderer | `docs/api-reference.md` § Terms Client |70| Feedback two-way thread (`sdk.feedback`, 3.4+): `create` / `list` / `get` | `docs/api-reference.md` § Feedback Client |71| Push device tokens (`sdk.push`): `registerDeviceToken` / `unregisterDeviceToken` / `listDevices` — Bearer, self-scoped, no raw tokens returned | declaration JSDoc from the installed `@reopt-ai/brandapp-sdk/push` export — `docs/` has only a one-line entry-point row for this surface |72| Analytics bridge (4.1): `sdk.analytics.getConfig()` + `startBrandappAnalytics(sdk)` from `@reopt-ai/brandapp-sdk/analytics` — initialises reopt-data collection with the brand's `baseUrl` / `writeKey`, returns `null` when the brand has no Data project | `CHANGELOG.md` `[4.1.0]` + declaration JSDoc from the installed `@reopt-ai/brandapp-sdk/analytics` export — neither `README.md` nor `docs/` mentions it |73| AI SDK provider (`createBrandappProvider`, `@reopt-ai/brandapp-sdk/ai-provider`) | `docs/api-reference.md` § AI SDK Provider |74| Dev server (`createDevServer`, `instrumentation.ts`, offline development) | `docs/dev-server.md` |75| Version migration / breaking changes | `docs/migration.md` for `≤ 3.0.0` — **it has no section past `2.x → 3.0.0`**, so read `CHANGELOG.md` for 3.1–4.2 (hosted checkout, Files, EAV `select`, Better Auth 1.7, analytics bridge, EAV concurrency/TTL) |76| Testing the integration | `docs/testing.md` |7778## Safety7980- Do not add GitHub Packages auth for `@reopt-ai/*`; the packages are public on npm. Remove only the legacy project-level scope override and preserve unrelated registry settings.81- Never put `NODE_TLS_REJECT_UNAUTHORIZED=0` in `.env` — script-scope only.82- `BRANDAPP_ID` is the **brandappId** (app), not the brandId (brand).83- `BETTER_AUTH_URL` must match the browser-facing origin exactly.84- `createReoptAdapter` / `createReoptOAuth` / `createReoptBetterAuth` throw in browser runtimes — keep behind `import "server-only"`.85- Keep `REOPT_ID_BASE_URL` (the discovered `issuer`) stable across deploys — 4.0 namespaces accounts by issuer, so changing it makes existing users resolve as new accounts.86- Do not enable `providerLogout` to "fix" a logout: most Reopt clients may not initiate OP logout and the redirect fails. It needs `enableEndSession` + a registered post-logout URI on the client; otherwise use `@reopt-ai/brandapp-sdk/logout`.87- Never pass `clientSecret` (or the webhook secret) into client bundles — 3.0 throws `CONFIG_BROWSER_SECRET`; use a server-minted `{ token }` client-side.88- `expiresAt` (4.2) is an absolute ISO timestamp the **client** computes — clock skew shifts the TTL; an expired record vanishes from reads, `count`, and unique checks before the hourly physical delete, so do not rely on it for audit history.89- The in-memory dev server refuses to start under `NODE_ENV=production` (3.0); only override with `REOPT_DEV_SERVER_ALLOW_PRODUCTION=1` for deliberate offline tests, never a real deploy.9091## Verify92931. `npx tsc --noEmit` passes.942. (Auth) `curl -I http://localhost:3000/api/auth/ok` → 200.953. (Any module) `await sdk.eav.entities.list()` succeeds from a server component. 401 → credentials. 404 → `BRANDAPP_ID`.964. (Optional) mirror `apps/brandapp-playground/app/health/` for a 12-probe deploy check.