All documentation

    Feature Gating

    Rank Pilot AI SEO controls access to tools and AI-heavy actions through a shared plan/feature model, enforced both client-side (for UI/UX) and server-side (for actual authorisation). The core pieces are the usePlanFeatures and usePlanUsage hooks, the check-feature-access edge function, and the LockedFeatureCard / upgrade-prompt components.

    Data model

    Two Supabase tables drive gating:

    • subscription_plans — one row per tier (Free, Growth, Pro, Enterprise), including tier_name, monthly_price, yearly_price and sort_order.
    • plan_features — one row per (plan_id, feature_key) pair, with feature_name, enabled (boolean) and limit_value (an integer usage cap, or null for unlimited).

    Feature keys checked across the product include basic_site_audit, full_technical_audit, ai_keyword_research, competitor_analysis, backlink_analysis, rank_monitoring, content_optimizer, autopilot_basic, autopilot_full, unlimited_scans, export_reports, custom_prompts, priority_ai_model, white_label_reports and api_access.

    usePlanFeatures

    src/hooks/usePlanFeatures.ts is the single source of truth for "what tier is this user on, and what can they do":

    1. Checks whether the current user has the admin role via the has_role RPC — admins get unrestricted access to every feature.
    2. Reads the user's row from user_subscriptions (tier, is_trial, trial_end_at, trial_tier). If a trial is active and not expired, the trial tier is used instead of the base tier; otherwise it falls back to the base tier, or "free" if there's no subscription row at all.
    3. Maps legacy internal tier names to the display names used in subscription_plans (free → Free, essential → Growth, pro → Pro, agency/enterprise → Enterprise).
    4. Fetches the matching plan's plan_features rows, plus all plans and all features (for building comparison tables).

    It exposes:

    • userTier — the resolved, display-cased tier name.
    • hasFeatureAccess(featureKey)true for admins, otherwise looks up whether the feature is enabled on the user's current plan.
    • getFeatureLimit(featureKey)null for admins (unlimited), 0 if the feature isn't enabled, otherwise the plan's limit_value (null meaning unlimited).
    • getRequiredTier(featureKey) — walks plans in sort_order and returns the name of the cheapest tier that has the feature enabled, defaulting to "Enterprise" if none do. This is what upgrade prompts point users towards.
    • allPlans / allFeatures / userFeatures — raw data for rendering full comparison tables (used on Pricing.tsx and Billing.tsx).

    The demo account (demo@rankpilot.app) is hard-coded to resolve to the agency (Enterprise) tier so sales demos always show full functionality.

    usePlanUsage

    src/hooks/usePlanUsage.ts builds on usePlanFeatures to compare actual consumption against plan limits, so the UI can nudge users before they hit a hard wall rather than only blocking them afterwards. It:

    • Counts this month's site_audits, all-time keywords, and this month's blog_articles for the current user (used as proxies for basic_site_audit, ai_keyword_research and content_optimizer usage).
    • For every enabled feature with a numeric limit_value, computes used, limit, percent (capped at 100), isNear (≥80% consumed) and isOver (used ≥ limit).
    • Works out nextTier — the next tier up from the user's current one in sort_order, defaulting to "Growth" for a first-time free user or "Enterprise" past Pro.
    • Returns items (all tracked usage), warnings (only the near-limit ones) and nextTier, which Billing.tsx and other dashboard widgets use to show progress bars and "You're approaching your monthly limit — Upgrade →" messaging.

    Server-side enforcement: check-feature-access

    Client-side hooks are for UX only; the check-feature-access Supabase edge function is the authoritative gate that back-end operations should call before performing a restricted action. Given a feature_key and the caller's auth token, it:

    1. Verifies the JWT and resolves the user.
    2. Grants unconditional access if the user has the admin role.
    3. Looks up user_subscriptions the same way as the client hook (respecting an active, unexpired trial's trial_tier).
    4. Maps the tier to a plan name and fetches the matching plan_features row for the requested feature_key.
    5. Returns { allowed: true, limit } if the feature is enabled on that plan, or { allowed: false, limit: 0, required_tier } — with required_tier worked out by finding the lowest-sort_order plan that has the feature enabled — if it isn't.

    Any caller (edge function or frontend) can call this to enforce gating outside of direct UI checks, since it doesn't trust client-reported tier information.

    Locked-feature UI

    • LockedFeatureCard (src/components/LockedFeatureCard.tsx) renders a dashed-border placeholder with a lock icon, a "{tier} feature" badge, a short explanation, and an "Upgrade to {tier}" button (colour-coded per tier — blue for Growth, amber for Pro, purple for Enterprise). It's used in place of a locked tool's normal UI, with the onUpgrade callback typically routing to /pricing.
    • UpgradePrompt and FeatureGateModal provide inline/modal variants of the same upsell pattern for contextual prompts (e.g. hitting a usage limit mid-action) rather than a full page section.
    • FeatureGate and PlanLimitBanner components wrap page sections or show persistent banners that read from usePlanFeatures/usePlanUsage to decide whether to render the real content, a locked card, or a near-limit warning banner.
    • AeoAccessGate is a specialised version of this pattern for the AEO/GEO add-on (see plans-and-billing.md), showing an "Included" card for Enterprise and a "Buy 7-Day Pass" card for Pro, with Stripe Checkout wired directly into the gate.

    What each tier unlocks (summary)

    • Free — Basic Site Audit, daily rank tracking, suggestion-only Autopilot, unlimited scans/crawls, PDF/CSV export, Google Tools.
    • Growth — adds full technical audit, AI keyword research, competitor and backlink analysis, AI content generation, Auto-Fix (20 pages/month) and API access.
    • Pro — adds full Autopilot (push to CMS), Auto-Fix up to 100 pages/month, 500 AI content credits, custom prompts and a priority AI model.
    • Enterprise — unlimited Auto-Fix, Core Web Vitals auto-fixes, white-label reports, and (on a paid, non-trial subscription) AEO/GEO mode included.

    Exact limits and toggles are ultimately driven by the plan_features table, so the values above reflect the current defaults shown on the pricing and billing pages — always treat that table, not this document, as the live source of truth.