---
description: Reference for verified Shopify webhook ingress from @flue/shopify.
title: Shopify Channel API | Flue
image: https://flueframework.com/docs/og4.jpg
---

# Shopify Channel API

AI-generated, awaiting review [ View as Markdown](https://flueframework.com/docs/api/shopify-channel/index.md) 

Import from `@flue/shopify`.

## Exports

```
export {
  createShopifyChannel,
  type ChannelRoute,
  type JsonValue,
  type ShopifyChannel,
  type ShopifyChannelOptions,
  type ShopifyHandlerResult,
  type ShopifyWebhookHandlerInput,
};
```

## `createShopifyChannel()`

```
function createShopifyChannel<E extends Env = Env>(
  options: ShopifyChannelOptions<E>,
): ShopifyChannel<E>;
```

Creates one stateless Shopify JSON webhook channel.

## `ShopifyChannelOptions`

```
interface ShopifyChannelOptions<E extends Env = Env> {
  clientSecret: string;
  previousClientSecret?: string;
  bodyLimit?: number;
  webhook(input: ShopifyWebhookHandlerInput<E>): ShopifyHandlerResult;
}
```

| Field                | Description                                                        |
| -------------------- | ------------------------------------------------------------------ |
| clientSecret         | Current Shopify app client secret used for webhook HMACs.          |
| previousClientSecret | Optional previous secret accepted during rotation overlap.         |
| bodyLimit            | Maximum request-body size in bytes. Defaults to 1 MiB.             |
| webhook              | Receives every verified, structurally valid JSON webhook delivery. |

Configured secrets must be non-empty. `bodyLimit` must be a positive integer.

## Handler input

```
interface ShopifyWebhookHandlerInput<E extends Env = Env> {
  c: Context<E>;
  payload: JsonValue;
  rawBody: string;
}
```

`c` is the authentic Hono context. `payload` is Shopify’s parsed JSON body with its original field names and nesting. `rawBody` is the exact UTF-8 body the signature was verified against. The callback runs only after exact-body HMAC verification, UTF-8 decoding, and JSON parsing.

Delivery metadata is read from the provider’s native headers through `c`, for example `c.req.header('x-shopify-topic')`, `c.req.header('x-shopify-shop-domain')`,`c.req.header('x-shopify-api-version')`, and `c.req.header('x-shopify-webhook-id')`. Optional `c.req.header('x-shopify-event-id')`, `c.req.header('x-shopify-triggered-at')`, and `c.req.header('x-shopify-sub-topic')` may be absent. The channel does not curate a typed header object, require any header’s presence, or read the non-standard `X-Shopify-Name` header; the application reads and validates whatever headers it consumes from `c`.

Topics remain open strings read from `c.req.header('x-shopify-topic')`. A verified topic newer than the installed package still reaches `webhook`.

Payload fields depend on topic, API version, and subscription field selection. The package parses JSON with `lossless-json`: safe numeric literals remain JavaScript numbers, while numeric literals outside the safe integer range are represented by their exact decimal strings. Applications must validate the fields they consume and should accept `string | number` for Shopify identifiers that can exceed `Number.MAX_SAFE_INTEGER`. Do not convert an unsafe identifier string to `number`.

Shopify signs the request body, not these delivery headers. Header values are provider-supplied routing metadata rather than independent cryptographic or authorization claims. The package does not expose a conversation-key helper or universal resource key.

## Handler result

```
type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };

type ShopifyHandlerResult =
  | undefined
  | JsonValue
  | Response
  | Promise<undefined | JsonValue | Response>;
```

Returning nothing produces an empty `200`. A JSON-compatible value becomes a JSON response. A normal Hono or Fetch `Response` passes through unchanged. A thrown callback propagates to Hono’s error handler.

Non-2xx responses request Shopify redelivery. Shopify’s total response deadline is five seconds. The channel does not enforce a deadline with a timer, because racing a JavaScript callback against a timer does not cancel it: the timed-out work keeps running and may complete after the failure response. Admit durable work promptly — dispatch and return — and rely on application-owned idempotency keyed on `x-shopify-webhook-id` rather than a timeout to keep retries safe.

## `ShopifyChannel`

```
interface ShopifyChannel<E extends Env = Env> {
  readonly routes: readonly ChannelRoute<E>[];
}

interface ChannelRoute<E extends Env = Env> {
  readonly method: string;
  readonly path: string;
  readonly handler: Handler<E>;
}
```

`routes` contains one `POST /webhook` declaration. A file named`channels/shopify.ts` is served at `POST /channels/shopify/webhook` relative to the `flue()` mount.

## Verification

The route accepts `application/json` and authenticates each delivery with the`X-Shopify-Hmac-Sha256` header.

The channel retains the exact request bytes and verifies base64 HMAC-SHA256 with `clientSecret`. If that fails and `previousClientSecret` is configured, it tries the previous secret. Verification runs before JSON parsing or application code. The channel verifies the body signature only; it does not require, curate, or validate the delivery metadata headers. A delivery missing a metadata header still reaches `webhook`, where the application reads and validates whatever headers it consumes from `c`.

Unsupported media types receive `415`; oversized bodies receive `413`; missing, malformed, or incorrect authentication receives `401`; malformed JSON or invalid UTF-8 receives `400`.

Shopify supplies no signed webhook timestamp or protocol replay window. Applications own replay policy, delivery-id persistence, deduplication, and ordering.

## Delivery and application boundary

Shopify can duplicate or reorder deliveries and retries failures eight times over four hours. Use `c.req.header('x-shopify-webhook-id')` for delivery deduplication. Use `c.req.header('x-shopify-event-id')`, when present, only to correlate deliveries caused by the same merchant action.

The channel supports JSON HTTPS webhooks, including mandatory`customers/data_request`, `customers/redact`, and `shop/redact` topics. It does not support XML delivery, EventBridge, Google Pub/Sub, or long-lived transports.

App installation, OAuth, Admin API tokens, webhook registration, subscription filters, secret-rotation orchestration, deduplication, persistence, compliance business actions, outbound clients, and tools remain application concerns.

`@flue/shopify` uses Hono, standards-based Web Crypto, and `lossless-json`; it does not depend on the Shopify SDK or `@flue/runtime`. See[Shopify setup](https://flueframework.com/docs/ecosystem/channels/shopify/) for the project-owned Admin GraphQL client and Node/workerd testing guidance.

## Docs Navigation

Current page: [Shopify Channel API](https://flueframework.com/docs/api/shopify-channel/)

### Sections

* [Guide](https://flueframework.com/docs/getting-started/quickstart/)
* [Reference](https://flueframework.com/docs/api/agent-api/)
* [CLI](https://flueframework.com/docs/cli/overview/)
* [SDK](https://flueframework.com/docs/sdk/overview/)
* [Ecosystem](https://flueframework.com/docs/ecosystem/)

### Runtime

* [ Configuration ](https://flueframework.com/docs/reference/configuration/)
* [ Errors Reference ](https://flueframework.com/docs/api/errors-reference/)
* [ Agent API ](https://flueframework.com/docs/api/agent-api/)
* [ Provider API ](https://flueframework.com/docs/api/provider-api/)
* [ Routing API ](https://flueframework.com/docs/api/routing-api/)
* [ GitHub Channel ](https://flueframework.com/docs/api/github-channel/)
* [ Stripe Channel ](https://flueframework.com/docs/api/stripe-channel/)
* [ Notion Channel ](https://flueframework.com/docs/api/notion-channel/)
* [ Resend Channel ](https://flueframework.com/docs/api/resend-channel/)
* [ Shopify Channel ](https://flueframework.com/docs/api/shopify-channel/)
* [ Intercom Channel ](https://flueframework.com/docs/api/intercom-channel/)
* [ Zendesk Channel ](https://flueframework.com/docs/api/zendesk-channel/)
* [ Salesforce Marketing Cloud Channel ](https://flueframework.com/docs/api/salesforce-marketing-cloud-channel/)
* [ Slack Channel ](https://flueframework.com/docs/api/slack-channel/)
* [ Discord Channel ](https://flueframework.com/docs/api/discord-channel/)
* [ Teams Channel ](https://flueframework.com/docs/api/teams-channel/)
* [ Google Chat Channel ](https://flueframework.com/docs/api/google-chat-channel/)
* [ Linear Channel ](https://flueframework.com/docs/api/linear-channel/)
* [ Telegram Channel ](https://flueframework.com/docs/api/telegram-channel/)
* [ WhatsApp Channel ](https://flueframework.com/docs/api/whatsapp-channel/)
* [ Twilio Channel ](https://flueframework.com/docs/api/twilio-channel/)
* [ Messenger Channel ](https://flueframework.com/docs/api/messenger-channel/)
* [ Streaming Protocol ](https://flueframework.com/docs/api/streaming-protocol/)
* [ Events Reference ](https://flueframework.com/docs/api/events-reference/)

### Advanced

* [ Sandbox Adapter API ](https://flueframework.com/docs/api/sandbox-api/)
* [ Data Persistence API ](https://flueframework.com/docs/api/data-persistence-api/)