Platform Guide

Marketplace Service — Platform Overview & Integration Guide

This guide explains what the Marketplace Service is, how to install it locally, how connectors are registered and enabled, and how consuming platform services receive effective configuration at runtime.

Overview

The Marketplace Service is the platform's app registry and connector marketplace. Think of it as the "app store" for the Equinox commerce platform: it holds a catalogue of apps (also called connectors) that extend or plug capabilities into other platform services (cart, PIM, OMS, subscription, user, account, etc.), and it governs their whole lifecycle — from registration and approval, through per-business and per-store enablement, layered configuration, versioning, health checks and audit, all the way to deprecation and archival.

A newcomer can picture it like this:

  1. A provider (in-house team or a third party) registers an app. The app declares how the platform reaches it (a deployment: an AWS Lambda, a bundled npm/Maven connector artifact, or an external HTTP service), what interfaces it implements (e.g. PaymentIntegrationV1), and what capabilities it offers (e.g. PAYMENT).
  2. An admin moves the app through an approval workflow (draftpendingapproved), then enables it for a specific business and store, optionally as the default app for an interface.
  3. Once enabled and configured, other platform services discover the app and its effective configuration (business + store + service override layers merged together) and invoke it. Marketplace publishes app.* events on EventBridge so those services keep their local copies in sync.

Important: Marketplace does not process checkout, payments, or shipping itself. It is the registry and configuration hub — consuming services (cart, user, account, etc.) resolve which connector to call and with what settings.


Service at a Glance

Property Value
Purpose App registry and connector marketplace — other services discover and invoke pluggable integration apps at runtime
Service name eq-marketplace-service
API prefix /v1
Framework Fastify 5.x + TypeScript
Database MongoDB 6.x
Cache Redis 6+ (optional)
Auth AWS Cognito JWT / test tokens (local dev)
Health check GET /health
Events AWS EventBridge (app.*, business.app.*)
Consumer SDK @equinox/marketplace-integration (eq-component/eq-marketplace-integration)

Architecture

High-Level Component View

High-Level Component View — eq-marketplace-service architecture

Connector Installation & Sync

After an admin PUTs a store override with instanceId or serviceInstanceMap, marketplace publishes app.selection.synced. Store enable must be performed only after the override is applied.

Connector Installation and Sync — 7-step centralized flow

Configuration Layer Hierarchy

Configuration Layer Hierarchy — override merge order

Deployment Types

Type Description Use case
Lambda Serverless function Third-party integrations, isolated execution
Bundled NPM module in consumer service In-house platform connectors (@equinox/eq-cart-apps, etc.)
External HTTP API Third-party REST APIs

Centralized App Selection Model

Marketplace is the single source of truth for app registration, enablement, and per-instance selection.

App Selection Flow

Centralized App Selection Flow — 7-step marketplace install path

1. Create Interface → 2. Register App → 3. Approve App → 4. Business Enable
→ 5. PUT Override (triggers app.selection.synced) → 6. Store Enable → 7. Verify

Capability-Specific Install Rules

All connectors follow the same enablement order: business enable → PUT override → store enable → verify. Store enable must not be done before the override is applied.

Capability Override required before store enable? Contract validation? Notes
PAYMENT Yes (paymentProvider in override) Yes (payment-v1.json) May use two override PUTs (provider config, then instanceId)
USER_PROFILE Yes No Single override with instanceId
NOTIFICATION Yes Varies Uses serviceInstanceMap for multi-service
CATALOG / INVENTORY / ORDER Yes (business settings in override) Via interfaces See PIM guide

Key Concepts

