Overview
Spree includes a powerful event system that allows you to react to various actions happening in your store. When something happens (an order is completed, a product is created, etc.), Spree publishes an event that your code can subscribe to and handle. This pattern enables loose coupling between components and makes it easy to:- Send email notifications when orders are placed
- Sync data with external services when products change
- Log audit trails for compliance
- Trigger webhooks to notify third-party systems
- Update caches when inventory changes
How Events Work
Spree’s event system is built on top ofActiveSupport::Notifications but provides a cleaner, more Rails-like API through:
Spree::Events- The main module for publishing and subscribing to eventsSpree::Subscriber- Base class for creating event subscribersSpree::Publishable- Concern that enables models to publish events
Creating a Subscriber
Create a subscriber class inapp/subscribers/ that inherits from Spree::Subscriber:
app/subscribers/my_app/order_completed_subscriber.rb
Subscriber DSL
TheSpree::Subscriber class provides a clean DSL for declaring subscriptions:
Handling Multiple Events
When subscribing to multiple events, use theon DSL to route events to specific methods:
app/subscribers/my_app/order_audit_subscriber.rb
Working with Events
Event Object
When your subscriber receives an event, you get aSpree::Event object with:
Finding the Record
The payload contains serialized attributes, not the actual record. To get the record:Available Events
Lifecycle Events
Models that includeSpree::Publishable and call publishes_lifecycle_events automatically publish:
| Event Pattern | Description |
|---|---|
{model}.created | Record was created |
{model}.updated | Record was updated |
{model}.deleted | Record was deleted |
Spree::Price publishes price.created, price.updated, and price.deleted.
Models with lifecycle events enabled include: Order, Payment, Price, Shipment, Variant, LineItem, StockItem, and many others.
Order Events
| Event | Description |
|---|---|
order.created | Order was created |
order.updated | Order was updated |
order.completed | Order checkout completed |
order.canceled | Order was canceled |
order.resumed | Canceled order was resumed |
order.paid | Order is fully paid |
order.shipped | All order shipments are shipped |
Shipment Events
| Event | Description |
|---|---|
shipment.created | Shipment was created |
shipment.updated | Shipment was updated |
shipment.shipped | Shipment was shipped |
shipment.canceled | Shipment was canceled |
shipment.resumed | Shipment was resumed |
Payment Events
| Event | Description |
|---|---|
payment.created | Payment was created |
payment.updated | Payment was updated |
payment.paid | Payment was completed |
Price Events
| Event | Description |
|---|---|
price.created | Price was created |
price.updated | Price was updated |
price.deleted | Price was deleted |
Stock Events
| Event | Description |
|---|---|
product.out_of_stock | Product has no stock left for any variant |
product.back_in_stock | Product was out of stock and now has stock again |
Customer Events
| Event | Description |
|---|---|
customer.created | Customer was created |
customer.updated | Customer was updated |
customer.deleted | Customer was deleted |
Admin Events
| Event | Description |
|---|---|
admin.created | Admin user was created |
admin.updated | Admin user was updated |
admin.deleted | Admin user was deleted |
Product Events
| Event | Description |
|---|---|
product.activate | Product status changed to active |
product.archive | Product status changed to archived |
Publishing Custom Events
You can publish custom events from anywhere in your application:From a Model
Models includingSpree::Publishable can use publish_event:
From Anywhere
UseSpree::Events.publish directly:
Registering Subscribers
Subscribers inapp/subscribers/ are automatically registered during Rails initialization.
For subscribers in other locations, register them manually in an initializer:
config/initializers/event_subscribers.rb
Synchronous vs Asynchronous
By default, subscribers run asynchronously viaSpree::Events::SubscriberJob. This prevents slow subscriber code from blocking HTTP requests.
For critical operations that must complete before the request finishes, use synchronous mode:
Temporarily Disabling Events
You can disable event publishing temporarily:- Data migrations where you don’t want to trigger side effects
- Test setup where subscribers would interfere
- Bulk operations where individual events would be too noisy
Testing Subscribers
Testing Event Handling
spec/subscribers/my_app/order_completed_subscriber_spec.rb
Testing Event Publishing
Use theemit_webhook_event matcher (if available) or stub the events:
Best Practices
Keep handlers fast
Move slow operations to background jobs. Subscribers should do minimal work and delegate heavy lifting.
Handle missing records
Always check if the record exists before processing. It may have been deleted between event publish and handler execution.
Be idempotent
Design handlers to be safely re-run. Events might be delivered more than once in edge cases.
Use specific patterns
Subscribe to specific events rather than wildcards when possible. This makes code easier to understand and debug.
Example: Inventory Alert Subscriber
Here’s a complete example of a subscriber that sends alerts when inventory is low:app/subscribers/my_app/inventory_alert_subscriber.rb
Custom Event Adapters
Spree’s event system uses an adapter pattern, making it possible to swap the underlying event infrastructure. By default, Spree usesActiveSupport::Notifications, but you can create custom adapters for other backends like Kafka, RabbitMQ, or Redis Pub/Sub.
Configuring a Custom Adapter
Set your adapter class in an initializer:config/initializers/spree.rb
Creating a Custom Adapter
Inherit fromSpree::Events::Adapters::Base and implement the required methods:
app/models/my_app/events/kafka_adapter.rb
Base Class Interface
TheSpree::Events::Adapters::Base class defines the required interface:
| Method | Description |
|---|---|
publish(event_name, payload, metadata) | Publish an event, return Spree::Event |
subscribe(pattern, subscriber, options) | Register a subscriber for a pattern |
unsubscribe(pattern, subscriber) | Remove a subscriber |
activate! | Called during Rails initialization |
deactivate! | Called during shutdown |
build_event(name, payload, metadata)- Creates aSpree::Eventinstancesubscriptions_for(event_name)- Finds matching subscriptions from the registryregistry- Access to theSpree::Events::Registryinstance
See
Spree::Events::Adapters::ActiveSupportNotifications for a complete reference implementation.Related Documentation
- Webhooks - HTTP callbacks for external integrations
- Dependencies - Customizing Spree services
- Decorators - Extending Spree classes

