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")
breakCheck 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 verificationimport 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 verificationcurl -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 "", 200MCP 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!",
},
)