Term Meaning
supportedServices Which consumer services receive events and may hold a selection. Values: cart, user, pim, order, account, etc.
capabilities / adapterCategory Connector type in UPPERCASE — lookup key in marketplace_selections (e.g. PAYMENT, USER_PROFILE, CATALOG). Derived from app.capabilities[0].
instanceId / collectionId Per-service tenant ID from foundation store associations. This is the selection lookup key used by adapters (getSelectedApp(instanceId, capability)).
serviceInstanceMap Routes one override to multiple services, each with its own instanceId: { "cart": "<cartCollectionId>", "user": "<userCollectionId>" }.
configurationSchema Effective connector config sent in sync events. Connectors read this field at runtime — not the deprecated settings field.
appSnapshot Frozen app metadata (name, version, deployment, endpoints, auth) included in sync events.
Schema default values Defined at app registration; merged into the event payload when omitted from the override PUT.
serviceIdentity Consumer service's identity in the SDK plugin (e.g. cart, user, account) — used to filter events and resolve serviceInstanceMap.

Resolving instanceIds (collectionIds)

Each service has its own instance ID for a store. Resolve them from foundation-service before calling the override API:

GET {FOUNDATION}/v1/stores/{storeId}/effective

Read serviceAssociations (or equivalent) — each entry has an associationType (service name) and collectionId (the instanceId for that service).

Example (dev):

Service collectionId (example)
cart 6a5f2ec09438bfc44392c8cd
user 6a5f2ebc6dc9464e559df225
pim 6a5f2ebe98e8e7574ee0ef3f

How Consuming Services Receive Apps

Consuming services integrate via @equinox/marketplace-integration. The SDK's MarketplaceEventHandler listens to EventBridge events and maintains two local MongoDB collections:

Collection Populated by Purpose
enabled_apps app.enabled, app.disabled, app.config.* Zero-hop runtime discovery — which apps are active for a store
marketplace_selections app.selection.synced Adapter lookup — getSelectedApp(instanceId, adapterCategory) returns app + config

Event Types Consumers Handle

Event What it does
app.registered / app.updated / app.version.published Updates local app projection (filtered by supportedServices)
app.enabled / app.disabled Toggles store enablement in enabled_apps
app.config.created / app.config.updated Applies layered override config
app.selection.synced Writes full selection to marketplace_selections + propagates configurationSchema to enabled_apps

The app.selection.synced event is published when a PUT override includes instanceId (single service) or overrides.serviceInstanceMap (multi-service). The SDK resolves the correct instanceId per serviceIdentity and skips events where the service is not in supportedServices.

SDK location: eq-component/eq-marketplace-integration/src/events/MarketplaceEventHandler.ts

Consumer Service Prerequisites

Before a service can receive marketplace selections, ensure:

Requirement Details
SDK package @equinox/marketplace-integration installed (version per service; e.g. account ≥ 1.0.30)
Plugin registration marketplaceHostPlugin with correct serviceIdentity (cart, user, account, …)
EventBridge rule Routes source: marketplace-service, detail-type prefix app. to the service Lambda
Bundled connectors npm packages deployed in consumer Lambda layer (e.g. @equinox/eq-cart-apps, @equinox/eq-account-apps)
MongoDB collections marketplace_selections, enabled_apps (created automatically by SDK)

Runtime Invocation

At checkout or other workflows, the consumer adapter:

  1. Calls getSelectedApp(instanceId, "USER_PROFILE") (or other capability)
  2. Reads selection.configurationSchema (not settings)
  3. Loads the bundled npm connector or calls the external HTTP endpoint
  4. Passes effective config + caller Bearer token to the connector

Platform Connector Catalog

The platform ships a set of pre-seeded, approved connectors via webbox migration scripts. After seeding, admins must PUT tenant-specific overrides first, then enable per business/store.

