Guides

Code Examples

Copy-pasteable code examples for common Trunx operations in JavaScript, Python, and curl.

Send an SMS

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const res = await fetch(`${BASE_URL}/api/sms`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "+14155559876",
    to: "+14155551234",
    message: "Hello from Trunx!",
  }),
});

const data = await res.json();
console.log(data);
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

res = requests.post(
    f"{BASE_URL}/api/sms",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "from": "+14155559876",
        "to": "+14155551234",
        "message": "Hello from Trunx!",
    },
)

print(res.json())
curl -X POST https://api.trunx.io/api/sms \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+14155559876",
    "to": "+14155551234",
    "message": "Hello from Trunx!"
  }'

Make a Voice Call

Plain call

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const res = await fetch(`${BASE_URL}/api/voice`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "+14155559876",
    to: "+14155551234",
  }),
});

const data = await res.json();
console.log(data.callId);
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

res = requests.post(
    f"{BASE_URL}/api/voice",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "from": "+14155559876",
        "to": "+14155551234",
    },
)

data = res.json()
print(data["callId"])
curl -X POST https://api.trunx.io/api/voice \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+14155559876",
    "to": "+14155551234"
  }'

AI-powered call

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const res = await fetch(`${BASE_URL}/api/voice`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "+14155559876",
    to: "+14155551234",
    systemPrompt: "You are a friendly appointment reminder assistant.",
    voice: "Mark",
    firstSpeaker: "agent",
    maxDuration: "300s",
  }),
});

const data = await res.json();
console.log(data.callId);
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

res = requests.post(
    f"{BASE_URL}/api/voice",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "from": "+14155559876",
        "to": "+14155551234",
        "systemPrompt": "You are a friendly appointment reminder assistant.",
        "voice": "Mark",
        "firstSpeaker": "agent",
        "maxDuration": "300s",
    },
)

data = res.json()
print(data["callId"])
curl -X POST https://api.trunx.io/api/voice \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "+14155559876",
    "to": "+14155551234",
    "systemPrompt": "You are a friendly appointment reminder assistant.",
    "voice": "Mark",
    "firstSpeaker": "agent",
    "maxDuration": "300s"
  }'

Create and Start a Campaign

Full workflow: create a campaign, add prospects, then start it.

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const headers = {
  Authorization: `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};

// Step 1: Create campaign
const campaignRes = await fetch(`${BASE_URL}/api/campaigns`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    name: "Q1 Outreach",
    callerIds: ["+14155559876"],
    mode: {
      ai: {
        systemPrompt: "You are a sales agent for Acme Corp.",
        voice: "Mark",
      },
    },
    callerIdStrategy: "round_robin",
    schedule: {
      timezone: "America/New_York",
      startHour: "09:00",
      endHour: "17:00",
      days: [1, 2, 3, 4, 5],
    },
  }),
});
const campaign = await campaignRes.json();

// Step 2: Add prospects
await fetch(`${BASE_URL}/api/campaigns/${campaign.id}/prospects`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    phones: ["+14155551001", "+14155551002", "+14155551003"],
  }),
});

// Step 3: Start campaign
const startRes = await fetch(`${BASE_URL}/api/campaigns/${campaign.id}/start`, {
  method: "POST",
  headers,
});
console.log(await startRes.json());
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

# Step 1: Create campaign
campaign_res = requests.post(
    f"{BASE_URL}/api/campaigns",
    headers=headers,
    json={
        "name": "Q1 Outreach",
        "callerIds": ["+14155559876"],
        "mode": {
            "ai": {
                "systemPrompt": "You are a sales agent for Acme Corp.",
                "voice": "Mark",
            },
        },
        "callerIdStrategy": "round_robin",
        "schedule": {
            "timezone": "America/New_York",
            "startHour": "09:00",
            "endHour": "17:00",
            "days": [1, 2, 3, 4, 5],
        },
    },
)
campaign = campaign_res.json()

# Step 2: Add prospects
requests.post(
    f"{BASE_URL}/api/campaigns/{campaign['id']}/prospects",
    headers=headers,
    json={
        "phones": ["+14155551001", "+14155551002", "+14155551003"],
    },
)

# Step 3: Start campaign
start_res = requests.post(
    f"{BASE_URL}/api/campaigns/{campaign['id']}/start",
    headers=headers,
)
print(start_res.json())
# Step 1: Create campaign
curl -X POST https://api.trunx.io/api/campaigns \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q1 Outreach",
    "callerIds": ["+14155559876"],
    "mode": { "ai": { "systemPrompt": "You are a sales agent for Acme Corp.", "voice": "Mark" } },
    "callerIdStrategy": "round_robin",
    "schedule": { "timezone": "America/New_York", "startHour": "09:00", "endHour": "17:00", "days": [1,2,3,4,5] }
  }'

# Step 2: Add prospects (replace CAMPAIGN_ID)
curl -X POST https://api.trunx.io/api/campaigns/CAMPAIGN_ID/prospects \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "phones": ["+14155551001", "+14155551002", "+14155551003"] }'

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

Search and Purchase DIDs

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const headers = {
  Authorization: `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};

