API Reference
Authentication
| Method |
Path |
Auth |
Description |
POST |
/api/auth/login |
– |
Login: {"username":"admin","password":"admin"} → access_token + refresh_token |
POST |
/api/auth/refresh |
– |
Refresh access token using refresh_token |
All endpoints except /api/route, /api/health/*, /api/monitoring/* require Authorization: Bearer <jwt>.
Roles: admin (full access), viewer (read-only nodes/metrics).
Core
| Method |
Path |
Auth |
Description |
GET |
/api/route |
– |
Route a call. Query: caller_id, dest, ingress_trunk |
GET |
/api/health |
– |
Stats: total nodes, healthy, requests, uptime |
GET |
/api/health/live |
– |
Liveness probe (always 200) |
GET |
/api/health/ready |
– |
Readiness probe (NATS + metrics) |
Route Response (success)
{ "node_id": "pbx-03", "score": 88, "sip_gateway": "sip:pbx03.lan:5060" }
Route Response (fallback — all nodes unhealthy)
{
"fallback": true,
"sip_gateway": "sip:operator.lan:5060",
"reason": "all_nodes_unhealthy",
"nodes": [...]
}
Route Response (no nodes registered)
{ "error": "no_nodes_registered" }
Nodes
| Method |
Path |
Role |
Description |
GET |
/api/nodes |
admin/viewer |
List all nodes with scores and state |
GET |
/api/nodes/{id}/metrics |
admin/viewer |
Metrics history (ring buffer, ~30 min) |
PUT |
/api/nodes/{id}/toggle |
admin |
{"disabled":true, "reason":"..."} |
Trunks
| Method |
Path |
Role |
Description |
GET |
/api/trunks |
admin |
All trunks. Query: type=ingress|balance|fallback |
POST |
/api/trunks |
admin |
Create trunk |
PUT |
/api/trunks/{id} |
admin |
Update trunk |
DELETE |
/api/trunks/{id} |
admin |
Delete trunk |
Trunk model:
{
"id": "trk-001",
"name": "pbx-03 balance",
"type": "ingress|balance|fallback",
"node_id": "pbx-03",
"gateway": "sip:pbx03.lan:5060",
"codecs": ["PCMU", "PCMA"],
"context": "default",
"enabled": true,
"description": "",
"created_at": "...",
"updated_at": "..."
}
Trunk types:
| Type |
Purpose |
Quantity |
ingress |
Incoming trunk (where calls come from) |
1..N |
balance |
Destination trunk (bound to a Node) |
1..N |
fallback |
Operator fallback (when all balance score < 0) |
exactly 1 |
Users (admin only)
| Method |
Path |
Description |
GET |
/api/users |
List users |
POST |
/api/users |
Create user |
PUT |
/api/users/{id} |
Update user |
DELETE |
/api/users/{id} |
Delete user |
Note: user-01 (primary admin) cannot be deleted or demoted to viewer.
Monitoring (API-key auth)
Use X-API-Key header (not JWT).
| Method |
Path |
Description |
GET |
/api/monitoring/zabbix |
JSON for Zabbix LLD + items |
GET |
/api/monitoring/prometheus |
text/plain metrics for Prometheus |
WebSocket
| Method |
Path |
Description |
WS |
/ws/metrics |
Real-time metrics stream for frontend (JWT in query ?token=) |
Examples
# Route (no auth)
curl 'http://localhost:8080/api/route?caller_id=74951234567&dest=123&ingress_trunk=trk-001'
# → {"node_id":"pbx-03","score":88,"sip_gateway":"sip:pbx03.lan:5060"}
# Login
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' | jq -r '.access_token')
# Nodes with JWT
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/nodes
# Toggle node (admin only)
curl -X PUT http://localhost:8080/api/nodes/pbx-01/toggle \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"disabled":true,"reason":"maintenance"}'
# Prometheus metrics (API-key)
curl -H "X-API-Key: $(jq -r '.monitoring_api_key' data/config.json)" \
http://localhost:8080/api/monitoring/prometheus