Skip to main content

Entitlement

Sellable product

This capability is granted by an API key scoped to the `` product (product face). See the product reference below.

Shared consumer entitlement/paywall capability. A (user_id, content-item, mode/tier)-scoped consumer access-DECISION engine (modes hard/soft/metered/time_based/entitlement) + a per-consumer metered free-quota with period reset + a per-category gate-rule engine + the canonical tier ladder + a unified consumer subscription state machine (create/supersede/cancel/pause/resume/prorate) + windowed entitlement grants. On a gate requiring payment the charge is DELEGATED to billing-metering (/v1/charge) and its 402 is relayed — this capability never re-implements charging. Distinct from billing-metering's (organization_id, metric) B2B tenant-metering.

  • Group: Commerce & revenue
  • Contract: contracts/entitlement/v1/openapi.yaml
  • Public base: https://api.vagarylabs.com (the consolidated API gateway — one host, per-brand sibling api.<zone>)
  • Auth: a product API key (vgk_…) issued from the console — Authorization: Bearer vgk_…
  • Product face (customer-keyed):
    • https://api.vagarylabs.com/product/v1/entitlement/check

Endpoints

MethodPathSummary
POST/v1/access/checkThe consumer access decision for a content item. Resolves the effective gate mode (a category rule overrides the org default; a free item with no active rule short-circuits to grant) then dispatches on mode. Item facts (access_tier/category/published_at) and the consumer user_id are CALLER-SUPPLIED inputs (I4 — the capability never fetches platform content). Returns {has_access, reason, preview, required_tier, mode, remaining?}.
POST/v1/view/&#123;item_id&#125;Record a metered content view for a consumer (advances the metered free-quota); returns remaining
GET/v1/remainingThe consumer's remaining free-quota this period
POST/v1/reset-usage/&#123;user_id&#125;Reset (clear) a consumer's metered usage
GET/v1/configThe org's gate config (enabled, free_article_limit, reset_period, default_mode, time_window_hours)
PUT/v1/configUpdate the org's gate config (persistent per-org; the platform kept this in-memory)
GET/v1/tiersThe canonical tier ladder (mechanism — order + labels; PRICES/FEATURES are domain packaging)
GET/v1/rulesList the org's per-category gate rules
PUT/v1/rules/&#123;category&#125;Set (upsert) a per-category gate rule {mode, required_tier, enabled} — the domain override point
DELETE/v1/rules/&#123;category&#125;Remove a per-category gate rule
POST/v1/membershipsCreate (supersede-on-create) a consumer subscription — at most one active per (org, user). If a payment block is present the charge is DELEGATED to billing-metering FIRST (fail-closed — no active subscription is written unless the charge is actually COLLECTED); a billing 402/422 is relayed verbatim, and a charge that only created a hosted checkout (requires_action) is relayed as 402 with its hosted_url rather than granting access.
GET/v1/memberships/activeThe consumer's active subscription (by user_id)
DELETE/v1/memberships/activeCancel the consumer's active subscription
GET/v1/memberships/&#123;subscription_id&#125;A subscription by id
POST/v1/memberships/&#123;subscription_id&#125;/pausePause an active subscription
POST/v1/memberships/&#123;subscription_id&#125;/resumeResume a paused subscription (shifts next_billing_date by the pause duration)
POST/v1/memberships/&#123;subscription_id&#125;/prorateChange a subscription's plan with proration. tier_prices is DOMAIN pricing the caller passes (the capability hardcodes no price); it computes the prorated credit/charge over the remaining billing cycle.
POST/v1/entitlementsGrant a windowed / one-time entitlement to a consumer for an item (the entitlement-window mode). If a payment block is present the charge is DELEGATED to billing-metering FIRST (fail-closed; a billing 402 is relayed). ttl_seconds (or expires_at epoch) bounds the window; omit both for a permanent grant.
GET/v1/entitlements/checkWhether a consumer holds an active (non-expired) entitlement for an item
GET/healthLiveness + store/tenancy/billing-consume posture (never the DSN itself)
GET/metricsPrometheus exposition

Schemas

Error

FieldTypeDescription
errorstringstable machine code (e.g. validation_error, not_found, unauthorized)
reasonstringhuman-readable one-line explanation (no secrets/PII)
detailobject
request_idstring

Payment

Optional payment block — when present, the charge is DELEGATED to billing-metering /v1/charge (this capability re-implements no charging). A billing failure (402/422) is relayed and the gated action is NOT applied (fail-closed).

SEGMENT ROUTING (ADR-110 D3 + its E14 addendum): supply EITHER rail OR customer_country. With a country, BILLING-METERING selects the rail — India to razorpay (Vagary Labs LLP is the seller of record), rest of world to the Dodo Merchant of Record (the MoR becomes the seller, issues its own invoice, and our tax is suppressed). Entitlement does NOT contain that map: rail choice is a tax decision owned by the money layer, and a copy here would be a second authority for one fact (payment-rail-build-vs-buy.md §3 rules 4-5). NOTE for subscriptions: neither rail supports unattended renewal from a stored card, so each period needs a fresh customer-completed checkout — call billing-metering /v1/rails/select to read unattended_renewal_supported before designing a renewal.

FieldTypeDescription
railstringbilling-metering rail (paypal
customer_countrystringISO 3166-1 alpha-2 of the CUSTOMER — forwarded to billing-metering, which selects the rail. Unrecognised/absent resolves to the Merchant-of-Record rail (the fail-safe direction).
select_railbooleanapply ADR-110 D3 routing with no country known (resolves to the MoR rail). Absent = false.
amount_centsinteger
currencystring
recurringboolean
idempotency_keystring

AccessCheckRequest

FieldTypeDescription
item_idstring
user_idstringnull/absent = anonymous consumer
access_tierstringthe item's required tier (caller-supplied, I4)
categorystringselects a per-category gate rule if one exists
published_atnumberepoch seconds (time_based mode input)
modestringoptional per-request mode override

AccessDecision

FieldTypeDescription
has_accessboolean
reasonstringmachine reason (free_content
previewbooleanmay a preview be shown on denial
required_tierstring
modestring
remainingintegermetered mode only — free views left after this one
organization_idstring
item_idstring

ViewRequest

FieldTypeDescription
user_idstring

Remaining

FieldTypeDescription
user_idstring
limitinteger
usedinteger
remaininginteger
reset_periodstring

GateConfig

FieldTypeDescription
organization_idstring
enabledboolean
free_article_limitinteger
reset_periodstring
default_modestring
time_window_hoursinteger

RuleCreate

FieldTypeDescription
modestring
required_tierstring
enabledboolean

Generated by scripts/gen-capability-docs.py from contracts/entitlement/v1/openapi.yaml — the contract IS the source of truth; edit the contract, not this page.