appId Capability supportedServices npm Package
eq9-connector-user-iam-v1 IAM user @equinox/eq-user-apps
eq9-connector-cart-userprofile-v1 USER_PROFILE cart @equinox/eq-cart-apps
eq9-connector-platform-notification-v1 NOTIFICATION order, user, account @equinox/connectors-notification-v1
eq9-connector-cart-catalog-v1 CATALOG, PRICING cart @equinox/eq-cart-apps
eq9-connector-platform-inventory-v1 INVENTORY cart, pim @equinox/eq-cart-apps
eq9-connector-cart-order-v1 ORDER cart @equinox/eq-cart-apps
eq9-connector-platform-payment-v1 PAYMENT cart, user, order @equinox/eq-cart-apps
eq9-connector-cart-tax-v1 TAX cart @equinox/eq-cart-apps
eq9-connector-cart-address-v1 ADDRESS cart @equinox/eq-cart-apps
eq9-connector-cart-shipping-v1 SHIPPING cart @equinox/eq-cart-apps
eq9-connector-account-foundation-v1 FOUNDATION account @equinox/eq-account-apps
eq9-connector-account-user-v1 USER account @equinox/eq-account-apps

Load in shared environments: npm run db:load


Walkthrough: Install a USER_PROFILE Connector

This walkthrough installs a USER_PROFILE connector for cart-service. Replace {MARKETPLACE}, {CART}, {businessId}, {storeId}, {TOKEN}, and {instanceId} with your environment values.

Order: PUT override (Step 5) must be completed before store enable (Step 6).

Step 1 — Create Interface

POST {MARKETPLACE}/v1/interfaces
Authorization: Bearer {TOKEN}
{
  "interfaceId": "iface-userprofile-integration-v1",
  "name": "UserProfileIntegrationV1",
  "version": "1.0",
  "category": "userprofile",
  "description": "User profile integration - payment profile lookup for saved cards",
  "ownerService": "cart-service",
  "operations": ["getPaymentProfile"]
}

Step 2 — Create App

POST {MARKETPLACE}/v1/apps
Authorization: Bearer {TOKEN}

Key fields:

{
  "appId": "eq-cart-userprofile-connector-v1",
  "name": "Cart User Profile Connector",
  "version": "1.0.0",
  "deployment": { "type": "bundled", "npmPackage": "@equinox/eq-cart-apps" },
  "supportedInterfaces": [{ "name": "UserProfileIntegrationV1", "version": "1.0" }],
  "capabilities": ["USER_PROFILE"],
  "supportedServices": ["cart"],
  "authentication": { "type": "bearer-jwt" },
  "syncSupported": true,
  "asyncSupported": false
}

See the full payload with endpoints and configurationSchema in the testing guide.

Step 3 — Approve App

POST {MARKETPLACE}/v1/apps/eq-cart-userprofile-connector-v1/status
{ "status": "pending", "reason": "Ready for review" }

Then:

{ "status": "approved", "reason": "User profile connector verified", "reviewedBy": "qa" }

Step 4 — Business Enable

POST {MARKETPLACE}/v1/businesses/{businessId}/apps/eq-cart-userprofile-connector-v1/enable

Body: {}

Step 5 — PUT Override with instanceId (triggers selection sync)

PUT {MARKETPLACE}/v1/businesses/{businessId}/stores/{storeId}/apps/eq-cart-userprofile-connector-v1/overrides
{
  "layer": "store",
  "instanceId": "{instanceId}",
  "appliedBy": "admin",
  "overrides": {
    "configurationSchema": {
      "connectorType": "userprofile",
      "targetServiceUrl": "https://eq9-dev-env.equinox.shop/userservices-dev",
      "hmacSecret": "<secret>",
      "storeId": "{storeId}",
      "instanceId": "{instanceId}",
      "appId": "eq-cart-userprofile-connector-v1",
      "timeout": 30000,
      "maxRetries": 3
    }
  }
}

What happens automatically:

  1. Marketplace publishes app.selection.synced to EventBridge
  2. EventBridge delivers to cart-service Lambda
  3. Cart SDK handler writes to marketplace_selections collection

Step 6 — Store Enable

POST {MARKETPLACE}/v1/stores/{storeId}/apps/eq-cart-userprofile-connector-v1/enable?businessId={businessId}
{
  "version": "1.0.0",
  "interfaceName": "UserProfileIntegrationV1",
  "setAsDefault": true,
  "seedDefaults": true
}

