Skip to main content
This guide assumes you’ve completed the Model and API tutorials — we’ll reuse the Brand serializer created there.
Almost every real store talks to other systems: an order management system (OMS), a warehouse (WMS), an ERP, a CRM, a marketing platform. In Spree, the integration surface is the events system — models publish events as things happen, and you react to them without touching core code. There are two ways to consume events, and they serve different audiences: We’ll do both: push completed orders to an OMS with a subscriber, give the Brand model its own lifecycle events, and set up an outbound webhook.

Step 1: Subscribe to a Core Event

When an order completes, send it to the OMS. Generate a subscriber:
This creates the subscriber, a spec stub, and — crucially — registers it in config/initializers/spree.rb (injected into the existing after_initialize block every Spree app ships with). Subscribers are not auto-discovered; a subscriber that never gets appended to Spree.subscribers is a silent no-op, which is why the generator owns that step (re-runs are idempotent, and each new subscriber appends to the same initializer). Fill in the handler:
app/subscribers/oms_order_sync_subscriber.rb
Two things worth understanding:
  • Payloads carry prefixed IDs, not raw database IDs — always look records up with find_by_prefix_id. The payload itself is the resource serialized with its v3 API serializer, so event.payload['number'], ['email'], etc. are available directly when you don’t need the full record.
  • Subscribers run async by default — each handle call is an ActiveJob on the events queue, so a slow OMS API never blocks checkout. Pass subscribes_to 'order.completed', async: false only when you genuinely need synchronous execution.
Restart the server, complete a test order, and watch the job fire (spree logs worker — or your job backend’s UI).

Step 2: Give Brand Its Own Lifecycle Events

Core models like Payment and Shipment publish *.created / *.updated / *.deleted events automatically. Your models can too — add one line to the Brand model:
app/models/spree/brand.rb
Now brand.created, brand.updated, and brand.deleted fire after the matching transactions commit — and because you created Spree::Api::V3::BrandSerializer in the API step, the payloads automatically use it (the events system resolves the serializer by naming convention). Anyone — subscriber or webhook — can react to brand changes with the same JSON shape your API serves.
app/subscribers/brand_sync_subscriber.rb

Step 3: Publish a Custom Event

Lifecycle events cover persistence; custom events express domain moments. Say featuring a brand should notify the marketing platform:
The payload defaults to the serializer output; pass your own hash as the second argument when the event needs different data. Subscribers consume it like any other event name.

Step 4: Outbound Webhooks — No Code Required

When the consumer is an external system you don’t deploy code into, use webhooks. In the admin, go to Settings → Webhooks, add an endpoint with the destination URL, and pick the events to deliver — order.completed, brand.created, anything publishing in your store. The endpoint’s signing secret is shown once on creation — store it in the receiving system. Each delivery is an HTTP POST with this envelope:
And three headers the receiver should use: Verify the signature before trusting a payload:
Delivery semantics to design around: failed deliveries (timeouts, connection errors, non-2xx responses) are recorded, not retried automatically — redeliver from the endpoint’s delivery history in the admin, or via POST /api/v3/admin/webhook_endpoints/:webhook_endpoint_id/deliveries/:id/redeliver. After 15 consecutive failures the endpoint auto-disables and store staff get an email; a successful delivery resets the counter.

Testing a Subscriber

Subscribers are plain Ruby — test handle directly with a constructed event:
spec/subscribers/oms_order_sync_subscriber_spec.rb
  • Events — the full event catalog, subscriber DSL, and delivery guarantees
  • Webhooks — endpoint management, security, and the per-event payload schemas
  • Extending Core Models — when a decorator is the right tool instead