API

Campaigns API

Create and manage outbound voice and SMS campaigns with AMD, voicemail deposit, live AI agent handoff, and human-like SMS drip sending.

Campaigns orchestrate large-scale outbound voice calling and SMS messaging. Voice campaigns include automatic answering machine detection (AMD), voicemail audio deposit, and live AI agent handoff. SMS campaigns send messages with human-like pacing (3-5 per DID per hour). Both channels share the same DID rotation, health monitoring, compliance enforcement, and retry logic.

Campaign lifecycle

Campaign status flow: createddialingcompleted | paused | cancelled | failed

A paused campaign can be resumed back to dialing.


Create campaign

POST /api/campaigns

curl -X POST https://api.trunx.io/api/campaigns \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q1 SoCal Outreach",
    "channel": "voice",
    "mode": {
      "ai": true,
      "voicemailAudioUrl": "https://example.com/vm.mp3",
      "calls_per_second": 10,
      "max_retries": 2,
      "retry_delay": "4h"
    },
    "callerIds": ["+19495551000", "+17145551000"],
    "callerIdStrategy": {
      "selection": "health_score",
      "local_presence": true
    },
    "schedule": {
      "timezone": "America/Los_Angeles",
      "startHour": 9,
      "endHour": 19,
      "days": [1, 2, 3, 4, 5]
    }
  }'
curl -X POST https://api.trunx.io/api/campaigns \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "March Follow-Up Texts",
    "channel": "sms",
    "mode": {
      "message": "Hi, this is Trunx following up on your inquiry. Reply YES to schedule a call."
    },
    "callerIds": ["+19495551000", "+17145551000"],
    "schedule": {
      "timezone": "America/Los_Angeles"
    }
  }'
const res = await fetch("https://api.trunx.io/api/campaigns", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tk_live_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Q1 SoCal Outreach",
    channel: "voice", // or "sms"
    mode: {
      ai: true,
      voicemailAudioUrl: "https://example.com/vm.mp3",
      calls_per_second: 10,
      max_retries: 2,
      retry_delay: "4h",
    },
    callerIds: ["+19495551000", "+17145551000"],
    callerIdStrategy: {
      selection: "health_score",
      local_presence: true,
    },
    schedule: {
      timezone: "America/Los_Angeles",
      startHour: 9,
      endHour: 19,
      days: [1, 2, 3, 4, 5],
    },
  }),
});
const data = await res.json();
import requests

res = requests.post(
    "https://api.trunx.io/api/campaigns",
    headers={"Authorization": "Bearer tk_live_..."},
    json={
        "name": "Q1 SoCal Outreach",
        "channel": "voice",  # or "sms"
        "mode": {
            "ai": True,
            "voicemailAudioUrl": "https://example.com/vm.mp3",
            "calls_per_second": 10,
            "max_retries": 2,
            "retry_delay": "4h",
        },
        "callerIds": ["+19495551000", "+17145551000"],
        "callerIdStrategy": {
            "selection": "health_score",
            "local_presence": True,
        },
        "schedule": {
            "timezone": "America/Los_Angeles",
            "startHour": 9,
            "endHour": 19,
            "days": [1, 2, 3, 4, 5],
        },
    },
)
data = res.json()

Response

{
  "id": "camp_abc123",
  "name": "Q1 SoCal Outreach",
  "status": "created",
  "channel": "voice"
}

Request body

FieldTypeRequiredDescription
namestringYesCampaign name (min 1 character)
channelstringNo"voice" (default) or "sms"
modeobjectNoChannel-specific configuration (see below)
callerIdsstring[]NoE.164 phone numbers or area codes to use as caller IDs
callerIdStrategyobjectNoDID selection and rotation strategy
scheduleobjectNoTCPA-compliant schedule

Mode options (voice)

FieldTypeDescription
aibooleanEnable AI agent for live calls
voicemailAudioUrlstringURL of audio to play when voicemail is detected
calls_per_secondnumberDialing rate limit
max_retriesnumberMaximum retry attempts per prospect
retry_delaystringDelay between retries (e.g. "4h", "30m")

