Gating is two checks, not one: an authoritative one on your backend, and a cosmetic one in your UI so the customer isn’t surprised.
The server check is the real one
userId is the Kerne user id — the same id the subscription was created against. Read it from whatever your request already carries.
A denial isn’t just “don’t run the code” — decide what the caller sees. For an API route, a 403 with a machine-readable reason your frontend can act on beats a generic failure. For a page render, redirect to your pricing page rather than rendering a broken state.
If the action is the thing being counted, use consume() instead: it checks and increments atomically, so a burst of concurrent requests can’t all slip past the limit.
Acting on behalf of a specific request’s user rather than your own backend identity? Scope the client to their token first:
The client check is for UX only
<Access> (and the useAccess hook under it) handles the loading state and scopes to the logged-in user.
A client-side check is a convenience, never a security boundary — anyone can bypass it by editing
the page. Always re-check on the server before doing the thing being gated.
Don’t wait for a “Save” click to tell someone they’ve hit a limit — disable the control ahead of time:
Block, or keep serving and charge?
The same mechanism, one setting apart. The judgment call is which fits what you’re limiting.
Block once the allowance is gone — “You’ve reached your 5 team member limit.” Right for things a customer should decide to buy more of on purpose: seats, storage, expensive infrastructure. Nobody should discover they bought more of these as a surprise invoice line.
Keep serving and charge for the excess — “the next ones are billed at $0.005 each.” Right for bandwidth, email sends, anything usage-shaped where blocking makes a customer angry without protecting anything. See Designing your pricing.
One thing to know before relying on it: a money ceiling stops the charging, never the service. If you want consumption to stop, that’s a separate ceiling in units — see why the cap doesn’t block.
Don’t lose the user’s work over it
Someone who spends 30 minutes on a report and gets blocked on “Save” doesn’t file a support ticket about your quota — they leave. “Error: storage limit reached” throws that work away; “Draft saved locally — upgrade to publish” doesn’t. Check before the expensive step starts, not only when they submit its result.