Skip to content

Tutorial: Warn Users About Low Credits

In this tutorial, you’ll display a user’s remaining credits and surface a warning placement when the balance runs low — at 80% consumed — so users top up before they’re blocked. Takes about 15 minutes.

A credits flow that:

  • Reports credit consumption to the SDK with updateUsage()
  • Reads the remaining balance with useEntitlement()
  • Fires a low-credits warning placement when consumption crosses 80%
  • A React 18+ application with @revturbine/sdk installed
  • A RevTurbineProvider set up (see Quickstart)
  1. Add a credits entitlement and a low-credits placement

    A credits entitlement with a per-plan allowance, plus a usage_credit_seat placement triggered by credit_threshold at 80%. This is a complete, valid ExportedConfig.

    exported_config.json
    {
    "version": "v1",
    "plans": [
    { "id": "plan_free", "unique_handle": "free", "name": "Free", "tier_position": 0, "sort_order": 0 },
    { "id": "plan_pro", "unique_handle": "pro", "name": "Pro", "tier_position": 1, "sort_order": 1 }
    ],
    "entitlements": [
    { "id": "ent_credits", "unique_handle": "credits", "name": "Render Credits", "type": "credits", "unit": "credits" }
    ],
    "entitlement_rules": [
    {
    "id": "er_credits_free",
    "entitlement_id": "ent_credits",
    "targets": [{ "kind": "plan", "id": "plan_free" }],
    "segment_ids": [],
    "type_fields": { "kind": "credits", "allowance": 20, "rollover": false, "max_balance": null }
    }
    ],
    "segments": [],
    "content_ui_paths": [],
    "surface_templates": [
    { "id": "banner_placement", "surface_type": "banner", "fields": [] }
    ],
    "placements": [
    {
    "id": "pl_credits_low",
    "name": "Low credits warning",
    "category": "usage_credit_seat",
    "trigger": { "type": "credit_threshold", "entitlement_handle": "credits", "threshold_percent": 80 },
    "payloads": [
    {
    "id": "pl_credits_low_p0",
    "target": { "plan_ids": ["plan_free"], "segment_chips": [] },
    "surfaces": [
    {
    "template_id": "banner_placement",
    "fields": {
    "header": "{{usage_remaining}} credits left",
    "body": "Top up to keep rendering.",
    "dismissible": "Yes"
    },
    "ctas": [
    { "label": "Buy credits", "path": "open_checkout", "config": { "purchase": "credit_pack" } }
    ]
    }
    ],
    "caps": { "max_per_period": { "count": 1, "period": "day" } },
    "status": "active"
    }
    ],
    "order": 0
    }
    ]
    }
  2. Report credit usage as it’s consumed

    Each time a user spends credits, report the new running total. This re-evaluates the warning’s threshold.

    src/hooks/useRender.ts
    import { useRevTurbine } from '@revturbine/sdk';
    import { useCallback, useRef } from 'react';
    export function useRender() {
    const { sdk } = useRevTurbine();
    const spent = useRef(0);
    return useCallback((cost: number) => {
    spent.current += cost;
    sdk?.updateUsage({ credits: spent.current });
    }, [sdk]);
    }
  3. Show the remaining balance

    Read the live balance with useEntitlementresult.remaining reflects every updateUsage() call.

    src/components/CreditBadge.tsx
    import { useEntitlement } from '@revturbine/sdk';
    export function CreditBadge() {
    const { result, limited } = useEntitlement({ handle: 'credits' });
    if (!result) return null;
    return (
    <span style={{ color: limited ? '#b45309' : 'inherit' }}>
    {result.remaining} / {result.limit} credits
    </span>
    );
    }

    limited flips to true as the balance approaches empty (the SDK’s “approaching limit” state), so you can restyle the badge without re-deriving the percentage yourself.

  4. Render the warning slot

    Place the slot where the banner should appear. It stays hidden until consumption crosses 80%, then renders the warning with the balance interpolated into the copy via {{usage_remaining}}.

    src/components/AppHeader.tsx
    import { FixedSurfaceSlot } from '@revturbine/sdk';
    export function AppHeader() {
    return (
    <header>
    <FixedSurfaceSlot id="app_header_banner" surfaceTemplateIds={['banner_placement']} />
    <nav>{/* ... */}</nav>
    </header>
    );
    }
  1. updateUsage() updates the SDK’s credit balance and re-evaluates placements.
  2. The credit_threshold trigger keeps the placement hidden until 80% of the allowance is consumed.
  3. Token expansion fills {{usage_remaining}} from the live balance.
  4. The CTA opens your top-up flow through the resolvers from Quickstart.

Interactive Playground → Message Banner