Skip to main content

Overview

Something happens in a store — an order is placed, a payment clears, a product sells out — and you want something else to happen: notify a warehouse, post to a channel, update a spreadsheet. Events are how you attach that behaviour without editing Spree. Spree announces what happened; your code decides what to do about it. There are two ways to listen, and which you want depends on where your code lives: If you’re building a headless storefront, webhooks are almost certainly what you want. Subscribers are for when you’re running Spree as your own application and want to add behaviour to it.

What Spree announces

Most records announce their own lifecycle: Those exist for orders, carts, products, variants, customers, payments, fulfillments, returns, media, price changes and more. On top of that, meaningful business moments get their own events: For the full list of events and their payloads, see Webhook Events & Payloads. The distinction matters when choosing what to listen for. order.updated fires whenever anything about an order changes — including an admin editing a note. order.placed fires once, when a customer actually bought something. Sending a confirmation email on the wrong one is how customers receive nine copies.

What an event carries

An event carries the record it’s about, serialized:
Event payload
IDs are the same prefixed IDs the API uses, so you can take an ID out of an event and fetch the full record without translating anything.

Reacting in another system

Point a webhook at your endpoint and subscribe it to the events you care about. That’s covered fully in Webhooks — including signature verification, which you should not skip.

Reacting inside Spree

A subscriber is a small class that names the events it wants and does something when one arrives.
The generator writes the class, a test, and — the step that’s easy to forget — registers it.
server/app/subscribers/order_placed_subscriber.rb
A subscriber can listen to several events, or to a whole family:
server/app/subscribers/order_placed_subscriber.rb
Subscribers must be registered — they aren’t discovered automatically, because a subscriber that starts running because of where its file sits is hard to reason about:
server/config/initializers/spree.rb

Subscribers run in the background

By default a subscriber runs as a background job, so a slow API call in your code doesn’t slow down the customer’s checkout — and a failure doesn’t roll back their order. That’s almost always what you want. If you genuinely need to run inside the same transaction, you can ask to:
server/app/subscribers/order_placed_subscriber.rb
A synchronous subscriber runs while the customer waits, so a slow one slows their checkout. An exception in it is reported to Rails.error and swallowed in production, but re-raised in development and test so you notice it. Reserve it for work that must happen immediately, and keep it fast.

Publishing your own events

Anything in your own code can announce something, and subscribers and webhooks treat it like any built-in event:
server/app/services/fraud_check.rb
Useful when your own domain has moments worth reacting to — a fraud check completing, an approval granted.

Guidance

Listen for the specific event. order.placed rather than order.updated with a status check. Assume events can arrive more than once. A retry after a network blip can redeliver. Make handlers safe to run twice — check whether you’ve already acted before acting. Don’t chain long sequences of subscribers. When one event triggers a subscriber that triggers another, working out what happened after the fact becomes archaeology. Prefer one handler that does the sequence. Keep failures contained. A subscriber that raises shouldn’t take down anything else. Handle your own errors and log them.