Documentation
Developers6 min readUpdated 28 July 2026

Plugins

The extension model: registering a plugin, declaring configuration, hooking into platform events and shipping it safely.

On this page

Plugins run inside the platform. They are how connectors, analytics integrations and custom workflow steps are built — including ours, which use exactly the surface described here.

Reach for a plugin when logic needs to run on platform events with platform data. Reach for webhooks and the API when the logic belongs in a system you already own.

Registering

A plugin declares an id, the capabilities it provides, a configuration schema and its handlers.

registerPlugin({
  id: 'warehouse-priority',
  name: 'Warehouse priority routing',
  capabilities: ['orders.route'],
  config: {
    priority: { type: 'array', items: { type: 'string' }, label: 'Location priority' },
    allowSplit: { type: 'boolean', default: false, label: 'Allow split shipments' }
  },
  handlers: {
    'orders.route': async ({ order, locations, config }) => {
      const ranked = config.priority
        .map(id => locations.find(location => location.id === id))
        .filter(Boolean)

      return assign(order, ranked, { allowSplit: config.allowSplit })
    }
  }
})

The config schema drives the settings form in the interface. You describe the fields; you do not build the screen.

Capabilities

CapabilityCalled whenTypical use
catalog.pushListings need to reach a channelMarketplace connectors
catalog.pullA channel owns some contentImporting from an existing platform
inventory.pushAvailability changesFeed-based channels
orders.pullNew orders are fetchedMarketplace order ingestion
orders.routeAn order needs locations assignedCustom routing strategies
pricing.resolveA price is being calculatedContract pricing, repricers
content.transformContent is rendered for a channelTitle patterns, attribute maps
analytics.trackA tracked event occursAnalytics and product telemetry

A plugin declares only what it implements. Anything it does not declare is never called, which keeps the failure surface small and legible.

Configuration and secrets

Configuration is stored per project, so the same plugin serves several brands with different settings. Fields marked as secrets are encrypted at rest and write-only — the interface and the API return a masked placeholder, never the value.

Worth knowing

Do not read credentials from environment variables inside a plugin. A plugin installed in two projects will get one project's credentials and quietly act on the wrong account.

Lifecycle

1
install

Runs once per project. Create whatever external resources you need and validate the credentials before reporting success.

2
enable and disable

Toggling a plugin stops it being called without discarding its configuration. Disable is the recovery path when something misbehaves at 2am.

3
handle

Your handlers run on platform events, scoped to the project that installed the plugin.

4
uninstall

Clean up external resources. Platform data — orders, listings, history — is never deleted by an uninstall.

Rules that keep plugins boring

  • Be idempotent. Handlers can be retried after a partial failure; a second run must not create a second record.
  • Be quick. Handlers have a time budget. Long work belongs on a queue that you drain on your own schedule.
  • Fail with intent. Throw with a clear message; the error reaches the operator with the plugin id attached and lands in the retry queue.
  • Log at the boundaries. The outbound request and the response, not every line in between.
Tip

Ship every plugin behind a dry-run mode. Being able to see what it would have written, on live data, before it writes anything, is worth more than any amount of test coverage on your own fixtures.

Testing

Run the plugin against a sandbox project with a copy of your catalog, exercise it with replayed webhook traffic, and check the audit log rather than your own logs — the audit log is what an operator will read when something goes wrong six months from now.

That is the end of the developer section. If something here is unclear or missing, tell us — these pages are maintained against real integration work, and a question that had to be asked once is usually a page that needs editing.

Ask about this page withChatGPTClaudePerplexity