# Sunboard Documentation — Full Content > Full text of every Sunboard documentation page, concatenated for AI tools. See https://www.sunboardhq.com/llms.txt for the index. # Quick start URL: https://www.sunboardhq.com/docs/quickstart Description: Install the Sunboard CLI, run sunboard init, and hand off to your AI agent to design your first activation experience. Sunboard turns your AI coding agent into an activation expert. This guide gets the CLI installed, wires the React SDK into your app, and installs the agent skills, so you can ask your agent to design and ship in-app onboarding. You'll need **Node.js 18 or later** and a **Next.js (App Router)** or **React + Vite** app. Don't have a Sunboard account yet? You can still run everything locally — `init` writes a placeholder key you can swap later. ## Install and initialize ### Install the CLI Install Sunboard globally so the `sunboard` command is available in any project. ```bash npm install -g sunboard ``` ```bash pnpm add -g sunboard ``` ```bash yarn global add sunboard ``` ```bash bun add -g sunboard ``` Confirm it's on your `PATH`: ```bash sunboard --version ``` ### Sign in A one-time browser auth links this machine to your Sunboard account. ```bash sunboard login ``` You can skip this. `sunboard init` will write a placeholder `pk_test_replace_me` key and you can run `sunboard login` later to bind a real project. ### Run init in your app From the root of the app you want to onboard: ```bash cd your-nextjs-or-vite-app sunboard init ``` `init` is interactive and asks a few things: - **Which framework** — Next.js App Router or React + Vite (auto-detected when it can). - **Which AI coding harnesses you use** — Codex, Claude Code, and/or opencode. It installs the matching skill files for each. - **Which Sunboard project to bind** — pick an existing project or create a new one. The runtime key is written to your env file. ## What `sunboard init` sets up In one run, `init` installs the SDK, wires the runtime into your app, and drops the agent skills into your repo: - **`@sunboard/react`** is added to `package.json` — the SDK that renders your checklist and tours. - **`sunboard-provider.tsx`** is created and mounted in your root layout (or app entry), wrapping your app with the Sunboard runtime. - **`globals.css`** gets the `--sunboard-*` theme variables appended, so you can restyle the built-in widgets. - No spec files are written. Sunboard is fileless: your agent authors onboarding specs in context and deploys them to the server, which is the source of truth. - The **skill files** are installed for whichever harnesses you picked. (`.claude/commands/` is shown above; Codex lands under `.agents/skills/`, opencode under `.opencode/commands/`.) - **`.env.local`** gets your runtime key. The exact name depends on the framework: ```bash title=".env.local" NEXT_PUBLIC_SUNBOARD_KEY=pk_live_xxx ``` The provider lives at `app/sunboard-provider.tsx`. ```bash title=".env.local" VITE_SUNBOARD_KEY=pk_live_xxx ``` The provider lives at `src/sunboard-provider.tsx`. After `init` finishes, install dependencies so `@sunboard/react` is available: ```bash npm install ``` ```bash pnpm install ``` ```bash yarn ``` ```bash bun install ``` ## Design your onboarding Now the fun part. Open your editor and ask your AI agent to run the onboarding skill: ```text /sunboard.onboarding Help me set up onboarding for trial users to create their first workflow. ``` The agent studies your app, runs a short conversation — your aha moment, the cohorts worth targeting, whether new users need a survey to route them — and agrees a plan with you. Then it drives the whole build: writing the experience specs, wiring the selectors and `track()` calls into your app, creating the segments, validating, and deploying to the sandbox so you can test the real loop on your `pk_test_` key. When you're happy, it promotes the tested versions to production. Every step the agent takes maps to a `sunboard` CLI command. Run `sunboard doctor` anytime to check your install, or see the [Skills](/docs/skills) section for what each command does. --- # What is Sunboard URL: https://www.sunboardhq.com/docs/what-is-sunboard Description: Sunboard turns your coding agent into a customer success hire: it builds your onboarding, Sunboard watches your funnel, and your agent ships the fixes. **Sunboard turns your coding agent into a customer success hire.** Your agent plans an activation flow _with_ you and wires it into your product: surveys, checklists, and tours in your real UI, not hand-built in a separate visual editor that drifts from your product. From then on, Sunboard watches your funnel and sends you insights, and your agent ships the fixes. You don't need to be a growth expert. Your agent studies your product, then interviews you about your users and the job they're hiring it for: the aha moment, the must-have setup, the segments worth targeting. It turns your answers into an activation plan and builds it. Sunboard hosts and versions every change, so the server is the source of truth and your agent drives it, not a dashboard you maintain by hand. ## What you can build Every step anchors to real UI through `data-sunboard-id` selectors, and completes when a real product event fires via `useSunboard().track()`. Because your agent already knows your codebase, the anchors land on the right elements. ## How it fits together ## The workflow You drive everything from your AI agent through two skills — **[Onboarding](/docs/skills/onboarding)** to set it up and **[Analyze](/docs/skills/analyze)** to improve it. Under the hood they run a loop you can repeat as your product grows: 1. **[Plan & author](/docs/skills/onboarding)** — Onboarding interviews you about the aha moment and your cohorts, then drafts the experience(s) — plus a [survey](/docs/skills/survey) when new users need routing. 2. **[Implement](/docs/skills/implement)** — wire the `data-sunboard-id` selectors and `track()` calls into your real product. 3. **[Deploy](/docs/skills/deploy)** — activate the version in the [sandbox](/docs/environments), where it's live for testing against your `pk_test_` key. 4. **Test** — run your local app and walk the real loop (survey → segment → experience) with real persistence and routing. 5. **Promote** — ship the tested version to production with `sunboard promote`. Old versions stay available, so rollback is one promote away. 6. **[Analyze](/docs/skills/analyze)** — read the real funnel, find where users drop off, and ship a concrete fix. Then measure again. Connect [your PostHog](/docs/integrations/posthog) and the agent also sees what dropped users did instead — detours, rage clicks, session replays. Deploy activates a version in the sandbox, never production. Production changes only through a separate `sunboard promote` step you approve. A public runtime key can read its environment's onboarding and report events, but never publish or alter what your users see. ## How it's different Tools like Pendo, Appcues, or Userpilot build onboarding in a separate visual dashboard that drifts from your product, and they stop once it ships. With Sunboard, your agent wires onboarding into the real codebase, so it targets your actual UI and activation events, and Sunboard keeps watching the funnel after launch. It's a natural fit for teams shipping with AI coding agents, including AI-built and vibe-coded SaaS apps.
--- # Next.js URL: https://www.sunboardhq.com/docs/manual-installation/nextjs Description: Wire the Sunboard React SDK into a Next.js App Router app by hand. `sunboard init` does all of this automatically — see the [Quick start](/docs/quickstart). Follow this page if you'd rather wire things by hand, or want to understand exactly what `init` changes. This wires the Sunboard runtime into a **Next.js App Router** app: install the SDK, mount a provider in your root layout, and point it at your project with a publishable key. ### Install the SDK ```bash npm install @sunboard/react ``` ```bash pnpm add @sunboard/react ``` ```bash yarn add @sunboard/react ``` ```bash bun add @sunboard/react ``` ### Add your publishable key Put your project's runtime key in `.env.local`. You'll find it in Sunboard (or run `sunboard init` once to have it written for you). ```bash title=".env.local" NEXT_PUBLIC_SUNBOARD_KEY=pk_live_xxx ``` ### Create the runtime wrapper This client component subscribes to the current route and renders the Sunboard widgets. Create it next to your root layout. ```tsx title="app/sunboard-provider.tsx" "use client"; export function SunboardRuntime({ children }: { children: ReactNode }) { const pathname = usePathname(); const router = useRouter(); const [route, setRoute] = useState(pathname); useEffect(() => { const search = window.location.search; setRoute(search ? `${pathname}${search}` : pathname); }, [pathname]); return ( router.push(url)} > {children} ); } ``` ### Mount it in your root layout Import the Sunboard stylesheet **before** your own global CSS (so your `--sunboard-*` overrides win), then wrap `{children}`. ```tsx title="app/layout.tsx" export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` If your app has multiple root layouts (e.g. `app/(marketing)/layout.tsx` and `app/(app)/layout.tsx`), wrap `{children}` in each one so the runtime mounts no matter which layout renders. ### Wire your real user The wrapper ships a placeholder `user`. Replace it with your authenticated user so segments and analytics are accurate. Only `id` is required; anything you put under `properties` is available for [segment](/docs/skills/segment) targeting: ```tsx user={{ id: currentUser.id, email: currentUser.email, group: currentUser.plan, // optional cohort, e.g. "trial" / "pro" properties: { // Any custom attributes you want to target on. role: currentUser.role, signupDate: currentUser.createdAt, workflowsCreated: currentUser.workflowCount, }, }} ``` ## Verify Run your app. Your dev app uses the `pk_test_` key, so once you've deployed a spec to the sandbox the checklist appears. Deploy one with [`/sunboard.deploy`](/docs/skills/deploy), or run `sunboard doctor` to check the wiring. ## Next steps --- # React Router URL: https://www.sunboardhq.com/docs/manual-installation/react-router Description: Wire the Sunboard React SDK into a React Router (framework mode) app by hand. `sunboard init` does all of this automatically — see the [Quick start](/docs/quickstart). Follow this page if you'd rather wire things by hand, or want to understand exactly what `init` changes. This covers **React Router framework mode** (the successor to Remix). It's Vite-based, so the setup mirrors [Vite](/docs/manual-installation/vite) — the one difference is **where** you mount the provider: your root route, `app/root.tsx`, inside the `Layout` export. ### Install the SDK ```bash npm install @sunboard/react ``` ```bash pnpm add @sunboard/react ``` ```bash yarn add @sunboard/react ``` ```bash bun add @sunboard/react ``` ### Add your publishable key React Router framework mode runs on Vite, so it exposes `VITE_`-prefixed env vars to the browser. Put your project's runtime key in `.env.local`: ```bash title=".env.local" VITE_SUNBOARD_KEY=pk_live_xxx ``` ### Create the runtime wrapper This component reads the current location and renders the Sunboard widgets. It uses React Router's `useNavigate` and `useLocation` so tour route changes stay client-side. Create it next to your root route, under `app/`. ```tsx title="app/sunboard-provider.tsx" export function SunboardRuntime({ children }: { children: ReactNode }) { const navigate = useNavigate(); const location = useLocation(); const route = `${location.pathname}${location.search}`; return ( navigate(url)} > {children} ); } ``` ### Mount it in your root route Import the Sunboard stylesheet **before** your own global CSS, then wrap the `{children}` your `Layout` renders (inside ``). The `Layout` export runs inside the router context, so `useNavigate` / `useLocation` work there. ```tsx title="app/root.tsx" export function Layout({ children }: { children: React.ReactNode }) { return ( {children} ); } export default function App() { return ; } ``` Keep your existing `Meta` / `Links` / `Scripts` and any providers — only add the Sunboard stylesheet import and wrap `{children}` with ``. If your `appDirectory` is `src/` instead of `app/`, adjust the paths to match. ### Wire your real user The wrapper ships a placeholder `user`. Replace it with your authenticated user so segments and analytics are accurate. Only `id` is required; anything you put under `properties` is available for [segment](/docs/skills/segment) targeting: ```tsx user={{ id: currentUser.id, email: currentUser.email, group: currentUser.plan, // optional cohort, e.g. "trial" / "pro" properties: { // Any custom attributes you want to target on. role: currentUser.role, signupDate: currentUser.createdAt, workflowsCreated: currentUser.workflowCount, }, }} ``` ## Verify Run your dev server. It uses the `pk_test_` key, so once you've deployed a spec to the sandbox the checklist appears. Deploy one with [`/sunboard.deploy`](/docs/skills/deploy), or run `sunboard doctor` to check the wiring. ## Next steps --- # TanStack Start URL: https://www.sunboardhq.com/docs/manual-installation/tanstack-start Description: Wire the Sunboard React SDK into a TanStack Start app by hand. `sunboard init` does all of this automatically — see the [Quick start](/docs/quickstart). Follow this page if you'd rather wire things by hand, or want to understand exactly what `init` changes. TanStack Start is Vite-based, so the setup is the same as [Vite](/docs/manual-installation/vite) — the one difference is **where** you mount the provider: your root route, `src/routes/__root.tsx`, instead of a client entry. ### Install the SDK ```bash npm install @sunboard/react ``` ```bash pnpm add @sunboard/react ``` ```bash yarn add @sunboard/react ``` ```bash bun add @sunboard/react ``` ### Add your publishable key Like Vite, TanStack Start exposes `VITE_`-prefixed env vars to the browser. Put your project's runtime key in `.env.local`: ```bash title=".env.local" VITE_SUNBOARD_KEY=pk_live_xxx ``` ### Create the runtime wrapper This component reads the current location and renders the Sunboard widgets. It uses TanStack Router's `useLocation` and `useRouter` so tour route changes stay client-side. ```tsx title="src/sunboard-provider.tsx" export function SunboardRuntime({ children }: { children: ReactNode }) { const router = useRouter(); const location = useLocation(); const route = `${location.pathname}${location.searchStr}`; return ( router.history.push(url)} > {children} ); } ``` `useLocation` / `useRouter` only work inside the router, so the wrapper must live below your root route — mounting it in `__root.tsx` (next step) satisfies this. ### Mount it in your root route Import the Sunboard stylesheet **before** your own global CSS, then wrap the `{children}` your root document renders (inside ``). ```tsx title="src/routes/__root.tsx" export const Route = createRootRoute({ head: () => ({ links: [{ rel: "stylesheet", href: appCss }], }), component: RootComponent, }); function RootComponent() { return ( ); } function RootDocument({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` The exact shape of `__root.tsx` varies by TanStack Start version. The only thing that matters is that `` wraps the `{children}` / `` your root renders. Keep your existing imports and head config. ### Wire your real user The wrapper ships a placeholder `user`. Replace it with your authenticated user so segments and analytics are accurate. Only `id` is required; anything you put under `properties` is available for [segment](/docs/skills/segment) targeting: ```tsx user={{ id: currentUser.id, email: currentUser.email, group: currentUser.plan, // optional cohort, e.g. "trial" / "pro" properties: { // Any custom attributes you want to target on. role: currentUser.role, signupDate: currentUser.createdAt, workflowsCreated: currentUser.workflowCount, }, }} ``` ## Verify Run your dev server. It uses the `pk_test_` key, so once you've deployed a spec to the sandbox the checklist appears. Deploy one with [`/sunboard.deploy`](/docs/skills/deploy), or run `sunboard doctor` to check the wiring. ## Next steps --- # Vite URL: https://www.sunboardhq.com/docs/manual-installation/vite Description: Wire the Sunboard React SDK into a React + Vite app by hand. `sunboard init` does all of this automatically — see the [Quick start](/docs/quickstart). Follow this page if you'd rather wire things by hand, or want to understand exactly what `init` changes. This wires the Sunboard runtime into a **React + Vite** app: install the SDK, mount a provider at your app entry, and point it at your project with a publishable key. ### Install the SDK ```bash npm install @sunboard/react ``` ```bash pnpm add @sunboard/react ``` ```bash yarn add @sunboard/react ``` ```bash bun add @sunboard/react ``` ### Add your publishable key Vite exposes env vars prefixed with `VITE_` to the browser. Put your project's runtime key in `.env.local`: ```bash title=".env.local" VITE_SUNBOARD_KEY=pk_live_xxx ``` ### Create the runtime wrapper This component reads the current location and renders the Sunboard widgets. Create it under `src/`. ```tsx title="src/sunboard-provider.tsx" export function SunboardRuntime({ children }: { children: ReactNode }) { const route = typeof window === "undefined" ? "/" : `${window.location.pathname}${window.location.search}`; return ( window.location.assign(url)} > {children} ); } ``` The default `navigate` does a full-page load. If you use a router (React Router, etc.), swap it for your router's navigation so tour route changes stay client-side. ### Mount it at your app entry Import the Sunboard stylesheet **before** your own global CSS, then wrap your root element. ```tsx title="src/main.tsx" createRoot(document.getElementById("root")!).render( , ); ``` ### Wire your real user The wrapper ships a placeholder `user`. Replace it with your authenticated user so segments and analytics are accurate. Only `id` is required; anything you put under `properties` is available for [segment](/docs/skills/segment) targeting: ```tsx user={{ id: currentUser.id, email: currentUser.email, group: currentUser.plan, // optional cohort, e.g. "trial" / "pro" properties: { // Any custom attributes you want to target on. role: currentUser.role, signupDate: currentUser.createdAt, workflowsCreated: currentUser.workflowCount, }, }} ``` ## Verify Run your dev server. It uses the `pk_test_` key, so once you've deployed a spec to the sandbox the checklist appears. Deploy one with [`/sunboard.deploy`](/docs/skills/deploy), or run `sunboard doctor` to check the wiring. ## Next steps --- # Architecture URL: https://www.sunboardhq.com/docs/architecture Description: What runs inside your app and what Sunboard handles — the line between local and hosted. Sunboard splits cleanly into two halves: the parts that run **inside your app**, and the parts **Sunboard hosts for you**. Knowing where that line falls makes the rest of the docs click. ## What runs where in your users' browsers"] code["Your selectors
and track() calls"] specs["Onboarding specs
(authored by your agent)"] end subgraph hosted["Sunboard · hosted"] direction TB runtime["Runtime
serves the live experience"] control["Dashboard & CLI
author · promote · target · analyze"] end sdk <-->|"publishable key"| runtime specs -.->|"sunboard deploy"| control control -->|"promoted version
+ segment rules"| runtime `} /> ### Your side (local) Everything here lives in your codebase and runs on your infrastructure. Sunboard never sees your source code. - **The Sunboard SDK** (`@sunboard/react`) runs in your users' browsers, inside your app. - **Your onboarding specs** are authored by your coding agent and deployed to Sunboard. They aren't committed to your repo — the server is the source of truth and versions every deploy. - **Your selectors and `track()` calls** live in your components. - **Your user identity** — you tell the SDK who the current user is (`id` + any `properties`). That data flows from your app, not from a Sunboard cookie or tracker. ### Sunboard's side (hosted) - **Stores** your deployed onboarding versions, segment rules, and the `onboarding.*` attributes collected from [surveys](/docs/surveys). - **Decides and serves** which experience a given user should see — and nothing goes live until you promote it. - **Collects** the activation events your app reports and turns them into analytics. - **Hosts the dashboard** (and backs the CLI) where you author, promote, and target. ## How a user session works At runtime the SDK makes two kinds of calls to Sunboard, both with your **publishable key**: >A: opens a page A->>S: which experience for this user + route? S-->>A: the active survey / checklist / tour (or nothing) A-->>U: renders the onboarding U->>A: completes an action A->>S: reports the event Note over S: marks the step complete
and updates analytics `} /> That's the whole runtime loop: **ask what to show**, then **report what happened**. The SDK never decides what's live on its own — it always reflects what's currently live in the environment its key points to. ## Two keys, two jobs Sunboard uses two separate credentials, and keeping them straight is the key to the whole model: `sunboard deploy` activates a version in the **sandbox**, never production. Production changes only through `sunboard promote`, a separate step you approve. A leaked publishable key can read that environment's onboarding and report events, but it can't publish or alter what your users see. See [Environments](/docs/environments) for the full workflow. ## Next steps --- # Environments URL: https://www.sunboardhq.com/docs/environments Description: How Sunboard separates a sandbox from production, and the deploy → test → promote workflow that ships onboarding safely. Every Sunboard project has two environments: **Production** and **Sandbox**. They are fully separate worlds. Each has its own experiences, segments, surveys, users, and analytics, so anything you do in the sandbox is invisible to your live users until you choose to ship it. You author and test in the sandbox, then **promote** the tested version to production. Your AI agent works only in the sandbox; production is reached through a deliberate promote that you approve. ## The key is the environment Each environment has its own browser-safe publishable key, and the key itself selects the environment: `sunboard init` writes the **test** key to your local `.env.local`, so your dev app runs against the sandbox out of the box. The **live** key goes wherever you deploy: ```bash title=".env.local" NEXT_PUBLIC_SUNBOARD_KEY=pk_test_xxx ``` Written for you by `sunboard init`. Your dev app reads and writes the sandbox. ```bash title="Vercel / Netlify / your host's env vars" NEXT_PUBLIC_SUNBOARD_KEY=pk_live_xxx ``` Paste the live key into your deployment's environment variables. Your shipped app reads and writes production. There is nothing to configure in your code: the same provider runs in both environments, and the key it's given decides which world it sees. ## The workflow ### Author and deploy to the sandbox Your agent (or you) authors the onboarding and runs `sunboard deploy`. Deploy activates the new version in the **sandbox**, where it's immediately live for testing. ### Test it on the sandbox key Run your app locally. Because `.env.local` holds the `pk_test_` key, you see the real loop with real persistence, segmentation, and routing — survey answers write attributes, segments route you, and experiences render. Reset the sandbox's test users anytime to run it again: ```bash sunboard env clear --environment development ``` ### Promote to production When it looks right, ship the tested version: ```bash sunboard promote # one experience (carries its bound segments) sunboard promote --survey # the onboarding survey ``` Promote copies the exact version you tested into production (verified by content hash) and shows a diff before it writes. Production analytics are untouched, and you can roll back by promoting an earlier version. There is no "promote everything" button. You promote one experience (or the survey) at a time, each with its own diff, so the sandbox and production can intentionally differ while you iterate on one thing. Promoting an experience also carries the rules of the segment(s) it's bound to, and warns you if a shared segment will re-target another live experience. ## Editing something that's already live To change an experience that's already in production, mirror production into the sandbox first, edit there, then promote back: ```bash sunboard env clone --from production --to development ``` This clears the sandbox's existing users, runtime data, and hosted content, then copies production's live experiences, bindings, segment rules, and survey. You start from exactly what users see, without copying production users or analytics. Cloning only goes into the sandbox — the path into production is always `promote`. ## In the dashboard The dashboard has a **Sandbox / Production** toggle that scopes everything you see — the flow overview, experiences, segments, the publishable key, and analytics — to one environment at a time. From the sandbox view, each experience and the survey has a **Promote to production** button, the same diff-and-confirm step as the CLI, for when you'd rather ship by hand than have the agent do it. ## Command reference | Command | What it does | | --- | --- | | `sunboard deploy` | Upload a version and activate it in the sandbox. | | `sunboard promote ` | Copy the tested experience to production (diff + confirm). | | `sunboard promote --survey` | Promote the active onboarding survey to production. | | `sunboard env clone --from production --to development` | Destructively rebuild the sandbox from production's live content; production users and analytics are not copied. | | `sunboard env clear --environment development` | Delete the sandbox's test users and runtime data (content kept). | | `sunboard pull [--environment ]` | Print an environment's live experience spec as YAML to stdout (for the agent to edit). | --- # Segmentation URL: https://www.sunboardhq.com/docs/segmentation Description: How Sunboard decides which users see an experience — segments, rules, and routing. A **segment** is a named rule that decides *who* sees an experience. Segments live in Sunboard's control plane — not in your specs — and at runtime Sunboard matches each user against them to choose what to show. ## What a segment matches on Every rule runs against the **user you pass to the SDK**, plus the events that user has completed: - `id`, `email`, `group` — the top-level fields on the `user` object. - Any custom attribute under `user.properties` — `plan`, `role`, `signupDate`, whatever you provide. - Survey-collected attributes under `user.onboarding.*` — what an onboarding [survey](/docs/surveys) gathered (e.g. `onboarding.useCase`). Sunboard owns these; you don't pass them on the user object. - Completed events — anything you've reported with `useSunboard().track()`. Segments can only match on what you tell the SDK. Pass an accurate `user` with the `properties` you want to target on — see [Manual installation](/docs/manual-installation/nextjs) for the shape. ## Rule types A segment's rules are made of two kinds of clause. ### Property rules Match on a user property with one of three operators — `equals` (exact), `in` (the value is one of a list), or `contains` (an array attribute includes the value, e.g. a multi-select survey answer): ```json { "property": "plan", "equals": "trial" } { "property": "onboarding.useCase", "in": ["marketing", "sales"] } { "property": "onboarding.goals", "contains": "automation" } ``` `property` can be `id`, `email`, `group`, any key under `properties`, or a dotted `onboarding.*` path for a [survey](/docs/surveys)-collected attribute. Values are strings, numbers, booleans, or — for `in` — a list. ### Event rules Match on whether the user has completed a tracked event: ```json { "event": "workflow.created", "completed": false } ``` `completed: false` matches users who **haven't** fired the event yet (e.g. "hasn't activated"); `completed: true` matches users who **have** (e.g. "graduate to the next phase once they've finished"). ## Combining rules: all & any Rules are grouped into `all` (every rule must match — AND) and `any` (at least one must match — OR): ```json { "all": [ { "property": "plan", "equals": "trial" }, { "event": "workflow.created", "completed": false } ] } ``` That targets trial users who haven't created a workflow yet. - `all` → **AND** (every rule matches). - `any` → **OR** (at least one matches). - Both present → `(all of all)` **and** `(any of any)`. - Empty rules (`{}`) → matches **everyone** — useful as an "all users" catch-all. ## How a user's experience is chosen Segments only do something once **routing** binds them. A deployment is the tuple **experience version × segment × environment**. When a user loads your app: id · group · properties · completed events"] U --> M{"Evaluate every bound
segment for this experience"} M -->|"matches a 2-rule segment"| A["candidate: v5"] M -->|"matches the catch-all (0 rules)"| B["candidate: v3"] A --> W["Most specific wins
→ serve v5"] B --> W `} /> 1. The SDK sends the current `user` (and environment) to Sunboard. 2. Sunboard evaluates every active deployment's segment against that user. 3. For each experience, the **most specific** matching segment wins — the one with the **most rules**. A 2-rule "trial + admin" segment beats a 0-rule "everyone" segment. 4. Ties break to the **higher version number**. So you can layer a broad catch-all and a narrow override on the same experience: the narrow segment wins for the users it matches, and everyone else falls back to the catch-all. Deployments are per environment (defaulting to `production`), so the same experience can have different versions bound in staging and production. ## Targeting is strategy Good segments map to a real cohort — role, plan, lifecycle stage, activation status, account size, acquisition channel. Name your **exclusions** too (already-activated users, internal admins, anyone missing a required entitlement), and expect targeting to evolve: pre-activation → post-activation → expansion. Reuse one canonical segment per cohort instead of creating parallel ones, which keeps analytics readable. ## Managing segments You never edit segments in your experience specs — they're hosted. Create, update, and bind them by asking your agent for the [Segment skill](/docs/skills/segment), or run the [CLI](/docs/cli/hosted) commands directly. ## Related --- # Surveys & owned attributes URL: https://www.sunboardhq.com/docs/surveys Description: Collect a couple of facts at signup, store them as onboarding.* attributes, and route each new user into the right onboarding. Sometimes the right onboarding depends on something you don't know yet — what the user is here to do, how big their team is, which role they hold. A **survey** is a short questionnaire shown right after signup that collects exactly those facts, stores them as **owned attributes**, and lets your [segments](/docs/segmentation) route the user into the right experience — all without your app touching a database. ## How it works 1. A new user signs in. If an onboarding survey is active for the environment and they haven't answered it yet, it's shown first — it pre-empts every other experience until answered. 2. They answer. Each answer is written to `user.onboarding.` — a question keyed `useCase` becomes `user.onboarding.useCase`. 3. The session **re-bootstraps immediately**: Sunboard re-evaluates segments with the new attributes, and the matching experience renders in the same session — no reload, no navigation. ```text sign up → survey → onboarding.useCase = "marketing" → marketing checklist ``` ## Owned attributes The `onboarding.*` namespace is **owned by Sunboard**, not your app: - You never pass them on the `user` object — the survey populates them, and they survive every later bootstrap that sends your own `properties`. - [Segments](/docs/segmentation) match on them like any other property — `{ "property": "onboarding.useCase", "equals": "marketing" }`, plus `in` and `contains` for multi-select answers. - Copy personalizes on them with `{{user.onboarding.useCase}}` tokens, so the experience the survey routes into can speak back to the answer. Survey answers are great for routing and personalization, but they're self-reported — never gate entitlement, billing, or access on them. Trusted facts come from your own user properties. ## One survey per environment A survey isn't bound to a segment like an experience is. There's **one optional onboarding survey per environment**, activated with `sunboard survey set`. Gating is implicit: it shows whenever it's active and the user has no response yet. You never write a "show the survey" rule. ## Setting one up Ask your agent for [Onboarding](/docs/skills/onboarding) — it decides whether a survey earns its place (you don't need one if the routing signal is already on your `user` object) and authors it alongside the segments it feeds. To author one directly, use the [Survey](/docs/skills/survey) skill; to style it, see [UI](/docs/skills/ui). ## Related --- # Revenue & retention URL: https://www.sunboardhq.com/docs/revenue Description: Report conversions and churn from your backend so your onboarding analytics show real money — what a trial is worth and which path pays off. Onboarding analytics show you who **activated**. Connect revenue and you also see who **paid**: what a trial is worth, which onboarding path turns into paying customers, and whether those customers stick around. You report billing events from your backend — a conversion, a renewal, a refund, a cancellation — using the same user id you already pass the browser SDK. Sunboard attributes the money to that user's onboarding journey. It works with any billing setup. You call it from the code that already runs when a customer pays or cancels, so Stripe, Paddle, and your own invoicing all work the same way. ## Set it up ### Get a secret key In your project dashboard, open **Settings → Secret key** and generate one. It's shown once, so store it in a server-side environment variable such as `SUNBOARD_SECRET_KEY`. Keep it on your server — never ship it to the browser. ### Install the server SDK ```bash npm i @sunboard/node ``` Requires Node.js 18 or later. ### Report billing from your backend ```ts const sunboard = new Sunboard({ secretKey: process.env.SUNBOARD_SECRET_KEY! }); // A trial converts to paid. Amounts are in the smallest currency unit (cents). await sunboard.revenue({ userId: user.id, amount: 4900, currency: "usd", idempotencyKey: invoice.id, }); // A customer cancels or lapses. await sunboard.churn({ userId: user.id }); ``` Pass the **same `userId`** you give the browser SDK so revenue lands on the right onboarding journey. ## Billing events `revenue()` records money coming in; `churn()` records a customer leaving. The event type defaults to `conversion`: | Moment | Call | | --- | --- | | Trial converts to paid | `revenue({ ..., type: "conversion" })` (default) | | Subscription renews | `revenue({ ..., type: "renewal" })` | | Upgrade or add-on | `revenue({ ..., type: "expansion" })` | | Money refunded | `revenue({ ..., amount: -4900, type: "refund" })` | | Subscription cancels or lapses | `churn({ userId })` | Amounts are whole numbers in the smallest currency unit (cents for USD), with an ISO `currency`. Refunds use a negative amount. `churn()` takes no amount — it just marks the end of a customer's subscription. Pass an `idempotencyKey` (your invoice or charge id works well) and sending the same event twice is ignored instead of counted twice. You can also record product events from your backend with `sunboard.track({ userId, eventName })` — useful when the action that proves value happens on your server instead of in the browser. ## What you see Once billing is flowing, your project dashboard and [`sunboard analytics revenue`](/docs/cli/hosted) show: - **Trial → paid conversion**, overall and per onboarding path and segment - **What a trial is worth** on average - **Revenue per activated user** versus everyone else - **Time to paid** — how long conversion takes - **Retention and churn** by onboarding path - **Net revenue** (refunds included) and **lifetime value per customer** The [`/sunboard.analyze`](/docs/skills/analyze) skill reads these too, so it can prioritize fixes by revenue, not just by where users drop off. Revenue tells the agent which drop-off is worth the most to fix; the [PostHog integration](/docs/integrations/posthog) tells it why users dropped there. Together a checkup can say "this step loses your highest-value cohort, and here's the replay of it happening." --- # Styling URL: https://www.sunboardhq.com/docs/styling Description: Restyle Sunboard's checklist, tours, and hotspots to match your product by overriding CSS variables. Sunboard ships a complete default theme in `@sunboard/react/styles.css`, so the checklist, tours, and hotspots look polished the moment you install. You restyle them by overriding **CSS custom properties** — there's no config file and no component theming API to learn. The defaults are production-ready. Override variables only where you want Sunboard to match your brand. `sunboard init` appends a starter block of these variables to your global stylesheet when it can find one — if it didn't (every project structures CSS differently), just add the overrides yourself as shown below. ## How it works Every Sunboard surface reads its colors, font, and radius from `--sunboard-*` variables defined on `:root`. To customize, declare the variables you want to change in a stylesheet that loads **after** `@sunboard/react/styles.css`, so your values win the cascade. `sunboard init` imports the Sunboard stylesheet first for exactly this reason — your global CSS comes after it. ```css title="your global stylesheet" :root { /* Shared across all Sunboard UI */ --sunboard-font: "Inter", system-ui, sans-serif; --sunboard-radius: 12px; /* Checklist card + buttons */ --sunboard-checklist-background: white; --sunboard-checklist-foreground: #111827; --sunboard-checklist-border: #e5e7eb; --sunboard-checklist-primary: #2563eb; --sunboard-checklist-primary-foreground: white; --sunboard-checklist-success: #16a34a; } ``` That's the common case — most teams only change the font, the corner radius, and the primary color to match their product. If your app already defines design tokens (shadcn variables, or your own), map Sunboard onto them instead of repeating color values. Dark mode then follows automatically whenever your tokens flip: ```css title="your global stylesheet" :root { --sunboard-checklist-background: var(--card); --sunboard-checklist-foreground: var(--card-foreground); --sunboard-checklist-border: var(--border); --sunboard-checklist-primary: var(--primary); --sunboard-checklist-primary-foreground: var(--primary-foreground); /* same pattern for --sunboard-tour-* and --sunboard-hotspot-* */ } ``` If your font variables live on `` (the default Next.js layout puts `next/font` variables there), `var(--font-...)` resolves to nothing inside a `:root` block. Declare your `--sunboard-*` overrides on `body` instead; color tokens still follow a theme class on ``. ## Full variable reference All variables are optional; any you don't set fall back to the shipped default. ### Shared | Variable | Purpose | | --- | --- | | `--sunboard-font` | Font family for all Sunboard UI | | `--sunboard-radius` | Corner radius for cards, buttons, and tooltips | ### Checklist | Variable | Purpose | | --- | --- | | `--sunboard-checklist-background` | Card surface | | `--sunboard-checklist-foreground` | Card text | | `--sunboard-checklist-border` | Card border | | `--sunboard-checklist-shadow` | Card shadow | | `--sunboard-checklist-muted` | Progress track / count pill background | | `--sunboard-checklist-muted-foreground` | Secondary text | | `--sunboard-checklist-primary` | Action buttons + progress fill | | `--sunboard-checklist-primary-foreground` | Text on primary | | `--sunboard-checklist-success` | Completed step indicator | ### Launcher The collapsed checklist button. These default to the checklist's primary colors, so you usually don't need to set them separately. | Variable | Purpose | | --- | --- | | `--sunboard-launcher-background` | Launcher button background | | `--sunboard-launcher-foreground` | Launcher icon | | `--sunboard-launcher-shadow` | Launcher shadow | | `--sunboard-launcher-badge-background` | Unfinished-count badge | | `--sunboard-launcher-badge-foreground` | Badge text | ### Tours Standalone tooltips share this palette — theming the tour restyles both at once. | Variable | Purpose | | --- | --- | | `--sunboard-tour-background` | Tooltip surface | | `--sunboard-tour-foreground` | Tooltip text | | `--sunboard-tour-border` | Tooltip border | | `--sunboard-tour-shadow` | Tooltip shadow | | `--sunboard-tour-muted-foreground` | Tooltip secondary text | | `--sunboard-tour-primary` | Primary (next) button | | `--sunboard-tour-primary-foreground` | Text on primary button | | `--sunboard-tour-secondary-background` | Secondary (back) button | | `--sunboard-tour-secondary-foreground` | Secondary button text | | `--sunboard-tour-secondary-border` | Secondary button border | | `--sunboard-overlay-color` | Dimmed page overlay behind an active tour | ### Hotspots | Variable | Purpose | | --- | --- | | `--sunboard-hotspot-background` | Launcher dot | | `--sunboard-hotspot-border` | Dot border | | `--sunboard-hotspot-dot` | Inner dot color | | `--sunboard-hotspot-shadow` | Dot shadow | | `--sunboard-hotspot-pulse-ring` | Pulse ring color | ### Survey The built-in survey modal's card and inputs read the checklist palette, so it follows your checklist overrides. The only survey-specific variable is the scrim behind it. | Variable | Purpose | | --- | --- | | `--sunboard-survey-overlay-color` | Scrim behind the centered survey modal | ## Dark mode Because everything is a CSS variable, dark mode is just another set of values under your dark selector — no Sunboard-specific switch: ```css .dark, [data-theme="dark"] { --sunboard-checklist-background: #0b0f19; --sunboard-checklist-foreground: #e5e7eb; --sunboard-checklist-border: #1f2937; --sunboard-tour-background: #0b0f19; --sunboard-tour-foreground: #e5e7eb; } ``` ## Every variable Here's the complete set with its default values — the block `sunboard init` appends to your global stylesheet when it can find one. Paste it into a stylesheet that loads after `@sunboard/react/styles.css` and edit any value you like. Keep the `/* Sunboard theme variables */` comment: rerunning `sunboard init` looks for it to know the block is already there. ```css title="your global stylesheet" /* Sunboard theme variables */ :root { /* Shared typography and corner radius for all Sunboard UI. */ --sunboard-font: Inter, sans-serif; --sunboard-radius: 12px; /* Checklist card surface. */ --sunboard-checklist-background: white; --sunboard-checklist-foreground: #111827; --sunboard-checklist-border: #e5e7eb; --sunboard-checklist-shadow: 0 4px 12px rgb(0 0 0 / 10%); /* Checklist secondary text, progress track, and small count pills. */ --sunboard-checklist-muted: #f3f4f6; --sunboard-checklist-muted-foreground: #6b7280; /* Checklist action buttons and progress fill. */ --sunboard-checklist-primary: #2563eb; --sunboard-checklist-primary-foreground: white; /* Completed checklist step indicator. */ --sunboard-checklist-success: #16a34a; /* Collapsed checklist launcher button and its unfinished-count badge. */ --sunboard-launcher-background: var(--sunboard-checklist-primary); --sunboard-launcher-foreground: var(--sunboard-checklist-primary-foreground); --sunboard-launcher-shadow: var(--sunboard-checklist-shadow); --sunboard-launcher-badge-background: #ef4444; --sunboard-launcher-badge-foreground: white; /* Product tour tooltip surface. */ --sunboard-tour-background: white; --sunboard-tour-foreground: #111827; --sunboard-tour-border: #e5e7eb; --sunboard-tour-shadow: 0 4px 12px rgb(0 0 0 / 10%); --sunboard-tour-muted-foreground: #6b7280; /* Product tour primary and secondary buttons. */ --sunboard-tour-primary: #2563eb; --sunboard-tour-primary-foreground: white; --sunboard-tour-secondary-background: white; --sunboard-tour-secondary-foreground: #111827; --sunboard-tour-secondary-border: #e5e7eb; /* Dimmed page overlay shown behind an active tour. */ --sunboard-overlay-color: rgb(15 23 42 / 18%); /* Stronger scrim behind the centered onboarding survey. */ --sunboard-survey-overlay-color: rgb(15 23 42 / 45%); /* Hotspot launcher dot and pulse ring. */ --sunboard-hotspot-background: #2563eb; --sunboard-hotspot-border: white; --sunboard-hotspot-dot: white; --sunboard-hotspot-shadow: 0 4px 12px rgb(0 0 0 / 10%); --sunboard-hotspot-pulse-ring: rgb(37 99 235 / 28%); } ``` ## Next steps --- # Analytics integrations URL: https://www.sunboardhq.com/docs/integrations Description: Connect the product analytics you already use and onboarding checkups diagnose from evidence — what dropped users did instead, not just where they dropped. Sunboard's funnel tells you **where** users drop out of onboarding. Your product analytics tool knows **what they did instead**. Connect the two and every checkup ([`/sunboard.analyze`](/docs/skills/analyze), the experience dashboard, `sunboard analytics experience --enrich`) is grounded in evidence: the pages dropped users left for, the buttons they rage-clicked, the errors they hit, the traffic source they came from, and session replays of real users failing the step. Because your agent drives Sunboard, it isn't limited to Sunboard's own data. An integration is how you hand it the rest of the picture. ## How every integration works The same rules apply to any provider we connect to: - **Explicit opt-in.** Nothing is ever queried until a workspace owner or admin connects the provider with a read-only API key, per [environment](/docs/environments). Connect production; that is where real users drop off. - **Read-only, on demand.** Sunboard asks targeted questions about the exact users who dropped at a funnel edge, at diagnosis time. It never mirrors or stores your analytics data; only short-lived aggregates. - **Write-only credentials.** The API key is verified against the provider before it is saved, encrypted at rest, used only server-side, and never displayed again. - **Strictly additive.** No connection means Sunboard behaves exactly as it does today; a provider outage degrades to a note in the output, never an error. Connections are managed in **Settings → Integrations** in your project dashboard, or with `sunboard integrations` in the terminal. ## Providers Amplitude and Mixpanel are on the roadmap; the integration layer is built provider-plural. If you want one of them (or something else), [tell us](mailto:sean@sunboardhq.com) and we'll prioritize it. --- # PostHog integration URL: https://www.sunboardhq.com/docs/integrations/posthog Description: Connect your PostHog and onboarding checkups show what dropped users did instead of finishing, with rage clicks, detours, and session replays as evidence. Your onboarding funnel tells you **where** users drop off. Your PostHog knows **what they did instead**: where they navigated, what they rage-clicked, which errors they hit, and it has the session replays to prove it. Connect the two and every checkup diagnoses from evidence instead of guessing. The integration is strictly additive. Without a connection, nothing changes; with one, `sunboard analytics experience --enrich`, the experience dashboard, and the [`/sunboard.analyze`](/docs/skills/analyze) skill gain an enrichment block anchored on your biggest drop-off: - **Events after the drop**: which product actions dropped users took next - **Where they went**: the pages they left for (a detour to `/settings/billing` tells you the step asked too much, too early) - **Frustration signals**: rage clicks and exceptions near the failing step, including the element they rage-clicked - **Acquisition skew**: whether the drop concentrates in one traffic source (a targeting problem, not a step problem) - **Session replays**: direct links to watch real users fail the step, in your own PostHog - **Replay timelines**: `sunboard analytics replays` turns dropped users' recordings into text timelines (clicks, rage clicks, navigations, console errors, idle time) that your agent reads directly, so it can see what went wrong without a human watching the replay ## Connect PostHog ### Create a personal API key In PostHog, open **Settings → Personal API keys** and create a key scoped to the one project your app sends events to, with read access on **Query**, **Session Recording**, **Person**, and **Error Tracking**. Sunboard only ever reads. A key with only query read access still works; it just cannot read replay timelines. Re-verify after widening a key's scopes and the extra capability switches on by itself. ### Connect it to your project In your Sunboard project dashboard, open **Settings → Integrations → Connect PostHog**: pick the environment (production is almost always the one you want), your PostHog region, the PostHog project id (the number in your PostHog URL), and paste the key. Or stay in the terminal: ```bash sunboard integrations connect posthog ``` The CLI walks through the same choices and reads the key from a hidden prompt (or `SUNBOARD_POSTHOG_KEY`, or `--key-stdin` for scripts). It is never accepted as a flag, so it never lands in your shell history. Either way, the key is verified against PostHog before anything is saved, encrypted at rest, and never shown again. Disconnect any time from the same places. ### Run a checkup ```bash sunboard analytics experience --enrich ``` Or just ask your agent how onboarding is doing: the [`/sunboard.analyze`](/docs/skills/analyze) skill pulls enrichment automatically once a connection exists. The experience page in the dashboard grows a "What dropped users did instead" panel too. ## How users are matched Sunboard and PostHog both identify users with your own user id, so for a standard `posthog.identify(user.id)` setup the join works out of the box. To make it exact regardless of your identify conventions, `@sunboard/react` also notices when PostHog is on the page and records the current `distinct_id` alongside the Sunboard user. This is a pseudonymous pointer your page already holds; Sunboard never queries PostHog with it until you connect. You can override it or turn it off: ```tsx // Override detection with an explicit id // Or disable capture entirely ``` Every enrichment result reports a **join rate**, the share of dropped users actually found in your PostHog, so you always know how much of the story the data covers. ## Data handling - Your API key is encrypted at rest and used only server-side. It is never sent to a browser, the CLI, or an agent; after connecting, only a hint like `phx_...k3Jd` is ever displayed. - Sunboard stores none of your PostHog data. Queries run on demand when you ask for enrichment, return aggregates only, and results are cached in memory for a few minutes to stay well inside PostHog's rate limits. - Replay links open in your own PostHog, behind your own PostHog login. - Replay timelines are built by fetching recording snapshots, transforming them in memory to behavioral text ("clicked the invite button", "console error"), and discarding the raw data. Typed input values are never read: a timeline says a user typed into a field, never what they typed. Connections are per environment, like everything else in Sunboard. Connect production: that is where real users drop off. Checkups read production data even when your agent is working in the sandbox. Want another provider? See [Analytics integrations](/docs/integrations) for what's available and what's planned. --- # Skills URL: https://www.sunboardhq.com/docs/skills Description: The agent commands that drive Sunboard — and how they're installed for Claude Code, Codex, and opencode. Skills are the first-party commands your AI coding agent uses to drive Sunboard. In practice you use just two: **`/sunboard.onboarding`** to set onboarding up or change it, and **`/sunboard.analyze`** to improve it from analytics. Each one plans the work with you, then runs the rest of the skills — spec, survey, segment, wiring, deploy — for you. You rarely run `sunboard`, or the other skills, directly. The agent always works in the [sandbox](/docs/environments): it deploys and tests there, and ships to production only when you approve a promote. ## How skills are installed `sunboard init` installs the skills for whichever AI coding harnesses you select — pick one or several. Each harness has its own file format and location, so Sunboard writes the matching files for each: | Harness | Installed to | How you invoke it | | --- | --- | --- | | **Claude Code** | `.claude/commands/sunboard..md` | `/sunboard.` slash command | | **opencode** | `.opencode/commands/sunboard..md` | `/sunboard.` slash command | | **Codex** | `.agents/skills/sunboard-/SKILL.md` | picked up as an agent skill (`sunboard-`) | These files live in your repo, so they're versioned with your code and shared with your team — anyone who clones the project gets the same skills. Run `sunboard skills update` to refresh the installed skill files for your harnesses to the latest version. ## Start here The two skills you actually invoke. Each one plans with you and drives the building blocks below. ## Building blocks The steps `Onboarding` and `Analyze` run on your behalf. You can invoke them directly when you want to do just one thing, but you usually won't need to. ## Styling Optional — `Onboarding` offers this at the end of its flow, or run it anytime. --- # Onboarding URL: https://www.sunboardhq.com/docs/skills/onboarding Description: Set up onboarding end-to-end — the one skill that plans the whole flow and drives the rest. `/sunboard.onboarding` is where you start. You describe what you want ("help me set up onboarding"); it runs a short conversation, agrees on a plan with you, then **drives the whole build itself** — the survey, one experience per cohort, the segments that route them, the selector/event wiring, and the deploy to the sandbox. You answer questions and approve the plan; you don't run the other skills by hand. ## When to use it - Setting up onboarding from scratch. - Making a structural change — adding a cohort, introducing a survey, or reshaping the whole flow. - Any time you'd otherwise have to figure out which skills to run in what order. ## What happens 1. **Studies your product and hosted state** — routes, how you load the user (what's already on the `user` object), and what's already deployed. 2. **Finds the aha moment** — the first measurable "this worked" event the whole flow should drive toward. 3. **Decides one path or many** — does the route to value differ by who the user is? If not, one catch-all experience. If so, it names the cohorts (e.g. enterprise vs. self-serve) and what's different about each. 4. **Decides how it knows the cohort** — a property your app already passes (`plan`, `role`) → segment on it; something you don't know at signup → a survey question collects it. 5. **Designs the path per cohort** — a short checklist toward the aha, plus a tour only where a screen needs guiding. 6. **Shows you the plan and waits for approval** — the activation event, any survey, each experience, the segments, the wiring needed. Nothing is written or deployed until you say yes. 7. **Carries it out** — runs [Survey](/docs/skills/survey), [Spec](/docs/skills/spec) (once per cohort, tours included), [Implement](/docs/skills/implement), [Segment](/docs/skills/segment), and [Deploy](/docs/skills/deploy) for you, in order, into the [sandbox](/docs/environments) where you test it. When you're happy, it promotes the tested versions to production. 8. **Offers to style it** — [UI](/docs/skills/ui) rebuilds the checklist and survey to match your design system. You only run `/sunboard.onboarding`. It plans the whole onboarding system with you and drives the underlying skills — so you never have to know which of Spec, Survey, Segment, Implement, or Deploy to reach for. To improve onboarding that already exists, use [Analyze](/docs/skills/analyze) instead. ## Related --- # Analyze URL: https://www.sunboardhq.com/docs/skills/analyze Description: Use Sunboard analytics to diagnose and plan onboarding improvements. `/sunboard.analyze` is the front door for improving onboarding that already exists. It grounds every "why aren't users activating?" question in real data, agrees on a plan with you, then **drives the fix itself** — editing the spec, re-wiring, re-targeting, and shipping a new version through the underlying skills. It's the improve-existing counterpart to [Onboarding](/docs/skills/onboarding). ## When to use it - An experience is live and you want to know where users drop off. - After shipping a fix, to confirm the funnel actually moved. - You want to prioritize by money — which onboarding path is worth the most, and whether activation is converting to revenue. ## What happens 1. **Finds the experience** (or reads `sunboard state` to pick one). 2. **Fetches analytics** — `sunboard analytics experience --json`, defaulting to a 30-day window. 3. **Reads the diagnostics first** — `noViews` (nothing wired or bound), `missingGoal` (no activation event), `largestDropoff` (the primary funnel signal), `noTourStarts` (launcher problem). 4. **Pulls enrichment when PostHog is connected** — reruns with `--enrich` to see what dropped users did instead: rage clicks and exceptions point at wiring, detours to other pages point at the spec, acquisition skew points at targeting, and session replay links go straight into the plan. See the [PostHog integration](/docs/integrations/posthog). 5. **Cross-references the spec** to form a hypothesis — wrong route/selector, an event firing from the wrong path, a step demanding too much before value, tour copy, or the wrong audience. 6. **Slices when needed** — by version, segment, environment, or origin. When the question is about money, it also pulls [`sunboard analytics revenue`](/docs/revenue) to rank fixes by conversion and revenue, not just funnel shape. 7. **Shows you a plan and waits for approval** — naming the fault, the numbers, the proposed change (cheapest plausible fix first — a copy tweak before a resegmentation), and which skill it'll use to make it. 8. **Drives the fix** — once you approve, it runs [Spec](/docs/skills/spec) (checklist and tour changes), [Implement](/docs/skills/implement), or [Segment](/docs/skills/segment), then deploys a **new** version to the [sandbox](/docs/environments) to test and promotes it to production (the old production version stays for rollback). 9. **Closes the loop** — tells you which metrics to watch, then compares versions on the next run to confirm the funnel moved. Analyze reads analytics, gets your sign-off on a plan, then makes the change for you — nothing live is touched before you approve. You don't get handed a to-do list of commands to run yourself. ## Related --- # Spec URL: https://www.sunboardhq.com/docs/skills/spec Description: Plan and author a Sunboard onboarding spec. `/sunboard.spec` authors or revises the spec for **one** onboarding experience — the checklist plus any tours, hotspots, and tooltips; there's one experience per cohort. It's usually run by [Onboarding](/docs/skills/onboarding) (or [Analyze](/docs/skills/analyze)), which decide the activation goal, the cohorts, and whether a survey is needed; this skill turns that plan into a valid spec. ## When to use it - As a step of `/sunboard.onboarding` — the usual path. - Directly, when you want to revise a single experience's steps or copy without re-planning the whole flow. - A screen needs a short guided tour, or [Analyze](/docs/skills/analyze) shows a tour with low starts or completions — tours live in the spec, so they're authored and refined here. ## What happens 1. **Studies your product** — README, routes, entry points, and existing experiences. 2. **Reads hosted state** with `sunboard state` so it reuses experience keys and segments instead of inventing parallel ones. To revise a live experience, it reads the current spec with `sunboard pull `. 3. **Takes the activation goal and cohort as inputs** — value, not configuration (e.g. `workflow.created`, not `profile.completed`). It doesn't run a fresh strategy interview; that's Onboarding's job. 4. **Drafts the experience, then composes the spec** in context (Sunboard is fileless — nothing is written to your repo): usually an ordered checklist (3–5 steps) with optional tours, personalized copy, and a completion screen. The checklist is optional, so an experience can be tours alone, for in-app guidance that starts from the UI instead of a checklist. Tours stay quiet by default: they launch from checklist items, hotspots, or your own UI (never auto-start), stay short (two to five steps), and once started auto-continue through route changes, async rendering, and modals. 5. **Validates** it with `sunboard validate` (piping the spec on stdin) and flags any assumptions to confirm. ## What you get A validated spec ready to deploy, plus a short customer-success plan: the activation goal, the suggested segment and exclusions, the `data-sunboard-id` selectors and `track()` calls that still need wiring, and the analytics to watch. `/sunboard.spec` authors and validates the spec — it never ships anything. Wiring happens in [Implement](/docs/skills/implement), shipping in [Deploy](/docs/skills/deploy). Targeting (who sees it) lives in [Segment](/docs/skills/segment), not in the spec. ## Related --- # Survey URL: https://www.sunboardhq.com/docs/skills/survey Description: Author and activate the onboarding survey that routes new users into a segment. `/sunboard.survey` authors the short questionnaire shown right after signup. Each answer becomes an **owned attribute** under `user.onboarding.*` — which your segments route on and your copy personalizes with `{{user.onboarding.*}}`. The survey is the *instrument* that collects what your product doesn't already know; the rest of onboarding routes off those answers. It's usually run as a step of [Onboarding](/docs/skills/onboarding), which decides whether a survey is needed and which answers route which cohort. ## When to use it - New users need different onboarding, and what distinguishes them **isn't already on the `user` object** you pass the SDK. - You want to personalize the first experience on something the user tells you. ## What happens 1. **Inspects your product and hosted state** — and whether an onboarding survey is already active for the environment (`sunboard survey show`). 2. **Confirms a survey earns its place** — if the routing signal is already a host property (`plan`, `role`), it says so and recommends segmenting on that instead. A survey is for what the product can't already tell you. 3. **Designs 2–4 questions that route** — each one mapping to a real cohort decision. Questions that wouldn't change what the user sees next are cut. 4. **Composes the `type: survey` spec** in context (Sunboard is fileless), with personalized copy and an `exampleUser.onboarding` block so `sunboard validate` can catch token typos. 5. **Validates, deploys, and activates** — `sunboard survey set` makes it the environment's onboarding survey. There's no "show the survey" segment to create; gating is implicit (active survey + no response yet). 6. **Hands downstream routing to [Segment](/docs/skills/segment)** — proposing segments over the collected `onboarding.*` attributes. ## Question types | Type | Stored as | Segment with | | --- | --- | --- | | `single-select` | string | `equals` / `in` | | `multi-select` | array | `contains` | | `text` | string | rarely — store, don't route | Option **values** (not labels) are stored, so you can reword copy without breaking segments. Survey answers are great for routing and personalization, but they're self-reported — never gate entitlement, billing, or access on them. Trusted facts come from your own user properties. ## Related --- # Implement URL: https://www.sunboardhq.com/docs/skills/implement Description: Wire selectors and track() calls so a Sunboard spec passes validation. `/sunboard.implement` makes an existing spec executable against your real app. It reads the spec, then patches your code so every `target` selector and `completeWhen.event` has a real counterpart. ## When to use it - After a spec is authored or revised, before you deploy it. - When [Analyze](/docs/skills/analyze) finds an event firing from the wrong place. ## What happens 1. **Reads the spec** and lists every selector and completion event. 2. **Checks hosted state** (`sunboard experiences get`) so it doesn't rename a `data-sunboard-id` that live versions still reference. 3. **Adds selectors** — a stable `data-sunboard-id` on the most specific element (the button, the input, the list item). 4. **Wires events** — `await useSunboard().track("")` in the success path of the real product action. 5. **Re-validates**, then deploys to the sandbox so you can verify route waits, modals, and hotspots in your app on the `pk_test_` key. ```ts // Preferred: fire from the success handler of the real action. const { track } = useSunboard(); await track("workflow.created", { workflowId: workflow.id }); // If the success signal is a prop/state change, fire from an effect and depend // on the stable `track` callback — never the whole useSunboard() value. useEffect(() => { track("workflow.created", { workflowId }); }, [track, workflowId]); ``` `track()` is safe by default — it never throws on a missing user, network error, or unready state. When you call it from `useEffect`, depend on the stable `track` callback (`[track, id]`), not the whole `useSunboard()` value: the provider recreates that object on every checklist update, so an effect depending on it re-runs and loops. Implement adds a selector or a `track()` call — it never changes your business logic. It wires events at **success**, not on click, and respects the server/client boundary (extracting a small client component rather than flipping a whole route to `"use client"`). It never deploys. ## Related --- # Segment URL: https://www.sunboardhq.com/docs/skills/segment Description: Plan, create, update, and bind Sunboard segments through the CLI. `/sunboard.segment` plans, creates, updates, or binds a segment. Segments decide **who** sees an experience — trial users, enterprise admins, a lifecycle stage, accounts above a size threshold. They live in the hosted control plane, not in YAML, so this skill never edits your experience specs. ## When to use it - Targeting an experience to a specific cohort. - Binding a deployed version live (create a segment, then attach it). - Pausing, resuming, or detaching an existing deployment. ## What happens 1. **Inspects hosted state** (`sunboard state`) — which segments, experiences, and bindings already exist. 2. **Takes the cohort as an input** — `/sunboard.onboarding` (or `/sunboard.analyze`) already decided who this targets. This skill expresses it as rules; it doesn't re-plan the targeting strategy. 3. **Reuses before creating** — one canonical segment per cohort, not parallel duplicates. 4. **Proposes rules in plain English, then JSON** — property rules (`{ property, equals }`, `{ property, in: [...] }`, `{ property, contains }`) and event rules (`{ event, completed }`), grouped by `all`/`any`. Properties can be host fields (`plan`, `role`) or survey-collected `onboarding.*` attributes. See [Segmentation](/docs/segmentation) for the full rule model. 5. **Dry-runs, then confirms** — every mutation is previewed and explicitly approved before it applies. 6. **Optionally binds** the segment to an experience version with `routing attach`, and prints the Sunboard URL. Targeting is hosted state — editing YAML won't fix it. Every change is shown as a `--dry-run` diff and only applied after you confirm in words. Deleting a segment requires detaching its deployments first. ## Related --- # Deploy URL: https://www.sunboardhq.com/docs/skills/deploy Description: Deploy a Sunboard spec to the sandbox, where it's live for testing. `/sunboard.deploy` uploads the spec your agent authored to Sunboard as a new, immutable version and activates it in the [sandbox](/docs/environments) (the `development` environment), where it's live for testing against your `pk_test_` key. It never touches production, that's a separate `sunboard promote` step. ## When to use it - You've authored or changed a spec and want to see it run end to end. ## What happens 1. **Confirms intent** — deploy creates a new immutable version. 2. **Validates** the spec and **checks the install** with `sunboard doctor`. 3. **Runs `sunboard deploy`** and reports whether the version was newly uploaded or **reused** (the spec hash already matched the latest version). 4. **Activates it in the sandbox** — if the experience is already bound to a segment in development, the new version is live there immediately; if it's brand new, bind it once with [Segment](/docs/skills/segment). Your local app runs on the `pk_test_` key, so a deploy is instantly testable with real persistence, segmentation, and routing. When it looks right, ship it with `sunboard promote `. Reset the sandbox's test users with `sunboard env clear --environment development` to run the flow again. ## Related --- # UI URL: https://www.sunboardhq.com/docs/skills/ui Description: Build custom checklist and survey components styled to your app's design system. By default, `sunboard init` wires the built-in surfaces: `` (a generic floating widget) and `` (a centered modal that shows every question at once). `/sunboard.ui` replaces either or both with **custom components that match your app's design system**, mounted in the runtime wrapper. The `useChecklist()` and `useSurvey()` hooks own the data; the skill owns the visual layer. ## When to use it - The default widgets don't match your look. - The tour or tooltips don't follow your theme (light surfaces in a dark app, a blue accent that isn't yours). - You want the checklist somewhere specific — a sidebar panel, a home-page card, a nav dropdown. - You want onboarding to read as a full "set up your account" page rather than a popup to dismiss. ## What happens 1. **Inventories your design system once** — Tailwind, shadcn, MUI, CSS modules, your button/radio/card/progress primitives, your icon library, and how dark mode works — then builds every surface from the same inventory, so the components come out looking like siblings. 2. **Remaps the `--sunboard-*` theme variables** that `sunboard init` wrote into your global stylesheet, creating the block if init couldn't find one. They start as neutral light values; the skill points them at your design tokens, so the built-in tour, tooltips, and hotspots pick up your palette and follow your dark mode. 3. **Builds the checklist component** at `components/sunboard/onboarding-checklist.tsx`, rendering from `useChecklist()` with your own primitives, including the completion screen when the spec defines one. It asks where the checklist should live rather than assuming the floating-corner shape. 4. **Builds the survey component** at `components/sunboard/onboarding-survey.tsx` — by default a dedicated `/onboarding` page with one question (or a small group) per step, a progress bar, and Back/Continue driven by `useSurvey()`. A modal or single-screen layout if you prefer. 5. **Mounts them** in `sunboard-provider.tsx`, replacing the built-ins. Tours, tooltips, and hotspots stay mounted as built-ins, now themed through the variables. 6. **Deploys to the sandbox** so you see the result against your real app on the `pk_test_` key. It builds only the surfaces you use — restyling the checklist never requires touching the survey, and vice versa. ## The hook contracts Everything you render comes from the hooks — copy is already token-interpolated, so you never re-interpolate: ```ts const { visible, // render nothing when false title, // user-facing heading (pre-interpolated) subtitle, // optional eyebrow steps, // ChecklistViewStep[] progress, // 0..100 isCompleted, // (key) => boolean startStep, // (step) => void — handles navigation + tour launch completeStep, // (step) => void — the user checks the step off completion, // success-screen content, when present dismissCompletion // () => void } = useChecklist(); ``` ```ts const { visible, // owed + not yet submitted — render nothing when false title, // pre-interpolated heading steps, // SurveyViewStep[] — authored groups, or one per question currentStep, // render this step's questions progress, // 0..100, by step isLastStep, canAdvance, // current step's required questions answered next, // advance, or submit on the last step back, answers, // Record setAnswer, // (key, value) — single-select / text toggleAnswer, // (key, value) — one value of a multi-select submit, // posts answers, then re-bootstraps } = useSurvey(); ``` If you find yourself reading `step.action.type` or `step.route`, stop — call `startStep(step)` and let the runtime decide what to do. Render the survey from `currentStep` and let `next`/`back`/`progress` drive the flow. The hooks' field names are a stable, append-only contract. For imperative work like `track()`, use `useSunboard()` separately. ## Related --- # CLI URL: https://www.sunboardhq.com/docs/cli Description: The sunboard command — every command, flag, and convention. Your agent runs these for you, but you can drive them directly too. The `sunboard` CLI is the deterministic tool layer your AI agent drives through [skills](/docs/skills). Most of the time you won't run it directly — but every command works on its own when you want to script something or work outside an agent. ## Install ```bash npm install -g sunboard ``` Requires Node.js 18 or later. Authenticate once with `sunboard login`. ## Conventions A handful of flags work across many commands: | Flag | Applies to | What it does | | --- | --- | --- | | `--json` | most commands | Print machine-readable JSON (stable, AI-friendly). | | `--app ` / `--framework ` | project commands | Locate the app whose env holds your publishable key. | | `--project ` | project commands | Target a project directly, skipping the app env lookup. | | `--dry-run` | mutating commands | Print the proposed change without applying it. | | `--yes`, `-y` | mutating commands | Skip the confirmation prompt (required in `--json` mode). | | `--no-update-check` | all commands | Skip the non-blocking CLI update check. | Commands that change hosted state (segments and routing) prompt for confirmation, support `--dry-run`, and require `--yes` when run with `--json`. ## Commands --- # Setup & health URL: https://www.sunboardhq.com/docs/cli/setup Description: Install Sunboard, check the wiring, and validate a spec locally. ## sunboard init Install Sunboard into a Next.js App Router or React/Vite app: adds the SDK, wires the runtime provider, scaffolds a starter spec, binds a project, and installs agent skills. Interactive by default. See the [Quick start](/docs/quickstart) for the full walkthrough. ```bash sunboard init [--app ] [--framework ] [--harness ] [--no-login] [--yes] ``` | Option | Description | | --- | --- | | `--app ` | Path to the app root, relative to the current directory. | | `--framework ` | Framework to install into (auto-detected when possible). | | `--harness ` | Harness(es) to install skills for. Repeat or comma-separate, e.g. `--harness codex,claude`. Valid: `codex`, `claude`, `opencode`. | | `--no-login` | Skip login and write a stub publishable key for offline dev. | | `--yes`, `-y` | Skip prompts, accept defaults, and overwrite generated files. | ## sunboard doctor Diagnose an installation — checks the runtime wrapper and publishable key, and reports anything missing. ```bash sunboard doctor [--app ] [--framework ] [--json] ``` | Option | Description | | --- | --- | | `--app ` | Path to the app root, relative to the current directory. | | `--framework ` | Framework to check. | | `--json` | Print machine-readable JSON output. | ## sunboard validate Validate a spec against the schema for fast local feedback. The spec is read from stdin (an explicit `[file]` is supported for scripts). Treat any reported issue as a blocker before deploying. ```bash sunboard validate --json <<'YAML' key: trial-activation name: Trial activation title: Welcome type: guide checklist: steps: - key: first-step title: First step YAML ``` | Argument / Option | Description | | --- | --- | | `[file]` | Optional path to a spec. Defaults to reading the spec from stdin. | | `--json` | Print machine-readable JSON output. | To see a spec running, deploy it to the sandbox and open your app on the `pk_test_` key. See [Deploy & promote](/docs/cli/deploy) and [Environments](/docs/environments). --- # Authentication URL: https://www.sunboardhq.com/docs/cli/authentication Description: Sign this machine in to Sunboard, check who you are, and sign out. Authentication is per-machine: `sunboard login` stores a session token used by the project and hosted-state commands. The browser SDK uses a separate publishable key — see [Architecture](/docs/architecture) for the difference. ## sunboard login Authorize this machine with a Sunboard account. Opens a browser approval URL and stores the session locally. ```bash sunboard login [--no-open] ``` | Option | Description | | --- | --- | | `--no-open` | Don't open the approval URL automatically — print it so you can open it yourself. | ## sunboard whoami Print the currently authenticated Sunboard user. ```bash sunboard whoami [--json] ``` | Option | Description | | --- | --- | | `--json` | Print machine-readable JSON output. | ## sunboard logout Clear the local Sunboard CLI session. ```bash sunboard logout ``` --- # Deploy & promote URL: https://www.sunboardhq.com/docs/cli/deploy Description: Deploy a version to the sandbox, promote it to production, and manage environment data. These commands move a spec through the [environments](/docs/environments) workflow: deploy activates it in the sandbox, and promote ships the tested version to production. ## sunboard deploy Upload a spec to Sunboard as a new, immutable version and activate it in the **sandbox** (the `development` environment), where it's live for testing against your `pk_test_` key. It never touches production. Sunboard is fileless: there are no spec files in your repo. Your coding agent composes the spec and pipes it to `deploy` on stdin (an explicit `[file]` is supported for scripts and CI, but isn't required). ```bash # The agent normally pipes the spec it authored: sunboard deploy --json <<'YAML' key: trial-activation name: Trial activation title: Welcome type: guide goal: event: workflow.created checklist: steps: - key: first-workflow title: Create your first workflow completeWhen: event: workflow.created YAML ``` | Argument / Option | Description | | --- | --- | | `[file]` | Optional path to a spec. Defaults to reading the spec from stdin. | | `--app ` | Path to the app root, relative to the current directory. | | `--framework ` | Framework of the app whose env contains the publishable key. | | `--json` | Print machine-readable JSON output. | The spec is validated, normalized, and hashed server-side on deploy, so an invalid spec is rejected. Run `sunboard validate` (piping the spec on stdin) for fast local feedback first. ## sunboard promote Copy a version you tested in the sandbox to production. It shows a diff and confirms before writing; production analytics are untouched, and you can roll back by promoting an earlier version. ```bash sunboard promote [--from ] [--to ] [--dry-run] [--yes] [--json] sunboard promote --survey [--from ] [--to ] [--dry-run] [--yes] [--json] ``` | Argument / Option | Description | | --- | --- | | `` | Experience key to promote. Carries the rules of the segment(s) it's bound to. | | `--survey` | Promote the active onboarding survey instead of an experience. | | `--from ` | Source environment. Defaults to `development`. | | `--to ` | Destination environment. Defaults to `production`. | | `--dry-run` | Print the diff without applying it. | | `--yes`, `-y` | Skip the confirmation prompt (required in `--json` mode). | Promotion is per-experience by design: ship one thing at a time, each with its own diff. There is no "promote everything" command. ## sunboard env Manage an environment's data and content. ```bash sunboard env clear --environment development [--yes] sunboard env clone [--from production] [--to development] [--yes] ``` | Command | Description | | --- | --- | | `env clear` | Delete the sandbox's test users and their runtime data (responses, events, progress). Content and config are kept. Refuses production. | | `env clone` | Clear the destination sandbox's users and hosted content, then rebuild it from the source environment's live experiences, bindings, segments, and survey. Source users and analytics are not copied; cloning into production is refused. | ## sunboard pull Print an environment's live experience spec as YAML to **stdout**. This is how the agent reads the current spec into its context to revise it, then re-deploys the edited version. Nothing is written to disk; the server stays the source of truth. ```bash sunboard pull [--environment ] [--app ] ``` | Argument / Option | Description | | --- | --- | | `` | Experience key to read. | | `--environment ` | Environment to read from. Defaults to `production`. | | `--app ` | Path to the app root, relative to the current directory. | ## sunboard skills update Refresh the installed agent skill files to match your current CLI version. Run this after upgrading `sunboard`. ```bash sunboard skills update [--yes] ``` | Option | Description | | --- | --- | | `--yes`, `-y` | Accept prompts and update modified owned sections. | --- # Hosted state URL: https://www.sunboardhq.com/docs/cli/hosted Description: Inspect and manage experiences, segments, routing, and analytics in your Sunboard project. These commands read and mutate hosted state, so they require `sunboard login`. All commands here accept `--app` / `--framework` / `--project` (to locate the project) and `--json`. Mutating subcommands (segment create/update/delete, all `routing` writes, `survey set`/`clear`, and `integrations connect`/`disconnect`) also accept `--dry-run` and `--yes` — and `--yes` is required in `--json` mode. ## sunboard state Print the project, its experiences, segments, and routing in a single call — the fastest way to see everything that's deployed. ```bash sunboard state [--app ] [--project ] [--json] ``` ## sunboard experiences Inspect the experiences in the current project. ```bash sunboard experiences list sunboard experiences get ``` | Command | Description | | --- | --- | | `list` | List experiences in the current project. | | `get ` | Show details for a single experience, including its versions. | ## sunboard segments Segments decide **who** sees an experience. List and inspect them, or create, update, and delete them. ```bash sunboard segments list sunboard segments get sunboard segments create --name [--key ] [--description ] [--rules ] [--rules-file ] sunboard segments update [--name ] [--description ] [--rules ] [--rules-file ] sunboard segments delete ``` | Option | Used by | Description | | --- | --- | --- | | `--name ` | create (required), update | Segment display name. | | `--key ` | create | Segment key. Defaults to a slug of the name. | | `--description ` | create, update | Segment description. | | `--rules ` | create, update | Segment rules as inline JSON. | | `--rules-file ` | create, update | Path to a JSON file containing the rules. | `segments delete ` fails if the segment still has deployments. Detach them with `routing detach` first. ## sunboard routing Routing binds an experience **version** to a **segment** for an environment — this is what makes onboarding go live. ```bash sunboard routing list sunboard routing attach --experience --segment --version [--environment ] sunboard routing detach (--deployment | --experience --segment [--environment ]) sunboard routing pause --deployment sunboard routing resume --deployment ``` | Option | Used by | Description | | --- | --- | --- | | `--experience ` | attach (required), detach | Experience key. | | `--segment ` | attach (required), detach | Segment key. | | `--version ` | attach (required) | Version number, or `latest`. | | `--environment ` | attach, detach | Environment key. Defaults to `production`. | | `--deployment ` | detach, pause (required), resume (required) | Deployment id. | | Subcommand | Description | | --- | --- | | `list` | List active deployments for the current project. | | `attach` | Bind or promote an experience version to a segment. | | `detach` | Remove a deployment (by id, or by experience + segment). | | `pause` | Pause a deployment (`enabled = false`). | | `resume` | Resume a paused deployment (`enabled = true`). | ## sunboard survey Manage the environment's active onboarding [survey](/docs/surveys) — the questionnaire shown to new users before any experience. There's one optional survey per environment. ```bash sunboard survey show [--environment ] sunboard survey set --survey [--environment ] sunboard survey clear [--environment ] ``` | Subcommand | Description | | --- | --- | | `show` | Show the active onboarding survey for the environment, if any. | | `set` | Make a deployed survey experience the active onboarding survey. | | `clear` | Remove the active onboarding survey so none is shown. | | Option | Used by | Description | | --- | --- | --- | | `--survey ` | set (required) | Key of a deployed `type: survey` experience. | | `--environment ` | show, set, clear | Environment key. Defaults to `production`. | A survey is activated, not bound to a segment. Author and deploy it first (`/sunboard.survey` or `sunboard deploy`), then `survey set` makes it live for the environment. ## sunboard analytics experience Show the funnel, segment, and tour analytics for one experience. ```bash sunboard analytics experience [--window 7d|30d|90d|all] [filters...] ``` | Option | Description | | --- | --- | | `--window <7d\|30d\|90d\|all>` | Time window. Defaults to `30d`. | | `--version ` | Version filter: `latest`, `active`, a version number, or id. Repeat or comma-separate. | | `--segment ` | Filter by segment key or id. Repeat or comma-separate. | | `--environment ` | Filter by environment key. Repeat or comma-separate. | | `--origin ` | Include only these origins, e.g. `https://app.example.com`. | | `--exclude-origin ` | Exclude these origins, e.g. `http://localhost:3000`. | | `--exclude-local-origins` | Exclude `localhost`, `127.0.0.1`, `[::1]`, and `.local` origins. | | `--include-legacy-steps` | Include step keys that only exist in older selected versions. | | `--enrich` | Add what dropped users did instead, from the connected [analytics provider](/docs/integrations/posthog). | | `--step ` | With `--enrich`: anchor enrichment on the edge into this funnel node (a step key, or `__goal__`) instead of the largest dropoff. | ## sunboard analytics revenue Show revenue and retention for the project — trial → paid conversion, expected trial value, revenue per activated user, retention, and a per-onboarding-path (and per-segment) breakdown. Needs the [server SDK](/docs/revenue) wired to report billing. ```bash sunboard analytics revenue [--window 7d|30d|90d] [--environment ] ``` | Option | Description | | --- | --- | | `--window <7d\|30d\|90d>` | Time window. Defaults to `30d`. | | `--environment ` | Environment to read. Defaults to production. | ## sunboard integrations Connect the product analytics you already use, so `analytics experience --enrich` and the [Analyze](/docs/skills/analyze) skill can show what dropped users did instead. See [Analytics integrations](/docs/integrations) for the full setup guide. ```bash sunboard integrations # list connections + health sunboard integrations connect posthog [--environment ] [--host ] [--posthog-project ] [--key-stdin] sunboard integrations disconnect posthog --environment ``` | Option | Used by | Description | | --- | --- | --- | | `--environment ` | connect, disconnect (required) | Environment the connection serves. Connect defaults to `production`. | | `--host ` | connect | Provider host. Defaults to `https://us.posthog.com`. | | `--posthog-project ` | connect | The PostHog project id (the number in the project URL). | | `--key-stdin` | connect | Read the API key from stdin (for scripts and agents). | `connect` reads the credential from a hidden prompt, the `SUNBOARD_POSTHOG_KEY` env var, or `--key-stdin` — never as a flag value, so it can't leak into shell history. It's verified against the provider before anything is saved and never displayed again.