Docs · Billing & Entitlements

Gating a Feature

Using RequireEntitlement to put a resource behind payment.

Putting a resource behind payment is one line: include RequireEntitlement in its controller. Leaving it out is also one line: don't include it. There's no separate configuration file listing which features are paid; the controller itself is the declaration.

Gating a controller

class InvoicesController < ApplicationController
  include RequireEntitlement
  # ...
end

On every action, this checks Billing.for(Current.account).entitled? and redirects to the pricing page when it's false. The example ProjectsController uses exactly this include; copy it as the reference when you scaffold a new paid resource with Adding a Resource.

Who gets through anyway

Admin accounts (User#role == "admin") are exempt from the entitlement check, so an admin can always reach a paid controller regardless of subscription status. This matters for support and for console-driven operations: see Roles and Teams for what admin? covers today.

What this doesn't do

RequireEntitlement answers one question: is this workspace allowed to use the paid surface at all. It says nothing about which specific records the signed-in user can see or modify within that surface; that's Pundit's job, and it applies regardless of whether the feature is paid or free. A controller almost always needs both: include RequireEntitlement at the top, and authorize @record / policy_scope(...) in each action. See Authorization for why these stay separate.

Testing the gate

Add an entitlement-gate test alongside the tenant-isolation test in your request spec: an unsubscribed account hitting a paid controller's action should be redirected to pricing, not shown the feature and not met with an error. The Project resource's request spec is the reference for this assertion.

Next

See how to configure the plan the gate checks against: Stripe Setup.