# Plan Enforcement

## Overview

WaDesk runs as a multi-tenant SaaS, so every premium capability is gated by the workspace's plan. The enforcement is layered and happens entirely on the server — hiding a button in the interface is never what actually protects a feature. Three layers do the work:

- An in-context check — the single place the code asks "is this allowed?" wherever a limited action happens.
- A whole-page gate — protects entire feature pages declaratively.
- A trial gate — the hard stop that blocks free trials once they expire.

The upgrade prompt and the small premium badge both read the exact same plan data as the server, so what the interface shows and what the server allows never disagree.

## Effective Plan Resolution

Every gate ultimately asks one question: does this workspace's effective plan allow this? The answer is worked out in a set order:

1. Per-workspace override. If an administrator set a custom value for this workspace, that wins — for both limits and on/off features.
2. The plan. Otherwise the value comes from the workspace's plan.
3. Fallback. If neither defines it (for example a brand-new setting the plans haven't been updated with yet), a safe default is used.

WaDesk looks up the plan reliably no matter how it was saved, and remembers the answer for the rest of the request so it isn't recalculated.

> Account-wide vs. per-workspace billing. By default, each workspace is billed on its own plan. If an administrator switches billing to account-wide (under Admin → Settings → General), the owner's best paid plan across all the workspaces they own applies to every one of them — so buying one paid plan unlocks the whole account and avoids hitting a second trial gate.

## In-context checks (counts & features)

Wherever a limited action happens, the code checks the plan right there before letting it through. It can:

- Check a count limit — blocks once you've reached the plan's limit (so it stops you as you try to create the one that would go over). A limit of zero or none set means unlimited.
- Check an on/off feature — blocks if the feature isn't included in the plan.

When blocked, an app or automated caller gets a clear "limit reached" error response; a normal page submission is sent back with an upgrade message.

These in-context checks cover features that don't have a page of their own — for example the Kanban view, message reactions and edits, internal notes, carousel templates, using your own AI key, business hours, drip campaigns, and call recording. They're enforced exactly where the action happens.

## Whole-page gating

Features that have their own page are protected at the page level. A page can require one feature or several at once (in which case the plan must include all of them). What happens when the feature is missing depends on what the user is trying to do:

Action | If the feature is missing
--- | ---
Trying to change something (save, create, update, delete) | Blocked outright, with an upgrade message. The action can never run on the server, even if someone bypasses the interface.
Just viewing the page | The page still loads, but the upgrade prompt slides up over it. There's no redirect — the user stays on the feature page and sees exactly what upgrading would unlock.

## The Paywall & Crown

Two on-screen elements turn a missing feature into an upgrade prompt, and both read the exact same plan data as the server:

- The paywall — a slide-up panel shown over a gated page. It loads the actual plans that include the feature you're trying to use, so you can pick one and check out without leaving the page.
- The crown badge — a small gold crown shown next to a feature your current plan doesn't include. Clicking it takes you to your account's Plans page.

This page-level gating covers features that have a page of their own — campaigns, broadcasts, flows, AI assistants and training, calling, storefront, analytics, integrations, and so on. Features without their own page are checked in context instead, as described above.

## Trial Gate

This is the hard stop for expired free trials. Once a workspace's free trial has run out, the user can no longer use any feature: opening a feature page sends them to the Plans page with a warning (or returns a "payment required" response with an upgrade link for app/automated requests). Features still appear in the menu (so the user can see what buying a plan unlocks), but opening or saving in one is blocked.

So a user is never stuck, these areas always stay reachable while the trial is expired:

- Account, plans, profile, billing, and invoices
- Checkout and payment confirmations (so they can pay)
- Support tickets, email verification, and the onboarding steps
- Switching to another (non-expired) workspace, and logout
- Public marketing and pricing pages

> Fails open by design. If anything ever goes wrong inside the trial gate, the request is let through rather than blocked — so a bug in the gate can never accidentally lock the whole app. Paid plans, and free plans with no expiry, are never "expired", so the gate does nothing for them.

## Admin Bypass

Platform administrators are never blocked by their own product. All three enforcement layers follow the same rule: a user with the Super Admin or Admin role bypasses every check.

Layer | For admins
--- | ---
In-context checks | Treated as allowed for every limit and feature.
Whole-page gating | Bypassed automatically.
Trial gate | Skipped before any trial check runs.
Paywall & crown badge | Show nothing — admins aren't "missing" anything.

> Recommended: The admin bypass is broad — the Super Admin and Admin roles override every plan limit and trial gate. Give those roles only to staff who genuinely run the platform. For day-to-day work, use the narrower Platform Support or Auditor roles, so a compromised account can't quietly run unlimited sends.