WhatsApp Bridge — MSG91 to AstraPBX¶
This guide documents the WhatsApp-to-PBX bridge that enables customers to request a callback from a WhatsApp message button.
Why¶
MSG91 WhatsApp flows can send HTTP POST requests but cannot manage JWT tokens. AstraPBX requires JWT Bearer auth for API calls, and tokens expire after 24 hours. The bridge handles token lifecycle so MSG91 only needs to send org credentials and the API request.
What¶
LogsUpdate (events.astradial.com) acts as a stateless proxy:
- Receives request from MSG91 with org credentials and target API call
- Exchanges
api_key+api_secretfor a JWT token (cached 23 hours) - Forwards the request to
devpbx.astradial.comwith Bearer auth - If PBX returns 401, refreshes token and retries once
- Returns PBX response to MSG91
How¶
Architecture¶
graph TD
A[Customer WhatsApp] -->|Taps button| B[MSG91 WhatsApp Flow]
B -->|POST /whatsapp-bridge| C[LogsUpdate<br/>events.astradial.com]
C -->|1. Login with api_key + api_secret| D[AstraPBX<br/>devpbx.astradial.com]
D -->|JWT token| C
C -->|2. Forward API call with Bearer token| D
D -->|3. Trigger call| E[Asterisk PBX]
E -->|Via Tata trunk| F[Customer's phone rings] Bridge Endpoint¶
POST https://events.astradial.com/whatsapp-bridge¶
Request Body¶
| Field | Type | Required | Description |
|---|---|---|---|
api_key | string | Yes | AstraPBX org API key |
api_secret | string | Yes | AstraPBX org API secret |
endpoint | string | Yes | PBX API path (e.g. /api/v1/calls/click-to-call) |
method | string | No | HTTP method: POST (default), GET, PUT, DELETE |
body | object | No | JSON body forwarded to PBX API |
Example: Trigger callback to reception¶
{
"api_key": "<GE_API_KEY>",
"api_secret": "secret_xxxxx",
"endpoint": "/api/v1/calls/click-to-call",
"body": {
"from": "9876543210",
"from_type": "external",
"to": "5001",
"to_type": "queue",
"caller_id": "08065978002",
"timeout": 30
}
}
Works with any AstraPBX API¶
The bridge is not limited to click-to-call. Change endpoint and body for any API:
// List users
{"api_key": "...", "api_secret": "...", "endpoint": "/api/v1/users", "method": "GET"}
// Get call records
{"api_key": "...", "api_secret": "...", "endpoint": "/api/v1/call-records", "method": "GET"}
// Update user ring target
{"api_key": "...", "api_secret": "...", "endpoint": "/api/v1/users/{userId}", "method": "PUT", "body": {"ring_target": "phone"}}
Token Lifecycle¶
graph TD
A[Request arrives] --> B{Token cached<br/>and valid?}
B -->|Yes| C[Use cached token]
B -->|No| D[POST /api/v1/auth/login]
D --> E[Cache token for 23h]
E --> C
C --> F[Forward to PBX]
F --> G{PBX returns 401?}
G -->|No| H[Return response]
G -->|Yes| I[Clear cache & refresh token]
I --> J[Retry request once]
J --> H - Tokens are cached in-memory for 23 hours (1h safety margin before 24h expiry)
- On 401 response, token is refreshed and request retried automatically
- Handles PBX server restarts and secret rotations transparently
MSG91 WhatsApp Flow Setup¶
GrandEstancia SuperHuman Flow¶
The flow sends a WhatsApp message with department buttons:
Grand Estancia Support...
How can we help you today? Choose options below to get call request
SuperHuman Team
[Front Office] [Reservation] [Olive Bar] [Room Dining] [No Reply]
Connection_Api_1 Configuration¶
| Setting | Value |
|---|---|
| URL | https://events.astradial.com/whatsapp-bridge |
| Method | POST |
| Content-Type | JSON-data |
Request body:
{
"api_key": "<GE_API_KEY>",
"api_secret": "SECRET_HERE",
"endpoint": "/api/v1/calls/click-to-call",
"body": {
"from": "{{customer_mobile}}",
"from_type": "external",
"to": "5001",
"to_type": "queue",
"caller_id": "08065978002",
"timeout": 30
}
}
Department Button Mapping¶
Each button should map to a different queue number:
| Button | Queue | "to" value |
|---|---|---|
| Front Office | Reception | "5001" |
| Reservation | Reservation queue | Create queue, use its number |
| Olive Bar | Olive Bar queue | Create queue, use its number |
| Room Dining | Room Dining queue | Create queue, use its number |
MSG91 Variables¶
| Variable | Description |
|---|---|
{{customer_mobile}} | Customer's WhatsApp phone number |
{{button_clicked}} | The button text user selected |
Adding a New Org¶
No deployment or configuration changes needed. Just:
- Create the org on AstraPBX (see Org Onboarding Guide)
- Note the
api_keyandapi_secretfrom org creation - Set up the MSG91 WhatsApp flow with the org's credentials
- Point the Connection_Api to
https://events.astradial.com/whatsapp-bridge
Debugging in GCP Console¶
The bridge logs every request with clear status indicators:
Successful call¶
[BRIDGE] ➜ POST /api/v1/calls/click-to-call | org: org_1938b... | body: {"from": "9876543210", "to": "5001", ...}
[BRIDGE] ✓ 200 | POST /api/v1/calls/click-to-call | org: org_1938b... | response: {"call_id": "abc-123"}
Token auto-refresh on 401¶
[BRIDGE] ➜ POST /api/v1/calls/click-to-call | org: org_1938b... | body: {...}
[BRIDGE] 401 from PBX — refreshing token | org: org_1938b...
JWT token cached for org: GrandEstancia
[BRIDGE] ✓ 200 | POST /api/v1/calls/click-to-call | org: org_1938b... | response: {...}
Bad credentials¶
PBX validation error¶
[BRIDGE] ➜ POST /api/v1/calls/click-to-call | org: org_1938b... | body: {}
[BRIDGE] ✗ 400 | POST /api/v1/calls/click-to-call | org: org_1938b... | response: {"error": "Missing required fields"}
Infrastructure¶
| System | URL | Purpose |
|---|---|---|
| LogsUpdate (bridge) | https://events.astradial.com | WhatsApp bridge on GCP Cloud Run |
| AstraPBX API | https://devpbx.astradial.com | Cloud PBX API |
| AstraPBX Swagger | https://devpbx.astradial.com/docs | API documentation |
| MSG91 | https://control.msg91.com | WhatsApp flow builder |
LogsUpdate Deployment¶
- GCP Service:
logsupdate(Cloud Run,us-central1) - Docker:
hariprasathe/logsupdate - Code:
server.py— endpointPOST /whatsapp-bridge