Skip to content

Latest commit

Β 

History

History
412 lines (336 loc) Β· 13.2 KB

File metadata and controls

412 lines (336 loc) Β· 13.2 KB

RustPBX API Integration Guide

RustPBX provides a comprehensive set of HTTP APIs and Webhooks designed to make it a fully programmable Software Defined PBX (SD-PBX). This guide details how to integrate your external business logic (CRM, ERP, AI assistants, billing systems) with RustPBX.


πŸ—οΈ Architecture Overview

RustPBX interacts with external systems in two ways:

  1. Inbound API (REST): Your system calls RustPBX to manage resources (extensions, trunks) or control active calls.
  2. Outbound Webhooks: RustPBX calls your system to make routing decisions, report events, or authenticate users.
Mechanism Direction Type Use Case
Console API Inbound REST CRUD extensions, download recordings, system config
Active Call Control Inbound REST Live hangup, transfer, mute, force-accept
AMI API Inbound REST Health checks, hot-reload, raw dialog inspection
HTTP Router Outbound Webhook Dynamic call routing (per-INVITE decision)
User Backend Outbound Webhook External SIP authentication (OAuth/LDAP proxy)
Locator Webhook Outbound Webhook Real-time registration/unregistration events
Call Record Push Outbound Webhook Push CDR JSON + Audio files to external server

πŸ“‘ 1. Outbound Webhooks (RustPBX β†’ Your Server)

1.1 HTTP Router (Dynamic Call Routing)

The most powerful extension point. Instead of static routing rules, RustPBX asks your API "Receive call from A to B, what should I do?".

  • Trigger: Every incoming SIP INVITE.
  • Config:
    [proxy.http_router]
    url = "https://your-api.com/pbx/route"
    fallback_to_static = true       # If your API fails, use internal routes
    timeout_ms = 5000
    [proxy.http_router.headers]
    X-API-Key = "secret-token"

Request (POST):

{
  "call_id": "ab39-551-229",
  "from": "<sip:1001@pbx.com>",
  "to": "<sip:200@pbx.com>",
  "source_addr": "1.2.3.4:5060",
  "direction": "inbound",  // inbound | outbound | internal
  "method": "INVITE",
  "uri": "sip:200@pbx.com",
  "headers": {
    "User-Agent": "Yealink T54W",
    "X-Client-ID": "998877"
  },
  "body": "v=0\r\n..." // Full SDP body
}

Response:

{
  "action": "forward",            // Actions: forward | reject | abort | spam | not_handled
  "targets": [
    "sip:1001@192.168.1.50:5060", // Target 1 (Extension)
    "sip:1002@192.168.1.51:5060"  // Target 2 (Mobile App)
  ],
  "strategy": "parallel",         // parallel (Ring All) | sequential (Failover)
  "record": true,                 // Enable recording for this call
  "record_start_at": "media",    // media (includes early media) | answer (wait for 200)
  "timeout": 30,                  // Ring timeout in seconds
  "media_proxy": "auto",          // all | auto | nat | none | bypass
  "headers": {                    // Inject custom SIP headers into the INVITE sent to B
    "X-Call-Reason": "support-ticket-123"
  }
}

record_start_at overrides [recording].auto_start_at for this call. When omitted, the global value is inherited; if neither is set, media is the default. The field may also be returned without record to override the timing while retaining the global recording enablement policy.

1.2 User Backend (SIP Authentication)

Delegate SIP registration password checking to your external DB or API.

  • Trigger: SIP REGISTER or INVITE with auth.
  • Config:
    [[proxy.user_backends]]
    type = "http"
    url = "https://your-api.com/pbx/auth"
    username_field = "u"
    realm_field = "r"

Request (GET): https://your-api.com/pbx/auth?u=1001&r=pbx.com

Response (200 OK):

{
  "id": 1001,
  "username": "1001",
  "password": "hashed_password_or_plaintext", // HA1 hash preferred
  "display_name": "John Doe",
  "email": "john@pbx.com",
  "allow_guest_calls": false
}

Response (403 Forbidden):

{ "reason": "invalid_password", "message": "Account locked" }

1.3 Locator Webhook (Presence Events)

Real-time notification when devices come online or go offline.

  • Config:
    [proxy.locator_webhook]
    url = "https://your-api.com/pbx/events"
    events = ["registered", "unregistered", "offline"]

Payload:

