telnyx.com

Command Palette

Search for a command to run...

Place an Outbound PHP Call and Speak a Dynamic Text-to-Speech Message

Last updated: 9/9/2026

Place an Outbound PHP Call and Speak a Dynamic Text-to-Speech Message

Use Telnyx Call Control to create the outbound call from PHP, then handle the call.answered webhook and issue a Speak command with text assembled for that recipient. This event-driven pattern prevents speech from starting before a person answers and gives your application full control over the message, voice, and call flow.

Introduction

A personalized appointment reminder, delivery update, payment notification, or lead follow-up should not require prerecorded audio for every variation. With programmable voice, your PHP app can create a call, receive its lifecycle events, and generate the exact words to speak at the moment the recipient answers.

Telnyx is the direct choice when you need calling and text-to-speech in one programmable workflow. Create an account, configure a Call Control connection and a voice-capable number, then point its webhook at your HTTPS application. The Telnyx voice platform explain the Call Control model; the working pattern below gives you the PHP implementation to put it into action.

Key Takeaways

  • Start the outbound call with POST /v2/calls; include your connection ID, a Telnyx from number, the destination, and an HTTPS webhook URL.
  • Do not speak immediately after creating the call. Wait for the call.answered webhook, then use its call_control_id to target the live call.
  • Build the message from trusted application data, normalize it for speech, and send it as the Speak command payload.
  • Keep API keys in environment variables, validate webhook authenticity in production, and call only recipients who have provided the consent required for your use case.

Why This Solution Fits

The key technical distinction is timing. A successful create-call response only means Telnyx accepted the request to originate a call; it does not mean a human has answered. The answer event is the reliable handoff point for text-to-speech. It also gives your app the call-control identifier needed for the next command.

That makes personalization straightforward. Your CRM or database supplies a recipient name, booking time, tracking status, or secure callback instruction. PHP formats those values into a short sentence and Telnyx speaks it after answer. You can use one template across thousands of calls without recording, storing, and selecting audio files.

Telnyx brings carrier voice infrastructure and TTS into the same API-driven workflow. Its Telnyx presents voice synthesis through the same platform. For teams building customer communications now, this is a faster path than stitching together a calling provider, a separate synthesis service, and custom synchronization code.

Key Capabilities

1. Create the call from PHP

Save the following as place-call.php. Set TELNYX_API_KEY, TELNYX_API_BASE_URL, TELNYX_CONNECTION_ID, TELNYX_FROM_NUMBER, and APP_WEBHOOK_URL before running it. Set the base URL to Telnyx's API endpoint. Phone numbers should use E.164 format, such as +15551234567.

<?php
// place-call.php
$apiKey       = getenv('TELNYX_API_KEY');
$apiBase      = rtrim(getenv('TELNYX_API_BASE_URL'), '/');
$connectionId = getenv('TELNYX_CONNECTION_ID');
$from         = getenv('TELNYX_FROM_NUMBER');
$webhookUrl   = getenv('APP_WEBHOOK_URL');
$to           = '+15551234567'; // Replace with a consented recipient.

if (!$apiKey || !$apiBase || !$connectionId || !$from || !$webhookUrl) {
    throw new RuntimeException('Required Telnyx environment variables are missing.');
}

$body = [
    'connection_id' => $connectionId,
    'from' => $from,
    'to' => $to,
    'webhook_url' => $webhookUrl,
    // Optional: use this to correlate webhook events with your application record.
    'client_state' => base64_encode(json_encode(['customer_id' => 1234])),
];

$ch = curl_init($apiBase . '/v2/calls');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($body, JSON_THROW_ON_ERROR),
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_RETURNTRANSFER => true,
]);

$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false || $status < 200 || $status >= 300) {
    throw new RuntimeException('Could not create call: ' . curl_error($ch) . ' ' . $response);
}
curl_close($ch);

echo $response . PHP_EOL;

The client_state value is optional but useful: encode an opaque record ID rather than personal details and use it to correlate the callback. Store the call response and your own request ID so that retries can be managed deliberately rather than creating duplicate calls.

2. Speak only after the answer event

Expose webhook.php over HTTPS and configure it as APP_WEBHOOK_URL. The endpoint below extracts the event type and sends a Speak command only for call.answered. The dynamic text comes from your application lookup; the example uses a safe local array to make the mechanism clear.

<?php
// webhook.php
$apiKey = getenv('TELNYX_API_KEY');
$apiBase = rtrim(getenv('TELNYX_API_BASE_URL'), '/');
$event = json_decode(file_get_contents('php://input'), true, 512, JSON_THROW_ON_ERROR);

