Welcome new Shopify customers by text
Send each new Shopify customer who agreed to texts a welcome from your own number a couple of hours after their first order, with Zapier or with a webhook handler that checks Shopify’s consent and signature.
Priya runs Fern & Folly, a plant shop on Shopify. Customers who tick “Text me with news and offers” at checkout get nothing personal from her, only Shopify’s order emails. She wants each of them to get a short welcome from her own number a couple of hours after their first order: who she is, how to reach her, and that a care card for their plant is in the box.
What you’ll have
- A welcome text from your number to each new customer who agreed to texts at checkout, two hours after their first order.
- Customers who didn’t agree left alone, and never texted twice.
- Their reply in BlueBot’s inbox and on your iPhone.
What you need
- A BlueBot account with your Mac connected.
- A Shopify store where you can change settings.
- Checkout set to collect a phone number and offer SMS sign-up. In Shopify’s admin, open Settings, then Checkout, and under Marketing options turn on SMS. Customers then see a box to tick for texts, and Shopify records what they chose on the customer as
sms_marketing_consent.
With Zapier
Start with Shopify’s new customer trigger
In Zapier, make a Zap. For the trigger, pick Shopify and New Customer, connect your store and press Test trigger to load a recent customer.
Keep only the customers who agreed
Add Filter by Zapier. Only continue if the customer’s SMS marketing consent state (exactly matches) subscribed, and the customer’s phone exists. Everyone else stops here.
Wait two hours
Add Delay by Zapier, Delay For, 2 hours. The welcome then arrives after the order confirmation rather than on top of it.
Send the welcome
Add BlueBot, Send Text, connected with an API key from Developers, then API keys:
| BlueBot field | Map it to |
|---|---|
| To | The customer’s Phone. |
| Text | Hi, the customer’s First Name, then , it’s Priya from Fern & Folly. Thanks for your first order. There’s a care card in the box, and you can text me here any time your plant looks unhappy. |
| Metadata | source set to shopify, and customer_id set to the customer’s ID. |
Turn the Zap on.
Shopify customers are made at checkout, so the trigger fires as the first order is placed. A customer who later buys again isn’t new and gets nothing.
With your own handler
Use this when you want to choose what the welcome says from the order, or record it in your own systems. Shopify posts each new customer to your handler; the handler checks Shopify’s signature and the consent, then schedules the text two hours out with send_at.
Add the webhook in Shopify
In Shopify’s admin, open Settings, then Notifications, scroll to Webhooks and press Create webhook. Choose the Customer creation event, the JSON format, and your handler’s address (https://fernandfolly.example/hooks/shopify). Shopify shows the secret it signs webhooks with on the same page. Put it in your server’s environment with your BlueBot key:
export BLUEBOT_API_KEY="bb_live_…"
export SHOPIFY_WEBHOOK_SECRET="the secret Shopify shows"Write the handler
Shopify signs each post with X-Shopify-Hmac-Sha256: the base64 HMAC-SHA256 of the raw body, keyed with that secret. Check it, check the consent, then schedule the welcome. The Idempotency-Key is built from the customer’s id, so Shopify sending the same customer again, as it does when your handler is slow, never texts them twice.
// app/hooks/shopify/route.ts (Next.js). Any framework works: read the raw body first.
import { createHmac, timingSafeEqual } from 'node:crypto';
import BlueBot from '@usebluebot/sdk';
const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });
function signedByShopify(payload: string, header: string | null): boolean {
if (!header) return false;
const expected = createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET!).update(payload).digest('base64');
return header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
export async function POST(request: Request) {
const payload = await request.text();
if (!signedByShopify(payload, request.headers.get('x-shopify-hmac-sha256'))) {
return new Response('Bad signature', { status: 401 });
}
const customer = JSON.parse(payload);
const agreed = customer.sms_marketing_consent?.state === 'subscribed';
if (!agreed || !customer.phone) return new Response(null, { status: 200 });
const twoHoursFromNow = new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString();
await client.messages.send(
{
to: customer.phone,
text: `Hi ${customer.first_name ?? 'there'}, it’s Priya from Fern & Folly. Thanks for your first order. There’s a care card in the box, and you can text me here any time your plant looks unhappy.`,
sendAt: twoHoursFromNow,
metadata: { source: 'shopify', customer_id: String(customer.id) },
},
{ idempotencyKey: `shopify-welcome-${customer.id}` },
);
return new Response(null, { status: 200 });
}The handler answers Shopify quickly, since the text is scheduled rather than sent while Shopify waits. Underneath, the call is this request:
/v1/messagescurl -X POST "https://usebluebot.com/api/v1/messages" \
-H "Authorization: Bearer $BLUEBOT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: shopify-welcome-7290918453304" \
-d '{
"to": "+15555550123",
"text": "Hi Maria, it’s Priya from Fern & Folly. Thanks for your first order. There’s a care card in the box, and you can text me here any time your plant looks unhappy.",
"send_at": "2026-11-13T21:41:00.000Z",
"metadata": {
"source": "shopify",
"customer_id": "7290918453304"
}
}'The answer is the text with state: "scheduled". Until it’s due you can change its words or time with PATCH /v1/messages/{id}, or cancel it.
Test it
Place a test order in your own store as a new customer, with your own mobile number, and tick the box for texts. Use a discount code or a test payment gateway so nothing is charged. To see it sooner, set the delay to two minutes while you test, then put it back.
| You got | It means | Do next |
|---|---|---|
| A text on your phone from your own number after the delay | It works. | Put the delay back to two hours and turn it on. |
The text in Messages with state: "scheduled" | It’s waiting for its time. | Wait, or cancel it there and test again with a shorter delay. |
| Nothing in Messages, and the Zap’s history says the filter stopped it | Shopify didn’t record your consent. | Check the box at checkout was ticked, and that the customer in Shopify shows SMS marketing as subscribed. |
401 in Shopify’s webhook deliveries | Your handler’s signature check failed. | Check the secret, and that you hash the raw body before parsing it. |
The text failed with failure.reason: "messages_refused" | Messages on your Mac wouldn’t send it, usually a regular text with no iPhone forwarding. | See . |
When it goes wrong
- A customer gave their number only for shipping. Shopify keeps a shipping phone on the address and a contact phone on the customer. The handler reads the customer’s
phone, which is the one consent applies to. Leave shipping numbers alone. - Your Mac was off when the text was due. The welcome waits for your Mac and goes when it’s back, unless it’s still waiting 24 hours after its time, when it expires. A welcome two days late is worse than none, so that default suits it.
- A busy sale weekend. More than 50 first-time customers in a day goes past your daily limit for new people, and the rest wait until tomorrow with
hold.reason: "daily_new_people_limit". Raise it in Settings, then Sending rules, only as far as you’re sure those people want the text. - A customer replies STOP. They go on your stop list, and BlueBot cancels every text to them from then on, scheduled ones included. Shopify isn’t told; if you also send texts from Shopify, unsubscribe them there too.