{
  "event": "registered",
  "timestamp": 1708201234,
  "location": {
    "aor": "sip:1001@1.2.3.4:12345",
    "home_proxy": "10.0.0.12:5060",
    "destination": "TLS 1.2.3.4:12345",
    "supports_webrtc": false,
    "transport": "TLS",
    "user_agent": "MicroSIP/3.21.3",
    "expires": 3600
  }
}

registered and unregistered events contain one location. An offline event contains a locations array because a transport close or expiry sweep can remove multiple bindings at once.

Field Meaning
event registered, unregistered, or offline
timestamp UNIX timestamp in seconds when RustPBX constructs the webhook payload
aor Device Contact URI from the REGISTER Contact header; this is not necessarily the user's canonical identity
home_proxy Advertised SIP address of the RustPBX node that accepted and owns the registration
destination Device network address observed by RustPBX, including transport when available
supports_webrtc Whether the registered device is treated as a WebRTC endpoint
transport SIP transport used by the registration, such as UDP, TCP, TLS, WS, or WSS
user_agent REGISTER User-Agent value when provided
expires Registration lifetime in seconds

The REGISTER Request-URI is not included in the locator webhook. It can be the same address as home_proxy in a direct, single-node deployment, but can differ when clients register through a domain, load balancer, SIP proxy, or NAT. Use home_proxy when identifying the RustPBX node that owns the registration.

Example offline payload:

{
  "event": "offline",
  "timestamp": 1708201294,
  "locations": [
    {
      "aor": "sip:1001@1.2.3.4:12345",
      "home_proxy": "10.0.0.12:5060",
      "destination": "TLS 1.2.3.4:12345",
      "supports_webrtc": false,
      "transport": "TLS",
      "user_agent": "MicroSIP/3.21.3",
      "expires": 3600
    }
  ]
}

1.4 CDR Event Push and Recording Upload

Push call details immediately after a call ends. Recording media upload is configured separately.

  • Config:
    [recording]
    enabled = true
    auto_start = true
    auto_start_at = "media"
    type = "http"
    path = "./config/recorders"
    url = "https://your-api.com/pbx/recording"
    
    [callrecord]
    type = "http"
    url = "https://your-api.com/pbx/cdr"
    # Maximum concurrent post-call CDR save/upload/hook tasks. Default: 64, minimum: 1.
    max_concurrent = 64
    # Accepted for compatibility, but ignored. Use [recording] for media upload.
    with_media = true

CDR format: multipart/form-data

  • Field calllog.json: The full CDR JSON (see next section).

Recording format: multipart/form-data

  • File field recording: The recorded WAV file.
  • Fields call_id and track_id: Recording metadata.

πŸ”Œ 2. Inbound REST API (You β†’ RustPBX)

Base URL: http://<rustpbx-ip>:8080/console
Authentication: Session cookie (login via the console UI) or a static API token:

[console]
api_tokens = [
  { token = "my-api-token", scopes = ["calls", "records", "routing"] }
]
Authorization: Bearer my-api-token

2.1 Active Call Control

Manage calls that are currently in progress.

List Active Calls: REST endpoints are mounted under the console api_prefix (default /api, configured via [console].api_prefix).

GET {api_prefix}/calls/active

Control a Call: POST {api_prefix}/calls/active/{call_id}/commands

Payloads:

  1. Hangup:
    { "action": "hangup", "reason": "admin_kick" }
  2. Blind Transfer:
    { "action": "transfer", "target": "sip:1002@pbx.com" }
  3. Mute/Unmute:
    { "action": "mute", "track_id": "audio-0" } // use 'unmute' to reverse
  4. Force Answer (for ringing channels):
    { 
      "action": "accept", 
      "sdp": "v=0..." // Server-generated SDP answer
    }

2.1.1 Live Call Transcription (SSE)

Stream real-time transcription text for an active call. Transcription starts lazily on first subscriber and stops when the last one disconnects or the call ends. Requires [proxy.transcript.remote] (streaming ASR, Deepgram-compatible) in config; returns 503 when unconfigured.

Endpoint: GET /cc/calls/{call_id}/transcript (SSE, text-only events: started / segment / error / ended)

The same segments are also delivered as standard RWI events (transcript_started / transcript_segment / transcript_error / transcript_ended) to webhooks / RWI WebSocket subscribers.

Full protocol, config and examples: Live Transcript SSE API

2.2 System Management (CRUD)

