Skip to content

Start

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:

Terminal window
npm install -D --save-exact @revturbine/cli
npx revturbine --help

Add the SDK to your React app:

Terminal window
npm install @revturbine/sdk

Create an account in the studio, then connect the CLI to it.

  1. Create your account. Sign up at the studio — revturbine.com/app — or headlessly from the terminal:

    Terminal window
    revturbine signup
  2. Authorize the CLI on this machine. This opens a browser device-flow login against your account:

    Terminal window
    revturbine login

    Your token is stored at ~/.revturbine/credentials.json (mode 0600) and scoped to your tenant.

  3. Confirm you’re connected. status shows your active config and recent releases:

    Terminal window
    revturbine status

Wrap your app once, then add slots (where placements render) and gates (entitlement checks).

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

    src/App.tsx
    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>
    );
    }
  2. 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.

    src/components/Dashboard.tsx
    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.

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

    src/components/BatchExport.tsx
    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:

Terminal window
revturbine download --live --save ./revturbine.playbook.json
# edit ./revturbine.playbook.json
revturbine validate ./revturbine.playbook.json
revturbine diff ./revturbine.playbook.json --live
revturbine launch ./revturbine.playbook.json

Decisioning, 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.

CapabilityAPIDescription
PlacementsSlot / usePlacement()Render upgrade banners, modals, toasts, and buttons based on targeting rules
EntitlementsGate / rt.can()Gate features, enforce usage limits, check credits and seats
Usage trackingrt.update()Track consumption for quota meters and passive placement triggers
Eventsrt.track()Behavioral signals for targeting and analytics
Interactionsdismiss() / snooze() / convert()CTA lifecycle tracking
Trial statusrt.getTrialStatus()Current trial state and countdown