Guides

Use cases

End-to-end recipes for the flows most e-commerce and SaaS teams build first. All use the small client from the Libraries guide.

OTP / login codes

Deliver a one-time code over WhatsApp. You generate, store, and verify the code — the API only delivers it. Use an AUTHENTICATION template such as otp_code with body Your verification code is {{1}}.

import { mywa } from './mywa.js';

async function sendOtp(phone) {
  const code = String(Math.floor(100000 + Math.random() * 900000));
  await saveCode(phone, code, { ttlSeconds: 300 }); // your storage

  await mywa.post('/messages', {
    to: phone,
    type: 'template',
    template: { name: 'otp_code', variables: [code] },
  });
}

async function verifyOtp(phone, entered) {
  const code = await readCode(phone);
  return code && code === entered;
}
Rate-limit OTP requests per phone number on your side and expire codes after a few minutes. WhatsApp authentication templates can’t contain custom URLs or media.

Abandoned cart

When a cart goes stale, upsert the shopper and send a MARKETING template like Hi {{1}}, you left {{2}} in your cart — finish here: {{3}}. Replies land in your inbox and open a 24-hour window for follow-up.

import { mywa } from './mywa.js';

async function cartReminder(shopper, cart) {
  // Keep the customer record fresh and attach cart context
  await mywa.post('/contacts', {
    phone: shopper.phone,
    name: shopper.name,
    attributes: { lastCartValue: cart.total, lastCartAt: new Date().toISOString() },
  });

  await mywa.post('/messages', {
    to: shopper.phone,
    type: 'template',
    template: {
      name: 'cart_reminder',
      variables: [shopper.firstName, cart.summary, cart.checkoutUrl],
    },
  });
}

Order & shipping updates

Fire a UTILITY template from your order webhook when status changes. If the customer recently messaged you, a plain text send works too and is cheaper.

// Called from your "order.shipped" webhook handler
async function onOrderShipped(order) {
  await mywa.post('/messages', {
    to: order.customerPhone,
    type: 'template',
    template: {
      name: 'order_shipped',
      variables: [order.customerName, order.number, order.trackingUrl],
    },
  });
}

Order placed → follow-up automation

Build the follow-up once in the automation builder — a thank-you, a delivery check, a review request — and end it with a “Goal reached” step. Then start it from your order handler, passing order details as vars so its messages can say {{vars.hook.orderId}}. Save the runId with the order and check later whether the run reached its goal.

import { mywa } from './mywa.js';

const FOLLOW_UP = 'wf_9f2'; // from GET /automations

// Called from your "order.created" handler
async function onOrderPlaced(order) {
  try {
    const { runId } = await mywa.post(`/automations/${FOLLOW_UP}/enroll`, {
      phone: order.customerPhone,
      name: order.customerName,
      attributes: { lastOrderAt: order.createdAt },
      vars: { orderId: order.number, amount: order.total },
    });
    await saveRunId(order.id, runId); // your storage
  } catch (e) {
    // reentry / opted_out never succeed — don't retry them
    if (/MYWA 409 (reentry|opted_out)/.test(e.message)) return;
    throw e;
  }
}

// A few days later, e.g. from a daily job
async function didFollowUpWork(order) {
  const run = await mywa.get(`/automation-runs/${order.runId}`);
  return run.goal !== null; // true once the "Goal reached" step ran
}
Start returns 202 as soon as the run is queued. A run that is still waiting for a reply or a timer has finishedAt: null — check again later rather than treating it as failed.

Getting leads back out?

To push each new WhatsApp lead into your CRM, use the outbound webhook — MYWA POSTs to your endpoint, signed so you can verify it.
MywaHey 👋 got a question? Say hi, a real human replies here.now