Volley / Templates / Shopify orders/create webhook
Webhooks templateShopify orders/create webhook, as a sequence diagram
What Shopify sends when an order is placed, how your app should answer, and the background job that follows, with the headers, order JSON and Admin API call on their arrows.
Click any arrow in the editor to see its request and response as a table or the raw body. Change the hosts, ids and fields to match your integration, then share a link or embed it in your README.
Step by step
- Shopify posts the order with
X-Shopify-Topic: orders/create, the shop domain, the API version, a webhook id and an HMAC signature. - Your app checks the HMAC: an HMAC-SHA256 of the raw body with the app's client secret, base64 encoded.
- Your app queues the work and answers 200 right away, or 401 if the signature is wrong.
- A worker does the slow part, here tagging the order through the GraphQL Admin API with
X-Shopify-Access-Token.
Easy to get wrong
- Shopify waits only a few seconds and retries failures, so slow handlers cause duplicate deliveries. Answer first.
- Use
X-Shopify-Webhook-Idto drop duplicates. - Compute the HMAC over the raw bytes. A re-serialized body will not match.
The source
The whole diagram is this plain text. Copy it into your repo, or open it in the editor.
shopify-order-webhook.volley
title: Shopify orders/create webhook
order: Shopify, App, Queue, Worker, Shopify Admin API
Shopify -> App: POST /webhooks/shopify (orders/create)
in: Delivery
```http
POST /webhooks/shopify HTTP/1.1
Content-Type: application/json
X-Shopify-Topic: orders/create
X-Shopify-Shop-Domain: example-store.myshopify.com
X-Shopify-API-Version: 2025-07
X-Shopify-Webhook-Id: b54557e4-bdd9-4b37-8a5f-bf7d70bcd043
X-Shopify-Hmac-Sha256: <redacted>
{
"id": 820982911946154508,
"name": "#1001",
"email": "jon@example.com",
"financial_status": "paid",
"total_price": "49.00",
"currency": "USD",
"line_items": [{ "sku": "TEE-BLK-M", "quantity": 1, "price": "49.00" }]
}
```
App -> App: Verify the HMAC over the raw body
note: Compute an HMAC-SHA256 of the raw request body with the app's client secret, base64 encode it and compare it with `X-Shopify-Hmac-Sha256`.
if: Signature is valid
App ->> Queue: Enqueue order 820982911946154508
App --> Shopify: 200 OK
else: Signature is invalid
App --> Shopify: 401 Unauthorized
end
note: Shopify waits only a few seconds for a response and retries failed deliveries, so answer first and do the work in the background. Use `X-Shopify-Webhook-Id` to skip duplicates.
...: {fas-clock} Moments later
Queue ->> Worker: Order job
Worker -> Shopify Admin API: POST /admin/api/2025-07/graphql.json
in: Tag the order
```http
POST /admin/api/2025-07/graphql.json HTTP/1.1
Content-Type: application/json
X-Shopify-Access-Token: <redacted>
{ "query": "mutation { tagsAdd(id: \"gid://shopify/Order/820982911946154508\", tags: [\"synced\"]) { userErrors { field message } } }" }
```
Shopify Admin API --> Worker: 200 OK
out: Result
```json
{ "data": { "tagsAdd": { "userErrors": [] } } }
```