Skip to content

Getting Started

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 monetization config (plans, entitlements, placements) and change sets from the terminal.
  • @revturbine/sdk — render placements and check entitlements in your app.

Install the CLI globally:

Terminal window
npm install -g @revturbine/cli
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 an 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 './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 an 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 deploy. Export an 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 CLI ships config through reviewable change sets (draft → submit → approve → deploy):

Terminal window
revturbine validate ./playbook.json # schema-validate locally, no writes
revturbine diff # compare your file against the live config
revturbine upload ./playbook.json # stage a draft change set
revturbine launch --draft # submit → approve → launch the draft

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