POST /v2/integrations/shopify/webhook/{topic} is the public wire-protocol endpoint
Shopify webhooks land on after a merchant connects their store to droplinked. It is the
hot path that keeps droplinked’s InventoryOS catalog mirror in sync with the merchant’s
Shopify store of record.
This page documents the wire protocol for engineers configuring Shopify webhooks
themselves. For the merchant-facing integration walkthrough — how to connect a store,
authorize the droplinked Shopify app, and verify the sync is healthy — see
Connect your Shopify store.
Although this endpoint has no JWT and no IP allowlist, it is not an open ingest: every
request is HMAC-validated against the per-shop webhook secret that droplinked issued at
connection time. Unauthed traffic is rejected with a 401.
Supported topics
The{topic} path segment selects the handler. Currently wired:
| Topic | Behavior |
|---|---|
products-create | Mirror the new product into droplinked’s InventoryOS catalog. Idempotent on shopify.product.id |
products-update | Upsert the product mirror with the latest title, variants, images, and inventory |
products-delete | Soft-remove the product from the mirror (preserves history; hides from the catalog feed) |
orders-create | Log the order event. v1 is a stub — v2 will fire affiliate-attribution and repayment-attestation side effects |
orders-updated | Log the order update event. v1 stub — v2 will reconcile attribution state when an order moves through fulfillment / refund states |
message: "shopify_webhook_topic_not_supported".
HMAC validation
Shopify computes an SHA-256 HMAC over the raw request body using the per-shop webhook secret and sends the result in theX-Shopify-Hmac-Sha256 header. Droplinked re-computes
the expected HMAC server-side using the secret it stored at connection time and
constant-time-compares against the header.
| Header | Description |
|---|---|
X-Shopify-Hmac-Sha256 | Base64-encoded SHA-256 HMAC of the raw body, keyed on the per-shop webhook secret |
X-Shopify-Shop-Domain | The source shop’s .myshopify.com domain — used to look up which secret to validate against |
X-Shopify-Topic | Informational — droplinked routes on the {topic} path segment, not this header |
Content-Type | application/json |
shopify_webhook_auth_failed.
The body is not parsed when validation fails, so no side effects fire.
If the X-Shopify-Shop-Domain does not resolve to a connected shop, the endpoint returns
404 with a message instructing the operator to call POST /admin/shopify-integration/connect
first.
Configuring the webhook in Shopify Admin
In Shopify Admin → Settings → Notifications → Webhooks, add one webhook per supported topic. For each:| Field | Value |
|---|---|
| Event | The matching Shopify event (e.g. Product creation for products-create) |
| Format | JSON |
| URL | https://apiv3.droplinked.com/v2/integrations/shopify/webhook/<topic> |
| Webhook API version | Latest stable (currently 2026-04) |
| Webhook signing secret | Provided by droplinked on POST /admin/shopify-integration/connect |
<topic> segment must match exactly one of the supported topics in the table
above. Example URLs:
Example request
A Shopify-emittedproducts-create webhook lands on droplinked as:
Response — 200 OK
Response — 401 Unauthorized (HMAC mismatch)
Response — 404 Not Found (shop not connected)
Response — 404 Not Found (unsupported topic)
Response — 400 Bad Request (malformed body)
Body forwarding and normalization
After HMAC validation, droplinked normalizes the Shopify product shape into its InventoryOS catalog model. The mapping is:| Shopify field | InventoryOS field |
|---|---|
id | externalSourceIds.shopify.productId |
title | title |
vendor | brand |
variants[].id | skus[].externalSourceIds.shopify.variantId |
variants[].sku | skus[].sku |
variants[].price | skus[].priceCents (×100) |
variants[].inventory_quantity | skus[].stockOnHand |
images[].src | images[].url |
Operator setup
Before pointing Shopify webhooks at this endpoint, the merchant must be connected via the admin API:Operator calls connect (SUPER_ADMIN-gated)
POST /admin/shopify-integration/connect registers the shop’s *.myshopify.com
domain, issues a per-shop webhook signing secret, and links the Shopify store to a
droplinked shop record.Operator passes the secret to the merchant
The webhook signing secret is returned in the response body of the connect call. It is
only retrievable once — re-running connect rotates the secret and invalidates the old
one.
Merchant configures webhooks in Shopify Admin
Following the table above, the merchant adds one webhook per supported topic using the
issued secret as the signing secret.
Related
- Connect your Shopify store — merchant-facing integration walkthrough.
- InventoryOS — the catalog model the webhook body is normalized into.