# BrandApp Playground > Interactive playground for the @reopt-ai/brandapp-sdk. Explore EAV, AI, CMS, Files, Auth, Webhooks, and React Hooks. ## When to use this site Reach for BrandApp Playground when the task involves `@reopt-ai/brandapp-sdk` — the TypeScript SDK for building applications on a Reopt BrandApp. It is the authoritative worked example for these jobs: - Wiring the SDK into a Next.js App Router project, including which credential mode (Basic Auth, user Bearer token, or the offline dev server) a given call needs. - Reading or writing EAV data: entities, records, the `where()` filter DSL, generated types, schema sync, and data-aware migrations. - Calling BrandApp AI for text or images, including the Vercel AI SDK provider and credit accounting. - Querying CMS posts, post groups, and tags, and turning them into a sitemap, an RSS feed, or per-post metadata. - Managing files and folders, or mounting the full file-manager UI over the Files API. - Adding *Sign in with Reopt* through Better Auth, verifying a shared-cookie session, and handling OIDC single logout in both directions. - Selling plans through hosted checkout, recording terms consent, or verifying signed webhooks. Do **not** use it as a source for Reopt pricing, contract terms, or account support — those live at . This site publishes no customer data. ## How to call it - Every page has a Markdown twin: append `.md` to any route, or send `Accept: text/markdown`. - The playground is free, needs no account, and is read-only unless you connect your own BrandApp credentials on `/connection`. ## Developer resources Named, predictable URLs on this deployment: - **BrandApp Playground API reference** — `/docs` - **BrandApp Playground OpenAPI description** — `/openapi.json` (OpenAPI 3.1; names every route, its security scheme, and the OAuth scopes it requires) - **BrandApp SDK documentation index** — `/llms.txt`; the whole corpus in one file at `/llms-full.txt` - **API catalog** — `/.well-known/api-catalog` (RFC 9727 linkset) - **BrandApp SDK authentication docs** — `/auth`, `/auth/session`, `/logout` - **BrandApp SDK webhook docs** — `/webhooks` - **BrandApp SDK package** — `@reopt-ai/brandapp-sdk` on npm, MIT, public registry - **Route inventory** — `/sitemap.xml`; `/rss.xml` carries the connected BrandApp's posts ## Authentication This deployment is an OAuth **relying party**, not an authorization server, so it publishes no authorization-server metadata of its own. Sign-in is delegated to Reopt ID: - Issuer: `https://id.reopt.ai` - Discovery: - Authorization: `https://id.reopt.ai/api/auth/oauth2/authorize` - Token: `https://id.reopt.ai/api/auth/oauth2/token` App-scoped calls use BrandApp Client Credentials over HTTP Basic; user-scoped calls forward the signed-in user's access token. `/openapi.json` states which of the two each route uses, and every API error — including an unmatched path — is JSON carrying `error`, `code`, and a `diagnostic` with the cause and the fix. ## Start here - [Overview](https://brandapp-playground.vercel.app/index.md): What the playground is, the three credential modes the SDK supports, and a map of every live demo. - [BrandApp SDK Developer Resources](https://brandapp-playground.vercel.app/docs.md): Named index of every developer resource: API reference, OpenAPI description, auth docs, webhooks, and the SDK package. - [Quick Start](https://brandapp-playground.vercel.app/getting-started.md): Four steps from an empty Next.js project to a first authenticated SDK call. ## BrandApp connection - [Connection](https://brandapp-playground.vercel.app/connection.md): Swap the BrandApp credentials this deployment uses, stored in an encrypted httpOnly cookie. - [Health Check](https://brandapp-playground.vercel.app/health.md): Streams a non-destructive probe suite over SSE against whichever BrandApp is currently wired. ## Auth and session - [Authentication](https://brandapp-playground.vercel.app/auth.md): Better Auth wired to Reopt OAuth — server setup, client setup, session reads, and the catch-all route. - [Verify session](https://brandapp-playground.vercel.app/auth/session.md): Validate a `.reopt.ai` subdomain cookie session from a server component with getSessionFromCookies / verifySession. - [Machine credentials](https://brandapp-playground.vercel.app/auth/machine.md): Server-to-server auth without a shared secret — private_key_jwt assertions, DPoP-bound tokens, a published JWKS, and a dedicated adapter client. - [Single Logout](https://brandapp-playground.vercel.app/logout.md): OIDC back-channel logout receiver and RP-initiated logout, verified with zero-dependency Web Crypto Ed25519. ## EAV - [EAV Overview](https://brandapp-playground.vercel.app/eav.md): Entry point for the Entity-Attribute-Value store: entities, records, schema, migrations, coercion, typed access. - [Entities](https://brandapp-playground.vercel.app/eav/entities.md): List EAV entities and resolve their attribute definitions from a React Server Component. - [Records](https://brandapp-playground.vercel.app/eav/records.md): Full EAV CRUD against a data grid, using the Server Component + Client Component split. - [Filter Builder](https://brandapp-playground.vercel.app/eav/filter.md): Compose AttributeFilter entries interactively and read back the equivalent where() DSL snippet. - [Schema Definition](https://brandapp-playground.vercel.app/eav/schema.md): Declare entities and attributes in TypeScript, then generate types from the declaration. - [Schema Sync](https://brandapp-playground.vercel.app/eav/sync.md): Run the CLI's sync code path against the in-memory dev server and watch the compatibility report. - [Migrations](https://brandapp-playground.vercel.app/eav/migrate.md): Data-aware schema migrations for changes that must move record values, not just reshape the schema. - [Type Coercion](https://brandapp-playground.vercel.app/eav/coerce.md): Eight helpers that pull typed values out of an EAV record's Record. - [Typed Client](https://brandapp-playground.vercel.app/eav/typed.md): Compile-time safe EAV access with TypedEavRecord and the generated ENTITY_IDS constants. ## AI - [AI Overview](https://brandapp-playground.vercel.app/ai.md): Text and image generation billed against the BrandApp's AI credit pool. - [Text Completion](https://brandapp-playground.vercel.app/ai/complete.md): Streaming (SSE) and non-streaming text generation, with usage metadata on both paths. - [Image Generation](https://brandapp-playground.vercel.app/ai/image.md): Prompt-based image generation with optional style and aspect ratio, reporting credit cost. - [AI SDK Provider](https://brandapp-playground.vercel.app/ai/provider.md): Use generateText / streamText from the Vercel AI SDK against BrandApp via createBrandappProvider(). - [Credits and Models](https://brandapp-playground.vercel.app/ai/credits.md): Read the AI credit balance and the available model catalog in parallel from a Server Component. ## CMS - [CMS Overview](https://brandapp-playground.vercel.app/cms.md): Read-only content APIs — posts, post groups, and tags — with authoring handled in Reopt Studio. - [Posts](https://brandapp-playground.vercel.app/cms/posts.md): List and fetch posts, including the slug-addressed detail route with generateMetadata. - [Post Groups](https://brandapp-playground.vercel.app/cms/post-groups.md): Resolve group metadata and filter posts by group; authoring lives in Reopt Studio. - [Tags](https://brandapp-playground.vercel.app/cms/tags.md): Enumerate the tags in use across the brand scope with sdk.cms.tags.list. - [SEO, Sitemap, and RSS](https://brandapp-playground.vercel.app/cms/seo.md): Turn CMS posts into a Next.js sitemap, an RSS feed, and per-post metadata with the SDK helpers. ## Files - [Files API](https://brandapp-playground.vercel.app/files.md): Upload, list, and delete files, with the initial listing fetched server-side. - [File Browser](https://brandapp-playground.vercel.app/files/browser.md): A complete file manager UI — nested folders, upload queue, rename, move, preview, usage — on the Files API. ## Terms and billing - [Terms and Consents](https://brandapp-playground.vercel.app/terms.md): List active agreements app-scoped, then record and withdraw a signed-in user's consent. - [Plans and Checkout](https://brandapp-playground.vercel.app/plans.md): Plan catalog, hosted checkout, entitlement verification, and subscription cancellation. ## React hooks - [React Hooks Overview](https://brandapp-playground.vercel.app/hooks.md): TanStack Query hooks for every module, with cache keys and invalidation already wired. - [EAV Hooks](https://brandapp-playground.vercel.app/hooks/eav.md): Query and mutation hooks for EAV records, each invalidating the records cache after a write. - [AI Hooks](https://brandapp-playground.vercel.app/hooks/ai.md): Credit balance querying and streaming text generation with observable state transitions. - [CMS Hooks](https://brandapp-playground.vercel.app/hooks/cms.md): Read-only query hooks for posts, post groups, and tags. - [File Hooks](https://brandapp-playground.vercel.app/hooks/files.md): File listing, uploads, folders, and storage usage as TanStack Query hooks. ## Integration - [Webhooks](https://brandapp-playground.vercel.app/webhooks.md): Signed webhook handling with replay protection, exercised by an in-browser HMAC simulator. - [Dev Server](https://brandapp-playground.vercel.app/dev-server.md): An in-memory BrandApp API on port 4300 so the whole SDK surface works offline. - [Testing Utilities](https://brandapp-playground.vercel.app/testing.md): In-memory SDKs, a real HTTP test server, and typed fixtures for fast offline tests. - [Error Handling](https://brandapp-playground.vercel.app/errors.md): The SDK error hierarchy, the diagnostic payload, and how to map errors onto HTTP responses. ## About this site - [About](https://brandapp-playground.vercel.app/about.md): Who publishes the playground, what it is for, and how it is built and licensed. - [Contact](https://brandapp-playground.vercel.app/contact.md): How to reach Reopt about the SDK, the playground, or a bug in either. - [Privacy](https://brandapp-playground.vercel.app/privacy.md): What this deployment stores, where it stores it, and what it deliberately does not collect. ## Elsewhere - [SDK on npm](https://www.npmjs.com/package/@reopt-ai/brandapp-sdk): the package every example imports. - [Source repository](https://github.com/reopt-ai/brandapp-playground): the code behind every page on this site. - [Reopt](https://reopt.ai): the product and company behind the BrandApp platform. # Overview — BrandApp Playground > What the playground is, the three credential modes the SDK supports, and a map of every live demo. - Canonical page: https://brandapp-playground.vercel.app/ - Markdown: https://brandapp-playground.vercel.app/index.md - SDK module: `@reopt-ai/brandapp-sdk` ## What this is An interactive playground and reference implementation for `@reopt-ai/brandapp-sdk`, the TypeScript SDK for building applications on a Reopt BrandApp. Every page pairs a running demo with the exact source you would ship. ## The three credential modes | Mode | Credential | Used by | | --- | --- | --- | | Basic Auth | `clientId:clientSecret` (app-scoped) | `sdk.eav`, `sdk.ai`, `sdk.cms`, `sdk.files`, `sdk.plans.list`, `sdk.terms.list` | | Bearer | end-user OAuth access token | `sdk.auth.*`, `sdk.plans.getMyPlan`, `sdk.terms.listMyConsents` | | Dev mode | none — `REOPT_DEV_MODE=true` | the in-memory server on port 4300 | All three share a single SDK instance and can be mixed inside one request. ## Install ```bash npm install @reopt-ai/brandapp-sdk ``` ```ts import { createReoptSDK } from "@reopt-ai/brandapp-sdk"; export const sdk = createReoptSDK({ clientId: process.env.BRANDAPP_CLIENT_ID!, clientSecret: process.env.BRANDAPP_CLIENT_SECRET!, brandappId: process.env.BRANDAPP_ID!, }); ``` ## SDK module map The SDK ships sub-path exports so bundlers can tree-shake what you do not use. | Sub-path | Purpose | | --- | --- | | `@reopt-ai/brandapp-sdk` | `createReoptSDK`, error classes, runtime types | | `/auth` | user-scoped Bearer calls (`getUserInfo`, link, withdraw) | | `/eav` | entities, records, the `where()` filter DSL | | `/eav/schema` | `defineSchema()` / `defineEntity()` | | `/eav/migrate` | data-aware schema migrations | | `/ai`, `/ai-provider` | AI client and the Vercel AI SDK provider | | `/cms` | posts, post groups, tags, SEO helpers | | `/files` | folders, uploads, content preview, storage usage | | `/better-auth` | Better Auth database adapter and OAuth plugin | | `/webhooks` | signed webhook handler and verifier | | `/logout` | OIDC single logout — back-channel receiver, end-session URL | | `/react/hooks` | TanStack Query hooks | | `/dev`, `/testing` | in-memory dev server and test fixtures | ## Related - [Quick Start](https://brandapp-playground.vercel.app/getting-started.md) - [Connection](https://brandapp-playground.vercel.app/connection.md) - [Health Check](https://brandapp-playground.vercel.app/health.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # BrandApp SDK Developer Resources — BrandApp Playground > Named index of every developer resource: API reference, OpenAPI description, auth docs, webhooks, and the SDK package. - Canonical page: https://brandapp-playground.vercel.app/docs - Markdown: https://brandapp-playground.vercel.app/docs.md - SDK module: `@reopt-ai/brandapp-sdk` ## Machine-readable resources | Resource | URL | | --- | --- | | BrandApp Playground OpenAPI description | `/openapi.json` | | Documentation index for agents (llms.txt) | `/llms.txt` | | Full documentation, single file | `/llms-full.txt` | | API catalog (RFC 9727) | `/.well-known/api-catalog` | | Route inventory | `/sitemap.xml` | | Markdown twin of any page | append `.md`, or send `Accept: text/markdown` | ## Documentation by topic - **Getting started** — install, credentials, first call: `/getting-started` - **Authentication** — Better Auth + Reopt OAuth, scopes, single logout: `/auth` - **EAV** — entities, records, filter DSL, schema, migrations: `/eav` - **AI** — text, images, the Vercel AI SDK provider, credits: `/ai` - **CMS** — posts, groups, tags, sitemap and RSS helpers: `/cms` - **Files** — folders, uploads, previews, storage usage: `/files` - **Webhooks** — signature and replay verification: `/webhooks` - **Errors** — error classes and the diagnostic payload: `/errors` - **Dev server and testing** — offline development: `/dev-server`, `/testing` ## Package `@reopt-ai/brandapp-sdk` on npm — MIT, public registry, no auth token required: ## Authentication model This deployment is an OAuth **relying party**, not an authorization server. The authorization server is Reopt ID, and its metadata is published at . `/openapi.json` names the scopes each user-scoped operation requires. ## Related - [Quick Start](https://brandapp-playground.vercel.app/getting-started.md) - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [Error Handling](https://brandapp-playground.vercel.app/errors.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Quick Start — BrandApp Playground > Four steps from an empty Next.js project to a first authenticated SDK call. - Canonical page: https://brandapp-playground.vercel.app/getting-started - Markdown: https://brandapp-playground.vercel.app/getting-started.md - SDK module: `@reopt-ai/brandapp-sdk` ## Steps 1. **Install** — `npm install @reopt-ai/brandapp-sdk`. The package is on the public npm registry; no auth token is required. 2. **Set environment variables** — `BRANDAPP_CLIENT_ID`, `BRANDAPP_CLIENT_SECRET`, and `BRANDAPP_ID` from the Reopt dashboard. `REOPT_BASE_URL` and `REOPT_ID_BASE_URL` are optional overrides. 3. **Initialize once** — call `createReoptSDK()` in a shared module and import that instance. Never construct it at module scope in a file that runs during `next build`, because deployment secrets are intentionally unavailable then. 4. **Make a first call** — `sdk.eav.entities.list()`, `sdk.ai.credits()`, or `sdk.files.list({ limit: 20 })`. ## Running without credentials `REOPT_DEV_MODE=true` starts an in-memory Reopt API on port 4300 and points the SDK at it. Every app-scoped demo works offline; state resets when the process restarts. ## Related - [Connection](https://brandapp-playground.vercel.app/connection.md) - [Dev Server](https://brandapp-playground.vercel.app/dev-server.md) - [EAV Overview](https://brandapp-playground.vercel.app/eav.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Connection — BrandApp Playground > Swap the BrandApp credentials this deployment uses, stored in an encrypted httpOnly cookie. - Canonical page: https://brandapp-playground.vercel.app/connection - Markdown: https://brandapp-playground.vercel.app/connection.md ## What it does Shows which credential source is active — `custom` (cookie), `env`, `dev`, or `unconfigured` — and lets you point the playground at a different BrandApp without editing `.env`. ## How the credentials are stored - Encrypted with AES-256-GCM before they are written to the `reopt-playground-config` cookie. Plaintext never reaches cookie storage. - `httpOnly`, `sameSite=strict`, 30-day lifetime. - The key comes from `PLAYGROUND_COOKIE_SECRET`, falling back to `BETTER_AUTH_SECRET`. - Custom API origins must be allow-listed in `PLAYGROUND_ALLOWED_BASE_URLS`. ## Public deployments Server environment credentials are never handed to anonymous visitors. Each visitor connects their own BrandApp. A private deployment behind its own access boundary can opt in with `PLAYGROUND_ALLOW_SHARED_ENV_CREDENTIALS=true`. Authentication (Better Auth) always uses environment variables and is not switchable at runtime — only the data modules (EAV, AI, CMS, Files) follow the cookie. ## API - `GET /api/connection` — current mode plus masked credentials - `POST /api/connection` — store credentials - `DELETE /api/connection` — clear the cookie and revert to the deployment default ## Related - [Health Check](https://brandapp-playground.vercel.app/health.md) - [Quick Start](https://brandapp-playground.vercel.app/getting-started.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Health Check — BrandApp Playground > Streams a non-destructive probe suite over SSE against whichever BrandApp is currently wired. - Canonical page: https://brandapp-playground.vercel.app/health - Markdown: https://brandapp-playground.vercel.app/health.md ## What it does Runs the full check catalog against the active connection and streams results over Server-Sent Events from `/api/health`. Every check is read-only: it lists entities, lists files, pings AI models, and verifies webhook signatures without mutating anything. Use it after upgrading the SDK, before a release, or to confirm that credentials entered on `/connection` work end to end. ## Customer Visibility Once you are signed in, the panel walks the user-scoped chain: Better Auth session → OAuth access token → OIDC `userinfo` → the linked `BrandappCustomer`. It also exposes the soft-withdraw path (`sdk.auth.withdrawCurrentUser`), which flips the customer to `churned` and removes the junction while retaining the record, so re-linking restores the same `customerId`. ## Related - [Connection](https://brandapp-playground.vercel.app/connection.md) - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [Error Handling](https://brandapp-playground.vercel.app/errors.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Authentication — BrandApp Playground > Better Auth wired to Reopt OAuth — server setup, client setup, session reads, and the catch-all route. - Canonical page: https://brandapp-playground.vercel.app/auth - Markdown: https://brandapp-playground.vercel.app/auth.md - SDK module: `@reopt-ai/brandapp-sdk/better-auth` ## What it demonstrates - `createReoptAdapter()` — stores users and sessions in Reopt's backend, so the consumer app needs no local database. - `createReoptOAuth()` — registers Reopt as an OAuth2 provider for *Sign in with Reopt*. - `nextCookies()` — must be the last Better Auth plugin so it can forward the `Set-Cookie` headers produced by the plugins before it. ## Scopes Beyond `openid`, `profile`, and `email`, the playground requests: - `offline_access` — refresh token - `brandapp:read` — read the linked BrandApp customer - `brandapp:write` — required by `linkCurrentUser` and withdrawal, which mutate the link ## Two OAuth clients The adapter and the login flow can use separate clients. Production keeps the brand-configured shared client for the adapter and uses `PLAYGROUND_BRANDAPP_CLIENT_ID` / `PLAYGROUND_BRANDAPP_CLIENT_SECRET` for the login flow, whose redirect URI must match the public host. ## Session caching The Reopt adapter delegates session reads to the Reopt API on every request. `session.cookieCache` skips that round trip for its `maxAge` (300s here); the trade-off is that session invalidation lags by up to that window. ## Related - [Verify session](https://brandapp-playground.vercel.app/auth/session.md) - [Single Logout](https://brandapp-playground.vercel.app/logout.md) - [Health Check](https://brandapp-playground.vercel.app/health.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Verify session — BrandApp Playground > Validate a `.reopt.ai` subdomain cookie session from a server component with getSessionFromCookies / verifySession. - Canonical page: https://brandapp-playground.vercel.app/auth/session - Markdown: https://brandapp-playground.vercel.app/auth/session.md - SDK module: `@reopt-ai/brandapp-sdk/auth` ## What it demonstrates A server component forwards the incoming request's `Cookie` header to the Reopt ID server's Better Auth `/api/auth/get-session` endpoint and uses the verified result. This is the standard pattern for an external marketing site that shares the `.reopt.ai` cookie domain and wants to recognize an existing session without running its own login flow. ## Related - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [Single Logout](https://brandapp-playground.vercel.app/logout.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Machine credentials — BrandApp Playground > Server-to-server auth without a shared secret — private_key_jwt assertions, DPoP-bound tokens, a published JWKS, and a dedicated adapter client. - Canonical page: https://brandapp-playground.vercel.app/auth/machine - Markdown: https://brandapp-playground.vercel.app/auth/machine.md - SDK module: `@reopt-ai/brandapp-sdk/machine` ## What it demonstrates - `reopt-keygen` — generates an EdDSA key; the private JWK lives only in `REOPT_CLIENT_PRIVATE_KEY_JWK`, the public half is served at `/.well-known/reopt-client-jwks.json` with `buildClientJwks()`. - `createMachineClient()` — signs a `private_key_jwt` assertion (RFC 7523) for the token endpoint and binds every request with a DPoP proof (RFC 9449). - Two Reopt OAuth clients share the key: `adapter` (`brandapp:auth-adapter` only, used by `createReoptAdapter`) and `machine` (`brandapp:read` + `brandapp:write`, used by every other SDK factory through `authorization: machine.headers`). ## Why A login client secret used to open every machine surface, including the remote auth adapter that stores user sessions. With RFC-0034 the login client keeps only the authorization-code flow; machine calls carry a token that names its audience and is useless without the key that minted its proof. ## Zero-downtime switch Without a key the playground keeps using `BRANDAPP_CLIENT_SECRET`. Deploy the code, register the key, confirm `/api/auth/machine?probe=1` reports both tokens, then let Reopt close the login client's transitional machine scopes. ## Rotation Set `REOPT_CLIENT_PRIVATE_KEY_JWK_NEXT`: the JWKS lists both keys and signing moves to the new one. After Reopt picks it up (≤ 5 minutes) move NEXT into the primary variable and drop NEXT. ## Related - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [EAV Overview](https://brandapp-playground.vercel.app/eav.md) - [Health Check](https://brandapp-playground.vercel.app/health.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Single Logout — BrandApp Playground > OIDC back-channel logout receiver and RP-initiated logout, verified with zero-dependency Web Crypto Ed25519. - Canonical page: https://brandapp-playground.vercel.app/logout - Markdown: https://brandapp-playground.vercel.app/logout.md - SDK module: `@reopt-ai/brandapp-sdk/logout` ## Two directions **Back-channel logout (receive).** Reopt notifies your server when a Reopt session is revoked. `createBackchannelLogoutHandler()` verifies the logout token and ends the matching local sessions. The demo drives the real `/api/auth/backchannel-logout` receiver end to end, minting signed logout tokens through the dev IdP. **RP-initiated logout (trigger).** When a user logs out of your app, send them through Reopt's `end_session_endpoint` with `buildEndSessionUrl()`. ## Verification Signature checking uses Web Crypto Ed25519 — no extra dependency. SDK 3.3 hardening adds a retryable 503 (versus a terminal 400), throttled JWKS refetching, and a faithful dev OIDC mock with `dev.mintLogoutToken`, which is what makes this demo runnable offline. ## Related - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [Verify session](https://brandapp-playground.vercel.app/auth/session.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # EAV Overview — BrandApp Playground > Entry point for the Entity-Attribute-Value store: entities, records, schema, migrations, coercion, typed access. - Canonical page: https://brandapp-playground.vercel.app/eav - Markdown: https://brandapp-playground.vercel.app/eav.md - SDK module: `@reopt-ai/brandapp-sdk/eav` ## What EAV is A flexible, schema-declared data store. Entities define attributes; records hold values as `Record`. The SDK layers typed schemas, a filter DSL, migrations, and generated types on top so the flexibility does not cost you type safety. ## Sections - Entities and records — browse metadata, CRUD rows - Filter builder — the `where()` DSL and its eleven operators - Schema — `defineEntity()` / `defineSchema()` and generated types - Schema sync — push a schema, generate `ATTRIBUTE_IDS` - Migrations — the imperative escape hatch when data must move - Coercion — pull typed values out of `unknown` - Typed client — `TypedEavRecord` plus the typed `entity()` client ## Related - [Entities](https://brandapp-playground.vercel.app/eav/entities.md) - [Records](https://brandapp-playground.vercel.app/eav/records.md) - [Schema Definition](https://brandapp-playground.vercel.app/eav/schema.md) - [Filter Builder](https://brandapp-playground.vercel.app/eav/filter.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Entities — BrandApp Playground > List EAV entities and resolve their attribute definitions from a React Server Component. - Canonical page: https://brandapp-playground.vercel.app/eav/entities - Markdown: https://brandapp-playground.vercel.app/eav/entities.md - SDK module: `@reopt-ai/brandapp-sdk/eav` ## API - `eav.entities.list()` → `EavEntity[]` — metadata **only**, no attributes. - `eav.entities.get(entityId)` → a single entity **including** its attribute definitions. Because `list()` omits attributes, an accurate attribute count needs a follow-up `entities.get(id)` per entity — that is the only endpoint carrying `attributes`. Fetching happens in a Server Component, so there is no `useEffect` and no client-side loading state for the initial render. ## Related - [Records](https://brandapp-playground.vercel.app/eav/records.md) - [Schema Definition](https://brandapp-playground.vercel.app/eav/schema.md) - [Typed Client](https://brandapp-playground.vercel.app/eav/typed.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Records — BrandApp Playground > Full EAV CRUD against a data grid, using the Server Component + Client Component split. - Canonical page: https://brandapp-playground.vercel.app/eav/records - Markdown: https://brandapp-playground.vercel.app/eav/records.md - SDK module: `@reopt-ai/brandapp-sdk/eav` ## Architecture 1. The Server Component fetches the first entity and its initial records. 2. It passes them as initial props to the client demo, so the first paint has data. 3. The client component renders `opt-datagrid` and persists inline edits through API route handlers. ## API - `eav.records.list(entityId, query?)` - `eav.records.create(entityId, input)` - `eav.records.update(entityId, recordId, input)` - `eav.records.delete(entityId, recordId)` - `where(attr).contains(value)` — search filter, built server-side in the route handler ## Related - [Entities](https://brandapp-playground.vercel.app/eav/entities.md) - [Filter Builder](https://brandapp-playground.vercel.app/eav/filter.md) - [EAV Hooks](https://brandapp-playground.vercel.app/hooks/eav.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Filter Builder — BrandApp Playground > Compose AttributeFilter entries interactively and read back the equivalent where() DSL snippet. - Canonical page: https://brandapp-playground.vercel.app/eav/filter - Markdown: https://brandapp-playground.vercel.app/eav/filter.md - SDK module: `@reopt-ai/brandapp-sdk/eav` ## What it demonstrates `where(attr).op(value)` and `createFilterBuilder()`. The demo composes `AttributeFilter[]` entries across the SDK's eleven operators and applies them through `POST /api/eav/{entityId}/filter`. The generated-code panel prints the equivalent inline `where()` chain, so the demo doubles as a copy-paste reference for the filter you just built by clicking. ## Related - [Records](https://brandapp-playground.vercel.app/eav/records.md) - [Typed Client](https://brandapp-playground.vercel.app/eav/typed.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Schema Definition — BrandApp Playground > Declare entities and attributes in TypeScript, then generate types from the declaration. - Canonical page: https://brandapp-playground.vercel.app/eav/schema - Markdown: https://brandapp-playground.vercel.app/eav/schema.md - SDK module: `@reopt-ai/brandapp-sdk/eav/schema` ## Workflow 1. `defineEntity()` — declare an entity with typed attributes. 2. `defineSchema()` — combine entities into one schema object. 3. `npx reopt brandapp eav sync` — push the schema and generate TypeScript. 4. Import the generated `ENTITY_IDS` / `ATTRIBUTE_IDS` constants and record types. Documentation page — no live demo, because pushing a schema mutates the target BrandApp. ## Related - [Schema Sync](https://brandapp-playground.vercel.app/eav/sync.md) - [Typed Client](https://brandapp-playground.vercel.app/eav/typed.md) - [Migrations](https://brandapp-playground.vercel.app/eav/migrate.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Schema Sync — BrandApp Playground > Run the CLI's sync code path against the in-memory dev server and watch the compatibility report. - Canonical page: https://brandapp-playground.vercel.app/eav/sync - Markdown: https://brandapp-playground.vercel.app/eav/sync.md - SDK module: `@reopt-ai/brandapp-sdk/eav/schema` ## What it demonstrates The end-to-end loop `defineSchema` → `reopt brandapp eav sync` → `ATTRIBUTE_IDS` → typed filters. The left pane runs the same SDK code path the CLI uses, against the in-memory dev server, so you can watch progress events and the compatibility report before wiring sync into CI. The right pane shows every file involved: seed, schema, CLI invocation, generated output, and usage. ## Related - [Schema Definition](https://brandapp-playground.vercel.app/eav/schema.md) - [Migrations](https://brandapp-playground.vercel.app/eav/migrate.md) - [Dev Server](https://brandapp-playground.vercel.app/dev-server.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Migrations — BrandApp Playground > Data-aware schema migrations for changes that must move record values, not just reshape the schema. - Canonical page: https://brandapp-playground.vercel.app/eav/migrate - Markdown: https://brandapp-playground.vercel.app/eav/migrate.md - SDK module: `@reopt-ai/brandapp-sdk/eav/migrate` ## When to reach for this Schema sync is declarative and handles most changes. Migrations are the imperative escape hatch for changes that must also move record data — the classic case being a rename that has to preserve every existing value (expand → backfill → contract). ## Surface - `defineMigration()` — author a forward and optional reverse migration - `runMigration()` — execute up/down against a live client, guarded by a checksum - `runMigrationCollecting()` — buffer schema ops for one atomic server-side apply - `createMigrationContext()` — build the context yourself for scripted runs Documentation page — migrations mutate schema and data and require a checksum plus a real backend, so they are shown as references rather than run against the dev server. ## Related - [Schema Sync](https://brandapp-playground.vercel.app/eav/sync.md) - [Schema Definition](https://brandapp-playground.vercel.app/eav/schema.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Type Coercion — BrandApp Playground > Eight helpers that pull typed values out of an EAV record's Record. - Canonical page: https://brandapp-playground.vercel.app/eav/coerce - Markdown: https://brandapp-playground.vercel.app/eav/coerce.md - SDK module: `@reopt-ai/brandapp-sdk/eav/coerce` ## Helpers | Helper | Returns | | --- | --- | | `asString(value)` | `string \| undefined` | | `asNumber(value)` | `number \| undefined`, NaN-safe | | `asBoolean(value)` | `boolean \| undefined`, handles truthy/falsy strings | | `asDate(value)` | `Date \| undefined` from ISO strings or timestamps | | `asJson(value)` | parsed JSON with type inference | | `asStringArray(value)` | `string[]` from an array, JSON, or single value | | `asEnum(value, allowed, fallback)` | a member of `allowed` | | `asEnumOrUndefined(value, allowed)` | as above, without a fallback | Every single-argument helper returns `undefined` for null, undefined, or invalid input. Pair it with `?? fallback` when you need a default. ## Related - [Typed Client](https://brandapp-playground.vercel.app/eav/typed.md) - [Records](https://brandapp-playground.vercel.app/eav/records.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Typed Client — BrandApp Playground > Compile-time safe EAV access with TypedEavRecord and the generated ENTITY_IDS constants. - Canonical page: https://brandapp-playground.vercel.app/eav/typed - Markdown: https://brandapp-playground.vercel.app/eav/typed.md - SDK module: `@reopt-ai/brandapp-sdk/eav` ## Workflow 1. Declare the schema with `defineSchema()` / `defineEntity()`. 2. Run `npx reopt brandapp eav sync` to generate types. 3. Use `TypedEavRecord` and the generated `ENTITY_IDS`. ## What you get - Auto-complete for entity and attribute names - Compile-time validation of field types - A properly typed `values` object instead of `Record` - No hand-written type assertions at call sites ## Related - [Schema Definition](https://brandapp-playground.vercel.app/eav/schema.md) - [Type Coercion](https://brandapp-playground.vercel.app/eav/coerce.md) - [Schema Sync](https://brandapp-playground.vercel.app/eav/sync.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # AI Overview — BrandApp Playground > Text and image generation billed against the BrandApp's AI credit pool. - Canonical page: https://brandapp-playground.vercel.app/ai - Markdown: https://brandapp-playground.vercel.app/ai.md - SDK module: `@reopt-ai/brandapp-sdk/ai` ## Sections - Text completion — streaming and blocking generation - Image generation — prompt, style, aspect ratio - AI SDK provider — the Vercel AI SDK adapter - Credits and models — balance accounting and the model catalog All AI calls are app-scoped (Basic Auth) and draw down the BrandApp's shared credit balance. Every response reports its own credit cost alongside the remaining balance. ## Related - [Text Completion](https://brandapp-playground.vercel.app/ai/complete.md) - [Image Generation](https://brandapp-playground.vercel.app/ai/image.md) - [AI SDK Provider](https://brandapp-playground.vercel.app/ai/provider.md) - [Credits and Models](https://brandapp-playground.vercel.app/ai/credits.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Text Completion — BrandApp Playground > Streaming (SSE) and non-streaming text generation, with usage metadata on both paths. - Canonical page: https://brandapp-playground.vercel.app/ai/complete - Markdown: https://brandapp-playground.vercel.app/ai/complete.md - SDK module: `@reopt-ai/brandapp-sdk/ai` ## API - `ai.stream({ messages })` — token-by-token output, returns an async iterable - `ai.complete({ messages })` — the full result in one JSON payload - `ai.streamWithMetadata()` — streams tokens and resolves a metadata promise carrying usage once the stream ends The demo drives the streaming route over Server-Sent Events; the code panel shows both the server route handler and the client consumption pattern. ## Related - [AI SDK Provider](https://brandapp-playground.vercel.app/ai/provider.md) - [Credits and Models](https://brandapp-playground.vercel.app/ai/credits.md) - [AI Hooks](https://brandapp-playground.vercel.app/hooks/ai.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Image Generation — BrandApp Playground > Prompt-based image generation with optional style and aspect ratio, reporting credit cost. - Canonical page: https://brandapp-playground.vercel.app/ai/image - Markdown: https://brandapp-playground.vercel.app/ai/image.md - SDK module: `@reopt-ai/brandapp-sdk/ai` ## API ```ts const result = await ai.generateImage({ prompt: "a topographic map of a coastline", style: "watercolor", // optional aspectRatio: "16:9", // 1:1 | 16:9 | 9:16 | 4:3 | 3:4 }); // → { url, size, usage: { creditCost }, creditsRemaining } ``` Generated images land in the BrandApp's file store, so they also appear on the Files page. ## Related - [Credits and Models](https://brandapp-playground.vercel.app/ai/credits.md) - [Files API](https://brandapp-playground.vercel.app/files.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # AI SDK Provider — BrandApp Playground > Use generateText / streamText from the Vercel AI SDK against BrandApp via createBrandappProvider(). - Canonical page: https://brandapp-playground.vercel.app/ai/provider - Markdown: https://brandapp-playground.vercel.app/ai/provider.md - SDK module: `@reopt-ai/brandapp-sdk/ai-provider` ## Why The provider makes BrandApp a drop-in model source for the Vercel AI SDK, so the same patterns work regardless of the underlying provider. BrandApp-specific accounting is still available through the standard `providerMetadata` field. ```ts const brandapp = createBrandappProvider({ ... }); const { text, providerMetadata } = await generateText({ model: brandapp("anthropic/claude-haiku-4.5"), prompt: "Hello!", }); const cost = providerMetadata?.brandapp?.creditCost; ``` ## Related - [Text Completion](https://brandapp-playground.vercel.app/ai/complete.md) - [Credits and Models](https://brandapp-playground.vercel.app/ai/credits.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Credits and Models — BrandApp Playground > Read the AI credit balance and the available model catalog in parallel from a Server Component. - Canonical page: https://brandapp-playground.vercel.app/ai/credits - Markdown: https://brandapp-playground.vercel.app/ai/credits.md - SDK module: `@reopt-ai/brandapp-sdk/ai` ## API - `ai.credits()` → `{ balance, limit, percentage, status, resetAt }` - `ai.models()` → available models with their tier information Both run under one `Promise.all()` in a Server Component, so the rendered page needs no client JavaScript for the data itself. ## Related - [AI Overview](https://brandapp-playground.vercel.app/ai.md) - [AI Hooks](https://brandapp-playground.vercel.app/hooks/ai.md) - [Error Handling](https://brandapp-playground.vercel.app/errors.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # CMS Overview — BrandApp Playground > Read-only content APIs — posts, post groups, and tags — with authoring handled in Reopt Studio. - Canonical page: https://brandapp-playground.vercel.app/cms - Markdown: https://brandapp-playground.vercel.app/cms.md - SDK module: `@reopt-ai/brandapp-sdk/cms` ## Shape of the module From SDK 1.8 the CMS client is read-only: Reopt Studio owns authoring, and the SDK covers the query scenarios an external site actually needs — list with filters, fetch one by id or slug, resolve group metadata, and enumerate tags. The dev server's CMS handler implements the same endpoints as a hosted BrandApp, so `REOPT_DEV_MODE=true` works offline against seeded sample content. ## Related - [Posts](https://brandapp-playground.vercel.app/cms/posts.md) - [Post Groups](https://brandapp-playground.vercel.app/cms/post-groups.md) - [Tags](https://brandapp-playground.vercel.app/cms/tags.md) - [SEO, Sitemap, and RSS](https://brandapp-playground.vercel.app/cms/seo.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Posts — BrandApp Playground > List and fetch posts, including the slug-addressed detail route with generateMetadata. - Canonical page: https://brandapp-playground.vercel.app/cms/posts - Markdown: https://brandapp-playground.vercel.app/cms/posts.md - SDK module: `@reopt-ai/brandapp-sdk/cms` ## API - `sdk.cms.posts.list({ status, page, limit, sortBy, sortOrder })` - `sdk.cms.posts.get(postId)` - `sdk.cms.posts.getBySlug(slug)` ## Detail route `/cms/posts/{slug}` calls `getBySlug` exactly once in a Server Component and derives both the rendered body and the page metadata from that single response. `toMetadata()` turns the post into a Next.js `Metadata` object, and `document.contentRich` is an `EditorSpec` that `opt-editor` renders directly. This is the pattern an external marketing site can lift wholesale for its own `/blog/[slug]` route. ## Related - [SEO, Sitemap, and RSS](https://brandapp-playground.vercel.app/cms/seo.md) - [Tags](https://brandapp-playground.vercel.app/cms/tags.md) - [CMS Hooks](https://brandapp-playground.vercel.app/hooks/cms.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Post Groups — BrandApp Playground > Resolve group metadata and filter posts by group; authoring lives in Reopt Studio. - Canonical page: https://brandapp-playground.vercel.app/cms/post-groups - Markdown: https://brandapp-playground.vercel.app/cms/post-groups.md - SDK module: `@reopt-ai/brandapp-sdk/cms` ## API - `sdk.cms.postGroups.list(query?)` - `sdk.cms.postGroups.get(groupId)` List and get only — enough for an external site to resolve a group's metadata and then filter posts by it. ## Related - [Posts](https://brandapp-playground.vercel.app/cms/posts.md) - [Tags](https://brandapp-playground.vercel.app/cms/tags.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Tags — BrandApp Playground > Enumerate the tags in use across the brand scope with sdk.cms.tags.list. - Canonical page: https://brandapp-playground.vercel.app/cms/tags - Markdown: https://brandapp-playground.vercel.app/cms/tags.md - SDK module: `@reopt-ai/brandapp-sdk/cms` ## API - `sdk.cms.tags.list(query?)` — the tags actually used in the brand scope Useful for building a tag index or filter UI without scanning every post. ## Related - [Posts](https://brandapp-playground.vercel.app/cms/posts.md) - [CMS Hooks](https://brandapp-playground.vercel.app/hooks/cms.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # SEO, Sitemap, and RSS — BrandApp Playground > Turn CMS posts into a Next.js sitemap, an RSS feed, and per-post metadata with the SDK helpers. - Canonical page: https://brandapp-playground.vercel.app/cms/seo - Markdown: https://brandapp-playground.vercel.app/cms/seo.md - SDK module: `@reopt-ai/brandapp-sdk/cms` ## Helpers - `toSitemapItems(records, { siteUrl, basePath })` — feeds `app/sitemap.ts` directly; the returned items are structurally compatible with `MetadataRoute.Sitemap`. - `toRssItems(records, ...)` — feeds a route handler that emits the feed XML. - `toMetadata(post)` — returns a Next.js `Metadata` object for `generateMetadata`. ## Live endpoints on this deployment - [`/sitemap.xml`](/sitemap.xml) - [`/rss.xml`](/rss.xml) - [`/robots.txt`](/robots.txt) The sitemap paginates through every published post and falls back to the static route inventory if the connected BrandApp's CMS is unavailable, so search engines never receive an empty document. ## Related - [Posts](https://brandapp-playground.vercel.app/cms/posts.md) - [CMS Overview](https://brandapp-playground.vercel.app/cms.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Files API — BrandApp Playground > Upload, list, and delete files, with the initial listing fetched server-side. - Canonical page: https://brandapp-playground.vercel.app/files - Markdown: https://brandapp-playground.vercel.app/files.md - SDK module: `@reopt-ai/brandapp-sdk/files` ## API - `files.list({ limit, sortOrder })` — paginated listing - `files.upload(buffer, { name, contentType })` - `files.delete(fileId)` The Server Component fetches the initial list with a `.catch()` fallback so a broken connection degrades to an empty gallery rather than an error page. AI-generated images show up in the same listing. Uploads are bounded before parsing by `PLAYGROUND_MAX_UPLOAD_BYTES` (10 MiB by default). ## Related - [File Browser](https://brandapp-playground.vercel.app/files/browser.md) - [File Hooks](https://brandapp-playground.vercel.app/hooks/files.md) - [Image Generation](https://brandapp-playground.vercel.app/ai/image.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # File Browser — BrandApp Playground > A complete file manager UI — nested folders, upload queue, rename, move, preview, usage — on the Files API. - Canonical page: https://brandapp-playground.vercel.app/files/browser - Markdown: https://brandapp-playground.vercel.app/files/browser.md - SDK module: `@reopt-ai/brandapp-sdk/files` ## What it is `@reopt-ai/opt-filemanager` mounted on the BrandApp Files API through server-side proxy routes, so no credential ever reaches the browser. Covers nested folders, an upload queue, rename and move, text preview, deletion, and storage usage. ## Proxy routes | Route | Purpose | | --- | --- | | `GET /api/files` | list files | | `POST /api/files/upload` | upload | | `GET /api/files/{fileId}` | metadata | | `PATCH /api/files/{fileId}` | rename or move | | `DELETE /api/files/{fileId}` | delete | | `GET /api/files/{fileId}/content` | text preview | | `GET \| POST /api/files/folders` | list or create folders | | `PATCH \| DELETE /api/files/folders/{folderId}` | rename or delete a folder | | `GET /api/files/usage` | storage usage | The repository ships a longer reference at `docs/file-browser.md` covering installation, architecture, the capability and endpoint maps, limits, errors, and compatibility. ## Related - [Files API](https://brandapp-playground.vercel.app/files.md) - [File Hooks](https://brandapp-playground.vercel.app/hooks/files.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Terms and Consents — BrandApp Playground > List active agreements app-scoped, then record and withdraw a signed-in user's consent. - Canonical page: https://brandapp-playground.vercel.app/terms - Markdown: https://brandapp-playground.vercel.app/terms.md - SDK module: `@reopt-ai/brandapp-sdk/terms` ## Two credential paths in one page - `sdk.terms.list()` — Basic Auth, fetched server-side, renders the active agreements. - `sdk.terms.consent` / `listMyConsents` / `withdraw` — Bearer, reached through `/api/terms/me/consents`, which forwards the signed-in user's access token. Agreements carry a `TermsJurisdiction`, so the same BrandApp can serve region-specific documents from one catalog. ## Related - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [Health Check](https://brandapp-playground.vercel.app/health.md) - [Plans and Checkout](https://brandapp-playground.vercel.app/plans.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Plans and Checkout — BrandApp Playground > Plan catalog, hosted checkout, entitlement verification, and subscription cancellation. - Canonical page: https://brandapp-playground.vercel.app/plans - Markdown: https://brandapp-playground.vercel.app/plans.md - SDK module: `@reopt-ai/brandapp-sdk/plans` ## Flow 1. `sdk.plans.list()` — the catalog, Basic Auth, fetched server-side. 2. `sdk.plans.getMyPlan()` — the signed-in customer's current plan, Bearer. 3. `sdk.plans.createCheckout()` — redirects to a checkout page on `id.reopt.ai`. 4. The browser returns to `/plans/return`, where the entitlement is verified. 5. `sdk.plans.cancel()` — reports the provider, the remaining access period, and whether the provider-side cancellation succeeded. ## Payment modes With real payment disabled (the default), checkout returns a sandbox virtual-payment page, so the whole list → subscribe → pay → entitlement loop runs with no payment provider setup at all. Against a hosted BrandApp in `live` mode, Paddle handles both recurring subscriptions (`monthly`, `yearly`) and one-time purchases (`once`, `lifetime`) through an overlay on `id.reopt.ai`. Zero-amount plans keep using the virtual checkout even in live mode. Toss live mode is not supported by SDK checkout and returns `LIVE_MODE_UNSUPPORTED`. ## Related - [Terms and Consents](https://brandapp-playground.vercel.app/terms.md) - [Authentication](https://brandapp-playground.vercel.app/auth.md) - [Health Check](https://brandapp-playground.vercel.app/health.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # React Hooks Overview — BrandApp Playground > TanStack Query hooks for every module, with cache keys and invalidation already wired. - Canonical page: https://brandapp-playground.vercel.app/hooks - Markdown: https://brandapp-playground.vercel.app/hooks.md - SDK module: `@reopt-ai/brandapp-sdk/react/hooks` ## Contract Every hook takes the `ReoptSDK` instance as its first argument and requires a `QueryClientProvider` ancestor. Mutations invalidate the matching query cache, so lists refresh on their own after a write. Hooks run in the browser, which means they need an SDK instance that is safe to expose — in practice, point them at your own route handlers rather than at credentials. ## Related - [EAV Hooks](https://brandapp-playground.vercel.app/hooks/eav.md) - [AI Hooks](https://brandapp-playground.vercel.app/hooks/ai.md) - [CMS Hooks](https://brandapp-playground.vercel.app/hooks/cms.md) - [File Hooks](https://brandapp-playground.vercel.app/hooks/files.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # EAV Hooks — BrandApp Playground > Query and mutation hooks for EAV records, each invalidating the records cache after a write. - Canonical page: https://brandapp-playground.vercel.app/hooks/eav - Markdown: https://brandapp-playground.vercel.app/hooks/eav.md - SDK module: `@reopt-ai/brandapp-sdk/react/hooks` ## Hooks - `useEavRecords(sdk, entityId, query?)` - `useCreateRecord(sdk, entityId)` - `useUpdateRecord(sdk, entityId)` — `mutate({ recordId, input })` - `useDeleteRecord(sdk, entityId)` — `mutate(recordId)` - `useUpsertRecord(sdk, entityId)` — update-or-create by filters - `useBulkCreateRecords(sdk, entityId)` — up to 100 records per call - `useDeleteRecordsWhere(sdk, entityId)` — delete everything matching a filter The demo visualizes the React Query lifecycle — `status`, `fetchStatus`, staleness, and cache state — so you can see exactly when invalidation fires. ## Related - [Records](https://brandapp-playground.vercel.app/eav/records.md) - [React Hooks Overview](https://brandapp-playground.vercel.app/hooks.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # AI Hooks — BrandApp Playground > Credit balance querying and streaming text generation with observable state transitions. - Canonical page: https://brandapp-playground.vercel.app/hooks/ai - Markdown: https://brandapp-playground.vercel.app/hooks/ai.md - SDK module: `@reopt-ai/brandapp-sdk/react/hooks` ## Hooks - `useAiCredits(sdk)` — cached credit balance - `useAiStream(sdk)` — streaming generation with real-time state updates The demo walks the stream lifecycle `idle → streaming → done` and reports token usage once generation completes. ## Related - [Text Completion](https://brandapp-playground.vercel.app/ai/complete.md) - [Credits and Models](https://brandapp-playground.vercel.app/ai/credits.md) - [React Hooks Overview](https://brandapp-playground.vercel.app/hooks.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # CMS Hooks — BrandApp Playground > Read-only query hooks for posts, post groups, and tags. - Canonical page: https://brandapp-playground.vercel.app/hooks/cms - Markdown: https://brandapp-playground.vercel.app/hooks/cms.md - SDK module: `@reopt-ai/brandapp-sdk/react/hooks` ## Hooks - `usePostList(sdk, query?)` - `usePost(sdk, postId)` - `usePostBySlug(sdk, slug, opts?)` - `usePostGroupList(sdk, query?)` - `usePostGroup(sdk, groupId)` - `useCmsTags(sdk, query?)` The mutation hooks (`useCreatePost`, `useUpdatePost`, `useDeletePost`) were removed in SDK 1.8 when the CMS client became read-only. Authoring belongs to Reopt Studio. ## Related - [Posts](https://brandapp-playground.vercel.app/cms/posts.md) - [React Hooks Overview](https://brandapp-playground.vercel.app/hooks.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # File Hooks — BrandApp Playground > File listing, uploads, folders, and storage usage as TanStack Query hooks. - Canonical page: https://brandapp-playground.vercel.app/hooks/files - Markdown: https://brandapp-playground.vercel.app/hooks/files.md - SDK module: `@reopt-ai/brandapp-sdk/react/hooks` ## Hooks - `useFiles(sdk, query?)` — paginated listing - `useUploadFile(sdk)` — upload, invalidates the listing on success - SDK 3.5 folder, update, and usage hooks The demo includes a cache inspector so you can watch manual refetch and invalidation affect the query state. ## Related - [Files API](https://brandapp-playground.vercel.app/files.md) - [File Browser](https://brandapp-playground.vercel.app/files/browser.md) - [React Hooks Overview](https://brandapp-playground.vercel.app/hooks.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Webhooks — BrandApp Playground > Signed webhook handling with replay protection, exercised by an in-browser HMAC simulator. - Canonical page: https://brandapp-playground.vercel.app/webhooks - Markdown: https://brandapp-playground.vercel.app/webhooks.md - SDK module: `@reopt-ai/brandapp-sdk/webhooks` ## The handler `createWebhookHandler()` verifies an HMAC-SHA256 signature over `{timestamp}.{body}`, sent as `x-reopt-signature` with `x-reopt-timestamp` and `x-reopt-event`. ## Branches the simulator exercises | Scenario | Response | | --- | --- | | valid signature | `200` | | replay, 6 minutes old | `410` (default `toleranceMs` is 5 minutes) | | tampered — wrong secret | `401` | | missing `x-reopt-timestamp` | `401` | | same delivery id twice | `200`, then `200 { deduplicated: true }` when `idempotency` is on | The 500 handler-error branch and the `onError` / `x-reopt-delivery-attempt` contract are covered by the code examples rather than the simulator. ## Related - [Error Handling](https://brandapp-playground.vercel.app/errors.md) - [Testing Utilities](https://brandapp-playground.vercel.app/testing.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Dev Server — BrandApp Playground > An in-memory BrandApp API on port 4300 so the whole SDK surface works offline. - Canonical page: https://brandapp-playground.vercel.app/dev-server - Markdown: https://brandapp-playground.vercel.app/dev-server.md - SDK module: `@reopt-ai/brandapp-sdk/dev` ## Quick start ```bash REOPT_DEV_MODE=true next dev ``` ## What it covers - `createDevServer()` — create the server programmatically with a schema - Seed data — pre-populate the in-memory database from `reopt.seed.ts` - `instrumentation.ts` — start the dev server on Next.js boot - A bootstrap module that injects environment variables, so existing SDK clients point at the local server without any code change The dev server also mocks the OIDC endpoints, which is what makes the single-logout demo runnable with no network. ## Related - [Testing Utilities](https://brandapp-playground.vercel.app/testing.md) - [Quick Start](https://brandapp-playground.vercel.app/getting-started.md) - [Schema Sync](https://brandapp-playground.vercel.app/eav/sync.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Testing Utilities — BrandApp Playground > In-memory SDKs, a real HTTP test server, and typed fixtures for fast offline tests. - Canonical page: https://brandapp-playground.vercel.app/testing - Markdown: https://brandapp-playground.vercel.app/testing.md - SDK module: `@reopt-ai/brandapp-sdk/testing` ## Utilities - `createTestSDK()` — an in-memory SDK for unit tests; no server, no network - `createTestServer()` — a real HTTP server on a random port for integration tests - `createFixture()` — typed fixtures that can be seeded into either Together they let you test SDK-dependent code deterministically and offline, which is what makes the suites in this repository runnable in CI without credentials. ## Related - [Dev Server](https://brandapp-playground.vercel.app/dev-server.md) - [Error Handling](https://brandapp-playground.vercel.app/errors.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Error Handling — BrandApp Playground > The SDK error hierarchy, the diagnostic payload, and how to map errors onto HTTP responses. - Canonical page: https://brandapp-playground.vercel.app/errors - Markdown: https://brandapp-playground.vercel.app/errors.md - SDK module: `@reopt-ai/brandapp-sdk` ## Type guards - `isReoptSDKError(err)` — structured SDK errors - `isValidationError(err)` plus `toFieldMap(err)` — field-level validation errors ## Classes `BadRequestError` (400), `AuthError` (401), `ForbiddenError` (403), `NotFoundError` (404), `ConflictError` (409), `ValidationError` (422), `RateLimitError` (429), `ServerError` (500), plus the AI-specific `CreditLimitError` (402), `ModelAccessError` (403), `ModelNotFoundError` (404), and `ContentFilterError` (422). ## Diagnostic Every structured error carries `{ cause, fix, envVar?, doc? }`, so a failure tells you what to change rather than only what broke. The live demo throws real instances and shows the `name`, `status`, `code`, `context`, and `diagnostic` your `catch` block will receive. ## Related - [Health Check](https://brandapp-playground.vercel.app/health.md) - [Webhooks](https://brandapp-playground.vercel.app/webhooks.md) - [Testing Utilities](https://brandapp-playground.vercel.app/testing.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # About — BrandApp Playground > Who publishes the playground, what it is for, and how it is built and licensed. - Canonical page: https://brandapp-playground.vercel.app/about - Markdown: https://brandapp-playground.vercel.app/about.md ## What this is BrandApp Playground is the public reference implementation for `@reopt-ai/brandapp-sdk`. It exists so a developer evaluating or integrating the SDK can see every module running against a real backend, next to the source that produced the result. ## Who publishes it Reopt (리옵트, legal name 주식회사 리옵트), the company behind the Reopt brand platform. ## How it is built Next.js App Router, React Server Components, TypeScript, Tailwind CSS, and the Reopt `opt-ui`, `opt-datagrid`, `opt-editor`, and `opt-filemanager` component packages. Better Auth handles sessions; the Reopt adapter stores them remotely, so the app carries no database of its own. ## Cost and access The site is free and needs no account. The SDK is MIT-licensed and published on the public npm registry. Bringing your own BrandApp credentials unlocks the live demos; without them the pages still document every API. ## Source - Repository: - SDK: ## Related - [Contact](https://brandapp-playground.vercel.app/contact.md) - [Privacy](https://brandapp-playground.vercel.app/privacy.md) - [Overview](https://brandapp-playground.vercel.app/index.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Contact — BrandApp Playground > How to reach Reopt about the SDK, the playground, or a bug in either. - Canonical page: https://brandapp-playground.vercel.app/contact - Markdown: https://brandapp-playground.vercel.app/contact.md ## Channels | Purpose | Where | | --- | --- | | Bug in the playground | | | SDK questions and general contact | contact@reopt.ai | | Phone (Korea) | 1644-2410 | | Product site | | ## Publisher 주식회사 리옵트 (Reopt Inc.) 서울특별시 광진구 천호대로101길 43, 1층 14호 (중곡동), Republic of Korea Support is available in Korean and English. ## Related - [About](https://brandapp-playground.vercel.app/about.md) - [Privacy](https://brandapp-playground.vercel.app/privacy.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json # Privacy — BrandApp Playground > What this deployment stores, where it stores it, and what it deliberately does not collect. - Canonical page: https://brandapp-playground.vercel.app/privacy - Markdown: https://brandapp-playground.vercel.app/privacy.md ## What the playground stores **BrandApp credentials you enter on `/connection`.** Sealed with AES-256-GCM and written to the `reopt-playground-config` cookie — `httpOnly`, `sameSite=strict`, 30 days. Plaintext never reaches cookie storage, and the cookie is readable only by this deployment's server. Clearing it (`DELETE /api/connection`, or the reset button on the connection page) removes it immediately. **Session state, if you sign in with Reopt.** Better Auth issues a session cookie; the Reopt adapter keeps the session record in Reopt's backend rather than in a database owned by this app. Signing out ends it, and back-channel logout ends it when the Reopt session is revoked elsewhere. **Theme preference.** `opt-preset` and `opt-mode` in `localStorage`, read before first paint. It never leaves the browser. ## What it does not do - No analytics, tracking pixels, or advertising identifiers. - No third-party scripts. The site loads only its own assets. - No credential is ever exposed to the browser; every SDK call runs server-side. - Public deployments never hand server environment credentials to anonymous visitors. ## Data you create in a demo Records, files, and AI generations created here are written to **the BrandApp whose credentials are connected** — yours, if you connected your own. They are governed by that BrandApp, not by this site. In dev mode everything lives in an in-memory server and disappears on restart. ## Contact Questions about this notice: contact@reopt.ai. ## Related - [About](https://brandapp-playground.vercel.app/about.md) - [Contact](https://brandapp-playground.vercel.app/contact.md) - [Connection](https://brandapp-playground.vercel.app/connection.md) --- Index of every document: https://brandapp-playground.vercel.app/llms.txt Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt HTTP API description: https://brandapp-playground.vercel.app/openapi.json