Add-on marketplace
Add-ons are platform capabilities a store can turn on — each gated by
@dscodotco/entitlements. The catalog is code-as-content
(surfaces/console/src/panels/add-ons/catalog.ts): one entry per add-on with a
key (the entitlements feature_key), a name, a category, a tagline, a
whatItDoes bullet list, an optional docsPath, and an optional
priceCentsMonthly. Enabling an add-on flips a single entitlement feature for
the tenant; resolution then falls through to tier defaults where no explicit
row exists.
Two surfaces
- Operators (console, the Add-ons panel): a marketplace grid plus a per-store
enable/disable that flips the tenant feature —
PUT /v1/entitlements/tenants/:tenant/features/:key. An operator can setallowor a harddeny; adenyis a floor a merchant can never write over. - Merchants (portal, the Add-ons panel): a marketplace with real state chips. Items on the self-serve allowlist flip immediately with the merchant’s own credential; everything else routes through a tracked activation request.
Self-serve enable/disable
A closed allowlist (SELF_SERVE_FEATURES in the entitlements module) is
merchant-flippable without operator involvement. It currently covers:
subscriptions, gift_cards, store_credit, pricing_groups,
subscribe_and_save, affiliates, bulk_fulfillment, and funnel_gate.
POST /v1/merchant/entitlements/enable { "feature_key": "affiliates" }
POST /v1/merchant/entitlements/disable { "feature_key": "affiliates" }Both require scope write:own_store, resolve the tenant from the credential
(credential-carries-ownership), and are:
- Idempotent — enabling an already-on feature is an honest no-op
(
already_enabled: true), not a pretended fresh enable; disabling removes theallowrow so resolution falls back to tier defaults (already_disabled: true). - Audited either way it writes (
addon.self_serve_enabled/addon.self_serve_disabled). - Deny-respecting — an operator-set
denyis never merchant-removable, and enabling over it is a403 operator_restricted.
A feature_key that is not on the allowlist (unknown, or an add-on kept
operator-managed) is a 403 not_self_serve — the honest route for those is the
tracked activation request (POST /v1/merchant/entitlements/requests).
Pricing and billing
An add-on’s priceCentsMonthly is the list price shown on the marketplace card
(some self-serve add-ons are priced — e.g. Subscriptions lists at $49/mo).
Enabling an add-on only flips the entitlement — it never bills on its own.
To actually charge for a paid add-on, add the fee to the store’s
merchant-billing contract (a platform_fee_cents_monthly
bump or a dedicated term). Wiring enable through to an automatic add-on fee line is the
next step to close the marketplace-to-revenue loop.
Adding an add-on
Add one entry to catalog.ts with its key (the entitlements feature key),
category, tagline, whatItDoes, and optional priceCentsMonthly /
docsPath. It appears in the marketplace automatically. Putting the key on the
SELF_SERVE_FEATURES allowlist is a separate, deliberate step in the
entitlements module — a catalog entry alone stays operator-managed.