Abandoned cart texts

A shopper starts checkout and leaves. An hour later, if no order came in, they get one text with their cart link:

text
checkout_started
  -> Wait until order_placed
       arrives: end
       not in 1 hour: continue
  -> Send SMS
text
Acme: you left something in your cart. Finish your order here:
https://acme.com/checkouts/c/4b1e... Reply STOP to opt out.

This flow is the Abandoned cart template in Console → Automations. It runs on two tracked events from your store:

EventWhenProperties
checkout_startedA shopper with a phone number reaches checkoutcheckout_url (the link in the text)
order_placedThe order goes throughnone needed

A connected Shopify store sends them for you. Otherwise send them from Shopify Flow, Klaviyo, or your own code. Each way is below.

Before you start

  • A number that can text the public: a registered local number or a verified toll-free number. See Compliance.
  • Opt-ins on file. A cart reminder is a marketing text, so it goes only to shoppers whose opt-in is recorded with TextKit. Shoppers who tick the SMS box at your Shopify checkout come in when you connect Shopify in Console → Contacts → Integrations. Subscribers from another tool come in as a CSV with consent columns (Contacts). Everyone else is skipped, and the run logs why.
  • An API key for the tool that calls TextKit. A restricted key is enough: the automations scope for POST /v1/track, or messages:send for POST /v1/messages.

Connected Shopify store

  1. Connect the store in Console → Contacts → Integrations. The store's app needs the read_customers and read_orders scopes. A store connected before cart reminders existed: reconnect it once.
  2. In Console → Automations, create a flow from the Abandoned cart template, pick your sending number, edit the text, and activate it.

That's all. Every 10 minutes TextKit checks the store for abandoned checkouts and new orders and sends the two events, each checkout once. Shoppers who ticked the SMS box at checkout are recorded as opted in on the way. A checkout becomes an event only for a shopper who agreed to texts (that box, or an opt-in already on file); nobody else gets a reminder. The checks run only while a flow that starts on checkout_started is active. If Shopify refuses them, the integration's card in the console says why.

Shopify Flow

Shopify Flow is Shopify's free automation app. Its Send HTTP request action is on the Grow, Advanced and Plus plans, not Basic.

  1. In Console → Automations, create a flow from the Abandoned cart template, pick your sending number, edit the text, and activate it.
  2. In Shopify admin, open Flow → Create workflow.
  3. Trigger: Customer abandons checkout.
  4. Action: Send HTTP request.
    • Method: POST
    • URL: https://api.jointextkit.com/v1/track
    • Headers: Authorization: Bearer YOUR_KEY (store the key as a Flow secret), and Content-Type: application/json
    • Body:
liquid
{
  "event": "checkout_started",
  "phone": "{{ customer.phone }}",
  "properties": {
    "checkout_url": "{{ abandonment.abandonedCheckoutPayload.abandonedCheckoutUrl }}"
  }
}
  1. Turn the workflow on.

Pick the two variables from Flow's variable list if your store's names differ. Shopify decides when a checkout counts as abandoned, so the text goes out about an hour after that. To stop a text for a shopper who orders in that hour, add a second workflow: trigger Order created, the same request with "event": "order_placed" and "phone": "{{ order.phone }}".

Klaviyo

Klaviyo's own Abandoned Checkout flow decides when to send and skips shoppers who ordered. TextKit sends the text, so you don't need the template here.

  1. Open the flow that starts on Checkout Started.
  2. After its time delay, add a Webhook action:
    • URL: https://api.jointextkit.com/v1/messages
    • Headers: Authorization: Bearer YOUR_KEY, and Content-Type: application/json
    • Body:
json
{
  "from": "+18335550100",
  "to": "{{ person.phone_number }}",
  "body": "Acme: you left something in your cart. Finish your order here: {{ event.extra.checkout_url }} Reply STOP to opt out.",
  "category": "marketing"
}
  1. Set the action to Live.

Klaviyo needs two-step authentication on your login before it sends webhooks. Open View profile and event variables in the webhook editor to check the checkout link's name in your account.

Your own code

Send the two events when they happen:

bash
curl -X POST https://api.jointextkit.com/v1/track \
  -H "Authorization: Bearer $TEXTKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event":"checkout_started","phone":"+14155550132","properties":{"checkout_url":"https://acme.com/checkouts/c/4b1e"}}'

curl -X POST https://api.jointextkit.com/v1/track \
  -H "Authorization: Bearer $TEXTKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event":"order_placed","phone":"+14155550132"}'

A shopper who starts checkout again while a run is waiting stays in that run: one text per checkout, not one per page view.

Order and shipping texts

A connected Shopify store also sends two events for its orders:

EventWhenProperties
order_placedA new order with a phone numberorder_name (#1001), order_url (the order status page), total, currency, items, item
order_shippedA shipment gets a tracking number or linkorder_name, tracking_number, tracking_url, tracking_company

Two templates in Console → Automations start on them:

text
Acme: thanks for your order #1001. Check its status here:
https://acme.com/... Reply STOP to opt out.

Acme: your order #1001 has shipped. Track it here:
https://www.ups.com/... Reply STOP to opt out.
  1. Connect the store as above. Order texts need read_orders; shipping texts from a store connected with one click also use read_fulfillments.
  2. Create a flow from Order confirmation or Shipping update, pick your sending number, edit the text, and activate it.

A store connected with one click sends each event within seconds. A store connected with its own token is checked every 10 minutes. Each order and each shipment counts once. Orders older than a day and shipments older than a week never start a flow. When a shipment has no tracking link, tracking_url is the order status page.

These texts go only to shoppers whose opt-in is on file with TextKit: shoppers who ticked the SMS box at your checkout, or who opted in any other way (Contacts). Everyone else is skipped, and the run logs why. Your own verified numbers always get them, so you can test on yourself. The same holds when you send order_placed or order_shipped yourself with POST /v1/track.

Check it

Each run has a timeline in Console → Automations: the event that started it, the wait, and the text, or why it was held (quiet hours) or skipped (no opt-in, opted out). Try the flow with a test key and your own number before you turn it on for shoppers.