Base 3:16
Concepts

Entitlements

Exactly how check decides, and every reason it can refuse.

access.check is the call on your request path. It answers whether an account may do something, and how much room they have left.

const decision = await base316.access.check({
	externalId: "acct_1234",
	feature: "api_calls",
	requested: 1,
});
{
	"allowed": true,
	"usage": {
		"used": 412,
		"limit": 1000,
		"credits": 0,
		"remaining": 588,
		"unit": "calls",
		"overagePriced": false,
		"overage": false,
		"billableOverage": 0,
		"hardCap": null
	}
}

The rule

For a metered feature, the allowance is:

limit + credits

and only usage beyond that is billable. Prepaid credits extend the plan's limit rather than being spent first, which is why a customer topped up past their limit is not over quota.

A limit declared per unit (limitPerUnit on the plan's entitlement) is multiplied by the subscription's quantity before it is compared: one hundred credits per seat on a five-seat subscription is a limit of five hundred. The check, the entitlement claims in a token and the invoice all scale it the same way, so an application is never refused for usage the invoice then includes.

requested is how much you are about to consume. Passing it lets the check refuse before the work happens rather than after — which is the difference between declining a request and eating the cost of one.

Soft limits and hard caps

Whether the limit refuses depends on whether the plan version prices the feature's usage — a PER_UNIT, MATRIX, tiered or PACKAGE price carrying the feature's id:

The plan version…Past limit + credits, check…
has no usage price on itrefuses with USAGE_LIMIT_EXCEEDED — a hard limit
prices the feature's usageallows, with usage.overage: true, and the usage is billed — a soft limit

On a soft limit, limit: 0 means every unit is billed; limit: null is still unlimited and bills nothing. A FLAT price on a feature does not charge per unit, so it leaves the limit hard. QUANTITY features are always hard: nothing bills a held count past its limit.

usage says which case you are in:

FieldMeaning
overagePricedThe limit is soft: usage past it is billed, not refused
overageThis request takes usage past limit + credits — it will be billed
billableOverageUsage already past limit + credits this period, as the invoice will charge it
hardCapThe ceiling past which check refuses with HARD_CAP_REACHED; null when there is none
remaininglimit + credits - used; below zero on a soft limit is how far into overage

A feature priced per model (a MATRIX price — see per-model pricing) is still checked on its total: used, remaining and billableOverage are the feature's, across every model. GET /v1/customers/{id}/entitlements adds byDimension — this period's usage per row, rated as the next invoice will rate it, with unpriced marking values no row prices.

A hard cap is set per entitlement on the plan (hardCap). It is an absolute ceiling on usage in the period: scaled by quantity like the limit when limitPerUnit is set, and never raised by credits. It needs a numeric limit and cannot be below it. On a hard limit it only matters if credits would carry usage past it.

The cap governs check, not ingestion. An event recorded past it is still metered and billed — see billing.

Entitlement claims in a token (entitlements scope) carry each feature's limit. For a soft-limited feature they add entitlements_overage, feature key to hard cap (null for none): a key there means the number in entitlements is an allowance billed past, not a ceiling to enforce.

Why it can refuse

reason is present only when refused, and is safe to log.

ReasonWhat happened
CUSTOMER_NOT_FOUNDNo customer with that id in this environment
NO_SUBSCRIPTIONThe account has no subscription
SUBSCRIPTION_INACTIVECancelled, paused or incomplete
FEATURE_NOT_FOUNDNo feature with that key
ENTITLEMENT_REQUIREDThe plan does not include this feature
USAGE_LIMIT_EXCEEDEDPast limit + credits on a hard limit
HARD_CAP_REACHEDPast the entitlement's hard cap
USER_NOT_FOUNDThe acting user does not exist
USER_DISABLEDThe user exists but is disabled
NOT_A_MEMBERThe user is not in the organisation being checked
PERMISSION_DENIEDThe user's role does not grant the permission

Show remaining in your own UI before somebody hits the wall. A refusal a customer could have seen coming is a support ticket.

Every feature at once

check answers one feature. To see every feature for an account — the customer page's Effective entitlements panel, or a support tool of your own — ask for all of them:

const { payerId, data } = await base316.customers.entitlements("cus_8f2k");

for (const feature of data) {
	console.log(feature.featureKey, feature.allowed, feature.reason, feature.remaining);
}

GET /v1/customers/:id/entitlements (secret key) runs the same evaluator as check, once per live feature, with the default requested of 1 — so a DENIED here is the refusal your application would get, with the same reason. Each feature also says what its answer rests on:

sourceWhat it names
planThe subscription and plan version deciding it; listed: false means the version omits the feature
creditsThe live credit grants extending the allowance, with what is left on each
payerThe paying account, when this customer is a managed tenant billed to its parent — whose plan decides

Add-ons never appear as a source: an add-on is a charge on the invoice and grants no feature. Nothing is consumed by asking.

Checking a permission too

One call can ask both questions:

await base316.access.check({
	externalId: "acct_1234",
	userId: "usr_abc",
	feature: "reports",
	permission: "reports.export",
});

The account must be entitled to the feature and the acting user must hold the permission. Checking a permission requires a user; checking a feature does not.

Statuses that still entitle

ACTIVE and TRIALING both entitle. So does PAST_DUE — the subscription is live, dunning is expected to recover the payment, and cutting somebody off the moment a card fails punishes them for their bank's decision. PAUSED, CANCELED and INCOMPLETE do not.

What check does not do

It does not record usage. It is a read, every time. Recording is a separate call you make after the work succeeds — see metering.

Keeping them apart is deliberate: work that fails should cost nothing, and a check that billed on inspection would make every retry expensive.

On this page