dist/codex/saleor-commerce/skills/saleor-webhooks/SKILL.md
Configure Saleor webhooks — async and sync events, subscription payloads, JWS/HMAC signature verification, retry policy, and event types. Use when building webhook-driven integrations.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins saleor-webhooksInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
https://docs.saleor.io/docs/developer/extending/webhooks/overview for webhook overviewsite:docs.saleor.io async sync webhook events list for event type referencesite:docs.saleor.io webhook subscription payload for subscription query syntaxsite:docs.saleor.io webhook payload signature JWS verification for signature verificationsite:docs.saleor.io webhook retry policy for retry behavior| Aspect | Async Webhooks | Sync Webhooks | |--------|---------------|---------------| | Timing | Fired after the action completes | Fired during the action, blocks response | | Response | Saleor ignores the response body | Saleor uses the response to alter behavior | | Timeout | Longer (configurable) | Short (seconds) -- must respond quickly | | Retry | Retried on failure | Not retried -- failure may abort the operation | | Use Case | Notifications, syncing, analytics | Tax calculation, payment gateways, shipping rates |
| Event | Trigger |
|-------|---------|
| ORDER_CREATED | New order is placed |
| ORDER_UPDATED | Order fields are modified |
| ORDER_CONFIRMED | Order is confirmed |
| ORDER_FULFILLED | All items in the order are fulfilled |
| ORDER_CANCELLED | Order is cancelled |
| ORDER_FULLY_PAID | Order payment is complete |
| PRODUCT_CREATED | New product is created |
| PRODUCT_UPDATED | Product fields are modified |
| PRODUCT_DELETED | Product is deleted |
| CUSTOMER_CREATED | New customer account is created |
| CUSTOMER_UPDATED | Customer data is modified |
| CHECKOUT_CREATED | New checkout session starts |
| CHECKOUT_UPDATED | Checkout data changes |
| FULFILLMENT_CREATED | Fulfillment record is created |
| INVOICE_SENT | Invoice email is dispatched |
| Event | Expected Response |
|-------|------------------|
| PAYMENT_GATEWAY_INITIALIZE_SESSION | Payment session configuration |
| TRANSACTION_INITIALIZE_SESSION | Transaction processing data |
| TRANSACTION_PROCESS_SESSION | Transaction result |
| TRANSACTION_CHARGE_REQUESTED | Charge confirmation |
| TRANSACTION_REFUND_REQUESTED | Refund confirmation |
| SHIPPING_LIST_METHODS_FOR_CHECKOUT | Available shipping methods and rates |
| CHECKOUT_CALCULATE_TAXES | Tax amounts for checkout lines |
| ORDER_CALCULATE_TAXES | Tax amounts for order lines |
| CHECKOUT_FILTER_SHIPPING_METHODS | Filter available shipping options |
| ORDER_FILTER_SHIPPING_METHODS | Filter shipping for existing orders |
Sync webhooks must return a response in the expected format -- fetch live docs for each event's response schema.
Saleor uses GraphQL subscription queries to define webhook payload shapes:
subscription OrderCreated {
event {
... on OrderCreated {
order { id number total { gross { amount currency } } user { email } }
}
}
}
This subscription query is attached to the webhook configuration and controls exactly which fields appear in the delivered payload.
| Benefit | Detail | |---------|--------| | Precise data | Request only the fields you need | | Reduced payload size | No unnecessary nested objects | | Type safety | Payload matches the subscription query shape | | Versioning | Update the query when you need new fields |
Webhooks are declared in the App manifest and registered automatically during installation:
| Manifest Field | Purpose |
|---------------|---------|
| webhooks[].name | Human-readable webhook name |
| webhooks[].asyncEvents | List of async event types |
| webhooks[].syncEvents | List of sync event types |
| webhooks[].query | GraphQL subscription query for payload |
| webhooks[].targetUrl | URL to receive the webhook POST |
| webhooks[].isActive | Enable or disable the webhook |
| Mutation | Purpose |
|----------|---------|
| webhookCreate | Register a new webhook |
| webhookUpdate | Modify an existing webhook |
| webhookDelete | Remove a webhook |
Staff users or Apps with MANAGE_APPS permission can manage webhooks via the API.
Saleor signs every webhook payload to ensure authenticity. Since Saleor 3.5+, the default method is JWS (JSON Web Signature) with RS256. Legacy HMAC-SHA256 is deprecated.
| Header | Value |
|--------|-------|
| Saleor-Signature | JWS signature (default) or HMAC-SHA256 hex digest (deprecated) |
| Saleor-Event | The event type (e.g., order_created) |
| Saleor-Domain | The Saleor instance domain |
| Saleor-Api-Url | Full URL of the Saleor GraphQL endpoint |
| Method | Status | How It Works |
|--------|--------|-------------|
| JWS (RS256) | Default (3.5+) | Payload-detached JWS; verify with public key from /.well-known/jwks.json |
| HMAC-SHA256 | Deprecated | Only used when a webhook secret key is explicitly set; will be removed in 4.0 |
| Step | Action |
|------|--------|
| 1 | Read the Saleor-Signature header (JWS compact serialization) |
| 2 | Fetch the public key from <saleor-domain>/.well-known/jwks.json |
| 3 | Verify the JWS signature using the RS256 public key |
| 4 | Reject the request if verification fails |
The saleor-app-sdk provides built-in middleware for automatic signature verification. Always verify signatures before processing any webhook payload.
| Aspect | Detail | |--------|--------| | Async retries | Saleor retries failed async webhooks with exponential backoff | | Max attempts | Configurable -- fetch live docs for default | | Failure criteria | Non-2xx HTTP response or connection timeout | | Sync retries | Sync webhooks are not retried | | Circuit breaker | Saleor may disable a webhook after repeated failures |
| Step | Action |
|------|--------|
| 1 | Receive POST request at targetUrl |
| 2 | Verify payload signature (JWS or HMAC) |
| 3 | Parse the JSON payload |
| 4 | Route by event type (Saleor-Event header) |
| 5 | Process the event (sync: return response; async: acknowledge with 200) |
| 6 | Return appropriate HTTP status |
For async webhooks, return HTTP 200 immediately and process the event asynchronously if it requires heavy work.
| Scenario | Behavior | |----------|----------| | App returns 5xx | Async: retried; Sync: operation may fail | | App returns 4xx | Async: retried; Sync: operation may fail | | App timeout | Async: retried; Sync: fallback or failure | | Invalid signature | App should return 401 and not process | | Malformed payload | App should return 400 and log the error |
Saleor-Event header to route to the correct handler functionFetch the Saleor webhook documentation for exact event type enumerations, subscription query patterns, and retry configuration before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.