Skip to content

feat(messaging): WhatsApp adapter for one-time codes - #267

Merged
ChiragAgg5k merged 6 commits into
mainfrom
feat/messaging-whatsapp-otp-adapter
Sep 16, 2026
Merged

ChiragAgg5k merged 6 commits into
mainfrom
feat/messaging-whatsapp-otp-adapter

Conversation

@ChiragAgg5k

@ChiragAgg5k ChiragAgg5k commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

Why

Users want to receive login and verification codes over WhatsApp instead of SMS. Every WhatsApp OTP route ends at Meta's Cloud API, and the aggregators (Twilio Verify, Vonage Verify, Infobip) all still require the customer's own WhatsApp Business Account, so calling the Graph API directly is the common denominator and adds no second vendor.

What

  • Adapter\SMS\WhatsApp: sends a code through a Meta authentication template (POST /{version}/{phoneNumberId}/messages). WhatsApp is modelled as an SMS-type adapter because callers already send the bare code as the message content, and a fixed-text authentication template accepts nothing else. The content is validated against Meta's 15-character alphanumeric limit before any request leaves, the recipient is reduced to digits as the Cloud API requires, and the code is passed in both the body and the copy-code button parameters.
  • Graph API errors are reported as Error <code>: <message>: <details> so a host can tell throughput limits (130429, 131056) from configuration mistakes (132001, 190) and undeliverable recipients (131026).
  • upsertTemplate() provisions the template in every requested language via upsert_message_templates, with optional expiry footer, security recommendation and TTL. Authentication templates are approved automatically, so this is a one-call setup.
  • WhatsApp\MetadataParameter::LANGUAGE overrides the template language per message, mirroring the Msg91 metadata pattern.

End-to-end testing

The suite runs against a real Meta developer test number in the e2e tier and is skipped when the TESTS_WHATSAPP_* secrets are absent. Meta's free test account ships sample templates only and refuses to create new ones, so there is no authentication template to deliver a code with; the suite covers the full request path and the error responses the account does return: unknown template (132001), recipient outside the allow list (131030) and invalid bearer token (190). All three pass against the live API.

Sample templates sent from the same test number and token land on the allow-listed phone, which confirms the number, token and recipient setup the tests rely on:

Messages from the Meta test number received on WhatsApp

The developer dashboard for the app, showing the test number, the allow-listed recipient and the webhook events produced by the runs:

Meta developer dashboard

Follow-up

Appwrite wiring (worker getSmsAdapter match, DSN parsing, per-project channel option) lands after this is tagged messaging/2.2.0. Delivering a real code end to end needs a WhatsApp Business Account we own, which requires registering a phone number in Meta's production setup.

…templates

Meta's Cloud API is the common denominator behind every WhatsApp OTP route
(Twilio, Vonage and Infobip all still require the customer's own business
account), so the adapter talks to the Graph API directly. It models WhatsApp
as an SMS-type adapter because callers already send the bare code as the
message content, which is the only thing an authentication template accepts.
The code is validated to Meta's 15-character alphanumeric limit before any
request leaves, and Graph API errors keep their numeric code so callers can
tell throughput limits from configuration mistakes. upsertTemplate() lets a
host provision the template in every language it needs.
@greptile-apps

greptile-apps Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge because the latest changes introduce no new actionable defect and no previous finding remains outstanding.

Summary

The PR adds a Meta Cloud API WhatsApp adapter for sending one-time codes through authentication templates.

  • Supports template creation, lookup, deletion, language and template overrides, callback metadata, OTP modes, and provider-error normalization.
  • Adds credential-gated Meta integration coverage and CI secret wiring.
  • Documents adapter setup and usage.

Reviews (8) · Last reviewed commit: "test(messaging): assert the adapter's co..."

Comment thread packages/messaging/src/Utopia/Messaging/Adapter/SMS/WhatsApp.php Outdated
Comment thread packages/messaging/src/Utopia/Messaging/Adapter/SMS/WhatsApp.php
Comment thread packages/messaging/tests/Messaging/Adapter/SMS/WhatsAppTest.php Outdated
…alling Meta

An SMS with no recipient reached the [0] access and died with a TypeError
rather than a clear argument error, and a time to live outside Meta's
documented 60 to 600 second window (or -1) was sent anyway only to fail
remotely. Both are now validated locally. Tests assert what a caller can
observe (code in both parameters, digits-only recipient, chosen language,
bearer credential) instead of pinning the whole request body and version.
…mber

The unit tests prove the request shape with a fake client but never reach
the Graph API. This suite runs in the e2e tier against Meta's free developer
test number, which can message five allow-listed recipients, and is skipped
unless the TESTS_WHATSAPP_* secrets are present so forks and PRs without
credentials stay green.
Comment thread packages/messaging/tests/Messaging/Adapter/SMS/WhatsAppE2ETest.php Outdated
Comment thread packages/messaging/tests/Messaging/Adapter/SMS/WhatsAppE2ETest.php Outdated
…account can run

Meta's free test account ships sample templates only and refuses to create
new ones, so no authentication template exists to deliver a code with. The
suite now covers what that account does answer for real: an unknown template
(132001), a recipient outside the allow list (131030) and a bad bearer token
(190), each through the full request path. The in-process unit suite goes
away so WhatsApp has one test that talks to the real API.
Comment thread packages/messaging/phpunit.xml
…face

A host will want more than a fixed template and language per adapter:
per-message template and language overrides let one instance serve login,
verification and MFA flows, and biz_opaque_callback_data lets delivery
webhooks be matched back to the record that triggered the send, which is
what a fallback to SMS on failure needs. Template creation now supports
one-tap and zero-tap autofill with the Android apps Meta requires, and
templates can be looked up and deleted so a host can check approval before
sending and clean up after itself.
Comment thread packages/messaging/tests/Messaging/Adapter/SMS/WhatsAppTest.php Outdated
…figuration

The suite pinned the sample template's approval status and language, the
exact refusal code for template creation, and Meta's error prose. Those are
provider details that can change without an adapter regression. It now
checks what a caller relies on: the requested template's languages come back
with a status, rejections surface as exceptions carrying a numeric code, and
a per-message template override reaches Meta.
@ChiragAgg5k
ChiragAgg5k merged commit c13dd6b into main Sep 16, 2026
80 of 82 checks passed

This branch had an error being deployed

1 failed deployment
vcs — b11408a4 Deployed Sep 16, 2026 by ChiragAgg5k via test (vcs, linked) #958
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants