useAppAccess
Check whether the authenticated end-user has access to a monetized built app, and open Stripe Checkout to unlock it. Use directly only for custom gate UI — AppPaywall wraps this with a default overlay.
import { useAppAccess } from 'flowstack-sdk/paywall'Polls GET /billing/app-access/status every 60 seconds and exposes the access state plus a checkout() function that opens a Stripe Checkout session for a one-time unlock. 0.3.0 adds per-app subscriptions (P0-162): a subscription status block plus subscribe() / cancelSubscription() for the builder's monthly plan. Fails open on non-200 responses so a billing outage never locks users out client-side — the backend gate remains the enforcement boundary regardless of what this hook reports.
Most apps should render <AppPaywall siteId={config.appScope}> instead of consuming this hook directly; reach for useAppAccess when you need a custom paywall surface (pricing copy, metered-usage meter, custom CTA).
Signature
const { hasAccess, queriesUsed, queriesRemaining, paymentMode, unlockPriceCents, unlockPriceLabel, isLoading, error, checkout, refetch } = useAppAccess(siteId, opts?)Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
siteId | string | required | Site ID of the built app (typically config.appScope). |
opts.builderTenantId | string | — | Builder's tenant ID — needed to load app config when the viewer belongs to a different tenant. |
Returns
| Field | Type | Description |
|---|---|---|
hasAccess | boolean | Whether the end-user has access to this built app (optimistic true until the first check resolves). |
queriesUsed | number | Free queries used this month. |
queriesRemaining | number | null | Remaining free queries this month (null = unlimited / paid access). |
paymentMode | 'stripe' | 'agent' | 'both' | null | Payment mode configured by the builder. |
unlockPriceCents | number | null | One-time unlock price in USD cents (null = free or not configured). |
unlockPriceLabel | string | null | Human-readable unlock price label. |
subscription | AppSubscriptionStatus | null | The end-user's subscription to this app (state, currentPeriodEnd, cancelAtPeriodEnd, label) — status-only since 0.3.3/P0-167, no allowance fields. null when the builder has no subscription plan configured. |
isLoading | boolean | Access check in flight. |
error | string | null | Error message if the status check failed. |
checkout | (opts?: { successUrl?: string; cancelUrl?: string }) => Promise<void> | Open Stripe Checkout to unlock this app. |
subscribe | (opts?: { successUrl?: string; cancelUrl?: string }) => Promise<void> | Open Stripe Checkout (mode=subscription) for the builder's monthly plan. 409s if already subscribed or the user owns a lifetime unlock. |
cancelSubscription | () => Promise<void> | Cancel at period end — access and allowance continue until currentPeriodEnd. |
refetch | () => Promise<void> | Manually refresh the access status. |
Examples
import { useAppAccess } from 'flowstack-sdk/paywall';
function Gate({ children }: { children: React.ReactNode }) {
const { hasAccess, queriesRemaining, unlockPriceLabel, isLoading, checkout } = useAppAccess(config.appScope);
if (isLoading) return <p>Checking access…</p>;
if (hasAccess) return <>{children}</>;
return (
<div>
<p>{queriesRemaining === 0 ? 'Free queries used up.' : 'This app is premium.'}</p>
<button onClick={() => checkout()}>Unlock for {unlockPriceLabel}</button>
</div>
);
}flowstack-sdk/paywall (0.3.2+), not flowstack-sdk/wallet — the wallet entry statically requires the wagmi optional peer and breaks built-app builds.AppPaywall) is UI, never the security boundary.setMonetization (or the Casino Monetization panel); this hook only reads the resulting access state.See also
AppPaywall · setMonetization · getMonetization · PaymentRequired · AppSubscriptionStatus · subscribeToApp · cancelAppSubscription
Source: wallet/useAppAccess.ts · Also available in llms-full.txt and registry.json.