Transactional is not a campaign
The difference is not the copy, it is the trigger. A campaign is decided by a person and goes to a list. A transactional notification is decided by the system, goes to one person and only exists because something happened to them. Typical examples:
- Order received, payment approved or declined, invoice issued.
- Password reset, new sign-in to the account, email change.
- Status change on a ticket, proposal, case or delivery.
- Plan renewal due, card expiring, usage limit reached.
Because customers expect these alerts, read rates on WhatsApp tend to be high and the risk of reports is low. On the other hand, tolerance for mistakes is low too: a payment alert that arrives twice creates a support ticket, and one that never arrives creates distrust. The technical design exists to prevent both.
An architecture that handles volume
The most common mistake is calling the WhatsApp API inside the request that processes the payment. If the call is slow, checkout is slow. If it fails, the payment ends up in a strange state. The recommended flow splits responsibilities:
- The domain publishes an event, for example
payment.approved, and moves on. No WhatsApp here. - A consumer reads the event from the queue, builds the message from a template and computes the idempotency key.
- The worker sends to D-API with
async: true. The API answers right away with acommandIdand runs the send in the background. - The result arrives on the webhook
command.result. The worker stores thecommandIdwith the notification and the webhook updates the record. - Delivery and read come later, through the
message.deliveredandmessage.readevents, if you want to show them in your dashboard.
The worker call looks like this:
curl -X POST https://api.d-api.cloud/api/v1/messages/send/text \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sessionId": "store-notifications",
"to": "14155550123",
"text": "Payment approved. Your order 48213 is being packed.",
"async": true
}'If your system would rather poll than wait for the event, the result is also available at GET /api/v1/commands/{commandId} for about an hour. For guaranteed delivery, the docs recommend the webhook. If you already run RabbitMQ, you can receive events straight into a queue, configured per session.
Idempotency: the notification that must not arrive twice
Queues deliver at least once, not exactly once. A worker that crashes after sending and before acknowledging will reprocess the same message. The protection lives on your side, and it is simple:
- Key derived from the event, not the clock. Something like
order:48213:paid. The same event always produces the same key. - Unique constraint in the database. Store the key in a notifications table with a unique index before calling the API. If the insert fails as a duplicate, the send already happened or is in progress.
- Explicit state.
pending,sentwith thecommandId,confirmedorfailed. Retry only what isfailed, with growing intervals and a maximum number of attempts. - The webhook is idempotent too. D-API retries a webhook up to 7 times with exponential backoff if your server does not respond, and deduplicates events within a short window. Your handler must accept the same event twice with no side effects.
Event format and retry details are in WhatsApp webhooks.
Official or unofficial API for notifications
Both paths work for transactional messages, under different rules. On D-API they share the same integration, so you can revisit the choice without rewriting the worker.
| Unofficial (QR code) | Official (Cloud API) | |
|---|---|---|
| First message to the customer | Free-form text | Approved template, usually utility category |
| Send route | messages/send/text and others | messages/send/template outside the conversation window |
| Cost | Per connection on D-API | Per-connection fee plus Meta’s per-template charge, which varies by category and country |
| Changing the copy | Immediate | New template, subject to Meta approval |
In an official template, the variable parts go in bodyVariables, and the template name and language are required. To go deeper on the choice, see official vs unofficial WhatsApp API and the official WhatsApp API (Meta Cloud API) page.
Content best practices
- Name the company in the first line. The customer needs to know who is talking before reading the rest.
- One notification, one subject. Do not use a payment alert to pitch a product.
- Include the identifier the customer recognizes: order number, ticket ID, last digits of the card.
- Group nearby events. Three status changes in two minutes become a single message.
- Respect the clock. An alert that is not urgent can wait until the next morning.
Two specific cases have their own guides: verification codes (OTP) and order tracking. If notifications come from internal systems such as an ERP or monitoring, also see WhatsApp API for internal operations. The 3-day free trial, no credit card, is enough to build the full flow, from the event to the confirmation webhook.
