All Docs
FeaturesNurtureHubUpdated March 20, 2026

Plan-Based Feature Gating — v1.0.9

Plan-Based Feature Gating

Released in v1.0.9

NurtureHub enforces plan limits and feature access server-side at the tRPC layer. This means every plan boundary — contact caps, sequence caps, CRM access, white-label footers — is validated on the server regardless of what the client sends.


How Feature Gating Works

When an authenticated request reaches a gated tRPC procedure, the server:

  1. Looks up the account's active plan via the subscription table.
  2. Reads current usage from the usageEvents table.
  3. Compares usage against the plan's defined limits.
  4. Either proceeds with the request or returns a structured error with an upgradeRequired flag.

Client-side UI reflects these server-enforced states but is not the enforcement mechanism.


Enforced Limits

Contact Count

Each plan has a maximum number of contacts. Attempting to create a contact when the limit is reached returns a BAD_REQUEST error. The account must upgrade or remove existing contacts before adding new ones.

Sequence Count

Each plan has a maximum number of active nurture sequences. Creating a sequence beyond this limit is rejected at the API layer.

CRM Integrations (Growth+)

Access to CRM integrations — including native agentOS sync and open-API connections to Reapit, Alto, Street, and Loop — requires a Growth plan or higher. Starter accounts receive a FORBIDDEN error when attempting to connect or use a CRM integration.

White-Label Email Footers (Agency Pro only)

Removing NurtureHub branding from outgoing email sequences is an Agency Pro-only feature. Accounts on Starter or Growth plans will see an upgrade prompt in the email settings UI.


Plan Limit Summary

FeatureStarterGrowthAgency Pro
Contact limit enforcement
Sequence limit enforcement
CRM integrations
White-label email footers

The usageLimits Query

The billing tRPC router exposes a usageLimits query that returns the account's current usage and plan limits in a single call.

Request

const limits = await trpc.billing.usageLimits.query();

Response Shape

{
  contacts: {
    used: number,       // Current number of contacts
    limit: number,      // Plan maximum (-1 = unlimited)
    percent: number     // Percentage used (0–100)
  },
  sequences: {
    used: number,
    limit: number,
    percent: number
  },
  crmIntegrations: {
    allowed: boolean    // true if plan is Growth or Agency Pro
  },
  whiteLabelFooters: {
    allowed: boolean    // true if plan is Agency Pro
  }
}

Data Sources

  • subscription table — determines the active plan and its defined limits.
  • usageEvents table — provides current usage counts.

No additional tables or schema changes were introduced in this release.


Upgrade Prompts

The UI surfaces upgrade prompts based on the usageLimits response:

StateTriggerBehaviour
Approaching limitUsage ≥ 80% of limitInline warning with an upgrade call-to-action
Limit reachedUsage = 100% of limitAction blocked; hard upgrade prompt shown in place of the action button
Feature not on planallowed: falseInline upgrade prompt shown on the relevant settings or action surface

Call billing.usageLimits on dashboard load to ensure upgrade prompt state is primed before the user encounters a blocked action.


Error Handling

When a gated action is blocked, the tRPC procedure throws a structured error. Example:

// Contact limit reached
{
  code: 'BAD_REQUEST',
  message: 'Contact limit reached for your current plan.',
  data: {
    upgradeRequired: true,
    limitType: 'contacts'
  }
}

// Feature not on plan
{
  code: 'FORBIDDEN',
  message: 'CRM integrations are available on Growth and above.',
  data: {
    upgradeRequired: true,
    limitType: 'crmIntegrations'
  }
}

Handle these errors in your client code to surface the appropriate upgrade prompt rather than a generic error message.