Webhooks allow your Spree store to send real-time HTTP POST notifications to external services when events occur. When an order is completed, a product is updated, or inventory changes, Spree can automatically notify your CRM, fulfillment service, analytics platform, or any other system.Webhooks are built on top of Spree’s event system, providing:
Multi-store support - Each store has its own webhook endpoints
Event filtering - Subscribe to specific events or patterns with wildcards
Secure delivery - HMAC-SHA256 signatures for payload verification
Automatic retries - Failed deliveries retry with exponential backoff
Full audit trail - Track every delivery attempt with response codes and timing
import { createAdminClient } from '@spree/admin-sdk'const client = createAdminClient({ baseUrl: 'https://store.example.com', secretKey: 'sk_xxx',})const endpoint = await client.webhookEndpoints.create({ url: 'https://example.com/webhooks/spree', subscriptions: ['order.*', 'product.created'], active: true,})// secret_key is auto-generated and returned ONLY on create —// store it now for HMAC signature verification.endpoint.secret_key // => "a1b2c3d4e5f6..." (64-character hex string)
The secret_key is returned only once, in the create response. Save it immediately — it can’t be retrieved later and is required to verify webhook signatures.
Auto-generated key for HMAC signature verification
Endpoints always belong to the current store — the association is set automatically from the request scope, so you never pass it when creating or updating an endpoint.
Each webhook delivery sends a JSON payload with the following structure. The data object uses the same Store API V3 serializers as the REST API, so webhook payloads and API responses share the same schema:
Unix timestamp (seconds) generated at send time; included in the HMAC-SHA256 signed string as {timestamp}.{payload} and required to verify X-Spree-Webhook-Signature
The Spree Storefront includes a ready-made webhook route handler with signature verification and event routing. See the storefront email docs for details.
Inspect an endpoint’s delivery log, and re-send a failed delivery, via the Admin API:
// Recent deliveries for an endpointconst { data: deliveries } = await client.webhookEndpoints.deliveries.list('whe_xxx')// Re-send a specific deliveryawait client.webhookEndpoints.deliveries.redeliver('whe_xxx', 'whd_xxx')
Use tools like ngrok or webhook.site to test webhooks locally. Create a test endpoint pointed at the tunnel:
const endpoint = await client.webhookEndpoints.create({ url: 'https://your-ngrok-url.ngrok.io/webhooks', subscriptions: ['order.*'], active: true,})// Fire a synthetic delivery to confirm it's reachableawait client.webhookEndpoints.sendTest(endpoint.id)
send_test delivers a synthetic webhook.test event so you can verify the endpoint is reachable and your signature-verification code works, without having to trigger a real order.
Check the delivery records for details — each carries error_type, request_errors, response_code, and response_body. Filter the log with Ransack predicates such as success_eq=false or event_name_eq: