API

Webhooks

Receive real-time event notifications from Trunx via webhooks.

Overview

Trunx webhooks deliver real-time notifications to your application when events occur — SMS delivery, call completion, campaign progress, and more.

Register a Webhook

curl -X POST https://api.trunx.io/api/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/trunx",
    "events": ["sms.*", "voice.*"]
  }'

Response:

{
  "id": "whk_abc123",
  "url": "https://your-app.com/webhooks/trunx",
  "events": ["sms.*", "voice.*"],
  "secret": "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ..."
}

Save the secret — it's only shown once and is used to verify webhook signatures.

Glob Patterns

The events array supports glob patterns:

PatternMatches
*All events
sms.*All SMS events
voice.*All voice events
campaign.*All campaign events
sms.deliveredOnly SMS delivered events

List Webhooks

curl https://api.trunx.io/api/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "webhooks": [
    {
      "id": "whk_abc123",
      "url": "https://your-app.com/webhooks/trunx",
      "events": ["sms.*", "voice.*"],
      "active": true,
      "createdAt": "2025-01-01T00:00:00.000Z"
    }
  ]
}

Delete a Webhook

curl -X DELETE https://api.trunx.io/api/webhooks/whk_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY"

Delivery Logs

curl https://api.trunx.io/api/webhooks/whk_abc123/logs?limit=20 \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "logs": [
    {
      "id": "log_123",
      "eventType": "sms.delivered",
      "status": "delivered",
      "attempts": 1,
      "responseCode": 200,
      "createdAt": "2025-01-01T12:00:00.000Z"
    }
  ]
}

Event Types

EventDescription
sms.sentSMS sent to carrier
sms.deliveredSMS delivered to recipient
sms.failedSMS delivery failed
sms.receivedInbound SMS received
voice.initiatedOutbound call initiated
voice.ai.initiatedAI voice call initiated
voice.inboundInbound call received
voice.answeredCall answered
voice.completedCall completed
voice.ai.completedAI call completed
voice.failedCall failed
voice.amdAMD detection result
campaign.call.initiatedCampaign call started
campaign.call.human_connectedHuman answered campaign call
campaign.call.voicemailCampaign call reached voicemail
campaign.call.failedCampaign call failed
campaign.stats.updatedCampaign stats refreshed
campaign.paused.no_didsCampaign paused — no healthy DIDs

Payload Format

{
  "channel": "sms",
  "eventType": "sms.delivered",
  "payload": {
    "id": "msg_xyz789",
    "from": "+15551234567",
    "to": "+15559876543",
    "status": "delivered"
  },
  "customerId": "cust_123",
  "timestamp": "2025-01-01T12:00:00.000Z"
}

Verifying Signatures

Every webhook includes an X-Trunx-Signature header containing an HMAC-SHA256 signature of the raw request body, using your webhook secret.

JavaScript:

import crypto from "crypto";

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your webhook handler:
app.post("/webhooks/trunx", (req, res) => {
  const signature = req.headers["x-trunx-signature"];
  const isValid = verifyWebhook(req.rawBody, signature, WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = req.body;
  console.log(`Received: ${event.eventType}`, event.payload);
  res.status(200).json({ received: true });
});

Python:

import hmac
import hashlib

def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Retry Policy

Failed deliveries retry with exponential backoff:

AttemptDelay
1Immediate
230 seconds
32 minutes
410 minutes
51 hour

After 5 failed attempts, the delivery is marked as failed.

SSE Alternative

For real-time streaming without webhooks, use Server-Sent Events. SSE supports channel filtering and Last-Event-ID replay.

On this page