# Conference Call with PHP and Laravel ## What Does This Example Do? Build a production-ready Laravel application that manages conference calls using the Telnyx Voice API. This tutorial demonstrates how to initiate calls, add participants to a conference, handle webhook events, and manage call state with proper error handling and secure credential management. ## Who Is This For? - **PHP developers** building voice features with Laravel. - **Backend engineers** integrating telephony or messaging into existing applications. - **DevOps teams** looking for containerized, production-ready telecom examples. - **Startups and enterprises** replacing legacy telecom providers with a modern API-first platform. ## Why Telnyx? Telnyx is an **AI Communications Infrastructure** platform that gives developers a single API for [voice](https://telnyx.com/products/voice-ai-agents), [messaging](https://telnyx.com/products/sms-api), [SIP](https://telnyx.com/products/sip-trunks), [AI](https://telnyx.com/ai-assistants), and [IoT](https://telnyx.com/products/iot-sim-card) — no Frankenstack required. - **Integrated platform** — [Voice](https://telnyx.com/products/voice-ai-agents), [SMS](https://telnyx.com/products/sms-api), [SIP trunking](https://telnyx.com/products/sip-trunks), [AI assistants](https://telnyx.com/ai-assistants), and [IoT SIM management](https://telnyx.com/products/iot-sim-card) under one roof. No stitching together multiple vendors. - **Global private network** — Calls and messages traverse the Telnyx-owned IP network for lower latency and higher reliability than the public internet. - **Developer-first** — SDKs for Python, Node.js, Go, Ruby, Java, and PHP. Comprehensive webhook event model. Sandbox environment for testing. - **Competitive pricing** — Pay-as-you-go with no minimums, contracts, or per-seat fees. ## Prerequisites - PHP 8.1 or higher. - Laravel 10 or higher. - Composer (PHP package manager). - A Telnyx account with an active API key from the [Telnyx Portal](https://portal.telnyx.com). - A Telnyx phone number enabled for outbound calls. - A publicly accessible URL for webhook callbacks (ngrok or similar for local development). - Basic understanding of Laravel routing, controllers, and middleware. ## Quick Start ### Option 1: Local (recommended) ```bash git clone https://github.com/team-telnyx/telnyx-code-examples.git cd telnyx-code-examples/build-conference-calling-php cp .env.example .env # Edit .env with your Telnyx API key and phone number make setup make run ``` ### Option 3: Manual See the [Implementation Details](#implementation-details) section below for step-by-step instructions. ## Implementation Details Create a controller to manage conference calls: ```bash php artisan make:controller ConferenceCallController ``` Edit `app/Http/Controllers/ConferenceCallController.php`: ```php client = new Client(apiKey: config('telnyx.api_key')); } /** * Initiate a conference call with the specified participants. * Stores conference metadata and initiates outbound calls to each participant. */ public function initiate(Request $request): JsonResponse { $validated = $request->validate([ 'participants' => 'required|array|min:2', 'participants.*' => 'required|string|regex:/^\+\d{10,15}$/', ]); $conferenceId = 'conf_' . uniqid(); $initiatorNumber = config('telnyx.phone_number'); // Store conference metadata in database $conference = ConferenceCall::create([ 'conference_id' => $conferenceId, 'initiator_number' => $initiatorNumber, 'participants' => json_encode($validated['participants']), 'status' => 'pending', ]); // Initiate outbound calls to each participant $callIds = []; foreach ($validated['participants'] as $participantNumber) { try { $response = $this->client->calls->dial( from_: $initiatorNumber, to: $participantNumber, connection_id: config('telnyx.connection_id'), custom_headers: [ 'X-Conference-ID' => $conferenceId, ], ); $callIds[] = [ 'participant' => $participantNumber, 'call_control_id' => $response->data->call_control_id, ]; } catch (ApiErrorException $e) { return response()->json([ 'error' => 'Failed to initiate call to ' . $participantNumber, 'details' => $e->getMessage(), ], 400); } } return response()->json([ 'conference_id' => $conferenceId, 'status' => 'initiated', 'calls' => $callIds, ], 201); } /** * Retrieve the current status of a conference call. */ public function status(string $conferenceId): JsonResponse { $conference = ConferenceCall::where('conference_id', $conferenceId)->first(); if (!$conference) { return response()->json(['error' => 'Conference not found'], 404); } return response()->json([ 'conference_id' => $conference->conference_id, 'status' => $conference->status, 'participants' => json_decode($conference->participants, true), 'started_at' => $conference->started_at, 'ended_at' => $conference->ended_at, ]); } /** * End a conference call and disconnect all participants. */ public function end(string $conferenceId): JsonResponse { $conference = ConferenceCall::where('conference_id', $conferenceId)->first(); if (!$conference) { return response()->json(['error' => 'Conference not found'], 404); } // Update conference status $conference->update([ 'status' => 'ended', 'ended_at' => now(), ]); return response()->json([ 'conference_id' => $conferenceId, 'status' => 'ended', ]); } } ``` Create a webhook controller to handle Telnyx voice events: ```bash php artisan make:controller WebhookController ``` Edit `app/Http/Controllers/WebhookController.php`: ```php input('data.event_type'); $callControlId = $request->input('data.payload.call_control_id'); $conferenceId = $request->input('data.payload.custom_headers.X-Conference-ID'); Log::info('Voice webhook received', [ 'event' => $event, 'call_control_id' => $callControlId, 'conference_id' => $conferenceId, ]); if (!$conferenceId) { return response('OK', 200); } $conference = ConferenceCall::where('conference_id', $conferenceId)->first(); if (!$conference) { Log::warning('Conference not found for webhook', ['conference_id' => $conferenceId]); return response('OK', 200); } switch ($event) { case 'call.initiated': // Call has been initiated to participant Log::info('Call initiated', ['call_control_id' => $callControlId]); break; case 'call.answered': // Participant has answered the call if ($conference->status === 'pending') { $conference->update([ 'status' => 'active', 'started_at' => now(), ]); } Log::info('Call answered', ['call_control_id' => $callControlId]); break; case 'call.hangup': // Participant has hung up Log::info('Call hangup', ['call_control_id' => $callControlId]); // Check if all participants have disconnected $participants = json_decode($conference->participants, true); if (count($participants) <= 1) { $conference->update([ 'status' => 'ended', 'ended_at' => now(), ]); } break; default: Log::debug('Unhandled event type', ['event' => $event]); } return response('OK', 200); } } ``` Create a model for conference calls: ```bash php artisan make:model ConferenceCall ``` Edit `app/Models/ConferenceCall.php`: ```php 'array', 'started_at' => 'datetime', 'ended_at' => 'datetime', ]; } ``` Register the routes in `routes/api.php`: ```php Search & Buy, and purchase a number with the capabilities you need (SMS, voice, or both). ## Resources - [Voice API Overview](https://developers.telnyx.com/docs/voice) - [Voice API Commands](https://developers.telnyx.com/docs/voice/programmable-voice/voice-api-commands-and-resources) - [AI Assistant Start](https://developers.telnyx.com/docs/voice/programmable-voice/ai-assistant-start) - [Call Control API Reference](https://developers.telnyx.com/api-reference/call-commands/dial) - [Telnyx Voice API](https://telnyx.com/products/voice-api) - [Voice AI Agents](https://telnyx.com/products/voice-ai-agents) ## Related Examples - [Handle Inbound Call Webhooks with PHP](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main//tutorials/voice/php/inbound-call-webhook). - [Record Conference Calls with PHP](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main//tutorials/voice/php/call-recording). - [Transfer Calls Between Participants with PHP](https://raw.githubusercontent.com/team-telnyx/telnyx-code-examples/main//tutorials/voice/php/call-transfer).