textbee Logotextbee.dev
Save 44% with yearly billing.View Plans
Send SMS from Shopify with Shopify Flow: Order and Shipping Texts From Your Own Phone

Send SMS from Shopify with Shopify Flow: Order and Shipping Texts From Your Own Phone

Text customers when a Shopify order is placed or shipped, from your own number, with one Shopify Flow workflow and the Send HTTP request action. Liquid body included.

TT

textbee team

11 min read
Share

TL;DR

  • Shopify sends one SMS by itself: an order confirmation, only when the customer checked out with a phone number and no email, only for stores in a short list of countries, and you cannot edit it. Every other text (shipped, ready for pickup, out for delivery) needs an app or a workflow.
  • On the Grow, Advanced and Plus plans, Shopify Flow has a Send HTTP request action. One workflow calls the textbee send endpoint with the order fields, and your Android phone sends the text from your own number. Nothing is installed in the store.
  • On the Basic plan, the same request runs from Zapier, Make or n8n with a Shopify trigger.
  • This guide builds two workflows: an order confirmation on Order created, and a shipping text with the tracking link on Fulfillment created. Both bodies are copy-paste Liquid.
  • The phone sends about 10 to 15 texts a minute. Order updates fit that pace; a flash sale with thousands of orders in an hour queues on the phone, so add a second phone or send fewer statuses.

What Shopify sends on its own

Before you build anything, know what you get for free. Shopify's own SMS notifications cover exactly one message: an order confirmation, sent when there is no email address on the customer profile. Stores in twelve countries can use it, the sender is your store name cut to eleven characters, and the text cannot be edited.

Everything else is on you. Shipping updates, pickup notices, cash-on-delivery checks and review requests all need either an SMS app from the App Store (a subscription plus a per-message fee, usually from a cloud provider) or a workflow that calls an SMS API.

This guide takes the second route with textbee: the workflow calls one endpoint, and the text goes out from the Android phone on your desk, from the number your customers already have. There is no app to install and no number to rent.

What you need

  • A Shopify store on the Grow, Advanced or Plus plan. The Send HTTP request action is limited to those plans (Shopify Help, checked September 2026). On Basic, skip to the Basic plan route.
  • A textbee account, the Android app on the phone that holds the SIM, and an API key from the dashboard. The quickstart takes about five minutes.
  • A checkout that collects a phone number. In your Shopify admin go to Settings, then Checkout, and set the shipping address phone number to Optional or Required. Shopify stores the number in E.164 form (+12015550123), which is exactly what the send endpoint expects.

The request every workflow makes

Both workflows send the same HTTP request. Only the trigger and the message change.

FieldValue
HTTP methodPost
URLhttps://api.textbee.dev/api/v1/gateway/send-sms
Headerx-api-key: YOUR_TEXTBEE_API_KEY
HeaderContent-Type: application/json
BodyJSON with recipients (an array of E.164 numbers) and message

The response is {"data": {"success": true, "smsBatchId": "..."}}. Keep the batch id in mind: it is how you look up delivery later.

Workflow 1: order confirmation

Step 1: create the workflow

Open Shopify Flow from Apps and click Create workflow. For the trigger pick Order created. If you capture payments by hand and only want to text once the money is in, pick Order paid instead.

Step 2: add a condition on the phone

Click the plus after the trigger and add a Condition. Choose the variable order.shippingAddress.phone and check that it is not empty. If your checkout lets customers use a phone number as their contact method, check order.phone too, so either one passes.

Without this step, an order with no phone reaches the action with an empty recipients array, textbee answers 400, and the run shows as failed. The condition keeps those runs clean.

Step 3: add the Send HTTP request action

On the Then branch add the Send HTTP request action and fill it in:

  • HTTP method: Post
  • URL: https://api.textbee.dev/api/v1/gateway/send-sms
  • Headers: add two rows, x-api-key with your key, and Content-Type with application/json

Flow can store the key as a secret so it never shows in the editor or the run logs. The Send HTTP request reference covers how.

Step 4: paste the body

{% assign phone = order.shippingAddress.phone | default: order.phone %}
{% capture text %}Hi {{ order.customer.firstName | default: "there" }}, order {{ order.name }} is confirmed. Total {{ order.totalPriceSet.shopMoney.amount }} {{ order.totalPriceSet.shopMoney.currencyCode }}. We will text you when it ships.{% endcapture %}
{
  "recipients": [{{ phone | json }}],
  "message": {{ text | json }}
}

Three things to notice:

  • assign picks the shipping phone and falls back to the contact phone, so one body works for both checkout settings.
  • capture builds the message as plain text first. That keeps the JSON at the bottom short and readable.
  • The json filter wraps each value in quotes and escapes it. Whatever a customer typed as their name, the body stays valid JSON. Never put {{ }} output inside hand-typed quotes.