// Search available numbers in area code 415
const searchRes = await fetch(`${BASE_URL}/api/dids/search?areaCode=415&quantity=5`, {
  headers,
});
const { available } = await searchRes.json();
console.log(`Found ${available.length} numbers`);

// Purchase selected numbers
const purchaseRes = await fetch(`${BASE_URL}/api/dids/purchase`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    numbers: [available[0].number],
  }),
});
const result = await purchaseRes.json();
console.log(`Purchased: ${result.purchased}`);
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

# Search available numbers in area code 415
search_res = requests.get(
    f"{BASE_URL}/api/dids/search",
    headers=headers,
    params={"areaCode": "415", "quantity": 5},
)
available = search_res.json()["available"]
print(f"Found {len(available)} numbers")

# Purchase selected numbers
purchase_res = requests.post(
    f"{BASE_URL}/api/dids/purchase",
    headers=headers,
    json={"numbers": [available[0]["number"]]},
)
result = purchase_res.json()
print(f"Purchased: {result['purchased']}")
# Search
curl "https://api.trunx.io/api/dids/search?areaCode=415&quantity=5" \
  -H "Authorization: Bearer tk_live_..."

# Purchase
curl -X POST https://api.trunx.io/api/dids/purchase \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "numbers": ["+14155551234"] }'

Monitor Campaign via SSE

Subscribe to real-time campaign events using Server-Sent Events.

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const evtSource = new EventSource(
  `${BASE_URL}/api/events?channel=campaign:camp_abc123`,
  {
    headers: { Authorization: `Bearer ${API_KEY}` },
  }
);

evtSource.addEventListener("call.completed", (e) => {
  const data = JSON.parse(e.data);
  console.log(`Call to ${data.to} — duration: ${data.duration}s`);
});

evtSource.addEventListener("campaign.completed", () => {
  console.log("Campaign finished");
  evtSource.close();
});

evtSource.onerror = (e) => {
  console.error("SSE error:", e);
};
import json
import sseclient
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

res = requests.get(
    f"{BASE_URL}/api/events?channel=campaign:camp_abc123",
    headers={"Authorization": f"Bearer {API_KEY}"},
    stream=True,
)

client = sseclient.SSEClient(res)
for event in client.events():
    data = json.loads(event.data)

    if event.event == "call.completed":
        print(f"Call to {data['to']} — duration: {data['duration']}s")
    elif event.event == "campaign.completed":
        print("Campaign finished")
        break

Check DID Health

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const res = await fetch(`${BASE_URL}/api/dids/${encodeURIComponent("+14155559876")}/health`, {
  headers: { Authorization: `Bearer ${API_KEY}` },
});