Resource Endpoint Methods Description
Extensions {api_prefix}/extensions GET, POST, PUT, DELETE Manage SIP users
Trunks {api_prefix}/sip-trunk GET, POST, PUT, DELETE Manage upstream carriers
Routes {api_prefix}/routing GET, POST, PUT, DELETE Manage dial plan rules
CDRs {api_prefix}/call-records GET, POST (Search) Query history
Recording {api_prefix}/call-records/{id}/recording GET Stream audio file
SIP Flow {api_prefix}/call-records/{id}/sip-flow GET Get PCAP-like ladder diagram JSON

2.3 AMI (Admin Interface)

Low-level system operations. Protected by IP whitelist ([ami].allows in config).

Base URL: http://<rustpbx-ip>:8080/ami/v1

  • Health: GET /health - System vital stats (uptime, active calls, load).
  • Reload: POST /reload/trunks, /reload/routes, /reload/acl - Hot reload config without restart.
  • Shutdown: POST /shutdown - Graceful shutdown (stops accepting new calls, waits for active ones).
  • Dialogs: GET /dialogs - Raw dump of internal SIP dialog states (for debugging).
  • SipFlow signaling: GET /sipflow/flow/{call_id} - Query SIP ladder data.
  • SipFlow media: GET /sipflow/media/{call_id} - Export call media as WAV.

SipFlow endpoints support optional time range query parameters:

  • start: range start time
  • end: range end time

Accepted formats:

  • RFC3339 datetime, e.g. 2026-04-16T10:00:00+08:00
  • Unix timestamp (seconds), e.g. 1713232800

Example:

GET /ami/v1/sipflow/flow/abc123?start=2026-04-16T10:00:00%2B08:00&end=2026-04-16T10:30:00%2B08:00
GET /ami/v1/sipflow/media/abc123?start=1713232800&end=1713234600

πŸ› οΈ Integration Workflows

Scenario A: CRM Click-to-Dial

  1. User clicks phone number in CRM.
  2. CRM backend sends POST /api/v1/commands (Future feature) OR uses AMI to originate call.
  3. Current workaround: CRM sends SIP REFER to RustPBX or uses a dedicated "Click-to-Dial" SIP extension that the web-app registers as.

Scenario B: AI Voice Assistant

  1. Inbound call hits RustPBX.
  2. HTTP Router sends INVITE details to AI backend.
  3. AI Backend returns {"action": "forward", "targets": ["sip:ai-bot-service@internal"]}.
  4. RustPBX routes audio to the AI bot via SIP/RTP.

Scenario C: Billing System

  1. User Backend authenticates user, checking balance > 0.
  2. Call proceeds.
  3. On hangup, CDR Push sends via HTTP POST to Billing System.
  4. Billing system calculates duration * rate and deducts balance.

Scenario D: Compliance Recording

[recording].enabled turns media capture on. [recording].type selects where media goes: local / http / s3 write a WAV (uploaded by [recording]); sipflow writes RTP into the [sipflow] backend (uploaded by [sipflow.upload].media). SIP signalling is captured whenever [sipflow] is configured. auto_start_at = "media" (default) installs the recorder after the first caller-media setup; use "answer" to wait for the final 200.

Option 1: Full SipFlow (RTP + SIP)

[recording]
enabled = true
type = "sipflow"
auto_start = true
auto_start_at = "media"

[sipflow]
type = "local"
root = "./config/sipflow"

[sipflow.upload]
type = "s3"
vendor = "aliyun"
bucket = "my-bucket"
region = "oss-cn-hangzhou"
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"
root = "recordings"
media = true
signaling = true

WAV is generated on-demand from stored RTP via GET /sipflow/media/{call_id} and uploaded by [sipflow.upload]. Signalling is uploaded as JSONL to the same target.

Option 2: WAV file + SipFlow signalling

[recording]
enabled = true
type = "local"   # or "http" / "s3"
auto_start = true
auto_start_at = "media"
path = "./config/recorders"

[sipflow]
type = "local"
root = "./config/sipflow"

[sipflow.upload]
signaling = true
media = false

Media stays on the [recording] path; SipFlow stores SIP only (no RTP).

Option 3: WAV only (no SIP ladder)

[recording]
enabled = true
type = "local"
auto_start = true
# No [sipflow] section β€” WAV only, no signalling capture

Outbound Dial (SSE)

POST {ami_path}/outbound/dial originates a call and streams every RWI event for it over one SSE connection. Full request/response contract and the [outbound] config: see Outbound Dial SSE API.