The variable names are the Admin GraphQL field names in camelCase: order.name is the order number with its prefix (#1001), order.customer.firstName is the first name, and order.totalPriceSet.shopMoney.amount is the total in your store currency. Shopify's Liquid variables page lists what each trigger exposes.

Step 5: set the error handling

Two settings below the body decide what Flow does when textbee answers with an error:

  • On client error (4XX response): choose Fail. A 401 (wrong key) or 400 (bad body) will not fix itself, and retrying for a day only fills the log.
  • On server error (5XX or 429 response): leave on Retry.

Step 6: turn it on and test

Click Turn on workflow, then place a test order with your own number. Within a few seconds your phone buzzes with the confirmation, and the run log in Flow shows the request body and the response with its smsBatchId.

Create a second workflow with the Fulfillment created trigger. This trigger runs once per fulfillment, so a split shipment sends one text per package, each with its own tracking link.

The condition is the same, but the order now sits under fulfillment.order, so the variable is fulfillment.order.shippingAddress.phone.

The action is the same request with this body:

{% assign order = fulfillment.order %}
{% assign phone = order.shippingAddress.phone | default: order.phone %}
{% capture link %}{% for tracking in fulfillment.trackingInfo %}{% if forloop.first %} Track it: {{ tracking.url }}{% endif %}{% endfor %}{% endcapture %}
{% capture text %}Hi {{ order.customer.firstName | default: "there" }}, order {{ order.name }} has shipped.{{ link }} Reply to this text if you have a question.{% endcapture %}
{
  "recipients": [{{ phone | json }}],
  "message": {{ text | json }}
}

fulfillment.trackingInfo is a list, because one package can carry several tracking numbers. The loop takes the first url and leaves the text unchanged when there is none. Use the carrier's link as it is: carriers filter shortened links more often than long ones.

If you would rather send one text per order, trigger on Order fulfilled instead and link to the order status page with order.statusPageUrl, which lists every package.

Other events worth a text

The same pattern works for any Flow trigger that carries an order:

EventTriggerWhat to say
Ready for pickupFulfillment order line items prepared for pickupStore address and opening hours
Cash on deliveryOrder created, plus a condition on the payment gateway nameAsk for a YES reply to confirm the order
Review requestOrder fulfilled, then a Wait action of a few daysOne text with the review link
High-value order to the ownerOrder created, condition on order.totalPriceSet.shopMoney.amountYour own number in recipients, order name and total

Keep every order text free of promotions. The moment a shipping text carries an offer it becomes marketing, which needs separate written consent and a working STOP. The compliance checklist has the details.

The Basic plan route

Send HTTP request is not available on Basic, but the Shopify apps in Zapier, Make and n8n are. The setup is the same idea in a different editor:

  1. Trigger: the Shopify trigger in your tool. Zapier calls it New Order or New Paid Order, Make has Watch Orders, and n8n's Shopify Trigger node listens for order and fulfillment events.
  2. Action: the HTTP request from above, with the phone and order fields mapped from the trigger output. The Zapier, Make and n8n pages show the exact module and field names.

The trade is a workflow tool subscription instead of a Shopify plan upgrade. For a store that already runs one of these tools, it is the cheaper route.

Getting replies back to the order

The text comes from a real mobile number, so customers reply to it. "Can you leave it with the neighbour?" is a reply to a shipping text, and it belongs on the order.

Flow cannot receive a webhook, so replies go through the same workflow tools:

  1. In Make, Zapier or n8n, create a webhook trigger and register its URL in the textbee dashboard under Webhooks for the MESSAGE_RECEIVED event. Each delivery is flat JSON with sender, message and receivedAt.
  2. Use the tool's Shopify module to search customers by phone with the sender value, then read the latest order.
  3. Add the message as an order note or a tag, or post it to the channel your support team watches.
  4. To answer, a second HTTP step posts to the send endpoint with recipients set to sender. The conversation stays on the number the customer already texted.

The webhooks documentation has the full payload and the signature header.

Troubleshooting

The run failed with 401. The x-api-key header is missing or wrong. Copy the key again from the dashboard and check for a trailing space in the header value.

The run failed with 400. The body was not valid JSON, or recipients was empty. Open the run log and read the rendered body. The usual causes are a {{ }} placed inside hand-typed quotes without the json filter, or a missing phone condition.

The phone came out empty. The checkout did not collect a phone. Check Settings, then Checkout, and make the shipping address phone number Optional or Required. Orders placed before the change still have no phone.

The action is not in the list. Send HTTP request needs the Grow, Advanced or Plus plan. On Basic, build the same request in Zapier, Make or n8n instead.

Two texts for one order. Fulfillment created runs per fulfillment. Either accept one text per package, or switch to Order fulfilled and link to the status page.

The text arrived late. The phone was offline or asleep. textbee queues the message and the phone sends it when it reconnects. Keep the gateway phone plugged in, on Wi-Fi, and excluded from battery optimization.

Cost

Shopify Flow is included with every plan, and the Send HTTP request action costs nothing beyond the plan that unlocks it. textbee's pricing is a flat monthly plan with messages included: no per-message fee, no number rental, no carrier surcharge. The only per-text cost is whatever your phone plan charges for SMS, which on most plans is nothing.

For a store sending confirmation and shipping texts on a few hundred orders a month, that is the whole bill.

Frequently asked questions

Is there a textbee app in the Shopify App Store?

No. This route uses the Send HTTP request action that Shopify ships. Nothing is installed in the store, no app reads your customer data, and you can turn the workflow off in one click.

Does Shopify count these texts against anything?

No. The request leaves Shopify as an ordinary HTTP call. Shopify Messaging and its per-message billing are not involved.

Can I send from a specific phone?

Yes. Add "deviceId": "..." to the JSON body. A store with one phone per market can pick it with a Liquid if on order.shippingAddress.countryCode.

How do I know the text was delivered?

The run log shows the smsBatchId textbee returned. Delivery status arrives through a MESSAGE_DELIVERED or MESSAGE_FAILED webhook, or you can add a second Send HTTP request that GETs https://api.textbee.dev/api/v1/gateway/messages?smsBatchId=... with the same header.

Can I text the store owner as well as the customer?

Yes. Put two numbers in the recipients array, or add a second Send HTTP request with your own number and a shorter message. An order over a set amount that texts the owner is a common first workflow.

Does this work for WooCommerce too?

Not with Flow, but the same idea works with one PHP snippet. See WooCommerce SMS notifications without a plugin.

Next steps

Get more textbee in Google Search

Add textbee.dev as a preferred source. Our posts show up more often in your Google results.

Add as a preferred source