Step 7 — Verify in Cart

GET {CART}/v1/marketplace/instances/{instanceId}/apps
Authorization: Bearer {TOKEN}

Expected: 200 OK with a selections array containing adapterCategory: "USER_PROFILE", appSnapshot, and configurationSchema.

Field Mapping: Old Cart Payloads → New Centralized

Old field (cart selection) Where it goes Which API
appId appId in Create App Step 2
adapterCategory: "USER_PROFILE" capabilities: ["USER_PROFILE"] Step 2
configurationSchema.connectorType overrides.configurationSchema.connectorType Step 5
configurationSchema.targetServiceUrl overrides.configurationSchema.targetServiceUrl Step 5
configurationSchema.hmacSecret overrides.configurationSchema.hmacSecret Step 5
configurationSchema.storeId overrides.configurationSchema.storeId Step 5
configurationSchema.instanceId Top-level instanceId in override body Step 5
configurationSchema.timeout overrides.configurationSchema.timeout Step 5
configurationSchema.maxRetries overrides.configurationSchema.maxRetries Step 5
selectedBy appliedBy in override body Step 5

Multi-Service Routing

When one app serves multiple consumer services (e.g. PAYMENT for cart + user + order, or NOTIFICATION for user + order + account), use serviceInstanceMap in the override body instead of a single top-level instanceId:

{
  "layer": "store",
  "appliedBy": "admin",
  "overrides": {
    "serviceInstanceMap": {
      "cart": "{cartCollectionId}",
      "user": "{userCollectionId}",
      "order": "{orderCollectionId}"
    },
    "configurationSchema": {
      "targetServiceUrl": "https://...",
      "hmacSecret": "<secret>"
    }
  }
}

Each consumer SDK:

  • Filters events where its serviceIdentity is in app.supportedServices
  • Resolves instanceId from serviceInstanceMap[serviceIdentity]
  • Writes its own row in marketplace_selections

Full multi-service examples: CENTRALIZED_MULTI_SERVICE_TESTING_GUIDE.md
Defaults merging: CENTRALIZED_MULTI_SERVICE_WITH_DEFAULTS.md


Marketplace Service Installation

Prerequisites

  • Node.js >= 18
  • npm >= 9
  • MongoDB >= 6.0
  • Redis >= 6.0 (optional — caching only)

Quick Start (Local)

# 1. Install dependencies
npm install

# 2. Copy environment file
cp .env.example .env

# 3. Start MongoDB (if not running)
docker run -d -p 27017:27017 mongo:latest

# 4. Verify setup
npm run db:verify

# 5. Seed test data (optional — 5 sample apps + configurations)
npm run db:seed

# 6. Start development server
npm run dev

Authentication (Local)

All API endpoints require a Bearer JWT. For local development, generate a test token when TEST_TOKEN_ALLOWED_ENVS=dev is set in .env. Never enable this in staging or production.

See guides/LOCAL_DEVELOPMENT.md for details.

Key Environment Variables

Variable Purpose
PORT Server port (default 5000)
STAGE Environment stage (local, dev, …)
MONGODB_URI MongoDB connection string
ENABLE_EVENT_BRIDGE Set false locally; true in deployed envs
ENCRYPTION_KEY AES-256-GCM for sensitive fields
JWT_SIGNING_SECRET / Cognito vars JWT validation
CACHE_ENABLED Enable/disable Redis cache

See .env.example for the full list. Detailed guide: guides/LOCAL_DEVELOPMENT.md

Deploy / Seed in Shared Environments

npm run db:load    # Load webbox migrations (connector catalog, interfaces)
npm run build
npm start          # Production mode

CDK deployment: npm run cdk:deploy (see team deployment process).


Local Dev Without EventBridge

When ENABLE_EVENT_BRIDGE=false (typical local setup), manually push the sync event to the consumer service after Step 5 (override):

