This workflow corresponds to n8n.io template #10237 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Send WhatsApp Apology & Reorder Link When Shopify Order is Cancelled",
"nodes": [
{
"id": "19ca882d-78dd-42f7-b57b-d10f46d4ec56",
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [
-1104,
544
],
"parameters": {
"jsCode": "// Get input data (assuming one item only)\nconst order = items[0].json;\n\n// Extract important data\nconst result = {\n number: order.number,\n orderNumber: order.order_number,\n orderUrl: order.order_status_url,\n confirmationNumber: order.confirmation_number,\n createdAt: order.created_at,\n cancelledAt: order.cancelled_at,\n status: {\n financial: order.financial_status,\n fulfillment: order.fulfillment_status,\n },\n total: {\n price: order.total_price,\n subtotal: order.subtotal_price,\n tax: order.total_tax,\n currency: order.currency,\n },\n customer: {\n email: order.customer?.email || null,\n phone: order.customer?.phone || null,\n name: `${order.customer?.first_name || ''} ${order.customer?.last_name || ''}`.trim(),\n },\n address: {\n billing: {\n name: `${order.billing_address?.first_name || ''} ${order.billing_address?.last_name || ''}`.trim(),\n phone: order.billing_address?.phone || null,\n address1: order.billing_address?.address1 || null,\n address2: order.billing_address?.address2 || null,\n city: order.billing_address?.city || null,\n zip: order.billing_address?.zip || null,\n country: order.billing_address?.country || null,\n },\n shipping: {\n name: `${order.shipping_address?.first_name || ''} ${order.shipping_address?.last_name || ''}`.trim(),\n phone: order.shipping_address?.phone || null,\n address1: order.shipping_address?.address1 || null,\n address2: order.shipping_address?.address2 || null,\n city: order.shipping_address?.city || null,\n zip: order.shipping_address?.zip || null,\n country: order.shipping_address?.country || null,\n }\n },\n product: order.line_items.map(item => ({\n title: item.title,\n price: item.price,\n quantity: item.quantity,\n tax: item.tax_lines?.[0]?.price || 0,\n })),\n fulfillment: order.fulfillments?.[0]\n ? {\n trackingNumber: order.fulfillments[0].tracking_number,\n trackingUrl: order.fulfillments[0].tracking_url,\n status: order.fulfillments[0].status,\n }\n : null,\n refund: order.refunds?.[0]\n ? {\n reason: order.refunds[0].note || 'N/A',\n amount: order.refunds[0].transactions?.[0]?.amount || '0',\n date: order.refunds[0].created_at,\n }\n : null,\n};\n\n// Return simplified data\nreturn [{ json: result }];\n"
},
"typeVersion": 2
},
{
"id": "c02ef42f-6092-4893-baf3-999228b07085",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-1328,
528
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "ed565cef-4455-4f18-a9c1-316d94ef2c65",
"name": "Shopify Trigger",
"type": "n8n-nodes-base.shopifyTrigger",
"position": [
-1520,
528
],
"parameters": {
"topic": "orders/cancelled",
"authentication": "accessToken"
},
"credentials": {
"shopifyAccessTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "4fb74742-5ee8-4a75-884d-311023a8a267",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-80,
544
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "3ab8725a-9268-45fd-b91c-0e055fe6fa7c",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.data.exists }}",
"rightValue": "=\"true\""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "35b0be87-6a5a-46da-bd70-35a73d80a373",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
352,
640
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "ddf47009-eef3-452c-ad02-ea18dfa9d171",
"name": "Clean WhatsApp Number",
"type": "n8n-nodes-base.code",
"position": [
-688,
544
],
"parameters": {
"jsCode": "const items = await $input.all();\n\nconst updatedItems = items.map((item) => {\n let rawNumber = item?.json?.address?.billing?.phone;\n rawNumber = rawNumber ? String(rawNumber) : \"\";\n\n const cleanedNumber = rawNumber.replace(/\\D/g, \"\"); // Remove non-digit characters\n\n // Update the nested phone field\n if (item.json.address?.billing) {\n item.json.address.billing.phone = cleanedNumber;\n }\n\n return item;\n});\n\nreturn updatedItems;\n"
},
"typeVersion": 2
},
{
"id": "95654791-084f-4847-babe-13685c2c45ee",
"name": "Save State of Rows in Verified & Sent",
"type": "n8n-nodes-base.googleSheets",
"position": [
336,
416
],
"parameters": {
"columns": {
"value": {
"name": "={{ $('Clean WhatsApp Number').item.json.customer.name }}",
"email": "={{ $('Clean WhatsApp Number').item.json.customer.email }}",
"price": "={{ $('Clean WhatsApp Number').item.json.total.currency }} {{ $('Clean WhatsApp Number').item.json.total.price }}",
"staus": "sent",
"title": "={{ $('Clean WhatsApp Number').item.json.product[0].title }}",
"number": "={{ $json.to }}",
"address1": "={{ $('Clean WhatsApp Number').item.json.address.billing.address1 }}",
"validity": "verified",
"re-order link": "={{ $('Code').item.json.orderUrl }}"
},
"schema": [
{
"id": "name",
"type": "string",
"display": true,
"required": false,
"displayName": "name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "number",
"type": "string",
"display": true,
"required": false,
"displayName": "number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "email",
"type": "string",
"display": true,
"required": false,
"displayName": "email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "address1",
"type": "string",
"display": true,
"required": false,
"displayName": "address1",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "price",
"type": "string",
"display": true,
"required": false,
"displayName": "price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "title",
"type": "string",
"display": true,
"required": false,
"displayName": "title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "re-order link",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "re-order link",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "validity",
"type": "string",
"display": true,
"required": false,
"displayName": "validity",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "staus",
"type": "string",
"display": true,
"required": false,
"displayName": "staus",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk/edit?usp=drivesdk",
"cachedResultName": "Automated WhatsApp Apology Flow for Cancelled Shopify Orders with Reorder Link"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "b2612612-56b2-47d3-ac2b-24bbae46b23a",
"name": "Save State of Rows in Verified & Sent1",
"type": "n8n-nodes-base.googleSheets",
"position": [
128,
640
],
"parameters": {
"columns": {
"value": {
"name": "={{ $('Clean WhatsApp Number').item.json.customer.name }}",
"email": "={{ $('Clean WhatsApp Number').item.json.customer.email }}",
"price": "={{ $('Clean WhatsApp Number').item.json.total.currency }} {{ $('Clean WhatsApp Number').item.json.total.price }}",
"staus": "not sent",
"title": "={{ $('Clean WhatsApp Number').item.json.product[0].title }}",
"number": "={{ $('Clean WhatsApp Number').item.json.customer.phone }}",
"address1": "={{ $('Clean WhatsApp Number').item.json.address.billing.address1 }}",
"validity": "unverified"
},
"schema": [
{
"id": "name",
"type": "string",
"display": true,
"required": false,
"displayName": "name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "number",
"type": "string",
"display": true,
"required": false,
"displayName": "number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "email",
"type": "string",
"display": true,
"required": false,
"displayName": "email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "address1",
"type": "string",
"display": true,
"required": false,
"displayName": "address1",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "price",
"type": "string",
"display": true,
"required": false,
"displayName": "price",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "title",
"type": "string",
"display": true,
"required": false,
"displayName": "title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "re-order link",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "re-order link",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "validity",
"type": "string",
"display": true,
"required": false,
"displayName": "validity",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "staus",
"type": "string",
"display": true,
"required": false,
"displayName": "staus",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk/edit?usp=drivesdk",
"cachedResultName": "Automated WhatsApp Apology Flow for Cancelled Shopify Orders with Reorder Link"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "df643747-05d9-4101-bdcf-a53eb3d20045",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2720,
96
],
"parameters": {
"width": 1072,
"height": 1536,
"content": "# Send WhatsApp Apology & Reorder Link When Shopify Order is Cancelled\n\n## Overview\nThis **n8n workflow** listens for order cancellations in Shopify, extracts relevant customer and order data, checks if the customer\u2019s phone number is registered on WhatsApp via the **Rapiwa API**, and sends a personalized apology message with a re-order link. It also logs successful and unsuccessful attempts in **Google Sheets** for tracking.\n\n\n## Features\n- Automatically detects cancelled orders via Shopify webhook.\n- Extracts customer data including name, email, phone, order details, and shipping address.\n- Cleans the phone number and converts it to WhatsApp-compatible format.\n- Verifies if the phone number is registered on WhatsApp using Rapiwa API.\n- Sends an apology message with a re-order link if the number is valid.\n- Logs both verified/sent and unverified/not sent entries in Google Sheets.\n- Rate limiting is controlled using `Wait` and `SplitInBatches` nodes.\n\n## Requirements\n- A Shopify store with order cancellation webhook enabled.\n- A Rapiwa account and API token.\n- An n8n instance (cloud or self-hosted).\n- Google Sheet set up to log message statuses.\n- Shopify Access Token (Private App or Admin API).\n- Verified Rapiwa WhatsApp number and message credits.\n\n\n## Google Sheet Required Columns\nYou\u2019ll need two Google Sheets (or two tabs in one spreadsheet):\n**A Google Sheet formatted like this** \u27a4 [sample](https://docs.google.com/spreadsheets/d/12zMJod9s3Ov0RZh-7-ZoqTvJr4yMIqOeAQKTBeZlrRk/edit?usp=sharing)\n\n## Nodes Used in the Workflow\n- **Shopify Trigger** (`orders/cancelled`)\n- **SplitInBatches**\n- **Code** (Simplify order data)\n- **Code** (Clean WhatsApp Number)\n- **HTTP Request** (Verify via Rapiwa)\n- **IF Node** (Check Rapiwa result)\n- **HTTP Request** (Send Message via Rapiwa)\n- **Google Sheets** (Append logs - verified)\n- **Google Sheets** (Append logs - unverified)\n- **Wait** (to control request flow)\n\n\n## Notes & Warnings\n- The `IF` node checks for `data.exists === true` to verify WhatsApp number status \u2014 adjust if the API changes.\n- Make sure **Rapiwa API rate limits** are respected to avoid temporary bans.\n- Number formatting must strip non-digits and use the correct country code (e.g., `88017xxxxxxx`).\n- Double-check message templates to avoid policy violations on WhatsApp Business.\n- Be careful with customer privacy. Ensure compliance with **GDPR** or your local laws.\n\n\n## Useful Links\n- **Dashboard:** [https://app.rapiwa.com](https://app.rapiwa.com/login)\n- **Official Website:** [https://rapiwa.com](https://rapiwa.com/)\n- **Documentation:** [https://docs.rapiwa.com](https://docs.rapiwa.com/)\n\n## Support & Help\n- **WhatsApp**: [Chat on WhatsApp](https://wa.me/8801322827799)\n- **Discord**: [SpaGreen Community](https://discord.gg/SsCChWEP)\n- **Facebook Group**: [SpaGreen Support](https://www.facebook.com/groups/spagreenbd)\n- **Website**: [https://spagreen.net](https://spagreen.net)\n- **Developer Portfolio**: [Codecanyon SpaGreen](https://codecanyon.net/user/spagreen/portfolio)\n"
},
"typeVersion": 1
},
{
"id": "a9e35272-f2c6-4785-9610-2269ffb3276c",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1600,
96
],
"parameters": {
"width": 656,
"height": 752,
"content": "## Shopify Trigger (Order Cancelled)\n**Purpose:** Starts the workflow whenever an order is cancelled in Shopify \n**How it works:** \n- Listens to the Shopify webhook for order cancellation events \n- Automatically triggers the workflow on each cancellation \n\n## SplitInBatches\n**Purpose:** Processes cancelled orders in manageable batches \n\n## Code (Simplify Order Data)\n**Purpose:** Extracts and formats important customer and order details for later use \n**How it works:** \n- Parses the Shopify webhook data \n- Extracts customer name, phone, email, address, order total, order ID, and product details \n- Converts order total to a fixed decimal string \n\n"
},
"typeVersion": 1
},
{
"id": "20ec2d81-8006-417d-92b5-668993d0586e",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-928,
208
],
"parameters": {
"width": 720,
"height": 640,
"content": "## Code (Clean WhatsApp Number)\n**Purpose:** Formats and sanitizes the customer phone number for WhatsApp usage\n**It works:** Removes all non-digit characters (spaces, dashes, parentheses) \n\n## HTTP Request (Check WhatsApp Number with Rapiwa)\n**Purpose:** Verifies if the cleaned phone number is registered on WhatsApp using Rapiwa API\n### How it works:\n- Sends POST request to Rapiwa\u2019s `/api/verify-whatsapp` endpoint \n- Uses Bearer token for authentication \n- **API Endpoint:** `https://app.rapiwa.com/api/verify-whatsapp`\n"
},
"typeVersion": 1
},
{
"id": "6f87f286-0aad-46a2-aef3-15a4b8e99fd6",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-192,
-160
],
"parameters": {
"width": 912,
"height": 1008,
"content": "## IF\n**Purpose:** Routes the workflow depending on WhatsApp number verification result `verified` or `unverified` number\n\n\n## HTTP Request (Send WhatsApp Message via Rapiwa)\n**Purpose:** Sends a personalized WhatsApp apology message to verified customers\n### How it works:\n- Sends a POST request to Rapiwa\u2019s `/api/send-message` \n- Uses Bearer token for authorization\n**API Endpoint:** `https://app.rapiwa.com/api/send-message`\n\n## Google Sheets (Log Verified & Sent)\n**Purpose:** Records details of customers successfully sent a WhatsApp message\n### It works:\n- Marks status as `\"verified\"` and `\"sent\"` Confirmation of row appended to Google Sheet\n\n\n## Google Sheets (Log Unverified & Not Sent)\n**Purpose:** Records details of customers whose WhatsApp numbers were not verified\n### It works:\n- Appends a row with customer and order details \n- Marks status as `\"unverified\"` and `\"not sent\"` Confirmation of row appended to Google Sheet"
},
"typeVersion": 1
},
{
"id": "ed95aab3-098c-4f38-ae39-0e469b3894aa",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1600,
896
],
"parameters": {
"color": 7,
"width": 560,
"height": 432,
"content": "## How to Use This Workflow\n### Step 1: Set Up Credentials\nAdd **Shopify**, **Google Sheets OAuth2**, and **Rapiwa Token** credentials in n8n.\n\n### Step 2: Prepare Google Sheet\n- Create a new Google Sheet with the columns listed above.\n- Connect it inside the two Google Sheets nodes in the workflow.\n\n### Step 3: Import Workflow\n- Use **Import JSON** in n8n to load this complete automation.\n- Make sure all credential references match your system.\n\n### Step 4: Customize Message\n- Edit the **Send Message Using Rapiwa** node to personalize your apology message \n or localize it by language or product."
},
"typeVersion": 1
},
{
"id": "f24902f4-c503-4844-b022-c1005adfe689",
"name": "Rapiwa",
"type": "n8n-nodes-rapiwa.rapiwa",
"position": [
-480,
544
],
"parameters": {
"number": "={{ $json.address.billing.phone }}",
"operation": "verifyWhatsAppNumber"
},
"credentials": {
"rapiwaApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "fde3fe4b-a404-4847-b8ce-7868eb71875f",
"name": "Rapiwa (send message)",
"type": "n8n-nodes-rapiwa.rapiwa",
"position": [
128,
416
],
"parameters": {
"number": "={{ $('Clean WhatsApp Number').item.json.address.billing.phone }}",
"messageType": "=Dear {{ $('Clean WhatsApp Number').item.json.customer.name }},\nWe\u2019re really sorry about the issue with your order. \ud83d\ude4f\nYou can re-order using this link:\ud83d\udc49 {{ $('Clean WhatsApp Number').item.json.orderUrl }}\nWe\u2019ve added a small discount for the inconvenience.\nThanks,\nTeam SpaGreen Creative"
},
"credentials": {
"rapiwaApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"connections": {
"If": {
"main": [
[
{
"node": "Rapiwa (send message)",
"type": "main",
"index": 0
}
],
[
{
"node": "Save State of Rows in Verified & Sent1",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Clean WhatsApp Number",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Rapiwa": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Shopify Trigger": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Clean WhatsApp Number": {
"main": [
[
{
"node": "Rapiwa",
"type": "main",
"index": 0
}
]
]
},
"Rapiwa (send message)": {
"main": [
[
{
"node": "Save State of Rows in Verified & Sent",
"type": "main",
"index": 0
}
]
]
},
"Save State of Rows in Verified & Sent": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Save State of Rows in Verified & Sent1": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleSheetsOAuth2ApirapiwaApishopifyAccessTokenApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This n8n workflow listens for order cancellations in Shopify, extracts relevant customer and order data, checks if the customer’s phone number is registered on WhatsApp via the Rapiwa API, and sends a personalised apology message with a re-order link. It also logs successful and…
Source: https://n8n.io/workflows/10237/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow automates the synchronization of product prices across Shopify and WooCommerce platforms to ensure retail consistency. It triggers when a price change is detected in either system, appli
This n8n workflow automatically sends WhatsApp notifications to customers when their Shopify orders are fulfilled. It extracts order details, validates customer phone numbers for WhatsApp compatibilit
This workflow is designed for online store owners, customer-success teams, and marketing operators who want to automatically verify customers' WhatsApp numbers and deliver order updates or invoice lin
This workflow automates the collection and standardization of sales data from Shopify and WooCommerce into a centralized Google Sheets ledger. It triggers on new orders, normalizes disparate platform
The workflow triggers on a new checkout event from Shopify and extracts all relevant cart data. It filters carts based on value and age to isolate qualified abandoned checkouts. For each qualified car