Skip to main content

Webhooks delivery structure

Each qualifying change sends a delivery to your uri as an HTTP POST with a JSON body and a set of headers. By default, the body is the full REST resource payload for the topic.

Add include_fields to receive only the specific fields your app needs.

Modify payloads

Every delivery includes a JSON body containing the full REST resource for the subscribed topic. The shape and fields depend on the topic. See the Webhooks reference for the payload structure of each topic.

Example payload (products/update)

{
"id": 9554194432293,
"title": "T-Shirt",
"status": "active",
"vendor": "My Store",
"product_type": "Shirts",
"updated_at": "2025-04-22T14:30:00-05:00",
"variants": [
{
"id": 123456789,
"title": "Default Title",
"price": "29.99",
"sku": "TSHIRT-001",
"taxable": true,
"updated_at": "2025-04-22T14:30:00-05:00"
}
],
"tags": "cotton, comfortable"
}

Every delivery includes the following headers. Treat header names as case-insensitive in your code, as HTTP/2 often lowercases them.

HeaderDescription
X-Shopify-TopicThe topic name (for example, products/update).
X-Shopify-Hmac-Sha256Base64-encoded HMAC signature for verifying the delivery came from Shopify. HTTPS only.
X-Shopify-Shop-DomainThe myshopify.com domain of the store that triggered the event.
X-Shopify-API-VersionThe API version used to serialize the payload.
X-Shopify-Webhook-IdA unique composite key per delivery. Use to identify and deduplicate individual deliveries.
X-Shopify-Triggered-AtTimestamp of when Shopify triggered the delivery.
X-Shopify-Event-IdA unique ID shared across all deliveries produced by the same merchant action.
X-Shopify-Name (optional)Developer-supplied subscription name, set via the name field in your subscription.

include_fields is an optional array of field paths on a webhook subscription. When set, the delivery payload includes only the specified fields instead of the full resource.

Without include_fields, every delivery includes the complete resource payload:

Without include_fields

[webhooks]
api_version = "2026-04"

[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "https://your-app.example.com/webhooks/products"
{
"id": 9554194432293,
"title": "T-Shirt",
"status": "active",
"vendor": "My Store",
"product_type": "Shirts",
"updated_at": "2025-04-22T14:30:00-05:00",
"variants": [
{
"id": 123456789,
"title": "Default Title",
"price": "29.99",
"sku": "TSHIRT-001",
"taxable": true,
"updated_at": "2025-04-22T14:30:00-05:00"
}
],
"tags": "cotton, comfortable"
}

Adding include_fields narrows the payload to only the specified fields. You denote nested fields using a period (for example, variants.price). You can also set include_fields using the includeFields input in the webhookSubscriptionCreate mutation.

With include_fields

[webhooks]
api_version = "2026-04"

[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "https://your-app.example.com/webhooks/products"
include_fields = ["id", "variants.id", "variants.price", "updated_at"]
{
"id": 9554194432293,
"variants": [
{
"id": 123456789,
"price": "29.99"
}
],
"updated_at": "2025-04-22T14:30:00-05:00"
}

When include_fields reduces the payload to a small set of fields, multiple qualifying events might produce identical payloads. Shopify debounces deliveries with identical payloads that arrive within a short time window, dropping the later one. For example, a subscription to orders/updated with include_fields = ["id", "line_items.title"] would debounce consecutive price changes, since neither the order ID nor line item titles change between them.

To prevent debouncing, include a field that always has a unique value. For example, updated_at changes with every update, ensuring no two consecutive deliveries are identical.

When both filter and include_fields are set, all fields referenced in the filter expression must also appear in include_fields.

shopify.app.toml

[webhooks]
api_version = "2026-04"

[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "https://example.com/webhooks"
include_fields = ["id", "status", "product_type", "variants.taxable", "variants.price", "variants.title", "updated_at"]
filter = "id:* AND status:active AND (product_type:Music OR product_type:Movies) AND variants.taxable:true AND variants.price:>=100 AND variants.title:'The Miseducation of'"

See Delivery filtering for filter syntax and examples.


Suppose you want deliveries only when an active product in the Music or Movies category has a variant priced above $100. The following subscription combines include_fields to narrow the payload and filter to gate delivery:

Active high-price music or movies product

[webhooks]
api_version = "2026-04"

[[webhooks.subscriptions]]
topics = ["products/update"]
uri = "https://your-app.example.com/webhooks/products"
include_fields = ["id", "status", "product_type", "variants.id", "variants.price", "updated_at"]
filter = "status:active AND (product_type:Music OR product_type:Movies) AND variants.price:>=100"
{
"id": 9554194432293,
"status": "active",
"product_type": "Music",
"variants": [
{
"id": 123456789,
"price": "129.99"
}
],
"updated_at": "2025-04-22T15:00:00-05:00"
}


Was this page helpful?