Mode options (SMS)

FieldTypeDescription
messagestringRequired. The SMS body text to send to each prospect

SMS campaigns enforce a stricter sending window (7 AM – 7 PM) and throttle to ~4 messages per DID per hour with 15-minute gaps for human-like pacing. Delivery status is tracked automatically via carrier webhooks.

Schedule

FieldTypeDescription
timezonestringIANA timezone (e.g. "America/Los_Angeles")
startHournumberDaily start hour (0-23)
endHournumberDaily end hour (0-23)
daysnumber[]Days of week (1=Monday through 7=Sunday)

TCPA compliance is enforced at the infrastructure level. Calls outside the configured schedule window or to numbers on the suppression list will be automatically blocked.


List campaigns

GET /api/campaigns

Returns all campaigns for the authenticated account.

curl "https://api.trunx.io/api/campaigns" \
  -H "Authorization: Bearer tk_live_..."
const res = await fetch("https://api.trunx.io/api/campaigns", {
  headers: { "Authorization": "Bearer tk_live_..." },
});
const data = await res.json();
import requests

res = requests.get(
    "https://api.trunx.io/api/campaigns",
    headers={"Authorization": "Bearer tk_live_..."},
)
data = res.json()

Response

{
  "campaigns": [
    { "id": "camp_abc123", "name": "Q1 SoCal Outreach", "status": "dialing", "channel": "voice" },
    { "id": "camp_def456", "name": "March Follow-Up Texts", "status": "created", "channel": "sms" }
  ]
}

Get campaign

GET /api/campaigns/:id

Returns the full campaign object with real-time stats.

curl "https://api.trunx.io/api/campaigns/camp_abc123" \
  -H "Authorization: Bearer tk_live_..."
const res = await fetch("https://api.trunx.io/api/campaigns/camp_abc123", {
  headers: { "Authorization": "Bearer tk_live_..." },
});
const data = await res.json();
import requests

res = requests.get(
    "https://api.trunx.io/api/campaigns/camp_abc123",
    headers={"Authorization": "Bearer tk_live_..."},
)
data = res.json()

Response

{
  "id": "camp_abc123",
  "name": "Q1 SoCal Outreach",
  "status": "dialing",
  "channel": "voice",
  "stats": {
    "total": 5000,
    "queued": 2100,
    "dialing": 10,
    "completed": 2890,
    "failed": 49,
    "cancelled": 0,
    "noAnswer": 284,
    "busy": 42,
    "voicemail": 2312,
    "humanConnected": 203,
    "sending": 0,
    "sent": 0,
    "delivered": 0,
    "progress": 57
  }
}

Campaign lifecycle actions

Start campaign

POST /api/campaigns/:id/start

Begins dialing (voice) or sending (SMS). The campaign must be in created status. SMS campaigns must have mode.message set.

Pause campaign

POST /api/campaigns/:id/pause

Pauses an active campaign. In-progress calls complete but no new calls are placed. Campaign must be in dialing status.

Resume campaign

POST /api/campaigns/:id/resume

Resumes a paused campaign from where it left off. Campaign must be in paused status.

Cancel campaign

POST /api/campaigns/:id/cancel

Permanently stops the campaign. All queued prospects are marked as cancelled.

All lifecycle actions return { id, name, status, channel }.

# Start a campaign
curl -X POST "https://api.trunx.io/api/campaigns/camp_abc123/start" \
  -H "Authorization: Bearer tk_live_..."

# Pause a campaign
curl -X POST "https://api.trunx.io/api/campaigns/camp_abc123/pause" \
  -H "Authorization: Bearer tk_live_..."

# Resume a campaign
curl -X POST "https://api.trunx.io/api/campaigns/camp_abc123/resume" \
  -H "Authorization: Bearer tk_live_..."

# Cancel a campaign
curl -X POST "https://api.trunx.io/api/campaigns/camp_abc123/cancel" \
  -H "Authorization: Bearer tk_live_..."