POST {CART}/v1/marketplace/events
Content-Type: application/json
Authorization: Bearer {TOKEN}
{
  "source": "marketplace-service",
  "detailType": "app.selection.synced",
  "detail": {
    "instanceId": "{instanceId}",
    "adapterCategory": "USER_PROFILE",
    "appId": "eq-cart-userprofile-connector-v1",
    "supportedServices": ["cart"],
    "appSnapshot": {
      "name": "Cart User Profile Connector",
      "version": "1.0.0",
      "deployment": { "type": "bundled", "npmPackage": "@equinox/eq-cart-apps" },
      "capabilities": ["USER_PROFILE"]
    },
    "configurationSchema": {
      "connectorType": "userprofile",
      "targetServiceUrl": "https://eq9-dev-env.equinox.shop/userservices-dev",
      "storeId": "{storeId}",
      "instanceId": "{instanceId}",
      "appId": "eq-cart-userprofile-connector-v1",
      "timeout": 30000,
      "maxRetries": 3
    },
    "enabled": true,
    "selectedBy": "admin"
  }
}

Expected: 200 OK with { "processed": true, "eventId": "..." }

Then verify with GET {CART}/v1/marketplace/instances/{instanceId}/apps.


Business Value

Capability Business outcome
Central app/connector registry One authoritative catalogue of every integration; no duplicated or drifting connector metadata across services
Approval workflow with lifecycle states Governance and safety — only approved apps can be enabled; risky changes are gated and audited
Per-business / per-store enablement Multi-tenant flexibility — each merchant/store runs exactly the apps it needs, with a default per interface
Layered config overrides Same connector reused everywhere but tuned per business/store/service without forking the app
Versioning + rollback + canary Safe rollout of new connector versions; instant rollback and gradual (canary) traffic shifting
Payment provider configuration & multi-provider routing Merchants configure payment gateways with primary/secondary failover and weighted routing
Notification event/action configuration Merchants tailor which notifications fire on which events, per channel
Health checks + events Automatic detection of unhealthy external apps and platform-wide event propagation
Self-registration + API keys Apps can register themselves at deploy time (CI/CD or cold start) using machine-to-machine keys
Full audit history + point-in-time snapshots Compliance — who changed what, when, and what the config looked like at any moment
Centralized selection sync Single install path via marketplace; all consumer services stay in sync via app.selection.synced

Who Uses It

Persona How they interact
Platform / marketplace admin Approves apps, enables/disables them per business & store, manages interfaces and config overrides (JWT + admin privileges)
App provider / developer Registers and versions apps, self-registers via API key at deploy time, configures webhooks and mock responses
Merchant / store operator Configures payment providers, notification events & actions for their store (via admin portal → JWT)
Consuming platform services (cart, PIM, OMS, subscription, user, account) Subscribe to app.* EventBridge events; read effective config to discover and invoke enabled apps
CI/CD pipeline Emits app.deployed EventBridge events (source: custom.pipeline) that trigger self-registration
Auditors Query app/enablement change history, compare and reconstruct point-in-time state (marketplace:auditor)

Core Features

