Test WhatsApp with Meta's test number
Prove the WhatsApp integration end to end using Meta's free test phone number — no business verification required. Includes a local simulator that sends correctly-signed payloads.
Testing the WhatsApp integration with Meta's test number
Meta gives every developer app a test phone number that works without business verification. It can message up to 5 recipient numbers you verify yourself, which is enough to prove the whole path end to end while the real business verification is still pending.
Nothing here costs money and nothing touches a customer.
Before you open the Meta dashboard
Prove the Keptly side works first. If the webhook is broken, Meta's error messages will not tell you that.
# Does the verification handshake answer correctly?
pnpm --filter @crm/api meta:test -- --verify \
--url https://keptly.net/api/whatsapp --token "$WHATSAPP_VERIFY_TOKEN"
# Does a correctly-signed inbound message get accepted?
pnpm --filter @crm/api meta:test -- \
--url https://keptly.net/api/whatsapp --secret "$WHATSAPP_APP_SECRET"
The simulator sends the exact bytes Meta sends, signed the way Meta signs them. If it passes, the webhook will work; if it fails, fix that before going near the dashboard.
1. Create the app
developers.facebook.com → My Apps → Create App → Business → add the WhatsApp product. No business verification required to get this far.
2. Collect four values
| Value | Where |
|---|---|
WHATSAPP_PHONE_NUMBER_ID | WhatsApp → API Setup → the test number's Phone number ID |
WHATSAPP_ACCESS_TOKEN | Same page — the temporary token (24 hours) |
WHATSAPP_APP_SECRET | App Settings → Basic → App Secret |
WHATSAPP_VERIFY_TOKEN | You invent this. Any long random string. |
The 24-hour token is fine for testing. For anything lasting, create a System User token under Business Settings, which does not expire.
3. Set them as GitHub secrets, not on the server
.env.prod is regenerated from GitHub secrets on every deploy, so anything edited by hand on the box is erased at the next push.
Repository → Settings → Secrets and variables → Actions. Add all four, then trigger a deploy.
SetWHATSAPP_APP_SECRETin the same batch asWHATSAPP_VERIFY_TOKEN. With the verify token set and the app secret missing, the webhook is live and accepting unsigned payloads — anyone who knows the URL could inject fabricated messages. Keptly now refuses that combination in production rather than trusting it, so a half-configured webhook fails closed instead of quietly accepting forgeries.
4. Point the webhook at Keptly
WhatsApp → Configuration → Webhook → Edit:
- Callback URL —
https://keptly.net/api/whatsapp - Verify token — the
WHATSAPP_VERIFY_TOKENyou set
Save. Meta performs the GET handshake immediately; if it fails, the value in the dashboard does not match the deployed one.
Then Manage → subscribe to the messages field. Without that subscription nothing is delivered and everything looks fine.
5. Claim the number for a tenant
The webhook routes an inbound message to a tenant by matching phone_number_id against Company.whatsappPhoneNumberId. Until a company claims the test number, messages are accepted and ignored — deliberately, so an unclaimed number never lands in someone else's inbox.
Either connect it in Dashboard → Settings → Channels, or for the demo tenant set it directly:
UPDATE "Company" SET "whatsappPhoneNumberId" = '<PHONE_NUMBER_ID>'
WHERE slug = '<your-tenant-slug>';
6. Send a real message
Add your own number under To in API Setup, then message the test number from WhatsApp on your phone.
Expected within a few seconds:
- Conversation appears in the tenant's inbox, channel
WHATSAPP - A contact is created or matched
- Extraction fills in name, intent, score — visible in the API logs as
ai: call ok with purpose: "extraction"
- If a CRM connector is configured, the contact is pushed
When it does not work
| Symptom | Cause |
|---|---|
| Handshake fails in the dashboard | Verify token mismatch. Confirm with meta:test -- --verify. |
401 invalid_signature in logs | Wrong app secret, or a proxy re-encoded the body. Meta signs raw bytes; any reserialisation breaks it. |
200 but nothing in the inbox | Either the messages field is not subscribed, or no company claims that phone_number_id. |
| Message stored, no extraction | ANTHROPIC_API_KEY unset, or the tenant is over quota / past trial. Both are logged. |
| Nothing at all | Meta only delivers to HTTPS with a valid certificate. Confirm curl -I https://keptly.net/api/whatsapp. |
Limits of the test number
- Sends only to numbers you explicitly verify (max 5)
- Cannot be used with real customers
- Displays as a Meta-owned test number, not your business
It proves the integration. It does not replace business verification.
Need help?
If you couldn't find what you needed, reach out to support directly. A human reads every email.
support@keptly.net