Skip to main content
Webhooks are HTTP callbacks that notify your server in real time when an event happens in Shoppex, such as a paid order, a new subscription, or a dispute.
This page covers normal Shoppex event webhooks like order:paid and subscription:created. If you build a DYNAMIC product with dynamic_webhook, read Dynamic product delivery instead.

Setting up webhooks

1

Create an endpoint

Create an HTTP endpoint on your server that accepts POST requests.
2

Register the endpoint

Go to Settings → Webhooks → Add Endpoint and enter your URL.
3

Select events

Choose the event names you want, for example:
  • order:paid
  • order:cancelled
  • subscription:created
If you use the Developer API, fetch the full allowlist from GET /dev/v1/webhooks/events.
For local development, use a tunnel service such as ngrok to expose your local server to the internet.
You can register webhooks through the dashboard or manage webhook subscriptions through the Developer API. The event payload is the same either way.
Developer webhooks must target your own HTTP(S) endpoint. For Discord notifications, use Notifications → Sales Alerts instead of a Discord webhook URL.

Order events

If a paid checkout reduces product stock, handle order:paid or order:paid:product. Shoppex does not send product:stock as a second event for every purchase-driven stock decrease. Use product:stock for direct catalog stock updates, for example when you edit stock through the dashboard or API.

Subscription events

Shoppex supports more event names than these two tables show. Examples include order:created, order:updated, order:partial, order:disputed, subscription:trial:started, subscription:updated, subscription:renewed, subscription:upcoming, plus product, query, feedback, and affiliate events. See Webhook events or GET /dev/v1/webhooks/events for the full current list.

Webhook payload

All webhooks follow this structure:
Dashboard test deliveries use the same event / data / created_at envelope as live deliveries. The values inside data are synthetic, but Shoppex signs the raw JSON body the same way as a real delivery.
The top-level webhook created_at field is a Unix timestamp. Nested timestamps inside data can be ISO 8601 strings.
Order webhooks can include payment-context fields such as exchange_rate, crypto_exchange_rate, crypto_gateway, and apm_method. These fields let you match and analyze payments without a follow-up invoice fetch in most cases.

Headers

Each webhook request includes these headers:

Signature verification

Always verify webhook signatures to confirm that a request came from Shoppex. New integrations must verify X-Shoppex-Signature-V2. Shoppex signs ${deliveryId}.${timestamp}.${rawBody} with HMAC-SHA256. Your webhook handler must reject timestamps outside a 5-minute window.
Shoppex signs the full JSON request body exactly as sent on the wire. Simple example: verify {"event":"order:paid","data":{...},"created_at":1775764170}, not just the inner data object. Use the per-webhook secret from Settings → Webhooks for that endpoint. Per-payment callbacks created with the webhook field on POST /dev/v1/payments use a different secret. Shoppex returns it once as webhook_secret in the payment creation response. The v1 segment in X-Shoppex-Signature-V2 is a version marker, not the signature. Simple example: in v1,t=1775764170,h=abc..., verify only the h value. Do not verify JSON.stringify(req.body) after you parse the request. Verify the raw body bytes from the incoming HTTP request instead.
X-Shoppex-Signature and X-Shoppex-Unescaped-Signature are deprecated legacy body-only HMAC-SHA512 headers. They stay available during the migration period, but new integrations must use X-Shoppex-Signature-V2.
Your webhook secret is available in Settings → Webhooks in the dashboard. Keep it secure and never expose it in client-side code.

Response handling

Return 200 OK as soon as you receive the request, then process the event in a background job. A slow synchronous handler risks the 30-second timeout described below. Shoppex can deliver the same webhook more than once. Use the X-Shoppex-Delivery header to detect duplicates, and make your fulfillment logic idempotent. One invoice can produce more than one payment attempt over time. For example, a customer can retry a payment or switch gateways. Treat the Shoppex invoice ID and event type as the durable signal, not a single provider session. Mark an order fulfilled when Shoppex reports the invoice as paid, not when you first see a provider-specific session ID. Always verify signatures in production to prevent spoofing, and always use an HTTPS endpoint.

Retry policy

If your endpoint returns an error (a non-2xx status) or times out, Shoppex retries the delivery automatically. After the 5th failed attempt, Shoppex marks the webhook as failed. You can retry it manually from the dashboard.
Your endpoint must respond within 30 seconds. Shoppex times out the request and counts it as a failure after that.

Testing webhooks

Use the dashboard to send test events.
1

Open webhook settings

Go to Settings → Webhooks.
2

Select your endpoint

Click your endpoint.
3

Send a test event

Click Send Test Event.
4

Choose an event type

Select an event type.
Test order:* deliveries include the main live fields you usually integrate against, for example gateway, total, total_display, currency, exchange_rate, crypto_gateway, apm_method, customer_email, and product context.
For local development:

Migrating from the legacy signature

Some existing integrations still verify the legacy X-Shoppex-Signature header. Move them to X-Shoppex-Signature-V2, using the code sample in Signature verification above. X-Shoppex-Signature-V2 brings two headers not shown in the headers table above.
Dynamic delivery webhooks send X-Shoppex-Delivery-Id instead of X-Shoppex-Delivery. See Dynamic product delivery for the full dynamic delivery header set.
1

Read the raw body

If your handler does not already do this, read the raw request body before you parse it as JSON.
2

Switch the verified header

Verify X-Shoppex-Signature-V2 with the code sample above, instead of X-Shoppex-Signature.
3

Check the timestamp window

Reject requests where X-Shoppex-Timestamp falls outside a 5-minute window, as the code sample does.
4

Remove the legacy check

Remove code that verifies X-Shoppex-Signature or X-Shoppex-Unescaped-Signature.
The legacy X-Shoppex-Signature and X-Shoppex-Unescaped-Signature headers carry a body-only HMAC-SHA512 signature, with no timestamp. Shoppex keeps both available during the migration period. The timestamp in X-Shoppex-Signature-V2 is what protects a migrated integration against replay.

Webhook events

Full list of event types and payload examples

Dynamic delivery

Deliver digital products in real time