// Start a campaign
await fetch("https://api.trunx.io/api/campaigns/camp_abc123/start", {
  method: "POST",
  headers: { "Authorization": "Bearer tk_live_..." },
});

// Pause
await fetch("https://api.trunx.io/api/campaigns/camp_abc123/pause", {
  method: "POST",
  headers: { "Authorization": "Bearer tk_live_..." },
});
import requests

headers = {"Authorization": "Bearer tk_live_..."}

# Start a campaign
requests.post("https://api.trunx.io/api/campaigns/camp_abc123/start", headers=headers)

# Pause
requests.post("https://api.trunx.io/api/campaigns/camp_abc123/pause", headers=headers)

Response (all actions)

{
  "id": "camp_abc123",
  "name": "Q1 SoCal Outreach",
  "status": "dialing",
  "channel": "voice"
}

Prospects

Add prospects

POST /api/campaigns/:id/prospects

Add phone numbers to a campaign. Can only add prospects when the campaign is in created or paused status. Accepts 1 to 10,000 phone numbers per request.

curl -X POST "https://api.trunx.io/api/campaigns/camp_abc123/prospects" \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "phones": [
      "+17145551001",
      "+16195551002",
      "+18185551003"
    ]
  }'
const res = await fetch("https://api.trunx.io/api/campaigns/camp_abc123/prospects", {
  method: "POST",
  headers: {
    "Authorization": "Bearer tk_live_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phones: ["+17145551001", "+16195551002", "+18185551003"],
  }),
});
const data = await res.json();
import requests

res = requests.post(
    "https://api.trunx.io/api/campaigns/camp_abc123/prospects",
    headers={"Authorization": "Bearer tk_live_..."},
    json={
        "phones": ["+17145551001", "+16195551002", "+18185551003"]
    },
)
data = res.json()

Response

{
  "added": 3
}

Phone numbers must be in E.164 format (e.g. +14155551234). Prospects are automatically checked against the suppression list (DNC) during dialing.

List prospects

GET /api/campaigns/:id/prospects

Returns all prospects for the campaign.

{
  "prospects": [
    { "id": "...", "campaignId": "camp_abc123", "phone": "+17145551001", "status": "queued" },
    { "id": "...", "campaignId": "camp_abc123", "phone": "+16195551002", "status": "dialing" }
  ]
}

Campaign stats

GET /api/campaigns/:id/stats

Returns real-time campaign statistics. All field names use camelCase.

curl "https://api.trunx.io/api/campaigns/camp_abc123/stats" \
  -H "Authorization: Bearer tk_live_..."
const res = await fetch("https://api.trunx.io/api/campaigns/camp_abc123/stats", {
  headers: { "Authorization": "Bearer tk_live_..." },
});
const data = await res.json();
import requests

res = requests.get(
    "https://api.trunx.io/api/campaigns/camp_abc123/stats",
    headers={"Authorization": "Bearer tk_live_..."},
)
data = res.json()

Response

{
  "total": 5000,
  "queued": 2100,
  "dialing": 10,
  "completed": 2890,
  "failed": 49,
  "cancelled": 0,
  "noAnswer": 284,
  "busy": 42,
  "voicemail": 2312,
  "humanConnected": 203,
  "sending": 0,
  "sent": 0,
  "delivered": 0,
  "progress": 57
}

Fields

FieldTypeDescription
totalnumberTotal prospects in the campaign
queuednumberWaiting to be dialed/sent
dialingnumberVoice call currently in-progress
completednumberFinished (any terminal outcome including delivered)
failednumberCall/message failed
cancellednumberCancelled before dialing/sending
noAnswernumberNo pickup within ring timeout (voice only)
busynumberLine busy (voice only)
voicemailnumberAMD detected machine (voice only)
humanConnectednumberHuman answered (voice only)
sendingnumberSMS being sent (SMS only)
sentnumberSMS sent, awaiting delivery confirmation (SMS only)
deliverednumberSMS delivered to recipient (SMS only)
progressnumberCompletion percentage (0-100)

On this page