# Can Shopify send its customers to Broadcast?

> Pipe Shopify order webhooks or Shopify Flow into Broadcast's subscribers API, tag customers, store their last order date, and let a tag start a sequence. A do-it-yourself guide with working code and honest limits.

Source: https://sendbroadcast.net/how-to/connect-shopify-to-broadcast

Yes, with a little plumbing you run yourself. Broadcast has no Shopify app and
does not listen for Shopify events. What it has is a
[Subscribers API](https://sendbroadcast.net/docs/api-subscribers) that takes tags and custom data. Shopify
can call an HTTP endpoint when an order is paid. Put the two together and a paid
order can tag a subscriber, store the order date, and start a sequence.

This page shows how. Read the limits section before you build anything.

## What you can build today

- A paid order adds the tags `customer` and `ordered` to the subscriber.
- The order date goes into custom data as `last_order_at`, and the order number
  as `last_order_name`. An email can print it with `{{ custom_data.last_order_name }}`.
- A sequence with the tag trigger `ordered` sends a post-purchase series.
- A segment on `last_order_at` finds customers who have not ordered in 90 days,
  for a win-back sequence.

[Post-purchase and win-back flows](/how-to/shopify-post-purchase-and-win-back-flows)
covers what to put in those sequences. This page is the plumbing.

## What you cannot build today

Read these before you start:

- Broadcast does not receive cart or checkout events. Abandoned checkout emails
  stay with Shopify. See [Shopify abandoned checkout emails](/how-to/shopify-abandoned-checkout-emails).
- There are no product blocks. An email cannot show the items from an order with
  images and prices.
- Custom data holds one value per key. You can store the last order number and
  print it, but the next order overwrites it.
- A subscriber goes through a given sequence once. A second order does not run the
  post-purchase sequence again.
- Broadcast cannot send SMS.

Native Shopify events are planned. Watch the [roadmap](/roadmap) for them.

## Pick a route

Shopify gives you three ways to call an outside URL.

| Route | Shopify plan | Can it send the Bearer token? | Needs a server? |
|-------|--------------|-------------------------------|-----------------|
| Admin webhooks (Settings > Notifications > Webhooks) | Every plan | No | Yes, a small one |
| Shopify Flow, Send HTTP request | Grow, Advanced, Plus | Yes | No, for simple calls |
| n8n, Make or Zapier | Every plan | Yes | No, their service runs it |

Broadcast's API wants an `Authorization: Bearer` header and its own JSON shape.
An admin webhook sends Shopify's order JSON with a Shopify signature, so something
has to sit in the middle and translate. That is the script below.

Shopify's **Customer events** in the admin are for tracking pixels that run in a
browser sandbox. They are not a server feed. For email, use webhooks or Flow.

## Before you connect anything

1. Create a channel access token in Broadcast with subscriber read and write
   permission. See [API Authentication](https://sendbroadcast.net/docs/api-authentication).
2. Create the sequence and set its **Tag trigger** to `ordered`. Do this first:
   subscribers who already have the tag when you set the trigger are not added.
   See [triggering a sequence](/how-to/trigger-a-sequence-from-a-segment).
3. Import your existing customers once, so the API has people to tag. See
   [bringing your Shopify list](/how-to/bring-your-existing-list#from-shopify).

## Route 1: admin webhook and a small script

In Shopify, open **Settings > Notifications**, click **Webhooks**, and click
**Create webhook**. Pick the **Order payment** event, JSON format, and the URL of
your script. Shopify shows the key it signs webhooks with on that page. Copy it.

The script does four things:

1. Checks the `X-Shopify-Hmac-SHA256` header against the raw body.
2. Answers Shopify at once. Shopify waits five seconds at most and retries
   failures.
3. Looks the buyer up with `GET /api/v1/subscribers/find.json`.
4. Updates a known subscriber, or creates a new one only when the buyer ticked
   the marketing box at checkout.

```js
// shopify-to-broadcast.mjs: Node 18 or later, no dependencies.
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";

const { SHOPIFY_WEBHOOK_KEY, BROADCAST_URL, BROADCAST_TOKEN, PORT = 8080 } = process.env;

const api = (method, path, body) =>
  fetch(`${BROADCAST_URL}/api/v1/${path}`, {
    method,
    headers: { Authorization: `Bearer ${BROADCAST_TOKEN}`, "Content-Type": "application/json" },
    body: body && JSON.stringify(body),
  });

async function sync(order) {
  const email = order.email || order.customer?.email;
  if (!email) return;
  const data = { last_order_at: order.processed_at, last_order_name: order.name };

  const found = await api("GET", `subscribers/find.json?email=${encodeURIComponent(email)}`);
  if (found.ok) {
    await api("PATCH", "subscribers.json", { email, custom_data_mode: "merge", subscriber: { custom_data: data } });
    await api("POST", "subscribers/add_tag.json", { email, tags: ["customer", "ordered"] });
  } else if (found.status === 404 && order.buyer_accepts_marketing) {
    await api("POST", "subscribers.json", { subscriber: {
      email, first_name: order.customer?.first_name, last_name: order.customer?.last_name,
      tags: ["customer", "ordered"], custom_data: data } });
  }
}

createServer(async (req, res) => {
  const chunks = [];
  for await (const chunk of req) chunks.push(chunk);
  const raw = Buffer.concat(chunks);

  const expected = createHmac("sha256", SHOPIFY_WEBHOOK_KEY).update(raw).digest();
  const received = Buffer.from(req.headers["x-shopify-hmac-sha256"] || "", "base64");
  if (received.length !== expected.length || !timingSafeEqual(received, expected)) {
    return res.writeHead(401).end();
  }
  res.writeHead(200).end();
  sync(JSON.parse(raw)).catch(console.error);
}).listen(PORT);
```

Why the script looks the subscriber up first, and does not send one create call
for everyone:

- `POST /api/v1/subscribers` on an email that already exists replaces its tags and
  custom data, and resubscribes a person who unsubscribed. Never send it for a
  buyer who is already on your list.
- `PATCH` with `custom_data_mode: "merge"` changes only the keys you send and keeps
  the rest.
- `add_tag` adds tags and leaves the others alone. A tag does not start a sequence
  for an inactive or unsubscribed subscriber, so tagging a past unsubscriber is safe.

Run it on any small server or function host that has a public HTTPS URL. For
production, put the work after the `200` on a queue so a slow call to Broadcast
cannot be lost.

## Route 2: Shopify Flow on Grow, Advanced or Plus

Flow's **Send HTTP request** action can set headers, so it can call Broadcast
directly. Store the token as a Flow secret and use it in the header value.

1. Create a workflow with the **Order paid** trigger.
2. Add **Send HTTP request**: method `POST`, URL
   `https://your-broadcast-domain.com/api/v1/subscribers/add_tag.json`,
   headers `Authorization: Bearer ` and `Content-Type: application/json`, body:
   ```json
   { "email": "{{ order.email }}", "tags": ["customer", "ordered"] }
   ```

 3. Add a second **Send HTTP request**: method `PATCH`, URL `https://your-broadcast-domain.com/api/v1/subscribers.json`, body:
   ```json
   {
     "email": "{{ order.email }}",
     "custom_data_mode": "merge",
     "subscriber": { "custom_data": { "last_order_at": "{{ order.processedAt }}" } }
   }
   ```

 4. Set 4XX responses to **Ignore**. Broadcast answers `404` for a buyer who is not a subscriber, and that is the right outcome. This tags people who are already on your list. It does not add new buyers, because Flow cannot easily branch on Broadcast's answer. To add buyers who opted in at checkout, point the Flow request at the script from route 1 instead. ## Route 3: n8n, Make or Zapier On a Basic plan without Flow's HTTP action, an automation service does the same job. Start with the service's Shopify trigger for orders, or with its own webhook URL that a Shopify webhook calls. Then add HTTP steps that copy the script's logic: find the subscriber, then merge custom data and add tags, or create the subscriber only when `buyer_accepts_marketing` is true. These services hold your Broadcast token, so give them a token with subscriber permission only. ## Check that it works 1. Place a test order with an email that is already a subscriber. 2. Open the subscriber in Broadcast. The tags show `customer` and `ordered`, and custom data shows `last_order_at`. 3. Open the sequence. The subscriber is enrolled on the first step. If nothing arrives, check the webhook delivery in Shopify first, then the response your script got from Broadcast. Write requests can succeed with a `warnings` array. See [API Response Warnings](https://sendbroadcast.net/docs/api-response-warnings). ## Frequently asked questions ### Do I need Shopify Plus to send webhooks to my email tool? No. Webhooks under Settings > Notifications work on every plan. Flow's Send HTTP request action needs Grow, Advanced or Plus. ### Why not send the Shopify webhook straight to Broadcast? Shopify sends its own order JSON with an HMAC signature. Broadcast expects a Bearer token and its own fields. A small script or an automation service translates between them. ### Can Broadcast email abandoned checkouts from Shopify? No. Broadcast does not receive checkout events today. Keep Shopify's own abandoned checkout email turned on. ### Does a repeat order start the post-purchase sequence again? No. A subscriber goes through a sequence once. The `last_order_at` value still updates on every order, so segments stay current. ### Is this the same as Broadcast's general API guide? This page is the Shopify recipe. For the API, CLI and MCP server in general, see [connecting your app or an AI agent](/how-to/connect-your-app-or-ai-agent) and the [email marketing API](/email-marketing-api) overview.  --- The API reference for every call on this page: [Subscribers API](https://sendbroadcast.net/docs/api-subscribers), [Sequences API](https://sendbroadcast.net/docs/api-sequences) and [Segments API](https://sendbroadcast.net/docs/api-segments). For events that go from Broadcast back to your own systems, see [Webhooks](https://sendbroadcast.net/docs/webhooks). Comparing tools for a Shopify store? Read our [Klaviyo alternative](/compare/klaviyo-alternative) page and the [e-commerce use case](/use-cases/ecommerce).
