Skip to content

Tenant API v1 — Marketplace

Base path: /api/tenant/v1/marketplace

Middleware: auth:tenant-api, tenant.user, not.suspended, verified, marketplace.enabled, plus permission middleware.

Permissions: marketplace.view (browse), marketplace.purchase (install / subscribe / remove).

Modules

MethodPathPermissionBehavior
GET/modulesmarketplace.viewPaginated published catalog (search, category_id) + per-row install flags
GET/modules/{module}marketplace.viewDetail + install state for the current workspace
POST/modules/{module}/purchasemarketplace.purchaseInstall free module or start paid checkout
POST/modules/{module}/confirm-checkoutmarketplace.purchaseConfirm return from payment gateway
POST/modules/{module}/cancelmarketplace.purchaseRemove / cancel a non-core installed module

List row extras

FieldMeaning
already_installedActive or trial subscription for this workspace
purchase_pendingPending checkout not yet activated
versionCatalog display version (semver metadata)
is_billablePaid module (SPA badge Billable when not installed)

SPA badge priority: InstalledPendingBillableAvailable (free, not installed). Do not label free opt-in modules as “Included”.

Display currency conversion

Catalog prices are stored in modules.currency (typically USD). Tenant list/detail responses convert monthly_price, yearly_price, and setup_fee into the workspace currency (tenants.currency) for display only.

FieldMeaning
monthly_price / yearly_price / setup_feeAmount in currency (tenant currency when conversion succeeds)
currencyDisplay currency for the amounts above
base_*_price / base_currencyOriginal catalog amounts / currency
billed_currencyCurrency charged at checkout (catalog / Stripe Price — usually USD)
exchange_rate / exchange_rate_atMid-market rate used for the conversion
price_convertedtrue when amounts were converted from the catalog currency

If the FX provider is unavailable, amounts stay in the catalog currency (price_converted: false). Checkout still charges the mapped Stripe (or gateway) Price in billed_currency — conversion is display-only.

Rates: CurrencyConversionServiceopen.er-api.com (configurable via CURRENCY_FX_* env), cached (~12h).

Detail payload extras

FieldMeaning
already_installedActive or trial subscription for this workspace
purchase_pendingPending checkout not yet activated
subscription_status / subscription_sourceCurrent row when present
can_cancelTenant may remove it now (not core-included, no blocking dependents)
required_modules / optional_modules / missing_required_modulesDependencies — modules this one needs (upstream)
blocking_dependentsDependents — installed modules that hard-depend on this one (downstream; blocks remove)

Dependency summaries (required_modules, optional_modules, missing_required_modules) include pricing and install flags so the SPA can offer Install / Subscribe on a missing required module without an extra detail round-trip:

FieldMeaning
id / name / slugCatalog identity
is_billable / is_default_includedCommercial / core flags
already_installed / purchase_pendingWorkspace install state
monthly_price / yearly_price / currencyDisplay amounts (workspace currency when FX succeeds)
base_* / billed_currency / price_convertedSame display-FX shape as list rows

blocking_dependents remains { id, name, slug } only.

UI copy must keep these directions distinct: “no dependencies” means nothing upstream is required; dependents (e.g. Meetings → Calendar) still block remove until those modules are removed first.

Cancel rules

  • Default-included core modules (today: Leads, Tasks) cannot be cancelled by tenants.
  • Hard dependents must be removed first (e.g. remove Meetings before Calendar).
  • Cancel sets status cancelled, clears entitlements immediately, and cancels any live provider subscription.
  • Response includes refreshed modules entitlement slugs for the workspace.

See Entitlements for install / cancel policy.

Official documentation for the EloSync SaaS Platform.