Buzz, purchase and tip hooks
Buzz balance and transactions, Buzz and goods purchases, entitlements, compensation and tips.
This page documents 9 of the 47 hooks in the Hooks reference, which indexes every hook by name. Generated from the published @civitai/blocks-react@0.73.0 package's type definitions and README.
Other pages: Context, frame and analytics hooks · Generation hooks · Batch, confirmed-step and training hooks · Viewer, consent and navigation hooks · Storage, picker and media hooks
useBuzzPurchase
useBuzzPurchase(): UseBuzzPurchaseOpen the Buzz purchase modal. It raises the viewer's WALLET, never a spend cap: offer it when the viewer's spendable Buzz is below a quoted cost, never on a resolved `failed` submit (see `useBuzzWorkflow`'s `submit` docs).
const { openPurchaseModal } = useBuzzPurchase();
const { purchased, newBalance } = await openPurchaseModal(suggestedAmount);
if (purchased) { /* retry the generation */ }useGoodPurchase
useGoodPurchase(): UseGoodPurchaseSell a **digital good** — a manifest-declared entitlement the platform sells to the viewer for Buzz on your app's behalf. Requires the `goods:purchase:self` scope **and** a `goods` entry in your manifest; without both the endpoint answers 404.
const { purchase, loading, error } = useGoodPurchase();
const { entitlement } = await purchase(
{ goodId: 'extra-slots', expectedPriceBuzz: 250 },
{ topUpOnInsufficientFunds: true },
);useEntitlements
useEntitlements(): UseEntitlementsWhat the viewer owns **from this app** — the read half of the goods rail. Scope `goods:read:self`, which is consent-exempt: the reply is scoped server-side to your own app, so a read-only block needs no purchase power and triggers no re-consent prompt.
function PaidFeature() {
const { owns, loading, error, unauthenticated, refetch } = useEntitlements();
if (loading) return <Spinner />;
if (unauthenticated) return <SignInToBuy />; // a logged-out viewer
if (error) return <RetryNotice onRetry={refetch} />; // NOT "you own nothing"
return owns('extra-slots') ? <Unlocked /> : <BuyButton onDone={refetch} />;
}useBuzzBalance
useBuzzBalance(): UseBuzzBalanceThe signed-in viewer's per-pool Buzz balance (`{ blue, green, yellow }` — the domain-clamped pools a block may read; never the platform-internal `red`/`purple`). Host-mediated over `GET_BUZZ_BALANCE` → `BUZZ_BALANCE_RESULT`; same trust model as `useBuzzWorkflow`/`useBuzzPurchase` (the host resolves the viewer from the block token — the block never touches the balance API). Fetches on mount; `refetch` for on-demand refreshes.
const { balance, loading, error, refetch } = useBuzzBalance();
// `balance` is null until the first successful fetch. refetch() after a
// generation debits it. An anon viewer / missing scope / host failure → `error`.
if (!loading && balance) console.log(`Yellow: ${balance.yellow}`);useBuzzTransactions
useBuzzTransactions(params?: BlockBuzzTransactionsParams): UseBuzzTransactionsThe signed-in viewer's Buzz-transaction ledger (a paged, host-projected read of the Buzz dashboard). Returns `{ transactions, cursor, loading, error, refetch }`; `transactions` rows are rehydrated so `date` is a `Date`. Pass the returned `cursor` back as `params.cursor` to page forward. Requires the `buzz:read:self` scope; host-mediated over `GET_BUZZ_TRANSACTIONS`.
const { transactions, cursor, loading, error } = useBuzzTransactions({ type: 'Tip', limit: 20 });
if (!loading && transactions) transactions.forEach((t) => console.log(t.type, t.amount, t.date));useBuzzAccounts
useBuzzAccounts(): UseBuzzAccountsThe viewer's all-pool Buzz balances — the three spendable pools **plus** the creator payout pools (`{ accountType, balance }[]`), a superset of `useBuzzBalance`. Returns `{ accounts, loading, error, refetch }`. Requires `buzz:read:self`; host-mediated over `GET_BUZZ_ACCOUNTS`.
const { accounts, loading, error } = useBuzzAccounts();
if (!loading && accounts) accounts.forEach((a) => console.log(a.accountType, a.balance));useDailyCompensation
useDailyCompensation(params: BlockDailyCompensationParams): UseDailyCompensationPer-modelVersion generation-compensation for the month containing `params.date` (Buzz totals + cash totals in pennies). Returns `{ resources, hasPublishedResources, loading, error, refetch }`. Requires `buzz:read:self`; host-mediated over `GET_DAILY_COMPENSATION`.
const { resources, hasPublishedResources } = useDailyCompensation({ date: '2026-07-01' });useTip
useTip(): UseTipSend a Buzz TIP from the viewer through the block-token-gated `POST /api/v1/blocks/tip` REST endpoint (scope `social:tip:self`). Direct-fetch (bypasses the postMessage bridge) against the VALIDATED host origin (`useHostOrigin()`) with the block bearer token (`useBlockToken().raw`) — the same security-reviewed pattern as {@link useGenerationResources}. The SENDER is always the token subject (server self-binds it); the block never supplies a `fromUserId`. IDEMPOTENCY: pass a stable `options.idempotencyKey` to make a retry-after- timeout safe (the server replays the first terminal result). Omitting it mints a fresh key per call, so each call is a distinct logical tip. 🔴 THE KEY'S FORMAT IS CONSTRAINED — see {@link TipOptions.idempotencyKey}. Letters, digits, `_` and `-` only, at most 64 characters, **no colons**; the host 400s anything else, and this hook now refuses it before the POST. 🔴 THIS EXAMPLE USED TO RECOMMEND `React.useId()`, AND THAT WAS A LIVE DEFECT: `useId()` wraps its value in characters outside the allowed class on most of the React versions this package's peer range admits (`^18.0.0 || ^19.0.0`) — React 18.3.1 returns `":R0:"` and early React 19 a guillemet-wrapped id, both of which 400. (React 19.2.6, resolved in this repo today, happens to return `_r_0_`, which does clear the charset — so the bug was INVISIBLE here while being guaranteed for a consumer on 18.) Anyone copying that line shipped a guaranteed rejection. Prefer a stable id you already have, which is also better idempotency: it is tied to the THING being tipped rather than to a component instance, so it survives a remount.
// No natural id to hand? Mint one with the SDK's generator and persist it
// for as long as the logical tip lives. Do NOT post-process `useId()` into
// shape: rewriting a key is exactly what this SDK refuses to do, because a
// silently-rewritten key breaks the identity the key exists to carry.
import { generateIdempotencyKey } from '@civitai/blocks-react';
const keyRef = React.useRef(generateIdempotencyKey());
await tip({ toUserId: 123, amount: 50 }, { idempotencyKey: keyRef.current });useTipAllowance
useTipAllowance(): UseTipAllowanceRead the viewer's REAL remaining daily tip allowance `{ cap, spent, remaining }` through the block-token-gated `GET /api/v1/blocks/tip-allowance` REST endpoint (scope `social:tip:self` — the SAME scope the app already holds to tip, so no manifest change). Direct-fetch against the validated host origin with the block bearer token, the same pattern as {@link useGenerationResources}. Lets a block show a genuinely-tracked remaining allowance and disable the tip button at the true ceiling — instead of a dead client-side full-cap guess (`localStorage` is inert in the opaque-origin sandbox). Fetches once on mount and exposes `refetch` (call it after a successful `useTip().tip(...)`). Only the LATEST request may write state: a reply superseded by a newer `refetch` — or one that lands after unmount — is dropped (#392). If the host origin never arrives the hook reaches a TERMINAL state rather than spinning: see `error` above (#398).
const { allowance, refetch } = useTipAllowance();
// allowance?.remaining — Buzz the viewer may still tip today