---
title: "Send Shopify Order Confirmation on WhatsApp (Without Expensive Apps)"
description: "Step-by-step guide to send Shopify order confirmation messages on WhatsApp using Shopify Webhooks + AWS API Gateway + AWS Lambda + Wati WhatsApp API. Includes retry handling, cold start fixes, and production hardening tips."
date: "2026-02-04"
author: "Jayesh Jain"
category: "Shopify"
tags: ["Shopify", "WhatsApp", "AWS Lambda", "Shopify Webhooks", "Wati", "Serverless", "Order Confirmation"]
keywords: "send Shopify order confirmation WhatsApp, Shopify WhatsApp order confirmation, Shopify webhook AWS Lambda, Wati WhatsApp API Shopify, WhatsApp order confirmation automation, cheap alternative to Shopify WhatsApp apps"
featuredImage: "/blog/send-shopify-order-confirmation-on-whatsapp.png"
cta: "Want WhatsApp automation for your Shopify store?"
ctaDescription: "We can build a production-grade WhatsApp notification system (order confirmation, fulfillment updates, abandoned cart) tailored to your exact workflow."
---

# 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)

1. A customer places an order on Shopify
2. Shopify fires an **Orders/Create** webhook (HTTP POST JSON)
3. API Gateway receives the webhook request
4. Lambda parses the payload, extracts:
   - Customer name
   - Phone number
   - Order number
   - Order status URL
5. 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**

![Shopify webhook setup for order creation](https://tirnav.com/blog/shopify-webhook-setup.webp)

**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)

```java
@Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
  LambdaLogger logger = context.getLogger();

  String waTemplateName = System.getenv("whatsappTemplateName");
  String watiEndpointURL = System.getenv("watiEndpointURL");
  String bearerToken = System.getenv("bearerToken");

  String orderJson = new String(input.readAllBytes(), StandardCharsets.UTF_8);

  if(StringUtils.isNullOrEmpty(watiEndpointURL) ||
     StringUtils.isNullOrEmpty(waTemplateName) ||
     StringUtils.isNullOrEmpty(bearerToken)) {
    logger.log("Environment variable missing.");
    return;
  }

  JsonNode data = mapper.readTree(orderJson);

  String customerName = data.get("shipping_address").get("name").asText();
  String orderNumber  = data.get("order_number").asText();
  String phoneNumber  = data.get("shipping_address").get("phone").asText();
  String orderURL     = data.get("order_status_url").asText();

  // Optional: remove authenticate token from URL (client requirement)
  if(orderURL != null && orderURL.contains("authenticate")) {
    orderURL = orderURL.substring(0, orderURL.indexOf("authenticate") - 1);
  }

  logger.log("New Shopify Order received for " + customerName + " order #" + orderNumber);

  // Build Wati payload with template params
  Receiver receiver = new Receiver();
  receiver.setWhatsappNumber(phoneNumber.replaceAll("[+]", ""));

  List<CustomParam> params = new ArrayList<>();
  params.add(new CustomParam("customername", customerName));
  params.add(new CustomParam("ordernumber", orderNumber));
  params.add(new CustomParam("orderurl", orderURL));
  receiver.setCustomParams(params);

  GDWhatsappModel payloadData = new GDWhatsappModel();
  payloadData.setTemplateName(waTemplateName);
  payloadData.setBroadcastName(customerName + "-" + orderNumber);
  payloadData.setReceivers(Arrays.asList(receiver));

  String payload = mapper.writeValueAsString(payloadData);

  HttpRequest request = HttpRequest.newBuilder()
    .uri(new URI(watiEndpointURL))
    .header("Content-Type", "application/json;charset=UTF-8")
    .header("Authorization", "Bearer " + bearerToken)
    .POST(HttpRequest.BodyPublishers.ofString(payload))
    .build();

  HttpResponse<String> response = HttpClient.newBuilder()
    .build()
    .send(request, BodyHandlers.ofString());

  if(response.statusCode() == 200) {
    logger.log("WhatsApp sent to " + phoneNumber + " for order #" + orderNumber);
  } else {
    logger.log("WhatsApp failed: " + response.body());
  }
}
```

### 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:

1. Create API Gateway
2. Create a POST route (e.g., **/shopify/order-created**)
3. Enable Lambda proxy integration
4. Deploy stage (e.g., prod)
5. 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)**

![WhatsApp order confirmation message sent after Shopify order](https://tirnav.com/blog/shopify-whatsapp-order-confirmation.webp)

### 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

1. Create the webhook in Shopify
2. Create Lambda + API Gateway endpoint
3. Configure Wati template + token
4. 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 `/blog/...` paths.