# Feature Description
1 App registry (CRUD) Create, read, update, soft-delete apps with rich metadata, deployment, interfaces, capabilities and auth config
2 Approval workflow Lifecycle states draftpendingapproveddeprecatedarchived (+ rejected, deleted) with a validated transition map
3 Quick create One-shot app creation with optional auto-approve and deployment validation (npm view, Lambda dry-run, HTTP health check)
4 Interface definitions Register named/versioned interfaces, validate apps against them, list apps per interface, produce migration reports
5 Store-level enablement Enable/disable apps per store per interface, set defaults, rollback, drain (graceful wind-down)
6 Business-level enablement Enable/disable apps at the business tier (prerequisite for some flows, e.g. PAYMENT)
7 Layered config overrides Business / store / service override layers deep-merged into an effective config, incl. serviceInstanceMap
8 Payment configuration Per-store payment provider config (encrypted secrets) + multi-provider routing (primary/secondary, weighted, failover)
9 Notification configuration Per-store notification events, actions (EMAIL/SMS/PUSH), restricted-event suppression, readiness checks
10 Webhooks Register external subscribers, deliver app.* notifications, view delivery history and audit logs
11 Mock responses Store and test mock responses embedded in an app for integration testing
12 Routing / hooks / canary Routing strategies, pre/post hooks, canary rollout with auto-promote/rollback
13 Self-registration & API keys Machine-to-machine app self-registration from an app manifest + API-key lifecycle (max 5 active)
14 Health checks Periodic health probing of external apps with healthy/degraded/unhealthy state + events
15 Eventing Publishes app.* / business.app.* EventBridge events; consumes app.deployed and interface/deprecation events
16 Centralized selection sync PUT override with instanceId / serviceInstanceMap publishes app.selection.synced to populate consumer marketplace_selections

Deep-Dive Guides Index

Topic Guide
Multi-service routing + defaults CENTRALIZED_MULTI_SERVICE_TESTING_GUIDE.md
ConfigurationSchema defaults CENTRALIZED_MULTI_SERVICE_WITH_DEFAULTS.md
PIM / Inventory / Order (cart) CENTRALIZED_PIM_INVENTORY_ORDER_APPS.md
Account foundation + user UNIFIED_ACCOUNT_FOUNDATION_USER_CONNECTOR_PAYLOADS.md
Unified notification connector UNIFIED_NOTIFICATION_CONNECTOR_PAYLOADS.md
App sync architecture apps/APP_MANAGEMENT_AND_SYNC.md
Centralized selection design CENTRALIZED_APP_SELECTION_PLAN.md
Local development guides/LOCAL_DEVELOPMENT.md
Quick reference (patterns) guides/quick-reference-guide.md

Troubleshooting

Symptom Likely cause Fix
Selection missing in consumer after override EventBridge disabled locally, wrong serviceIdentity, or service not in supportedServices Check ENABLE_EVENT_BRIDGE; verify plugin serviceIdentity; confirm override had instanceId or serviceInstanceMap; use manual event POST locally
Wrong config at runtime Connector reading deprecated settings instead of configurationSchema Verify selection document has configurationSchema; upgrade connector/SDK
Store enable fails Override not applied before store enable PUT override with required config before store enable
instanceId mismatch Used storeId instead of service collectionId Re-fetch from GET {FOUNDATION}/v1/stores/{storeId}/effective
Multi-service app only synced to one service Missing or incorrect serviceInstanceMap key Ensure map keys match serviceIdentity values (cart, user, account, …)
Override validation fails Required schema fields missing Add required fields or define default values in app configurationSchema at registration

Resource Location
Admin portal Front end consuming the API (apps, enablement, config overrides, contracts, provisioning, package management)
Consumer SDK eq-component/eq-marketplace-integration — event handler and plugin used by other services
Shared connector packages @equinox/eq-cart-apps, @equinox/eq-account-apps, @equinox/eq-user-apps, @equinox/connectors-notification-v1
Contracts / schemas Capability contracts and the app manifest schema (docs/contracts/)
Event fan-out rule AWS EventBridge rule that fans app.* events out to consumer service Lambdas
Webbox seed data webbox/v1.0.0/ — connector catalog and interface definitions
Service README README.md

Revision History

Date Author Change
2026-08-05 JP Created the Confluence page (marketplace-doc.txt)
2026-08-12 Platform Engineering Enhanced with architecture diagrams, centralized selection model, installation steps, consumer sync mechanics, platform connector catalog, and USER_PROFILE walkthrough
2026-08-12 Platform Engineering Replaced Mermaid diagrams with SVG images for Confluence/markdown compatibility

Revision History
2026-08-12 | JP – Created the page and added the content.