Campaigns
Upload a list of leads, dial them with an AI agent on a controlled schedule, and track outcomes per row.
A Campaign runs an outbound voice agent against a list of phone numbers — typically uploaded as a CSV — with full control over scheduling, concurrency, retries, and per-lead variables. Every call's outcome (qualified, voicemail, no-answer, callback) is captured against the lead row so you get a clean post-campaign report.
What you can build
- Lead-gen outreach — qualify cold or warm prospects from a CRM export
- Renewal reminders — auto-call policy holders before expiry
- Appointment confirmations — pre-call patients/clients the day before
- Customer feedback / NPS — short post-purchase surveys
- Membership renewals / collections — friendly first-touch before escalation
Lifecycle
draft ─→ scheduled ─→ running ←─→ paused ─→ completed
↑
failed (if all leads fail) ┘| State | What's allowed |
|---|---|
draft | Edit config, upload leads, delete |
scheduled | Edit config; will auto-start at scheduledStartAt |
running | Pause, stop |
paused | Resume, stop |
completed | View report, export, delete |
failed | View report, delete |
Quick start
1. Create a campaign
curl -X POST https://api.osmtalk.com/api/campaigns \
-H "Authorization: Bearer $OSMTALK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Q2 Renewals — Mumbai",
"agentId": "agent_xxx",
"phoneNumberId": "pn_xxx",
"maxConcurrent": 5,
"schedule": {
"timezone": "Asia/Kolkata",
"windowStart": "10:00",
"windowEnd": "18:00",
"weekdays": [1, 2, 3, 4, 5]
},
"retryPolicy": {
"maxAttempts": 3,
"backoffMinutes": 60,
"retryOn": ["no_answer", "busy"]
},
"webhookUrl": "https://your-crm.example.com/webhooks/osmtalk"
}'2. Upload leads (CSV)
curl -X POST https://api.osmtalk.com/api/campaigns/{campaignId}/leads \
-H "Authorization: Bearer $OSMTALK_API_KEY" \
-H "Content-Type: text/csv" \
--data-binary @leads.csvThe CSV format is documented in CSV format — short version: one phone column required, every other column becomes a {{variable}} your agent prompt can reference.
3. Start
curl -X POST https://api.osmtalk.com/api/campaigns/{campaignId}/startThe campaign immediately transitions to running and the in-process dialer begins placing calls at the next 15-second tick.
4. Watch outcomes
curl https://api.osmtalk.com/api/campaigns/{campaignId}/reportReturns per-status and per-disposition counts. The dashboard's Campaigns → Live Monitor page shows the same data live.
How dialing works
Every 15 seconds the dialer:
- Iterates every running campaign
- Skips those outside their
schedule.windowStart–windowEndin their timezone - Calculates available concurrency:
min(campaign.maxConcurrent − active_calls_for_this_campaign, org_remaining_capacity) - Pulls up to that many leads where
status='pending'OR (status in ('no_answer','busy','failed')ANDnextAttemptAt <= now) - Scrubs each against the DNC list — DNC matches go straight to
dnc_blocked - Spawns a call per remaining lead with the lead's row data injected as
dynamicVariables
When the call ends:
- The post-call analyzer (if enabled on the agent) extracts structured fields
- The disposition is set based on
analysis.lead_qualified/analysis.next_action - The webhook URL receives a
campaign.lead_completedevent - If the outcome is retryable and attempts <
maxAttempts,nextAttemptAtis set tonow + backoffMinutes
The campaign auto-transitions to completed once every lead has reached a terminal status.
Personalization with {{variables}}
Each CSV row's columns become per-call variables. Reference them in the agent's system prompt or welcome message:
System prompt:
You're calling {{first_name}} from {{company}} about their {{policy_type}} policy
renewal due {{renewal_date}}. The new premium is ₹{{premium}}.CSV row:
phone,first_name,company,policy_type,renewal_date,premium
+919876543210,Arjun,Acme,Health,2026-05-28,12450When this lead is dialed, the bot speaks: "Hi Arjun, I'm calling from Acme about your Health policy renewal due 2026-05-28. The new premium is ₹12,450 …"
See Dynamic Variables for the full template syntax.
Outcomes (dispositions)
After each call:
| Disposition | Meaning |
|---|---|
qualified | Lead expressed interest or matched success criteria |
not_interested | Caller declined |
callback | Caller asked to be called later |
voicemail | Hit voicemail; bot may have left a message |
no_answer | No one picked up — will retry if policy allows |
busy | Line busy — will retry |
failed | Bot crashed / SIP error — will retry if policy allows |
dnc | Phone matched the DNC list — never dialed |
Dispositions come from analysis.next_action (when post-call analysis is enabled) or call status (otherwise).