Send Shopify Order Confirmation on WhatsApp (Without Expensive Apps)
A common Shopify requirement is: as soon as an order is placed, send an order confirmation on WhatsApp.
Many Shopify apps can do this — but they often come with monthly subscription plans and bundled features you may not need.
This guide shows a lean, budget-friendly, and production-ready approach we implemented for a client using:
- Shopify Webhooks (trigger on order creation)
- AWS API Gateway (public endpoint for Shopify to call)
- AWS Lambda (serverless compute to process the payload)
- Wati WhatsApp API (official WhatsApp Business messaging provider)
You’ll get a setup that is:
- Simple (one webhook → one message)
- Cheap to run (pay-per-use serverless)
- Flexible (you control template + payload mapping)
Architecture (How It Works)
- A customer places an order on Shopify
- Shopify fires an Orders/Create webhook (HTTP POST JSON)
- API Gateway receives the webhook request
- Lambda parses the payload, extracts:
- Customer name
- Phone number
- Order number
- Order status URL
- Lambda calls Wati’s API to send a WhatsApp template message
Prerequisites
- Shopify store admin access
- AWS account (API Gateway + Lambda)
- Wati account with WhatsApp Business number approved
- A WhatsApp template created and approved in Wati
Step 1: Create an Order Webhook in Shopify
In Shopify Admin:
Settings → Notifications → Webhooks → Create webhook
Suggested configuration:
- Event: Order creation
- Format: JSON
- URL: Your API Gateway endpoint (we create it in Step 2)
- Webhook API version: use the latest stable version
Screenshot: Shopify webhook setup

