filippotoso / driver-vonage-whatsapp
Vonage WhatsApp driver for BotMan
Package info
github.com/filippotoso/driver-vonage-whatsapp
pkg:composer/filippotoso/driver-vonage-whatsapp
Requires
- php: ^8.1
- ext-json: *
- ext-openssl: *
- botman/botman: ^2.0
- firebase/php-jwt: ^7.0
Requires (Dev)
- mockery/mockery: ^1.6
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
WhatsApp channel driver for BotMan using the Vonage Messages API (v1).
Installation
composer require filippotoso/driver-vonage-whatsapp
Requires PHP 8.1+, botman/botman ^2.0, and firebase/php-jwt ^7 (Application JWT mint + webhook verify).
Laravel / BotMan Studio
The package registers VonageWhatsAppServiceProvider via Composer’s Laravel extras. Publish the config stub:
php artisan vendor:publish --provider="FilippoToso\BotMan\Drivers\VonageWhatsApp\Providers\VonageWhatsAppServiceProvider"
Studio discovery is also available through discovery.json (botman/driver + botman/driver-config).
Plain PHP
use BotMan\BotMan\Drivers\DriverManager; use FilippoToso\BotMan\Drivers\VonageWhatsApp\VonageWhatsAppDriver; DriverManager::loadDriver(VonageWhatsAppDriver::class); $config = [ 'vonage_whatsapp' => [ 'application_id' => getenv('VONAGE_WHATSAPP_APPLICATION_ID'), 'private_key' => getenv('VONAGE_WHATSAPP_PRIVATE_KEY'), // or: 'private_key_path' => '/path/to/private.key', 'signature_secret' => getenv('VONAGE_WHATSAPP_SIGNATURE_SECRET'), 'from' => getenv('VONAGE_WHATSAPP_FROM'), 'api_region' => getenv('VONAGE_WHATSAPP_API_REGION') ?: 'api', 'sandbox' => filter_var(getenv('VONAGE_WHATSAPP_SANDBOX'), FILTER_VALIDATE_BOOLEAN), 'throw_http_exceptions' => false, ], ];
Configuration
| Key | Env | Required | Notes |
|---|---|---|---|
application_id |
VONAGE_WHATSAPP_APPLICATION_ID |
yes | Vonage Application UUID |
private_key |
VONAGE_WHATSAPP_PRIVATE_KEY |
one of key/path | Inline PEM (wins over path) |
private_key_path |
VONAGE_WHATSAPP_PRIVATE_KEY_PATH |
one of key/path | Path to PEM file |
signature_secret |
VONAGE_WHATSAPP_SIGNATURE_SECRET |
no* | Account signature secret |
from |
VONAGE_WHATSAPP_FROM |
yes | Business WhatsApp number |
api_region |
VONAGE_WHATSAPP_API_REGION |
no | api, api-eu, api-us, api-ap (default api) |
sandbox |
VONAGE_WHATSAPP_SANDBOX |
no | Use Messages Sandbox host |
throw_http_exceptions |
VONAGE_WHATSAPP_THROW_HTTP_EXCEPTIONS |
no | Throw on non-2xx sends |
*Optional for local development. When set, inbound webhooks must present a valid Authorization: Bearer JWT (HS256) whose payload_hash matches the SHA-256 of the raw body; otherwise the driver does not match the request.
Phone numbers on outbound sends are normalized to E.164 without a leading + or 00.
Webhooks
Outbound sends use Application JWT auth (Authorization: Bearer). There is no HTTP Basic path.
Vonage needs two webhook URLs. Only the inbound URL should hit BotMan::listen().
| URL | What arrives | What your app should do |
|---|---|---|
| Inbound | User messages (v1 JSON: channel, message_uuid, from, to) |
BotMan listen route |
| Status | Delivery, read, and rejected callbacks (top-level status) |
A separate route that returns HTTP 2xx. This driver does not match status callbacks, and they are not BotMan events. |
Set Messages to API version v1. Legacy v0.1 bodies (nested to / from, message.content) are ignored.
Sandbox vs production
| Sandbox | Production | |
|---|---|---|
| Send host | https://messages-sandbox.nexmo.com/v1/messages |
https://{api_region}.nexmo.com/v1/messages |
| Config | sandbox true (api_region is ignored) |
sandbox false and api_region of api, api-eu, api-us, or api-ap |
| Where to set webhooks | Messages API Sandbox settings. These are separate from a production Application. | Vonage Application → Messages capability → Inbound URL and Status URL |
| Templates | Not available on the sandbox | Approved in WhatsApp Manager (this driver does not send them) |
Messaging surface (v1)
Inbound → BotMan
Vonage message_type |
Mapping |
|---|---|
text |
Message text |
image / audio / video / file |
Attachment with media URL passthrough |
reply |
Interactive Answer (reply.id / reply.title) |
location |
Location attachment when coords present |
| other | Empty-text IncomingMessage (still matched) |
Media URLs are not downloaded inside the driver. If Secure Inbound Media is enabled, your app must JWT-authenticate GETs to those URLs.
Outbound ← BotMan
| BotMan type | Vonage payload |
|---|---|
string / Question (text only) |
message_type: text |
OutgoingMessage + Image/Video/File |
matching media type; message text → caption |
OutgoingMessage + Audio |
audio (no caption) |
Pass extra Vonage fields (e.g. client_ref) via BotMan $additionalParameters (shallow merge).
Out of scope for v1: templates / MTM, interactive buttons/lists outbound, typing/read (types()), outbound location/contact, reactions, delivery events in BotMan.
24-hour customer care window
WhatsApp only allows free-form replies inside the customer care window. Outside that window (or for the first business-initiated message) you must send an approved template. This driver does not send templates; plan that at the application layer or a later package version.
Testing
composer install
composer test
Tests use fixtures and a mocked HttpInterface — no live Vonage calls.
License
MIT. Copyright (c) 2026 Filippo Toso.