Keptly
Channels

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

ValueWhere
WHATSAPP_PHONE_NUMBER_IDWhatsApp → API Setup → the test number's Phone number ID
WHATSAPP_ACCESS_TOKENSame page — the temporary token (24 hours)
WHATSAPP_APP_SECRETApp Settings → Basic → App Secret
WHATSAPP_VERIFY_TOKENYou 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.

Set WHATSAPP_APP_SECRET in the same batch as WHATSAPP_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_TOKEN you 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:

  1. Conversation appears in the tenant's inbox, channel WHATSAPP
  2. A contact is created or matched
  3. Extraction fills in name, intent, score — visible in the API logs as

ai: call ok with purpose: "extraction"

  1. If a CRM connector is configured, the contact is pushed

When it does not work

SymptomCause
Handshake fails in the dashboardVerify token mismatch. Confirm with meta:test -- --verify.
401 invalid_signature in logsWrong app secret, or a proxy re-encoded the body. Meta signs raw bytes; any reserialisation breaks it.
200 but nothing in the inboxEither the messages field is not subscribed, or no company claims that phone_number_id.
Message stored, no extractionANTHROPIC_API_KEY unset, or the tenant is over quota / past trial. Both are logged.
Nothing at allMeta 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
Test WhatsApp with Meta's test number — Keptly docs