medusa-plugin-events
Forwards Medusa events to Evolve services over HTTP, so a search index, a catalog page or an order email follows what changes in Medusa.
A transport and nothing more. The plugin sends the event name and the id; the receiver decides what that means: mapping onto Evolve's shared message schemas, updating a search index, or ignoring it.
That split is deliberate. It is what lets a consumer see the same events whether commercetools or Medusa is underneath, and it keeps this plugin free of Evolve's framework packages, which would otherwise pull a GraphQL server and its whole toolchain into a Medusa application that never serves GraphQL.
When to use
Install it in every Medusa backend that has Evolve services listening for catalog or order events, such as the catalog subgraph that keeps search current. A Medusa with nothing downstream to tell does not need it.
Get started
Install the package with the peer dependencies it needs. It comes from the Evolve registry, which needs a one-time sign-in.
pnpm add @evolve-framework/medusa-plugin-events @medusajs/cli @medusajs/framework @medusajs/medusaRequirements4
node>=20@medusajs/cli2.21.2@medusajs/framework2.21.2@medusajs/medusa2.21.2
For example:
// medusa-config.tsimport { defineConfig } from "@medusajs/framework/utils";
module.exports = defineConfig({ // projectConfig, modules and other plugins as before plugins: [ { resolve: "@evolve-framework/medusa-plugin-events", options: { endpoint: process.env.EVOLVE_EVENTS_URL, secret: process.env.EVOLVE_EVENTS_SECRET, }, }, ],});Options
| Option | Environment | Meaning |
|---|---|---|
endpoint |
EVOLVE_EVENTS_URL |
Where to POST every event that no route claims |
secret |
EVOLVE_EVENTS_SECRET |
Sent as the x-events-secret header |
routes |
A list of { events, endpoint }: send events with these names or prefixes to another endpoint |
|
EVOLVE_EVENTS |
Comma-separated event names to forward, instead of the default set |
Both endpoint and secret fall back to their environment variable, so the
options block can be left empty.
No endpoint means no forwarding, silently. A Medusa running on its own with nothing downstream to tell is a valid deployment, not a misconfiguration.
Routing by prefix
A deployment that splits its graph by domain wants each service to hear only
its own events. routes sends an event to the first route that names it, or
names its prefix: the part before the first dot. So order claims
order.placed and order.canceled.
options: {
routes: [
{
events: ["product", "product-category"],
endpoint: process.env.CATALOG_EVENTS_URL,
},
{ events: ["order"], endpoint: process.env.ORDER_EVENTS_URL },
],
secret: process.env.EVOLVE_EVENTS_SECRET,
},
An event that no route claims goes to endpoint. The plugin still knows nothing
about those domains: it only matches names.
What it sends
POST <endpoint>
content-type: application/json
x-events-secret: <secret>
{ "type": "product.updated", "id": "prod_01...", "timestamp": "2026-08-14T..." }
No payload travels with the event, by design. Medusa's own events carry only an id, and a receiver that needs more can read it back from the right store and locale, which the event could not have known.
The secret is a shared string rather than a signature: the endpoint is meant to be cluster-internal, so it only has to turn away a stray caller, not an adversary who can already read the traffic.
Which events
By default the catalog and the order lifecycle: the catalog so a search index stays in step, and the orders so a shopper is told their order was placed and later shipped.
product.created product-category.created
product.updated product-category.updated
product.deleted product-category.deleted
order.placed order.fulfillment_created
order.canceled order.fulfillment_canceled
So installing the plugin and pointing it at the Evolve services is enough to
keep search current after an edit in the admin. order.fulfillment_created is
Medusa's shipment, since a fulfillment is what actually leaves the warehouse.
Forward a different set with EVOLVE_EVENTS, a comma-separated list that
replaces the default:
EVOLVE_EVENTS=order.placed,customer.created
It is an environment variable rather than a plugin option because Medusa reads a subscriber's event list when the module loads, before there is a container to resolve plugin options from.
Failure
A forwarding failure is logged and swallowed. The write really did happen, and
throwing here would fail a merchant's save because something downstream was
briefly unreachable. A lost event is recoverable with a reindex; a refused write
is not. A misconfigured endpoint therefore fails silently: look for
Could not forward in Medusa's log.
The plugin does not retry. Make the receiving side idempotent, so that a reindex or an event that arrives twice does no harm.

