--- title: Verify Events deliveries description: >- Verify HMAC signatures and ignore duplicate deliveries using Shopify-Webhook-Id. source_url: html: 'https://shopify.dev/docs/apps/build/events/verify-deliveries' md: 'https://shopify.dev/docs/apps/build/events/verify-deliveries.md' --- # Verify Events deliveries **Developer preview:** Events is in developer preview on the [`unstable`](https://shopify.dev/docs/api/usage/versioning#making-requests-to-an-api-version) API version, available today for a [subset of topics](https://shopify.dev/docs/api/events). Use it for early testing ahead of a stable release and broader topic coverage. For topics not yet supported, use webhooks alongside Events in the same `shopify.app.toml`. As Events expands topic coverage, it will become the primary subscription mechanism. Each delivery includes an HMAC signature to confirm it came from Shopify, and a delivery ID you can use to detect duplicates. Verify both before processing. *** ## HMAC verification Every delivery includes a base64-encoded HMAC signature in the [`Shopify-Hmac-Sha256` header](https://shopify.dev/docs/apps/build/events/delivery-structure#headers), generated using your app's client secret and the raw request body. Verify this signature before processing the payload to confirm the delivery came from Shopify. HMAC verification applies to HTTPS deliveries only, as Google Cloud Pub/Sub and Amazon EventBridge deliveries don't require it. If you're using the React Router template, verification is handled automatically before your handler runs: ## app/routes/events.jsx ```javascript import { authenticate } from "../shopify.server"; export const action = async ({ request }) => { const { shop, session, topic } = await authenticate.webhook(request); console.log(`Received ${topic} webhook for ${shop}`); return new Response(); }; ``` Always verify HMAC before trusting payload contents. Skip verification only in development with mock tools. If you [rotate your app's client secret](https://shopify.dev/docs/apps/build/authentication-authorization/client-secrets/rotate-revoke-client-credentials), it can take up to an hour for the HMAC digest to be generated using the new secret. To validate manually, compute HMAC-SHA256 of the raw request body using your app's client secret as the key, then compare it to the decoded header value. Reject any delivery where the signatures don't match. For code examples and common pitfalls, see [Manual verification](https://shopify.dev/docs/apps/build/webhooks/verify-deliveries#manual-verification) for webhooks. *** ## Ignoring duplicates Shopify minimizes duplicate deliveries, but your app might receive the same delivery more than once, for example, after a network timeout or a retry. Process deliveries using idempotent operations so that receiving the same delivery twice doesn't produce a different outcome. If your processing isn't idempotent, use `Shopify-Webhook-Id` to detect and skip duplicates: 1. Extract `Shopify-Webhook-Id` from the request headers. 2. Check your persistent store for that ID. 3. If it exists, skip processing and return a success response. 4. If it's new, process the delivery and save the ID. **Note:** If you have more than one subscription matching the same change, you'll receive a separate delivery per subscription. Each has a different `Shopify-Webhook-Id` but shares the same `Shopify-Event-Id`. Use `Shopify-Webhook-Id` to deduplicate individual deliveries. Use `Shopify-Event-Id` to correlate deliveries that originated from the same merchant action. *** ## Next steps * [Troubleshoot Events](https://shopify.dev/docs/apps/build/events/troubleshoot): Diagnose delivery failures and inspect logs. * [Events reference](https://shopify.dev/docs/api/events): Full list of topics, triggers, and required scopes. ***