> ## Documentation Index
> Fetch the complete documentation index at: https://nango.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to set up webhooks with Shopify on Nango

> Learn how to receive real-time Shopify events in your app using Nango webhooks

Shopify webhooks notify your app when something changes in a store — an order is created, a product is updated, a customer is deleted. You register webhooks against Shopify's Admin API with the callback address set to your Nango webhook URL; Nango verifies the request and routes it to the right connection.

## How it works

1. You register a webhook via Shopify's Admin API (REST or GraphQL), pointing the callback address at your Nango webhook URL.
2. Shopify sends a POST request to that URL when the subscribed event occurs, signing it with `X-Shopify-Hmac-Sha256` and including the `X-Shopify-Topic` and `X-Shopify-Shop-Domain` headers.
3. Nango verifies the signature, matches the shop domain in `X-Shopify-Shop-Domain` to the connection's `subdomain`, and forwards the event to your app.

## Setup

### 1. Get your Nango webhook URL

In the Nango dashboard, open your Shopify integration and copy the **Webhook URL**.

### 2. Register the webhook in Shopify

Registering a webhook for a topic requires the scope that grants read access to the underlying resource (for example, `read_orders` for `orders/create`). Add the scope to your Shopify integration's **Scopes** field in the Nango dashboard and re-authorize the connection if it was created before the scope was added.

> [!NOTE]
>
> Shopify's REST Admin API has been legacy since October 2024, and public (OAuth) apps created after April 2025 must use the GraphQL Admin API — the REST tab below still works for existing apps and for the API Key (custom app) auth mode, but prefer GraphQL for any new OAuth integration.

> [tab] REST API
>
> ```bash theme={null}
> curl -X POST "https://api.nango.dev/proxy/admin/api/2025-01/webhooks.json" \
>   -H "Authorization: Bearer <NANGO-API-KEY>" \
>   -H "Provider-Config-Key: <INTEGRATION-ID>" \
>   -H "Connection-Id: <CONNECTION-ID>" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "webhook": {
>       "topic": "orders/create",
>       "address": "<NANGO-WEBHOOK-URL>",
>       "format": "json"
>     }
>   }'
> ```

> [tab] GraphQL
>
> ```bash theme={null}
> curl -X POST "https://api.nango.dev/proxy/admin/api/2025-01/graphql.json" \
>   -H "Authorization: Bearer <NANGO-API-KEY>" \
>   -H "Provider-Config-Key: <INTEGRATION-ID>" \
>   -H "Connection-Id: <CONNECTION-ID>" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "query": "mutation { webhookSubscriptionCreate(topic: ORDERS_CREATE, webhookSubscription: {callbackUrl: \"<NANGO-WEBHOOK-URL>\", format: JSON}) { webhookSubscription { id } userErrors { field message } } }"
>   }'
> ```

Repeat for each topic you want to receive. REST topics use lowercase slash-separated names (`orders/create`); the GraphQL enum uses the equivalent screaming-snake-case name (`ORDERS_CREATE`).

## Webhook payload

The POST body is the raw resource as JSON — the same shape as the REST Admin API response for that resource, with a numeric `id` (not a GraphQL global ID), regardless of whether you registered the webhook via REST or GraphQL. Example for `orders/create`:

```json theme={null}
{
  "id": 820982911946154508,
  "order_number": 1001,
  "email": "customer@example.com",
  "financial_status": "paid",
  "currency": "USD",
  "total_price": "398.00",
  "line_items": [
    {
      "id": 866550311766439020,
      "title": "IPod Nano - 8GB",
      "quantity": 1,
      "price": "199.00"
    }
  ],
  "customer": {
    "id": 115310627314723954,
    "email": "customer@example.com"
  }
}
```

Every delivery also includes these headers:

| Header | Description |
| - | - |
| `X-Shopify-Topic` | The subscribed topic, e.g. `orders/create`. |
| `X-Shopify-Shop-Domain` | The store's `myshopify.com` domain — used for connection matching. |
| `X-Shopify-Hmac-Sha256` | Base64-encoded HMAC-SHA256 signature — see [Signature verification](#signature-verification). |
| `X-Shopify-Webhook-Id` | Unique per delivery. Use it to deduplicate retries — Shopify redelivers on a non-2xx response or timeout, so expect occasional duplicates. |
| `X-Shopify-Event-Id` | Shared across every subscription's delivery for the same merchant action. If you have multiple webhooks on the same topic, use this (not `X-Shopify-Webhook-Id`) to correlate them. |
| `X-Shopify-Triggered-At` | When the event occurred — use it to order out-of-sequence deliveries. |
| `X-Shopify-API-Version` | The API version the subscription was created with. |

## Connection matching

Nango matches an incoming webhook to a connection using the shop domain in the `X-Shopify-Shop-Domain` header against the connection's `subdomain`. No extra configuration is needed on the connection.

> [!WARNING]
>
> If no connection's `subdomain` matches the shop domain in `X-Shopify-Shop-Domain`, the webhook is not routed to any connection.

## Signature verification

* **Shopify (OAuth)**: Nango verifies `X-Shopify-Hmac-Sha256` automatically using your integration's client secret — no extra setup needed.
* **Shopify (API Key)**: set a **Webhook Secret** on the integration in the Nango dashboard, matching the secret configured when you subscribed the webhook, so Nango can verify the signature against it.

## Handle the webhook

Once routed to a connection, you have two options:

* **Forward it to your app** — Nango forwards the event to your webhook URL with connection attribution. See [External webhook forwarding](https://nango.dev/docs/guides/platform/webhook-forwarding.md).
* **Process it in a sync** — run a sync when the webhook arrives using `webhookSubscriptions` and `onWebhook` in a sync script. See [Real-time syncs](https://nango.dev/docs/guides/functions/syncs/realtime-syncs.md).

## Supported events

Nango can route any topic Shopify delivers, since routing only depends on the `X-Shopify-Shop-Domain` header. Common topics:

| Topic | Description | Required scope |
| - | - | - |
| `orders/create` | An order was created | `read_orders` |
| `orders/updated` | An order was updated | `read_orders` |
| `orders/cancelled` | An order was cancelled | `read_orders` |
| `products/create` | A product was created | `read_products` |
| `products/update` | A product was updated | `read_products` |
| `customers/create` | A customer was created | `read_customers` |
| `customers/update` | A customer was updated | `read_customers` |
| `app/uninstalled` | A merchant uninstalled your app | none |

For the full list, see [Shopify's webhook topics reference](https://shopify.dev/docs/api/webhooks?reference=toml#list-of-topics).

## Rollback strategy

To stop webhooks, delete the subscription using the `id` Shopify returned when you created it:

1. REST: send a `DELETE` request to `/admin/api/2025-01/webhooks/<id>.json`.
2. GraphQL: call the `webhookSubscriptionDelete` mutation with that `id`.

Deleting a subscription stops delivery immediately. Re-create it with the steps above to resume.

> [!TIP]
>
> Need help getting started? Get help in the [community](https://nango.dev/slack).

***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.