OpenPhone Webhooks in Laravel: Building a Smart Communication Hub
In today's interconnected business world, efficient communication is key. OpenPhone, a modern business phone system, offers powerful webhook capabilities that can transform how you handle calls and messages. This guide will walk you through integrating OpenPhone webhooks with your Laravel application, enabling you to create a smart, responsive communication hub.
Understanding OpenPhone Webhooks
OpenPhone webhooks provide real-time notifications about various communication events, such as completed calls and received messages. By leveraging these webhooks, you can:
- Automatically log call details in your CRM
- Trigger automated responses to incoming messages
- Generate analytics on communication patterns
- Integrate voice and text communications with your existing workflows
Let's dive into how we can set this up in a Laravel application.
Setting Up Your Laravel Project
First, ensure you have a Laravel project ready. If not, create one:
composer create-project laravel/laravel openphone-webhook-hub
cd openphone-webhook-hub
Creating the Webhook Controller
Let's create a controller to handle our OpenPhone webhook events:
php artisan make:controller OpenPhoneWebhookController
Now, open app/Http/Controllers/OpenPhoneWebhookController.php
and add the following structure:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class OpenPhoneWebhookController extends Controller
{
public function handle(Request $request)
{
$payload = $request->all();
$eventType = $payload['type'];
Log::info("Received OpenPhone webhook: {$eventType}", $payload);
switch ($eventType) {
case 'call.completed':
return $this->handleCallCompleted($payload['data']['object']);
case 'message.received':
return $this->handleMessageReceived($payload['data']['object']);
default:
Log::warning("Unhandled OpenPhone event: {$eventType}");
return response()->json(['status' => 'unhandled event'], 200);
}
}
private function handleCallCompleted(array $callData)
{
// Logic for completed call
return response()->json(['status' => 'call completed handled'], 200);
}
private function handleMessageReceived(array $messageData)
{
// Logic for received message
return response()->json(['status' => 'message received handled'], 200);
}
}
Setting Up the Route
Add a route for your webhook in routes/api.php
:
use App\Http\Controllers\OpenPhoneWebhookController;
Route::post('webhooks/openphone', [OpenPhoneWebhookController::class, 'handle']);
Implementing Webhook Security
OpenPhone uses a specific method to secure webhooks. Let's implement this verification correctly:
- Add a new configuration item in
config/services.php
:
'openphone' => [
'signing_secret' => env('OPENPHONE_SIGNING_SECRET'),
],
- Update your
.env
file with the signing secret from your OpenPhone account:
OPENPHONE_SIGNING_SECRET=your_signing_secret_here
- Implement the correct verification in your controller:
private function verifyWebhookSignature(Request $request)
{
$signature = $request->header('openphone-signature');
$signingSecret = config('services.openphone.signing_secret');
if (!$signature || !$signingSecret) {
return false;
}
$fields = explode(';', $signature);
$timestamp = $fields[2];
$providedDigest = $fields[3];
$signedData = $timestamp . '.' . $request->getContent();
$signingKeyBinary = base64_decode($signingSecret);
$computedDigest = base64_encode(hash_hmac('sha256', $signedData, $signingKeyBinary, true));
return hash_equals($providedDigest, $computedDigest);
}
- Use this method in your
handle
function:
public function handle(Request $request)
{
if (!$this->verifyWebhookSignature($request)) {
Log::warning('Invalid OpenPhone webhook signature');
return response()->json(['error' => 'Invalid signature'], 401);
}
// Rest of your handle method...
}
Handling Specific Events
Let's implement handlers for the call.completed and message.received events:
private function handleCallCompleted(array $callData)
{
$callId = $callData['id'];
$from = $callData['from'];
$to = $callData['to'];
$duration = $callData['completedAt']
? (strtotime($callData['completedAt']) - strtotime($callData['createdAt']))
: null;
Log::info("Call completed: {$callId} from {$from} to {$to}, duration: {$duration} seconds");
if (isset($callData['voicemail'])) {
Log::info("Voicemail received: {$callData['voicemail']['url']}");
// Maybe transcribe the voicemail and send it to the user
// TranscriptionService::transcribeAndNotify($callData['voicemail']['url']);
}
// Update your CRM or internal systems
// CRM::logCall($from, $to, $duration, $callData['direction']);
return response()->json(['status' => 'call completed handled'], 200);
}
private function handleMessageReceived(array $messageData)
{
$messageId = $messageData['id'];
$from = $messageData['from'];
$to = $messageData['to'];
$body = $messageData['body'];
Log::info("Message received: {$messageId} from {$from} to {$to}");
// Check for media attachments
if (!empty($messageData['media'])) {
foreach ($messageData['media'] as $media) {
Log::info("Media attachment: {$media['url']} ({$media['type']})");
// Maybe process or store the media
// MediaProcessor::processAttachment($media['url'], $media['type']);
}
}
// Maybe trigger an automated response
// AutoResponder::sendReply($to, $from, $body);
return response()->json(['status' => 'message received handled'], 200);
}
Testing Your Integration
To test your webhook integration, you can use Webhook Simulator:
-
Sign up for a Webhook Simulator account, visit "Platforms" page and add OpenPhone to your library.
-
Install the Webhook Simulator CLI tool. You can find installation instructions in the documentation.
-
Start the Webhook Simulator CLI to listen for webhook events and forward them to your local Laravel application:
ws-cli listen --forward-to http://localhost:8000/api/webhooks/openphone
Make sure to replace http://localhost:8000/api/webhooks/openphone
with the actual URL where your Laravel application is listening for webhooks.
- Now you have two options for simulating OpenPhone webhook events:
a. Use the Webhook Simulator web application to create and send custom payloads.
b. Use the CLI to trigger predefined events. Open a new terminal window and run:
ws-cli trigger openphone call.completed
You can replace call.completed
with any other OpenPhone event you wish to test.
-
The simulated webhook will be sent to your local Laravel application via the forwarding set up in step 3.
-
Check your Laravel logs to see how your application handled the simulated webhook event.
Here's an example of what an OpenPhone call.completed
event payload might look like (note that this is a simplified version):
{
"apiVersion": "v2",
"createdAt": "2022-02-01T12:34:56.789Z",
"data": {
"object": {
"answeredAt": null,
"completedAt": "2022-02-01T12:34:01.000Z",
"conversationId": "CNe9a8b7c6d5",
"createdAt": "2022-02-01T12:33:45.678Z",
"direction": "incoming",
"from": "+15555550123",
"id": "AC5f7d2a1b4a0b4b2a8c7d2e1f3a4b5c6",
"media": [],
"object": "call",
"phoneNumberId": "PNf5a6b7c8d",
"status": "completed",
"to": "+16665550456",
"userId": "USb1c2d3e4f",
"voicemail": {
"duration": 10,
"type": "audio/mpeg",
"url": "https://m.openph.one/static/b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5.mp3"
}
}
},
"id": "EVbf9e2f2a8b0b4a71a76f46d4f3a78f1f",
"object": "event",
"type": "call.completed"
}
Optimizing for Production
As you prepare for production, consider these optimizations:
- Use Queues: For time-consuming tasks, use Laravel's queue system:
public function handle(Request $request)
{
// Verify signature first
ProcessOpenPhoneWebhook::dispatch($request->all());
return response()->json(['message' => 'Webhook queued for processing']);
}
-
Implement Idempotency: Ensure your handlers can safely process the same event multiple times.
-
Set Up Monitoring: Use Laravel's logging and a service like Sentry for error monitoring.
-
Scale Your Infrastructure: Ensure your server can handle the expected volume of webhooks.
Conclusion
Integrating OpenPhone webhooks with your Laravel application can significantly enhance your communication workflows. From automating call logging to triggering smart responses for messages, webhooks offer a powerful way to make your communication system more intelligent and efficient.
Remember, while Webhook Simulator is great for development and testing, always thoroughly test your integration in a staging environment before going live.
Happy coding, and may your communications always be smart and timely!