Outbound call campaigns look simple until you need them to behave like real software.
Calling one number is easy. Calling a list of numbers means you suddenly care about queueing, rate limits, call state, webhook verification, result tracking, and summaries for the team.
I built a small example for that pattern: an AI Call Campaign Orchestrator using Python, Flask, Telnyx Call Control, the Telnyx Agent SDK, SQLite, and SMS.
Code: https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-call-campaign-orchestrator
What the app does
The app exposes a simple API:
-
POST /campaignstarts a campaign from a list of phone numbers -
GET /campaign/<campaign_id>returns campaign status and call results -
POST /webhooks/callreceives Telnyx Call Control events -
GET /healthconfirms the service is running
The interesting part is what happens after you submit the campaign.
The CampaignAgent queues the calls, applies a rate limit, places outbound calls through Call Control, stores results in SQLite, and sends an SMS summary when the campaign is complete.
POST /campaign
-> queue calls
-> rate-limit delivery
-> place calls with Call Control
-> receive signed webhooks
-> store results in SQLite
-> send SMS summary
Starting a campaign
You send a list of E.164 phone numbers to POST /campaign:
curl -X POST http://localhost:5000/campaign \
-H "Content-Type: application/json" \
-d '{
"phone_numbers": [
"+15551234567",
"+15557654321",
"+15559876543"
]
}'
The API returns a campaign ID:
{
"campaign_id": "3f2c1a5e-8b7d-4e6f-9a0c-1d2e3f4a5b6c",
"status": "started"
}
From there, the agent owns the campaign lifecycle.
Why rate limiting matters
Outbound communication workflows need pacing.
If you fire too many calls at once, you can create a bad user experience and run into carrier or operational limits. This sample uses CALL_RATE_LIMIT_PER_MINUTE so campaign delivery is controlled from configuration instead of buried inside the call loop.
That makes the campaign behavior easier to tune without rewriting the app.
Placing calls with Call Control
For each queued number, the app places an outbound Call Control call with the Telnyx Python SDK.
The app needs:
TELNYX_API_KEYTELNYX_CONNECTION_IDTELNYX_PHONE_NUMBERTELNYX_WEBHOOK_URL
The webhook URL points back to the app, so call lifecycle events can update campaign state as calls are answered, completed, or fail.
Verifying webhooks
The webhook endpoint uses telnyx.webhooks.unwrap() with the Telnyx Ed25519 public key.
That matters because webhook handlers should not blindly trust incoming HTTP requests. The app verifies that the event really came from Telnyx before it updates call state.
Telnyx Call Control event
-> POST /webhooks/call
-> verify signature
-> update campaign result
Tracking status
At any point, you can poll:
curl http://localhost:5000/campaign/<campaign_id>
The response includes total calls, completion status, and per-number results.
That gives you a simple campaign dashboard API without adding another database service just for the sample.
Closing the loop with SMS
When the campaign finishes, the app sends a summary SMS using Telnyx Messaging.
You configure:
TELNYX_SMS_FROMTELNYX_SMS_TO
For a demo, this is a clean way to prove the workflow completed. In a production version, the same step could also notify Slack, update a CRM, or send a report to another internal system.
What I like about this pattern
The app is not trying to be a full campaign product.
It is showing the backend shape you need for one:
- one API to start the work
- an agent to manage queueing and pacing
- Call Control for the voice workflow
- signed webhooks for lifecycle updates
- SQL for campaign state
- SMS for completion notification
That is the useful part. It gives you the skeleton for a durable communications workflow without starting from a pile of disconnected services.
Run it yourself
Clone the repo:
git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/ai-call-campaign-orchestrator
Create a virtual environment and install dependencies:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Configure your environment:
cp .env.example .env
Then fill in your Telnyx API key, connection ID, phone number, webhook URL, public key, SMS settings, and call rate limit.
Run the app:
python app.py
Resources:
- Code example: https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-call-campaign-orchestrator
- Telnyx AI toolkit: https://github.com/team-telnyx/ai
- Telnyx docs: https://developers.telnyx.com
- Telnyx Portal: https://portal.telnyx.com
Top comments (0)