JiwaAI
Blog
โ†All posts
whatsapp
engineering
resilience

The Invisible Migration: When WhatsApp Kills Your Buttons

Jiwa AI Teamยท

The Feature That Stopped Working

For months, our WhatsApp content approval flow was elegant. After onboarding, business owners received their AI-generated posts as images with two tappable buttons: Approve and Reject. One tap, post reviewed, next one queued up. It felt instant and native.

Then it stopped working. No error messages. No failed API calls. The buttons still appeared to send. But users tapping them got silence. Posts stayed pending forever.

The Silent Deprecation

WhatsApp updated their infrastructure to verify the sender of interactive button messages. Only official Business API partners โ€” companies paying for Meta's enterprise tier โ€” could send buttons that actually rendered on the recipient's phone. Third-party API providers, including ours, were quietly cut off.

Our messaging provider migrated to polls as the replacement. Polls are WhatsApp's native interactive feature that works for everyone. Two choices, single select, tap to respond. Functionally identical to buttons for approve/reject flows. The provider updated their API: our button-sending functions now redirected internally to poll-sending functions. The migration on the sending side was seamless.

The receiving side was not.

Where the Data Disappeared

Our webhook handler parsed incoming messages and extracted several fields from the payload: the sender's phone number, the message text, and a button identifier. When the system migrated to polls, the provider started sending poll responses in different fields โ€” a poll name and the selected choice.

Our message handler function accepted three parameters. The webhook caller passed five. In JavaScript, extra arguments are silently dropped. The poll name and choice arrived at our server, were parsed from the request body, passed to the handler, and evaporated. No type error. No runtime exception. The function simply never saw them.

Every approval tap from every user on every post was lost.

The Fix Is Smaller Than the Bug

The handler needed two additional parameters and a new routing function. When a poll response arrives, we extract the post identifier from the poll name and determine whether the chosen option means approve or reject by matching against keywords in both English and Indonesian. Then we route to the same approval logic that the old button handler used โ€” ownership verification, idempotency checks, mood board feedback, pending count updates.

The entire fix was about twenty lines of code. The bug had been live for months.

Why This Kind of Bug Is Hard to Catch

Dashboard approvals still worked perfectly. The web interface uses a standard REST endpoint that was never affected. Users who preferred the dashboard never noticed anything wrong. The bug only manifested in the WhatsApp-native flow โ€” which is exactly the flow we built for Indonesian small business owners who live in WhatsApp and rarely open web dashboards.

There was no test that could have caught this without mocking the exact payload format that the messaging provider sends for poll responses. Our integration tests verified button payloads, which no longer existed in production.

Lessons for API-Dependent Products

Third-party API deprecations rarely announce themselves with errors. They degrade silently. The sending side may adapt automatically while the receiving side stays frozen in the old format. When your product depends on a provider's webhook payload structure, any change in that provider's feature set can break your intake without breaking your output.

We now log every webhook payload field at the parsing layer, not just the ones we extract. If a field appears that we don't handle, we flag it. And if expected fields stop appearing, we know within hours instead of months.

The most dangerous bugs are the ones where everything looks fine from the inside.