ok:true does not mean delivered: request vs result
Nearly every integration answers a request with something like ok:true. The developer sees success, logs "sent" and moves on. But that ok almost never means the thing the request was for has happened. It means the other side accepted the request for processing. Between “accepted” and "done" sits an entire system that can fail silently. We have hit this in messages, in payments and in documents, and the rule that comes out of it is the same.
Acceptance and result are two different facts
Any external system answers the cheapest question first: "did I accept your request?". The question the business cares about — did the customer get the message, did the bay start, was the receipt registered — is answered later, on another channel, and sometimes never, unless you ask. A system that counts the first as the second reports success on a day when part of the work was not done.
Example 1. A message to a customer or a colleague
In the WhatsApp Business API, a success response means the platform took the message. It can still fail to deliver: outside the 24-hour window free-form text does not go through at all, and even an approved template can be held back with code 131049 — the platform itself limits how many messages reach one recipient.
In our own messenger-based sales system this shows up on staff notifications. Outside the window, long text is replaced by a template that keeps only the first meaningful line. And several notifications in a row to one person exhaust their limit — the later ones simply do not arrive, even though each one got a success response. Two rules follow: the first line of any service message must carry the point on its own, and one combined notification beats three in a row.
Example 2. Payment at an unattended machine
The acquirer confirmed the payment — that is a fact about money, not about service. The customer may have paid and the bay never started: a lost link to the controller, a power cut, a failed start command. In reconciliation this is its own category of discrepancy: more payments than starts. It matters as much as a shortfall, because these are customers who paid for nothing, and their complaints are on the way.
Example 3. A fiscal receipt
Sending a receipt and registering it are different events too. What was taken and what was fiscalised are two numbers, and their difference is the health indicator of the whole installation. While it is zero, all is well. When it is not, something broke — better to learn that from your own dashboard than from an inspection.
How to build a check on the result
Store two statuses, not one. "Accepted" and "result confirmed" are separate fields with separate timestamps. Between them sits a "waiting" state with its own deadline.
Read the result channel. Delivery-status webhooks for messages, the start event from the controller for payments, the fiscal server's response for receipts. Not the response code of the send.
Log failure loudly. Silent failure is the most expensive kind. If "not delivered" is visible only to someone who went looking, nobody will see it.
Reconcile the totals. Once a day: how many sent, how many confirmed, how many stuck. The difference is either zero or a list with reasons.
Have a fallback path. Whatever did not get through automatically must reach a person who will finish it by hand.
Why this is not only a technical question
Over twenty months our messenger-based sales system processed 3,332 messages, handled 260 orders and generated 196 documents. At that volume "almost everything arrived" means specific people who missed something: a customer who never saw an invoice, a colleague who did not know about a task. A report that counts accepted as delivered hides exactly them.
Frequently asked
Can we trust an HTTP 200 from an API?
What is WhatsApp error 131049?
How do we avoid flooding staff with notifications without losing what matters?
What does this kind of checking cost?
More answers
Updated: 2026-10-10