Skip to content

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:

  1. Receives request from MSG91 with org credentials and target API call
  2. Exchanges api_key + api_secret for a JWT token (cached 23 hours)
  3. Forwards the request to devpbx.astradial.com with Bearer auth
  4. If PBX returns 401, refreshes token and retries once
  5. 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:

  1. Create the org on AstraPBX (see Org Onboarding Guide)
  2. Note the api_key and api_secret from org creation
  3. Set up the MSG91 WhatsApp flow with the org's credentials
  4. 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 auth failed for org_bad_key: 401 {"error": "Invalid 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

cd LogsUpdate && ./deploy.sh
  • GCP Service: logsupdate (Cloud Run, us-central1)
  • Docker: hariprasathe/logsupdate
  • Code: server.py — endpoint POST /whatsapp-bridge