const health = await res.json();
console.log(`Health score: ${health.healthScore}/100`);
console.log(`Action: ${health.action}`);

if (health.healthScore < 50) {
  console.warn("DID health is low — consider cooling this number.");
}
import requests
from urllib.parse import quote

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

res = requests.get(
    f"{BASE_URL}/api/dids/{quote('+14155559876', safe='')}/health",
    headers={"Authorization": f"Bearer {API_KEY}"},
)

health = res.json()
print(f"Health score: {health['healthScore']}/100")
print(f"Action: {health['action']}")

if health["healthScore"] < 50:
    print("DID health is low — consider cooling this number.")
curl "https://api.trunx.io/api/dids/%2B14155559876/health" \
  -H "Authorization: Bearer tk_live_..."

Register a Webhook

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const res = await fetch(`${BASE_URL}/api/webhooks`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/webhook",
    events: ["sms.*", "voice.*"],
  }),
});

const webhook = await res.json();
console.log(`Webhook ID: ${webhook.id}`);
console.log(`Secret: ${webhook.secret}`); // Save this for signature verification
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

res = requests.post(
    f"{BASE_URL}/api/webhooks",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "url": "https://example.com/webhook",
        "events": ["sms.*", "voice.*"],
    },
)

webhook = res.json()
print(f"Webhook ID: {webhook['id']}")
print(f"Secret: {webhook['secret']}")  # Save this for signature verification
curl -X POST https://api.trunx.io/api/webhooks \
  -H "Authorization: Bearer tk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhook",
    "events": ["sms.*", "voice.*"]
  }'

Webhook Signature Verification

Every webhook request includes an X-Trunx-Signature header containing an HMAC-SHA256 signature of the raw request body. Verify it to ensure the request came from Trunx.

import { createHmac, timingSafeEqual } from "crypto";

const WEBHOOK_SECRET = "whsec_...";

function verifySignature(body, signature) {
  const expected = createHmac("sha256", WEBHOOK_SECRET)
    .update(body)
    .digest("hex");

  const expectedBuf = Buffer.from(expected, "hex");
  const signatureBuf = Buffer.from(signature, "hex");

  if (expectedBuf.length !== signatureBuf.length) return false;
  return timingSafeEqual(expectedBuf, signatureBuf);
}

// In your webhook handler (e.g., Express)
app.post("/webhooks/trunx", (req, res) => {
  const signature = req.headers["x-trunx-signature"];
  const rawBody = req.body; // must be raw string/buffer

  if (!verifySignature(rawBody, signature)) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(rawBody);
  console.log(`Received: ${event.type}`);
  res.sendStatus(200);
});
import hmac
import hashlib
from flask import Flask, request, abort

WEBHOOK_SECRET = "whsec_..."

app = Flask(__name__)

def verify_signature(body: bytes, signature: str) -> bool:
    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.route("/webhooks/trunx", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Trunx-Signature", "")
    raw_body = request.get_data()

    if not verify_signature(raw_body, signature):
        abort(401)

    event = request.get_json()
    print(f"Received: {event['type']}")
    return "", 200

MCP Connection

Connect to the Trunx MCP endpoint to give your AI agent access to telecom tools.

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const API_KEY = "tk_live_...";

const transport = new StreamableHTTPClientTransport(
  new URL("https://api.trunx.io/mcp"),
  {
    requestInit: {
      headers: {
        Authorization: `Bearer ${API_KEY}`,
      },
    },
  }
);

const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(transport);

// List available tools
const { tools } = await client.listTools();
console.log(`${tools.length} tools available`);

// Call a tool
const result = await client.callTool({
  name: "send_sms",
  arguments: {
    from: "+14155559876",
    to: "+14155551234",
    message: "Sent via MCP!",
  },
});
console.log(result);

Bulk SMS

Send SMS to multiple recipients with built-in rate limiting.

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

const recipients = [
  "+14155551001",
  "+14155551002",
  "+14155551003",
  "+14155551004",
  "+14155551005",
];

const delay = (ms) => new Promise((r) => setTimeout(r, ms));

const results = [];

for (const to of recipients) {
  const res = await fetch(`${BASE_URL}/api/sms`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      from: "+14155559876",
      to,
      message: "Your appointment is tomorrow at 2 PM. Reply STOP to opt out.",
    }),
  });

  const data = await res.json();
  results.push({ to, status: res.status, id: data.id });

  // Rate limit: 5 messages per second
  await delay(200);
}

