# Brandapp Sdk Install

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

- Skill: `reopt-ai/brandapp-sdk-install` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add reopt-ai/brandapp-sdk-install`
- Raw SKILL.md: https://api.skillmd.com/api/skills/reopt-ai/brandapp-sdk-install/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: reopt-ai (https://skillmd.com/u/reopt-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/reopt-ai/brandapp-sdk-install

---


# 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/`.

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

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

3. **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. **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)`.

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

1. `npx tsc --noEmit` passes.
2. (Auth) `curl -I http://localhost:3000/api/auth/ok` → 200.
3. (Any module) `await sdk.eav.entities.list()` succeeds from a server component. 401 → credentials. 404 → `BRANDAPP_ID`.
4. (Optional) mirror `apps/brandapp-playground/app/health/` for a 12-probe deploy check.