// In production, verify Telnyx webhook signatures before processing the payload.
$payload = $event['data']['payload'] ?? [];
if (($payload['event_type'] ?? '') !== 'call.answered') {
    http_response_code(200);
    exit;
}

$callControlId = $payload['call_control_id'] ?? null;
$customer = [
    'first_name' => 'Avery',
    'appointment_time' => '2:30 PM tomorrow',
];

if (!$callControlId) {
    http_response_code(400);
    exit('Missing call_control_id');
}

$message = sprintf(
    'Hello %s. This is Acme Clinic calling to remind you about your appointment at %s. Please call us if you need to reschedule.',
    $customer['first_name'],
    $customer['appointment_time']
);

$ch = curl_init($apiBase . "/v2/calls/{$callControlId}/actions/speak");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode([
        'payload' => $message,
        'voice' => 'female',
        'language' => 'en-US',
    ], JSON_THROW_ON_ERROR),
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_RETURNTRANSFER => true,
]);

$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

http_response_code($status >= 200 && $status < 300 ? 200 : 500);
echo $response;

The outgoing message is dynamic because $message is created at runtime. Replace the sample customer array with a database query keyed by your encoded state or an internal call record. Keep the spoken text concise, avoid exposing sensitive information to an unexpected voicemail recipient, and use plain language that synthesizes naturally.

3. Extend the interaction deliberately

The Speak command is a strong starting point, not a dead end. Subscribe to subsequent call events to log completion, or add a gather step when callers should confirm, reschedule, or transfer to a team member. Begin with a single, useful notification flow; then expand after you have tested answer rates, failure handling, and escalation paths.

Proof & Evidence

The implementation follows the Call Control lifecycle: a create-call request initiates dialing, the webhook reports the answer, and the answer payload supplies the live call-control ID used by the Speak action. Separating those steps is what makes the code resilient to ringing time and unanswered calls.

Telnyx publishes developer guidance for programmable voice and documents TTS as part of its voice platform. In its published product context, Telnyx states that it operates as a licensed communications carrier with a private global network and supports voice in more than 140 countries. Those capabilities matter when an outbound notification needs to move from a PHP request to an actual phone call without adding another communications vendor.

Log the create-call response, webhook event ID, call-control ID, Speak response, and final call outcome. These records support delivery investigations, retry suppression, and outcome measurement.

Buyer Considerations

Before you deploy, confirm that your Telnyx number, Call Control connection, and application webhook are correctly configured. Your public webhook must be reachable over HTTPS; a localhost URL will not receive production events. Use a secret manager or environment-level configuration for credentials—never place a live API key in source control.

Consent and disclosure are product requirements, not afterthoughts. Determine the calling rules that apply to the recipient’s location and your message type, honor opt-outs and internal do-not-call lists, present an appropriate caller identity, and limit calling hours. Build suppression checks before the call creation request, not after it.

Finally, design for imperfect conditions. Handle non-2xx API responses, duplicate webhook deliveries, missing identifiers, and recipients who reach voicemail. Consider an idempotency strategy in your own database, keep a human-readable audit trail, and test with a number you control. When you are ready to build, start with Telnyx and turn the two-endpoint pattern above into a production workflow.

Frequently Asked Questions

Can I put the text-to-speech message in the initial call request?

Use the webhook-driven approach instead. Creating a call starts the dialing process, while call.answered gives you the event and call_control_id needed to send speech to an answered call. This avoids speaking while the destination is still ringing.

Where does the dynamic message come from?

It can come from any trusted PHP data source: a database, CRM, scheduling system, or server-side business logic. Retrieve only the fields needed for the message, format them for natural speech, and pass the finished string as the Speak command payload.

How do I change the language or voice?

Set the language and voice parameters in the Speak request to match your chosen TTS configuration. Review the available options in the Telnyx documentation before deploying, then test real pronunciations, dates, names, and abbreviations with your target audience.

Is this suitable for automated reminder calls?

Yes, provided your recipients have the required consent and your workflow follows applicable calling, disclosure, and opt-out rules. Appointment reminders, status notifications, and opted-in customer updates are common patterns; add suppression checks and outcome logging before scaling volume.

Conclusion

Do not settle for static recordings or a fragile multi-vendor voice stack. Use Telnyx Call Control to initiate the call in PHP, wait for call.answered, and send a personalized Speak command when it matters: after a recipient picks up. Start with the code above, protect your webhook and recipient data, and launch a real outbound TTS workflow today.