console.log(`Sent ${results.filter((r) => r.status === 200).length}/${recipients.length}`);
import time
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

recipients = [
    "+14155551001",
    "+14155551002",
    "+14155551003",
    "+14155551004",
    "+14155551005",
]

results = []

for to in recipients:
    res = requests.post(
        f"{BASE_URL}/api/sms",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "from": "+14155559876",
            "to": to,
            "message": "Your appointment is tomorrow at 2 PM. Reply STOP to opt out.",
        },
    )

    results.append({"to": to, "status": res.status_code, "id": res.json().get("id")})

    # Rate limit: 5 messages per second
    time.sleep(0.2)

sent = sum(1 for r in results if r["status"] == 200)
print(f"Sent {sent}/{len(recipients)}")

Error Handling Pattern

Retry logic with exponential backoff for transient errors (429 rate limit and 5xx server errors).

const API_KEY = "tk_live_...";
const BASE_URL = "https://api.trunx.io";

async function trunxRequest(path, options = {}, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(`${BASE_URL}${path}`, {
      ...options,
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
        ...options.headers,
      },
    });

    if (res.ok) return res.json();

    // Retry on 429 (rate limit) and 5xx (server error)
    if ((res.status === 429 || res.status >= 500) && attempt < maxRetries) {
      const retryAfter = res.headers.get("Retry-After");
      const backoff = retryAfter
        ? parseInt(retryAfter, 10) * 1000
        : Math.min(1000 * 2 ** attempt, 30000);

      console.warn(`Retrying in ${backoff}ms (attempt ${attempt + 1}/${maxRetries})`);
      await new Promise((r) => setTimeout(r, backoff));
      continue;
    }

    // Non-retryable error — throw with RFC 7807 details
    const error = await res.json();
    throw new Error(`${error.title}: ${error.detail} (status ${res.status})`);
  }
}

// Usage
const sms = await trunxRequest("/api/sms", {
  method: "POST",
  body: JSON.stringify({
    from: "+14155559876",
    to: "+14155551234",
    message: "Hello!",
  }),
});
import time
import requests

API_KEY = "tk_live_..."
BASE_URL = "https://api.trunx.io"

def trunx_request(method, path, max_retries=3, **kwargs):
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    }
    headers.update(kwargs.pop("headers", {}))

    for attempt in range(max_retries + 1):
        res = requests.request(method, f"{BASE_URL}{path}", headers=headers, **kwargs)

        if res.ok:
            return res.json()

        # Retry on 429 (rate limit) and 5xx (server error)
        if (res.status_code == 429 or res.status_code >= 500) and attempt < max_retries:
            retry_after = res.headers.get("Retry-After")
            backoff = (
                int(retry_after)
                if retry_after
                else min(2 ** attempt, 30)
            )
            print(f"Retrying in {backoff}s (attempt {attempt + 1}/{max_retries})")
            time.sleep(backoff)
            continue

        # Non-retryable error — raise with RFC 7807 details
        error = res.json()
        raise Exception(f"{error['title']}: {error['detail']} (status {res.status_code})")

# Usage
sms = trunx_request(
    "POST",
    "/api/sms",
    json={
        "from": "+14155559876",
        "to": "+14155551234",
        "message": "Hello!",
    },
)

On this page