Skip to main content
Software Notifications WhatsApp

One event, three channels

A payment, a booking, and a delivery each need one message. Email, SMS, and WhatsApp are channels. They are not three copies of the business rule, and the customer should not get all three for the same fact.

LesterTechLabs Engineering Estimated 3 min read
A smartphone, a letter, and a feature phone on a desk, one message three ways

A payment, a booking, and a delivery each need one message. Email, SMS, and WhatsApp are channels. They are not three copies of the business rule, and the customer should not get all three for the same fact.

The event is the fact

Record the fact once: invoice 1042 was paid, booking 88 was confirmed, delivery 12 is out. The message is a rendering of that fact for a channel the customer has agreed to. If you build the sentence inside each provider’s code, the SMS and the email will drift the week someone edits only one of them.

Choose the channel from a preference on the customer, with a fallback you have tested. A WhatsApp template that is not approved yet is not a fallback. SMS is the fallback only if you have the phone and the customer has not opted out.

Send once, even when the job retries

The worker will die after the provider accepts the message and before you record success. Store a key of event id plus channel, and refuse a second send when that key exists. A customer who receives three identical SMS will not trust the fourth, which might be the one about a real problem.

Providers return an id. Store it. When they later say delivered or failed, attach that to the same row. A failed WhatsApp message can fall through to SMS if you still have time and the customer has a phone. That fall-through is another send, with its own key, not a silent retry of the same call.

What you are allowed to put in the message

The message needs the fact and the next step. It does not need the customer’s full balance, their national id, or a link that logs them in without a second check. A link can open the invoice if the person still has to sign in, or if the link expires and is tied to that invoice.

Marketing is a different event from a receipt. Do not attach an offer to the receipt unless the customer has agreed to offers. The receipt is why they gave you the number. Burying a promotion in it is how opt-outs start, and then the payment alerts stop too.

A send that can be retried

The unique key is created before the provider is called. If the process dies, the next attempt finds the key and does not call again. If you need a fall-through to SMS, that is a new key.

Python · notify/send.py

def send_once(event_id: int, channel: str, render):
    key = f"{event_id}:{channel}"
    row, created = OutboundMessage.objects.get_or_create(
        idempotency_key=key,
        defaults={"channel": channel, "event_id": event_id},
    )
    if not created:
        return row
    row.provider_id = render(channel)
    row.save(update_fields=["provider_id"])
    return row

One fact, one send

The fact is recorded once: the invoice was paid, the booking is confirmed, the delivery left. Each channel renders that fact. If the sentence is written inside each provider's code, the SMS and the email will disagree the week someone edits only one. Render from the event. The provider only transports.

The customer has a preference and a fallback you have tested. A WhatsApp template that is not approved is not a fallback. SMS is the fallback only when you have the phone and they have not opted out of the kinds of messages this is. A receipt is not an offer. Do not attach an offer to the receipt or the opt-out will kill the alerts.

The send key is the event plus the channel, stored before the call. A worker that dies after the provider accepts and before you record success must not send again. A fall-through to SMS is a new key, not a retry of the same call. Store the provider id so a later delivered or failed status lands on that row.

Look at a week of sends. The duplicates are the bug. The failures without a fallback are the gap. Add a channel only when the log shows people are not receiving the one you have, not because a vendor demo looked modern.

Before you add a third provider

  1. Record the business fact once, then render it per channel.
  2. Use a send key of event plus channel so retries do not double-send.
  3. Keep receipts and marketing as separate events.

On the site

Software

Have a system to build?

We design and ship custom software with a scope you can track, from the first request to handover.