API

Error Handling

Understand Trunx error responses, status codes, and retry patterns.

All error responses follow the RFC 7807 Problem Details format. Every error includes a machine-readable type URI, a human-readable title, the HTTP status code, and a detail message.

Error response format

{
  "type": "https://api.trunx.io/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "The requested resource was not found."
}
FieldTypeDescription
typestringURI identifying the error type
titlestringShort human-readable summary
statusnumberHTTP status code
detailstringExplanation of what went wrong

Status codes

CodeTitleWhen it occurs
400Bad RequestInvalid input, malformed JSON, E.164 format errors
401UnauthorizedMissing or invalid API key
403ForbiddenAPI key lacks the required scope for this action
404Not FoundResource does not exist
409ConflictAction conflicts with current state (e.g. starting an already-active campaign)
422Unprocessable EntityRequest is well-formed but fails validation (e.g. invalid area code, missing required field)
429Too Many RequestsRate limit exceeded — check the Retry-After header
500Internal Server ErrorUnexpected server error

Error types

Type URIDescription
https://api.trunx.io/errors/bad-requestRequest body or query parameters are invalid
https://api.trunx.io/errors/unauthorizedNo API key provided or key is invalid
https://api.trunx.io/errors/forbiddenKey is valid but lacks required scope
https://api.trunx.io/errors/not-foundResource not found
https://api.trunx.io/errors/conflictState conflict (e.g. campaign already started)
https://api.trunx.io/errors/validation-errorInput validation failed
https://api.trunx.io/errors/rate-limitedToo many requests
https://api.trunx.io/errors/dnc-blockedNumber is on the suppression/DNC list
https://api.trunx.io/errors/tcpa-blockedCall blocked — outside legal calling hours
https://api.trunx.io/errors/content-blockedSMS content flagged by compliance scanner
https://api.trunx.io/errors/channel-budget-exceededSIP channel pool exhausted
https://api.trunx.io/errors/internalUnexpected server error

Retry patterns

429 Too Many Requests

Use the Retry-After response header to determine when to retry. If the header is not present, use exponential backoff starting at 1 second.

HTTP/1.1 429 Too Many Requests
Retry-After: 5

{
  "type": "https://api.trunx.io/errors/rate-limited",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Try again in 5 seconds."
}

5xx Server Errors

Retry with exponential backoff. Start at 1 second, double each attempt, cap at 3 retries.

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(url, options);

    if (res.status === 429) {
      const retryAfter = parseInt(res.headers.get("Retry-After") || "1", 10);
      await new Promise((r) => setTimeout(r, retryAfter * 1000));
      continue;
    }

    if (res.status >= 500 && attempt < maxRetries) {
      await new Promise((r) => setTimeout(r, Math.pow(2, attempt) * 1000));
      continue;
    }

    return res;
  }
}
import time
import requests

def fetch_with_retry(url, headers, max_retries=3):
    for attempt in range(max_retries + 1):
        res = requests.get(url, headers=headers)

        if res.status_code == 429:
            retry_after = int(res.headers.get("Retry-After", "1"))
            time.sleep(retry_after)
            continue

        if res.status_code >= 500 and attempt < max_retries:
            time.sleep(2 ** attempt)
            continue

        return res

4xx Client Errors (except 429)

Do not retry. Fix the request and resubmit. Common fixes:

  • 400: Check that phone numbers are in E.164 format and required fields are present
  • 401: Verify your API key is correct and the Authorization header is set
  • 403: Check that your API key has the required scopes for this endpoint
  • 422: Review the detail field for specific validation errors

Guardrail errors

These errors are returned when infrastructure-level compliance checks block an action. They cannot be bypassed — the guardrails are enforced before the request reaches any provider.

DNC blocked

{
  "type": "https://api.trunx.io/errors/dnc-blocked",
  "title": "DNC Blocked",
  "status": 403,
  "detail": "The number +14155551234 is on the suppression list and cannot be contacted."
}

The destination number is on your DNC/suppression list. Remove it from the suppression list first if the contact has opted back in.

TCPA blocked

{
  "type": "https://api.trunx.io/errors/tcpa-blocked",
  "title": "TCPA Blocked",
  "status": 403,
  "detail": "Calls to +14155551234 are not permitted outside the 9:00 AM - 9:00 PM window in the destination timezone."
}

The call falls outside the legal calling hours for the destination's timezone. Adjust your campaign schedule or retry during permitted hours.

Content blocked

{
  "type": "https://api.trunx.io/errors/content-blocked",
  "title": "Content Blocked",
  "status": 422,
  "detail": "Message content was flagged by the compliance scanner for prohibited content."
}

The outbound SMS message was flagged by the content scanner. Review and revise the message content.

Channel budget exceeded

{
  "type": "https://api.trunx.io/errors/channel-budget-exceeded",
  "title": "Channel Budget Exceeded",
  "status": 429,
  "detail": "All SIP channels are currently in use. Retry after active calls complete."
}

The SIP channel pool is fully utilized. This is a transient condition — retry after a short delay as active calls complete and release channels.

On this page