Skip to main content

Overview

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
  • Failure handling - Failed deliveries are retried automatically and can be re-sent by hand, and an endpoint that keeps failing is disabled
  • Full audit trail - Track every delivery attempt with response codes and timing

How Webhooks Work

  1. An event is published (e.g., order.placed)
  2. The WebhookEventSubscriber receives all events
  3. It finds active webhook endpoints subscribed to that event
  4. For each endpoint, it creates a WebhookDelivery record and queues a job
  5. The job sends an HTTP POST request with the event payload and HMAC signature

Creating Webhook Endpoints

Via Admin Panel

Navigate to Settings → Developer → Webhooks in the admin panel to create and manage webhook endpoints.

Via the Admin API

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.

Endpoint Attributes

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.

Event Subscriptions

The subscriptions attribute controls which events trigger webhooks to this endpoint. Set it when creating the endpoint, or change it later:
The subscriptions array accepts exact event names and wildcard patterns: An event that carries a credential — customer.password_reset_requested, which includes a reset token — is never matched by a pattern. An endpoint receives it only when it names it, and naming it needs the permission to manage customers. Password reset requests for staff and sellers are never sent to webhooks: Spree emails those itself. To see every event you can subscribe to, including those your installed extensions add, list them:
Each entry says whether the event carries a credential (and which permission subscribing to it needs), and whether it is a deprecated name along with the event that replaces it.

Webhook Payload

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:
Payload
Payloads carry what a customer could see, never back-office fields such as costs, internal notes or staff names. When an integration needs those, it fetches the record from the Admin API with the id in the payload, using a key scoped to what it may read. For complete payload schemas for each event type, see Webhook Events & Payloads.

HTTP Request Details

Headers

Each webhook request includes these headers:

Verifying Webhook Signatures

To ensure webhooks are genuinely from your Spree store, verify the signature.

Next.js

The Spree Storefront includes a ready-made webhook route handler with signature verification and event routing. See the storefront email docs for details.

Any JavaScript/TypeScript framework

Use @spree/sdk/webhooks for framework-agnostic verification. constructWebhookEvent checks the signature and returns a typed event: once you check event.name, event.data has the type of the record that event carries.
Store SDK
Pass the body exactly as received: parsing and re-serializing it changes the bytes the signature covers. verifyWebhookSignature is still available when you only need the check. Events your own code or an extension publishes can be typed too, by adding them to WebhookEventMap:

Ruby

server/app/controllers/webhooks_controller.rb

Delivery Status

Failed Deliveries

Any response other than 2xx — including a timeout or a connection error — marks the delivery as failed. Spree then sends it again, waiting longer before each try, up to 5 attempts in total over about six minutes. The delivery log shows the outcome of the latest attempt. Retries stop as soon as the endpoint is turned off. Every attempt carries the same event id, so use it to ignore an event your receiver has already processed. To send a delivery again after the retries run out, re-send it from the delivery log (see below). If an endpoint fails 15 deliveries in a row, Spree disables it and emails the store staff. Fix the endpoint, then turn it back on by marking it active again.

Checking Delivery Status

Inspect an endpoint’s delivery log, and re-send a failed delivery, via the Admin API:

Delivery Attributes

Configuration

Webhooks are on by default and can be switched off installation-wide. SSL certificates are verified in production, and not in development — so you can point an endpoint at a local tunnel with a self-signed certificate while building. Both are configurable if your environment needs something different.

Internal endpoints

Outside development, Spree refuses webhook URLs that resolve to a private or internal address — both when the endpoint is saved and when each delivery is sent — so an endpoint can’t be used to reach your internal network. If the receiver is a service you run on a private network, such as a storefront in the same Kubernetes cluster, allow its host with SPREE_WEBHOOKS_ALLOWED_INTERNAL_HOSTS:
An entry matches that exact host; an entry with a leading dot matches any host under it (.svc.cluster.local matches storefront.shop.svc.cluster.local, not evil-svc.cluster.local). Matching is case-insensitive. The list is installation config only — store admins can’t change it — and every host not on it stays behind the guard.

Available Events

Webhooks can subscribe to any event in Spree’s event system. See Webhook Events & Payloads for the complete list, or list them through the Admin API to include the events your extensions add. Common webhook events include:

Testing Webhooks

In Development

Use tools like ngrok or webhook.site to test webhooks locally. Create a test endpoint pointed at the tunnel:
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.

Best Practices

Respond quickly

Return a 2xx response as fast as possible. Process webhook data asynchronously in a background job.

Verify signatures

Always verify the X-Spree-Webhook-Signature header to ensure the webhook is authentic.

Handle duplicates

Use the event id to detect and handle duplicate deliveries. A delivery that is re-sent carries the same event id.

Subscribe selectively

Only subscribe to events you need. Use specific patterns rather than * when possible.

Troubleshooting

Webhooks Not Delivering

  1. Check that webhooks are enabled installation-wide
  2. Verify the endpoint is marked active
  3. Confirm the endpoint actually subscribes to the event you expect
  4. Check the event belongs to the same store as the endpoint
  5. If the endpoint is on a private network, check its host is in SPREE_WEBHOOKS_ALLOWED_INTERNAL_HOSTS — otherwise every delivery fails with has no public ip addresses

Signature Verification Failing

  1. Ensure you’re using the raw request body (not parsed JSON)
  2. Verify you’re using the correct secret_key for this endpoint
  3. Check that no middleware is modifying the request body

Deliveries Failing

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: