Plan and Add-on Entitlements
Not every feature is on for every tenant. Core value movement (stablecoin payments, escrow, wallets) is always available, but capabilities like treasury, full compliance screening, and the tokenization module are add-ons that a tenant enables. A workspace's effective feature set is the union of what its plan includes, any mode-specific inclusion, and any overlay grants layered on top. When a request hits a feature it does not have, the API returns a single, predictable error so your integration can react. This page covers how entitlements resolve, how to read them, how to redeem a Feature Access Code, and the feature_not_enabled contract.
How Entitlements Resolve
A workspace's effective feature set is a union of three sources. There is no precedence to reason about: a feature is active if any source grants it.
| Source | What it is | How it changes |
|---|---|---|
| Plan baseline | The features included in the tenant's assigned plan. | Changes when the tenant's plan changes. |
| Workspace mode | Sandbox includes managed OTP and email without broadening the tenant's production grant. | Fixed by the authenticated workspace mode. |
| Overlay grants | Per-tenant add-on grants layered on top of the plan, from a redeemed Feature Access Code or a direct grant by XKOVA staff. | Changes when a code is redeemed, or a grant is added or revoked. |
An overlay grant that duplicates a plan feature is a no-op: the plan already grants it. Revoking an overlay grant only drops the add-on, not a feature the plan itself includes. To drop a plan feature, change the plan.
The Feature Catalog
Feature gating is binary and module-level. There is one feature key per top-level capability, not a read/write split. Either the tenant has the key, or it does not. The add-on keys are:
| Feature key | Gates |
|---|---|
treasury | The treasury surface: multi-signature wallets, signers, and N-of-M approved transfers. See Treasury. |
revenue_controls | Revenue and fee controls, including the revenue treasury link and fee schedules. See Fee Schedules. |
screening_full | The upper-tier compliance screening provider used during the pre-sign pipeline. See Screening and Sanctions. |
byo_oidc | Bring-your-own OIDC identity provider configuration for member sign-in. |
rwa_tokenization | The ERC-721 and ERC-1155 tokenization module and the entire real-world-asset metadata surface. See Real-World Assets. |
billing_webhooks | Customer webhook endpoint and delivery management. Included in every current plan for sandbox and production. |
managed_messaging | XKOVA-managed OTP and member email, including optional Hosted Auth. Included in sandbox; production follows the paid-plan matrix or an explicit grant. |
custom_domains | Verified customer sending domains. Available through Scale or an explicit grant. |
rwa_tokenization.Reading Effective Entitlements
Fetch the caller-tenant's plan and full feature catalog with a single read. The call is tenant-scoped: it reports the plan and, for every feature in the catalog, whether it is active for this tenant and how it became active. The inactive features are returned too, so a settings surface can show the add-ons a tenant has not enabled.
The response carries a plan object (key and name) and a
features array. Each feature row reports its key, name,
description, category, an active boolean, a granted_via
provenance value, and a granted_at timestamp.
granted_via | Meaning | granted_at |
|---|---|---|
plan | Active because the tenant's plan includes it. | null (plan grants are implicit, with no per-tenant timestamp). |
sandbox | Active because the authenticated workspace is sandbox. | null (workspace-mode inclusion is implicit). |
activation_code | An overlay grant from a redeemed Feature Access Code. | ISO-8601 grant time. |
admin_grant | An overlay grant set directly by XKOVA staff. | ISO-8601 grant time. |
null | The feature is not active for this tenant. | null. |
This read is what the console uses to drive sidebar gating and the Plan and Add-ons settings section. See Settings and Branding for the operator view.
Redeeming a Feature Access Code
An add-on is enabled by redeeming a Feature Access Code. Redemption is an admin staff action: it requires the
entitlement.redeem permission, a step-up MFA session (AAL2), and it is audited. The request body
carries only the plaintext code. XKOVA hashes the code before lookup and never logs the
plaintext.
A code references a named grant template, a fixed bundle of feature keys it enables. The templates are:
| Template | Enables |
|---|---|
all_addons | Every add-on feature. |
treasury_only | treasury. |
rwa_only | rwa_tokenization. |
The response reports what changed. granted_features lists the keys this redemption newly
activated, already_active lists keys the tenant already held (nothing to do), and
redemption_id is the audit row id to quote in a support ticket.
Two idempotency properties matter here, and they are different:
- The redemption is idempotent at the feature-set layer. Re-granting a feature the tenant
already has is a no-op, reported in
already_activerather than raising an error. - The code itself is one-time-use across all tenants. Once a code has been redeemed, a second redemption of that same code is rejected. To grant the same features again, issue a fresh code.
Codes may be scoped to a single tenant. A scoped code presented by a different tenant is rejected, and support can distinguish that condition from a mistyped or unknown code. Codes issued via the redeem endpoint cannot be created there; code issuance is an XKOVA management function, not a customer API surface.
The feature_not_enabled Contract
When a request reaches a surface gated by an add-on the tenant does not have, the API returns
403 feature_not_enabled. The check runs after authentication and before the handler, so the
request never touches the gated resource. The error body's details.feature carries the exact
feature key that was missing, so your integration (or the console) can render a targeted "redeem a code to
enable X" prompt rather than a generic denial.
feature_not_enabled is not retryable. Retrying the same call changes nothing; the tenant must
first gain the feature (redeem a code, or have staff grant it). This is distinct from an authorization role
gap: a role failure is role_denied (you are not allowed to perform the action), while
feature_not_enabled means the capability is not turned on for the tenant at all.
The customer-facing errors you may see across entitlement reads and gated calls are:
| Code | Status | When |
|---|---|---|
feature_not_enabled | 403 | The tenant lacks the entitlement a gated
surface requires. details.feature names the key. |
role_denied | 403 | The caller is not an admin staff member, so cannot redeem. |
step_up_required | 403 | The admin caller does not hold a step-up MFA (AAL2) session. |
Feature Access Code ceremony failures belong to the private management surface and are intentionally absent from the customer error catalog.
See the error catalog for the full envelope shape and correlation IDs.
Related
- Authorization and RBAC: how permissions and roles decide who may act, the layer above the entitlement check.
- Settings and Branding: the Plan and Add-ons view where operators read entitlements and redeem codes.
- Treasury, Real-World Assets, and Screening and Sanctions: surfaces that individual add-ons enable.