Skip to main content

Entitlements

Resources a customer on a plan is allowed to use.

Entitlements live inside plan definitions, identified by a string ID. An entitlement without a limit is a boolean flag — present means the customer has access. An entitlement with a limit is metered.


Entitlement schema

entitlements:
<entitlement-id>:
description: string # Optional. Appears in event payloads.
hidden: bool # Exclude from public serialization. Default: false.
scope: string # Optional. Customer type scope (e.g. 'org').
limit: # Optional. Omit for a boolean access entitlement.
<Limit fields>

description

Human-readable description. Included in event payloads (meter-limit, meter-overage, meter-changed) and in the customer object. Useful for support tooling and dashboards.

hidden

If true, the entitlement is excluded from public policy serialization. Use for internal metering that customers shouldn't see.

scope

Ties the entitlement to a customer type. When set, enforcement operations for this entitlement look through the customer's refs to find a customer of the matching type, and operate against that customer's meter instead.

The canonical use case is org-scoped entitlements — a seats entitlement with scope: org means all users who ref the same org draw from the org's shared seat pool, not their individual meters.

seats:
scope: org
limit:
credit: seat
mode: hard
value: 10
increment: 1

Any member of the org calling policy.increment('user_123', 'seats') draws from the org's pool.


Limit schema

limit:
credit: string # Required. Credit ID for this limit.
mode: string # 'hard' | 'soft' | 'observe'. Default: 'hard'.
value: float | string # Maximum value. Supports unit strings (e.g. '2GiB'). Default: 0.
increment: float | string # Amount per increment()/decrement(). Default: 1.
minimum: float | string # Optional floor value for decrement().
resets: bool # Does this meter reset? Default: false.
reset_inc: duration # Reset interval. Default: 30days.
override_expires_on: ms # Expiry for customer overrides only.

credit

The credit ID this limit is denominated in. Must reference a credit defined in policy.credits. Required.

mode

Controls what happens when consumption reaches the limit value.

ModeBehavior
hardBlocks the operation. allow() returns false. Fires meter-limit. No overage unless covered by a grant. Default.
softAllows the operation. Fires meter-overage. Limitr draws from applicable grants first before firing the event.
observeNever blocks. Meters indefinitely. No enforcement. Useful for tracking usage without restricting it.

value

The enforced maximum. When a customer's meter reaches this value, hard mode blocks and soft mode fires overage. Supports raw floats or unit strings when the credit has stof_units defined.

value: 500000 # raw float
value: '2GiB' # unit string — converted to the credit's stof_units
value: 0 # soft limit of 0: every increment fires meter-overage immediately

increment

The amount consumed or released per increment() / decrement() call. Defaults to 1. Supports unit strings.

increment: 1 # one seat per call
increment: '100MB' # 100MB per upload slot

minimum

An optional floor value. decrement() will not reduce the meter below this value.

resets and reset_inc

Whether and how often the meter resets. When resets: true, the meter is zeroed at each reset_inc interval starting from when the customer was created.

resets: true
reset_inc: 1day # resets daily
reset_inc: 30days # resets monthly (default when reset_inc is omitted)

Valid time units: ms, s, min, hr, day, days.

Calendar awareness

Resets are not calendar-aware by default. They run from the customer's creation timestamp, not from the start of a calendar month or day.


Boolean entitlements

An entitlement with no limit field is a boolean flag. allow() or check() against it returns true if the entitlement exists on the plan, false if it doesn't.

entitlements:
pdf_export:
description: Access to PDF export feature
const canExport = await policy.check('user_123', 'pdf_export');
if (!canExport) return { error: 'Upgrade to export PDFs' };

Customer overrides

An override replaces the limit for a specific customer without changing the plan. Any limit field can be overridden. Overrides can have an expiry, after which the customer reverts to their plan's limit.

// Give enterprise_org 500 seats instead of the plan default
await policy.createCustomerOverride('enterprise_org', 'seats', 500);

// Override with an expiry
await policy.createCustomerOverride(
'user_123',
'chat_input',
2000000,
Date.now() + 30 * 24 * 60 * 60 * 1000 // expires in 30 days
);

// Remove an override — customer reverts to plan limit
await policy.removeCustomerOverride('user_123', 'chat_input');

Full signature:

policy.createCustomerOverride(
id: string, // customer ID
entitlement: string, // entitlement name
value?: string | number, // limit value
expires_on?: number, // expiry timestamp (ms)
credit?: string, // credit ID (if changing the credit)
mode?: string, // 'hard' | 'soft' | 'observe'
increment?: number | string,
resets?: boolean,
reset_inc?: number | string
): Promise<string | null> // returns override node ID, or null on failure

SDK

policy.entitlement()

Returns the entitlement record for a given plan ID or customer ID and entitlement name. Includes the resolved limit with any customer override applied.

const ent = await policy.entitlement('user_123', 'chat_input');
// { description, limit: { credit, mode, value, resets, reset_inc, ... } }

policy.limit()

Returns the enforced limit value for a customer's entitlement. Includes credit grant balances in the effective limit when grants is true (default). Returns null if no limit is defined.

const limit = await policy.limit('user_123', 'chat_input'); // with grants
const limit = await policy.limit('user_123', 'chat_input', false); // without grants

policy.createCustomerOverride()

See Customer overrides above.

policy.removeCustomerOverride()

Removes a customer override. The customer reverts to their plan's limit.

await policy.removeCustomerOverride('user_123', 'chat_input');