# Order notifications over WhatsApp and SMS

A complete recipe for a messaging app that sends customers order, payment and delivery confirmations and abandoned-cart reminders through the messaging provider the merchant chooses.

## How it works

LARM does not send messages on your app's behalf, and needs no special integration with your provider. Your app receives order webhooks and sends the message from the merchant's account with the provider the merchant chooses (WhatsApp Business, a Saudi SMS provider, or another).

| You need | Scope |
|---|---|
| The order webhook and its contents | `orders:read` |
| Customer name and E.164 phone number | `customers:read` |
| The digital code in the message | `orders.digital:read` |
| Abandoned-cart reminders | `carts:read` |

Choose the **Messaging & marketing** category when you create the app.

## 1. Subscribe to webhooks

```json
{
  "endpoint_url": "https://app.example.com/larm/webhooks",
  "event_types": ["order.created", "order.paid", "order.status.updated", "order.digital.delivered", "abandoned_cart.created"],
  "payload_version": 1
}
```

## 2. Send the right message

```js
app.post('/larm/webhooks', express.raw({ type: 'application/json' }), async (req, res) => {
  const event = verifyLarmWebhook(req.body, req.headers, secrets)   // as in the Webhooks guide
  res.sendStatus(204)                                                 // acknowledge immediately

  const order = event.data
  const phone = order.customer?.phone                                 // +9665… or null
  if (!phone || await alreadySent(event.id)) return

  switch (event.type) {
    case 'order.paid':
      await provider.send(phone, `Thank you ${order.customer.name}, we received payment for order #${order.number}. Track it at ${order.order_url}`)
      break
    case 'order.digital.delivered':
      for (const item of order.digital_items ?? [])
        await provider.send(phone, `${item.name}: ${item.value}`)
      break
    case 'order.status.updated':
      if (order.status === 'completed') await provider.send(phone, `Your order #${order.number} is complete.`)
      break
    case 'abandoned_cart.created':
      await provider.send(phone, `Your cart is waiting: ${order.checkout_url}`)
      break
  }
  await markSent(event.id)
})
```

- **Acknowledge before sending.** A slow response counts as a failure and the webhook is retried.
- **Store `event.id`** and ignore duplicates, so the customer never receives a message twice.
- `order.created` arrives before payment. For an "order placed" message use `order.paid`, unless the merchant wants alerts for unpaid orders.

## 3. Respect the customer

- Send only messages about the order itself. Marketing messages require the customer's consent under telecom regulations, and the merchant and your app are responsible for them.
- An abandoned-cart reminder is a single message. Do not send a repeated sequence.
- On [`customers.redact`](/guides/app-uninstall), delete the customer's phone number and message history.

## Try it

From the **Webhook log** in the Developer Portal, send a test webhook for any topic to your endpoint, and watch the delivery and response. On a [development store](/guides/test-stores), create an order and pay it with the test payment method to see the full sequence.
