Meta / WhatsApp / payment callbacks
The exact URLs to paste into Meta, WhatsApp Cloud API, Razorpay, Cashfree and the Android app — where each is generated in the CRM and how to verify it.
After go-live, each external platform needs one or more URLs pointing back at your install. Every URL below is generated or displayed by the CRM — never hand-craft the tenant ids or tokens. This page is the single checklist of "what to paste where".
Subfolder installs If your install is in a subfolder (document root not pointed at public/), every URL includes the subfolder — e.g. https://crm.example.com/crm/webhook/.... The CRM generates URLs from APP_URL, so set APP_URL correctly first (Domain, subdomain & SSL) and copy the URLs from the screens.
Who this is for
- Super-admin / hosting admin — wiring the platform against external services.
- Developer — verifying the wiring end-to-end.
- Support — "webhook rejected" tickets.
The master list
| Platform | URL to configure | Where the CRM shows it |
|---|---|---|
| Meta app — OAuth | {APP_URL}/oauth/meta/callback |
Super Admin → Settings → Meta Developer (redirect URI field + health card) |
| Meta lead ads — webhook | {APP_URL}/webhook/{tenant}/{source}/{token} |
The lead-source connection (URL built with the connection's webhook token) |
| WhatsApp Cloud API — callback | {APP_URL}/api/messaging/whatsapp/webhook/{tenant}/{token} |
Workspace → Settings → WhatsApp connection (copyable field) |
| WhatsApp Cloud API — verify token | The connection's verify token | Same WhatsApp connection page |
| Razorpay | {APP_URL}/api/webhooks/razorpay |
Super Admin → Billing → Providers & Settings (secret configured there) |
| Cashfree | {APP_URL}/api/webhooks/cashfree |
Same providers page |
| Twilio voice | {APP_URL}/api/voice/{tenant}/twiml|status|recording/{token} |
Generated with the workspace's voice configuration |
| Android app | {APP_URL}/api/v1/mobile/ |
App build/runtime setting (see below) |
Meta (Facebook) — OAuth + lead ads
OAuth redirect URI
-
Super Admin → Settings → Meta Developer.
-
Copy the redirect URI — it is
route('oauth.callback', ['source' => 'meta']), i.e.:https://crm.yourdomain.com/oauth/meta/callback -
Paste it into the Meta app dashboard under the login product's Valid OAuth Redirect URIs.
The health card on that page checks it: in production the domain must be HTTPS (an http:// or localhost URL is flagged), and the callback route must be registered. Fix issues before attempting a tenant connection — Meta surfaces redirect mismatches as a generic error.
The connect flow requests these permissions: leads_retrieval, pages_show_list, pages_read_engagement, pages_manage_ads.
Lead ads webhook
Each lead-source connection gets a tokenised URL (webhook_token is a random 48-character string, one per connection):
https://crm.yourdomain.com/webhook/{tenant_id}/{source}/{webhook_token}
- GET — Meta's verification handshake: the challenge (
hub.challenge/hub.challenge) is echoed back. - POST — lead delivery: signature-checked (
X-Hub-Signature-256), logged, then queued for processing.
The Super Admin Meta Developer page's health card also confirms the inbound webhook route is registered. Full payload/retry behaviour: Webhook endpoints.
Verify with curl first
curl -i "https://crm.yourdomain.com/webhook/1/meta/yourConnectionToken?hub.mode=subscribe&hub.challenge=ping"
# → 200, body: ping
A silent 200 OK with no echo means the token didn't match a connection.
WhatsApp Cloud API
-
In the workspace: Settings → WhatsApp connection.
-
Copy the displayed Callback URL:
https://crm.yourdomain.com/api/messaging/whatsapp/webhook/{tenant_id}/{token}(
tokenis the workspace's messaging webhook token; the URL is built fromconfig('app.url').) -
Copy the Verify token shown for the connection.
-
In Meta → WhatsApp → Configuration, paste both: Callback URL + Verify Token, and subscribe to the
messagesfield.
Meta immediately sends the verification GET; all three must line up (path token, verify token, hub_mode=subscribe) or Meta shows "The callback URL or verify token couldn't be validated" — the route echoes the challenge only when they match.
Inbound messages are signature-checked when the connection has an app secret, de-duplicated by wamid, and STOP keywords feed the opt-out guard. Details: WhatsApp Business API.
Razorpay & Cashfree (commercial billing)
-
Super Admin → Billing → Providers & Settings; enter the provider keys and the webhook secret first.
-
Register the webhook URL in the provider dashboard:
Razorpay: https://crm.yourdomain.com/api/webhooks/razorpay Cashfree: https://crm.yourdomain.com/api/webhooks/cashfree -
Subscribe to the payment/subscription events the provider offers — the CRM maps
subscription.authorized/charged/cancelled/halted,payment.captured/failed,mandate.revoked/expired; unknown event types are stored as processed without error.
Both endpoints verify the signature over the raw body with the configured secret, are idempotent (duplicate deliveries answer {"ok":true,"duplicate":true}), and happy-path events are applied to invoices/subscriptions through the queue. A valid signature is mandatory — a 400 {"ok":false,"error":"invalid signature"} in the provider's delivery log means the secret in the CRM and the dashboard differ.
The Android app
The app talks to the Mobile API at:
https://crm.yourdomain.com/api/v1/mobile/
Two ways to point an app build at your server:
| Method | Where | Notes |
|---|---|---|
| Build-time default | BASE_URL in the app's Gradle config (android/app/build.gradle.kts) |
The shipped default targets a local emulator (http://10.0.2.2/Lead%20Hub/api/v1/mobile/) — change it for your distribution |
| Runtime override | Login screen → Advanced → Server URL | Persisted on the device; must start with http:///https:// and end with / |
Cleartext HTTP only works in dev builds; production requires HTTPS. Push registration posts the FCM token to POST /api/v1/mobile/device/register; the workspace's Firebase service account is configured in the CRM's Android app settings. See Installation and Mobile API (device tokens).
Twilio voice
For voice calling, the workspace's voice configuration generates tokenised callback URLs:
Call TwiML: https://crm.yourdomain.com/api/voice/{tenant}/twiml/{token}
Status: https://crm.yourdomain.com/api/voice/{tenant}/status/{token}
Recording: https://crm.yourdomain.com/api/voice/{tenant}/recording/{token}
Paste each into the corresponding Twilio number/application field exactly as generated.
Verify everything
# 1. Platform health
curl -s https://crm.yourdomain.com/api/v1/health
# {"status":"ok","checks":{...}}
# 2. Meta handshake (replace token)
curl -si "https://crm.yourdomain.com/webhook/1/meta/TOKEN?hub.challenge=ping" | tail -1
# ping
# 3. WhatsApp handshake (replace tokens)
curl -si "https://crm.yourdomain.com/api/messaging/whatsapp/webhook/1/TOKEN?hub_mode=subscribe&hub_verify_token=VERIFY&hub_challenge=ping" | tail -1
# ping
# 4. Payment webhook endpoint exists (expect 400/200 JSON, never HTML 404)
curl -s -o /dev/null -w "%{http_code}\n" -X POST https://crm.yourdomain.com/api/webhooks/razorpay
Then send a live test: use the workspace's test lead flow for Meta, message the WhatsApp number, or trigger a test payment in the provider's sandbox.
Common mistakes
- Pasting
http://URLs or alocalhostURL into Meta (rejected in production). - Copying the URL but not the verify token (WhatsApp) — both must match.
- Registering payment webhooks before saving the provider secret in the CRM (every delivery fails signature).
- Changing the domain and not revisiting this whole list.
- On subfolder installs, forgetting the subfolder segment in one URL.
- Using a connection URL from one workspace with another workspace's credentials.
Troubleshooting
| Symptom | Check |
|---|---|
| Meta "URL couldn't be validated" | GET echo test with curl; path token; HTTPS reachability |
| WhatsApp shows 403 to Meta | Verify token mismatch; path token from a different workspace |
| Leads arriving in webhook logs but not the CRM | Signature invalid (app secret mismatch) or form not in the allowlist — see Leads not entering the CRM |
Payment webhook log shows invalid signature |
Secret mismatch between dashboard and SA providers page |
| App can't log in on a real phone | Server URL not HTTPS / missing trailing slash; device on a different network |
Technical details
- Route names:
oauth.callback,webhook.inbound,messaging.webhook(.verify),commercial.webhook.razorpay/cashfree,voice.twiml/status/recording. - Meta redirect display + health:
App\Filament\SuperAdmin\Pages\MetaDeveloperSettings::redirectUri(). - WhatsApp callback display:
App\Filament\Pages\Settings\WhatsAppConnectionPage.php(built fromconfig('app.url')+messaging.whatsapp_webhook_token). - Connection URL/token:
App\Models\LeadSourceConnection(webhook_token= random 48 chars). - Mobile base URL:
android/README.md,android/app/build.gradle.kts.
Related guides
Was this page helpful?
Thanks — noted. ✦