Tip: Use a dedicated endpoint for this webhook (don’t reuse an endpoint for multiple webhook types unless you’re intentionally routing).
Step 2: Create the AWS Lambda Function
You can implement Lambda in Node.js, Python, Java, Go, C#, etc.
Below is a Java-style implementation outline (similar to what we used) showing the key ideas:
Environment variables
Store secrets/config in Lambda environment variables:
- whatsappTemplateName
- watiEndpointURL
- bearerToken
Lambda handler (Java example)
1@Override 2public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { 3 LambdaLogger logger = context.getLogger(); 4 5 String waTemplateName = System.getenv("whatsappTemplateName"); 6 String watiEndpointURL = System.getenv("watiEndpointURL"); 7 String bearerToken = System.getenv("bearerToken"); 8 9 String orderJson = new String(input.readAllBytes(), StandardCharsets.UTF_8); 10 11 if(StringUtils.isNullOrEmpty(watiEndpointURL) || 12 StringUtils.isNullOrEmpty(waTemplateName) || 13 StringUtils.isNullOrEmpty(bearerToken)) { 14 logger.log("Environment variable missing."); 15 return; 16 } 17 18 JsonNode data = mapper.readTree(orderJson); 19 20 String customerName = data.get("shipping_address").get("name").asText(); 21 String orderNumber = data.get("order_number").asText(); 22 String phoneNumber = data.get("shipping_address").get("phone").asText(); 23 String orderURL = data.get("order_status_url").asText(); 24 25 // Optional: remove authenticate token from URL (client requirement) 26 if(orderURL != null && orderURL.contains("authenticate")) { 27 orderURL = orderURL.substring(0, orderURL.indexOf("authenticate") - 1); 28 } 29 30 logger.log("New Shopify Order received for " + customerName + " order #" + orderNumber); 31 32 // Build Wati payload with template params 33 Receiver receiver = new Receiver(); 34 receiver.setWhatsappNumber(phoneNumber.replaceAll("[+]", "")); 35 36 List<CustomParam> params = new ArrayList<>(); 37 params.add(new CustomParam("customername", customerName)); 38 params.add(new CustomParam("ordernumber", orderNumber)); 39 params.add(new CustomParam("orderurl", orderURL)); 40 receiver.setCustomParams(params); 41 42 GDWhatsappModel payloadData = new GDWhatsappModel(); 43 payloadData.setTemplateName(waTemplateName); 44 payloadData.setBroadcastName(customerName + "-" + orderNumber); 45 payloadData.setReceivers(Arrays.asList(receiver)); 46 47 String payload = mapper.writeValueAsString(payloadData); 48 49 HttpRequest request = HttpRequest.newBuilder() 50 .uri(new URI(watiEndpointURL)) 51 .header("Content-Type", "application/json;charset=UTF-8") 52 .header("Authorization", "Bearer " + bearerToken) 53 .POST(HttpRequest.BodyPublishers.ofString(payload)) 54 .build(); 55 56 HttpResponse<String> response = HttpClient.newBuilder() 57 .build() 58 .send(request, BodyHandlers.ofString()); 59 60 if(response.statusCode() == 200) { 61 logger.log("WhatsApp sent to " + phoneNumber + " for order #" + orderNumber); 62 } else { 63 logger.log("WhatsApp failed: " + response.body()); 64 } 65}
A more production-ready checklist
- Validate the webhook payload fields exist (shipping address may be missing)
- Normalize phone numbers (E.164 format recommended)
- Handle missing phone numbers (skip sending, log)
- Add timeouts + retries when calling Wati
- Log correlation id (Shopify webhook id) for tracing
Step 3: Expose Lambda via API Gateway (Required)
Shopify webhooks send an HTTP POST. Lambda alone needs a public HTTP endpoint.
Use API Gateway REST API with Lambda proxy integration.
High level steps:
- Create API Gateway
- Create a POST route (e.g., /shopify/order-created)
- Enable Lambda proxy integration
- Deploy stage (e.g., prod)
- Copy the invoke URL and paste it into Shopify webhook configuration
Step 4: Deploy & Test
Test via real Shopify order
Create a test order in Shopify and wait for the webhook to fire.
If everything is correct, WhatsApp should arrive within seconds.
Screenshot: WhatsApp order confirmation (example output)

Where to debug
- API Gateway execution logs
- Lambda CloudWatch logs
- Wati API response body
Important: Shopify Webhook Retry Policy (Why You May Get Duplicate Messages)
If Shopify does not receive a timely 200 OK, it retries the webhook.
In our case, the webhook executed twice and the client received two WhatsApp messages.
Root cause: the first Lambda execution took too long (cold start), and Shopify retried.
Fix 1: Reduce Lambda cold start
Recommended approaches:
- Provisioned Concurrency (best for consistent low-latency)
- Keep-warm schedule (cheap workaround: CloudWatch rule pings the function)
- Keep the handler lightweight (reuse HTTP client, avoid heavy init)
Fix 2: Make sending idempotent (best practice)
Even with performance improvements, production systems should be idempotent.
Simple options:
- Use Shopify webhook id as a unique key
- Store processed ids in DynamoDB with TTL (e.g., 24 hours)
- If the id exists → skip sending
This guarantees you never send duplicates even if Shopify retries.
Security Best Practices
- Verify Shopify webhook signature (HMAC) to prevent spoofed requests
- Store Wati token in AWS Secrets Manager (not plaintext env) if required by policy
- Restrict API Gateway (WAF allowlist, IP allowlist if possible)
Cost & ROI
This approach is typically much cheaper than monthly Shopify apps when you only need:
- One message type (order confirmation)
- Low/medium order volume
You pay for:
- Lambda invocations + duration
- API Gateway requests
- Wati WhatsApp messages (per conversation / template)
Conclusion
Using Shopify Webhooks + AWS Lambda + Wati, you can send Shopify order confirmation on WhatsApp quickly, affordably, and with full control.
If you want, we can extend the same setup to:
- Fulfillment shipped notifications
- Delivery updates
- COD confirmations
- Abandoned cart reminders
Next Steps
- Create the webhook in Shopify
- Create Lambda + API Gateway endpoint
- Configure Wati template + token
- Add idempotency + cold start mitigation for production
Adding the screenshots
Save the two screenshots you shared into your repo at:
- public/blog/shopify-webhook-setup.png
- public/blog/shopify-whatsapp-order-confirmation.png
This post already references them using
1/blog/...

