SDK initialization failed during {initStatus.phase}: {initStatus.message}
{initStatus.remediation}
{content?.body}
API calls: {usage.api_calls?.current} / {usage.api_calls?.limit}
{content?.body}
{content?.body}
{content?.body}
{trial.days_remaining} days left in your trial
; } ``` ## How It Works 1. The `trial_ending` trigger keeps the placement hidden until the trial is within `days_before_end` of expiring. 2. `{{days_remaining}}` is filled from the trial status the SDK holds. 3. As the trial advances day by day, the banner copy updates automatically. 4. The CTA opens checkout through the resolvers from [Quickstart](/docs/). **Tip:** RevTurbine supports both **forward trials** (paid features for a limited time) and **reverse trials** (full access that downgrades to free). The same trigger pattern applies — only the trial status the SDK reports differs. ## Try It Live > [Interactive Playground → Banner Slot](/docs/playground/#fixed-banner) ## Next Steps - [Entitlements Guide](/docs/guides/entitlements/) — trials, usage limits, and credits - [Placements Guide](/docs/guides/placements/) — triggers and targeting - [Tutorial: Show a Banner Placement](/docs/tutorials/banner-placement/) — targeted banners without a trigger # Tutorial: Add an Upgrade Button > End-to-end tutorial — install the SDK, configure local mode, and place an upgrade button that renders based on targeting rules. In this tutorial, you'll install the SDK, configure it in local mode, and render an upgrade button that only appears for users on the Starter plan. Takes about 10 minutes. ## What You'll Build An upgrade button in your navigation bar that: - Shows "Upgrade to Pro" for Starter plan users - Hides automatically for Professional/Enterprise users - Tracks impressions and CTA clicks - Navigates to your pricing page on click ## Prerequisites - A React 18+ application (Next.js, Vite, or CRA) - Node.js 22.13+ ## Steps 1. **Install the SDK** ```bash pnpm add @revturbine/sdk ``` 2. **Create an Playbook fixture** For local development, you need a Playbook with plans, entitlements, and placements. Create `src/fixtures/revturbine.playbook.json`: ```json title="revturbine.playbook.json" { "artifact_type": "playbook", "format_version": "1.0.0", "plans": [ { "unique_handle": "starter", "name": "Starter", "tier_position": 0, "sort_order": 0 }, { "unique_handle": "pro", "name": "Pro", "tier_position": 1, "sort_order": 1 } ], "entitlements": [], "entitlement_rules": [], "segments": [], "content_ui_paths": [], "surface_templates": [ { "id": "button", "surface_type": "button", "fields": [] } ], "placements": [ { "id": "pl_nav_upgrade", "name": "Upgrade button", "category": "fixed", "trigger": { "type": "surface_render", "slot_id": "nav_upgrade" }, "payloads": [ { "id": "pl_nav_upgrade_p0", "target": { "plan_ids": ["starter"], "segment_chips": [] }, "surfaces": [ { "template_id": "button", "fields": {}, "ctas": [ { "label": "Upgrade to Pro", "path": "open_checkout", "config": { "purchase": "pro" } } ] } ], "caps": {}, "status": "active" } ], "order": 0 } ] } ``` 3. **Wrap your app with the provider** ```tsx // src/App.tsx import { RevTurbineProvider, RuntimeMode } from '@revturbine/sdk'; import playbook from './fixtures/revturbine.playbook.json'; export default function App() { return (Current: {calls} calls