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 exported_config.json you ship with your app (see step 4 for where it comes from).

    src/App.tsx
    import { RevTurbineProvider } from '@revturbine/sdk';
    import exportedConfig from './exported_config.json';
    import { useMemo } from 'react';
    function App() {
    const options = useMemo(() => ({
    localRuntime: { exportedConfig },
    user: { id: 'user_123', context: { 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 ExportedConfig. 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 exported_config.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 verify ./exported_config.json # schema-validate locally, no writes
revturbine diff # compare your file against the live config
revturbine upload ./exported_config.json # stage a draft change set
revturbine deploy # submit → approve → deploy the draft

Decisioning, not just rendering

RevTurbine decides — caps, cooldowns, eligibility, priority across competing placements, and ML-scored propensity for conversion, expansion, and retention nudges. One getPlacement() call returns the winner. No client-side orchestration, no homegrown segment logic.

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.

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 propensity scoring
Interactionsdismiss() / snooze() / convert()CTA lifecycle tracking
Trial statusrt.getTrialStatus()Current trial state and countdown