RevTurbine UI (studio)
Edit visually at revturbine.com/app — define plans, entitlements, targeting rules, and placements, preview their impact, and launch. Download a revturbine.playbook.json snapshot anytime for local mode.
RevTurbine is web SaaS monetization in a single SDK: placement decisioning, entitlements, and conversion experiments — without a code deploy for every change. This guide takes you from install to a live placement and entitlement gate, then shows how to manage your monetization config.
It’s additive — if RevTurbine is disconnected, misconfigured, or every placement is paused, your product keeps working and slots simply render nothing.
Two packages, both on the public npm registry — no auth token or registry config.
@revturbine/cli — manage your Playbook (plans, entitlements, placements) and Releases from the terminal.@revturbine/sdk — render placements and check entitlements in your app.Pin the CLI in your project so local development, CI, and coding agents use the same schema snapshot:
npm install -D --save-exact @revturbine/clinpx revturbine --helpAdd the SDK to your React app:
npm install @revturbine/sdkpnpm add @revturbine/sdkyarn add @revturbine/sdkCreate an account in the studio, then connect the CLI to it.
Create your account. Sign up at the studio — revturbine.com/app — or headlessly from the terminal:
revturbine signupAuthorize the CLI on this machine. This opens a browser device-flow login against your account:
revturbine loginYour token is stored at ~/.revturbine/credentials.json (mode 0600) and scoped to your tenant.
Confirm you’re connected. status shows your active config and recent releases:
revturbine statusWrap your app once, then add slots (where placements render) and gates (entitlement checks).
Wrap your app in RevTurbineProvider. This example runs in local_only mode — no backend, no network calls — from a revturbine.playbook.json you ship with your app (see step 4 for where it comes from).
import { RevTurbineProvider } from '@revturbine/sdk';import playbook from './revturbine.playbook.json';import { useMemo } from 'react';
function App() { const options = useMemo(() => ({ localRuntime: { playbook }, user: { id: 'user_123', plan_handle: 'starter' }, // UI path resolvers connect placement CTAs to your app's navigation. uiPathResolvers: { navigate_to_plans: () => { window.location.href = '/pricing'; }, open_checkout_modal: (ctx) => { console.log('Checkout:', ctx.plan_handle); }, }, }), []);
return ( <RevTurbineProvider options={options}> <YourApp /> </RevTurbineProvider> );}Add a slot. Drop a surface slot where you want a placement to appear. It renders the matching placement for the current user — or nothing.
import { Slot } from '@revturbine/sdk';
function Dashboard() { return ( <div> <h1>Dashboard</h1> {/* RevTurbine renders an upgrade banner here — or nothing */} <Slot id="dashboard_top_banner" surfaceTemplateIds={["banner_placement"]} /> </div> );}<Slot> is the one surface-slot component — it covers banners, modals, in-page content, buttons, and event-triggered messages alike. To drive rendering yourself instead, use the usePlacement hook.
Gate a feature. Wrap gated UI in <Gate>. It renders its children when the user is entitled, and automatically shows the configured upgrade placement on denial — no manual check.
import { Gate } from '@revturbine/sdk';
function BatchExport() { return ( <Gate id="batch_export_gate" can="batch_export"> <BatchExportButton /> </Gate> );}can="batch_export" is shorthand for check={{ entitlement: 'batch_export' }}. For custom deny UI, use the useCan hook — it returns { can, limited, result }:
import { useCan } from '@revturbine/sdk';
function BatchExport() { const { can, limited, result } = useCan('batch_export'); if (!can) return <UpgradePrompt reason={result?.reason} />; return <BatchExportButton warnLowBalance={limited} />;}can is true while the user is entitled — including the limited (running-low) state, so gate on !can, not !allowed. For an imperative check (server-side or in an event handler), call await rt.can('batch_export'). See the Entitlements guide.
Your monetization model — plans, entitlements, entitlement rules, segments, surface templates, and placements — is a single portable snapshot called a Playbook. It’s what the SDK reads (bundled in local mode, or served live), and you can author it two ways.
RevTurbine UI (studio)
Edit visually at revturbine.com/app — define plans, entitlements, targeting rules, and placements, preview their impact, and launch. Download a revturbine.playbook.json snapshot anytime for local mode.
RevTurbine CLI + Agent
Treat config as code. Author or edit it by hand — or with the RevTurbine AI agent (in the studio, or via an MCP-connected coding agent) — then validate and ship it from the terminal.
The default CLI loop takes a Playbook from file to a live Release. launch <file> validates, stages, and releases it as one guarded operation:
revturbine download --live --save ./revturbine.playbook.json# edit ./revturbine.playbook.jsonrevturbine validate ./revturbine.playbook.jsonrevturbine diff ./revturbine.playbook.json --liverevturbine launch ./revturbine.playbook.jsonDecisioning, not just rendering
RevTurbine decides — caps, cooldowns, eligibility, and priority across competing placements. One <Slot> (or usePlacement()) returns the winner. No client-side orchestration, no homegrown segment logic. ML-scored propensity is on the roadmap, not shipped — see Beyond gating.
Entitlements and placements in one engine, linked to billing
A <Slot> surfaces usage warnings, trial nudges, and lifecycle prompts tied to billing state; rt.can() confirms or denies feature access. Pair them to gate a feature and surface the customer-specific upgrade prompt that converts it. Like RevenueCat for SaaS.
PM- and marketer-controlled, dev-decoupled
Drop a slot once. Your PM, growth, marketing, or CRO team changes content, targeting, and CTAs in the studio — no engineering tickets, no deploys. UI path resolvers keep the navigation contract in your code; everything else iterates without you.
Framework-agnostic, and the same answer everywhere
The React components are a convenience, not the product. @revturbine/sdk/headless imports no React and runs on Svelte, Vue, plain TypeScript, or a Node server; Python and Rust SDKs evaluate the same Playbook. Every one of them runs the same engine over the same inputs, and a cross-language gate diffs their decisions byte-for-byte — so a check answers identically wherever you make it. See Headless API.
| Capability | API | Description |
|---|---|---|
| Placements | Slot / usePlacement() | Render upgrade banners, modals, toasts, and buttons based on targeting rules |
| Entitlements | Gate / rt.can() | Gate features, enforce usage limits, check credits and seats |
| Usage tracking | rt.update() | Track consumption for quota meters and passive placement triggers |
| Events | rt.track() | Behavioral signals for targeting and analytics |
| Interactions | dismiss() / snooze() / convert() | CTA lifecycle tracking |
| Trial status | rt.getTrialStatus() | Current trial state and countdown |