Skip to content

Entitlements

Features and limits a brand grants through its products, resolved for each customer from what they've bought. Ownrecord says what a customer may use; your product enforces it.

An entitlement is a capability or limit a customer has because of what they've bought, such as feature.export or seats.max = 10. Your product asks Ownrecord what a customer is entitled to, instead of keeping its own copy of each customer's plan.

Features

Each brand defines its features. A feature has:

  • a key, such as seats.max;
  • a type: boolean (on or off), limit (a number) or enum (one of a set of values);
  • a rule for combining values when a customer gets the same feature from several sources;
  • a default value;
  • for limits, whether it's per unit: the value a product grants is multiplied by the quantity, so a plan granting 5 projects per seat gives a customer with 3 seats 15 projects.

Products grant features: each product lists feature keys and the value it grants. A product marked lifetime entitlements grants them for good when it's bought once, as with a lifetime licence; otherwise only subscriptions to it grant them, while they last.

Create features with features.create and grant them in products.create or products.update, or in the console under Catalog. Test and live mode have separate features and products.

How a customer's entitlements are resolved

Ownrecord combines these sources, using each feature's combine rule:

  1. Manual grants: overrides set by your support team, with a reason and an optional expiry date.
  2. Subscriptions that are active or trialing (a trial grants the plan in full), past_due ones until the grace period ends, and paused ones only if the brand keeps entitlements during a pause (off by default). A subscription item with a quantity of 0 grants nothing.
  3. One-time purchases of products with lifetime entitlements, while the payment stands (a fully refunded payment no longer counts).
  4. Feature defaults, for every feature nothing else grants.
Combine ruleFitsResult
maxlimitsThe highest value. An override can raise a limit but not lower it.
sumlimitsAll values added up, so an override or an add-on adds to the plan.
orbooleansOn if any source switches it on. An override can switch a feature on but not off.
firstbooleans, limits, enumsThe first source in the order above decides: the newest override, then the oldest subscription, then the oldest purchase.

The result has one value for every feature of the brand, and lists the sources each value came from.

When entitlements change

Ownrecord stores each customer's entitlements and recomputes them within one job cycle of every change: a subscription created, changed, paused or canceled, a lifetime purchase, an override granted or removed, a feature's combine rule, default or per-unit setting changed, or a product's entitlements changed. Time-based changes are picked up the same way: an override reaching its expiry date, or the end of a past-due grace period, is applied by a sweep that runs every minute.

A customer on a test clock lives at the clock's time instead: their entitlements are resolved at the clock's time (computedAt is a time on the clock), an override's expiry date is a time on the clock, and time-based changes happen when you advance the clock, not when real time passes.

When a value changes, the brand's webhook endpoints receive customer.entitlements.updated. Its data.object is the customer's full set, as the API returns it, plus changes: one entry per feature whose value changed, with before and after. previous_attributes.values has the previous set.

JSON
{
  "type": "customer.entitlements.updated",
  "data": {
    "object": {
      "object": "customer_entitlements",
      "customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
      "values": { "feature.export": true, "seats.max": 20 },
      "sources": [
        {
          "type": "override",
          "ref": "ceo_01m4ty3c2b5qx7mhd9k8w6s0b2",
          "featureKey": "seats.max",
          "value": 20
        }
      ],
      "computedAt": "2026-10-14T10:00:05.000Z",
      "changes": [{ "featureKey": "seats.max", "before": 15, "after": 20 }]
    }
  },
  "previous_attributes": { "values": { "feature.export": true, "seats.max": 15 } }
}

A customer who only has defaults hasn't been materialised yet and gets no event when a default changes; the feature.updated event tells you that.

Reading entitlements

  • customers.entitlements.get (GET /customers/{id}/entitlements) returns values, sources and computedAt.
  • entitlements.check (GET /entitlements/check?customer=…&feature=…) returns one feature's value, and enabled (true for true or a limit above 0). Use it for the checks your product makes on every request.

Both read the stored set. While a recompute is pending (a change just happened or an override just expired), they resolve the values from the sources instead, so a read never returns values known to be out of date.

The TypeScript SDK caches each customer's entitlements in memory for 60 seconds and drops them when the webhook arrives:

TypeScript
import { createEntitlements } from "@ownrecord/sdk";

const entitlements = createEntitlements({ apiKey: process.env.OWNRECORD_API_KEY });

if (await entitlements.has(customerId, "feature.export")) {
  // …
}
const seats = await entitlements.limit(customerId, "seats.max");

// In your webhook route, with the raw body: verifies the signature, and on customer.entitlements.updated
// forgets that customer's cached entitlements so the next check reads the new values.
const event = await entitlements.handleWebhook(rawBody, request.headers.get("Ownrecord-Signature"), secret);

has fails closed: a missing feature, false, 0 or an enum value is "not enabled". limit returns 0 when the value isn't a whole number. Compare enum values with entitlements.value(customerId, "support.level").

Overrides

Support can grant a customer a value of one feature, with a reason and an optional expiry: extra seats while a team migrates, or a feature switched on for a beta tester. In the console, open the customer and use Entitlements → Grant override; through the API, call customers.entitlement_overrides.create.

  • An override is one more source and merges by the feature's combine rule (see the table above).
  • It applies until expiresAt, or until it's deleted with customers.entitlement_overrides.delete, which also takes a reason.
  • customers.entitlement_overrides.list lists a customer's overrides, including expired ones.
  • A customer can have up to 100 active overrides. Granting and removing them needs customers:write (owners, admins and support), and both are in the audit log.

You enforce, Ownrecord informs

Entitlements are information. Ownrecord doesn't deliver your service or block your users; your product checks entitlements and decides what to allow.

Entitlements aren't permissions. Roles control what your team members may do in Ownrecord. Entitlements describe what your customers may do in your product.

Ownrecord uses entitlements for its own plans too: the limits of your Ownrecord plan, such as how many brands you can create, are entitlements of the Ownrecord brand.