ReferenceAdd-on marketplace

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 set allow or a hard deny; a deny is 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 the allow row 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 deny is never merchant-removable, and enabling over it is a 403 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.