# 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 `. 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) ```json { "node_id": "pbx-03", "score": 88, "sip_gateway": "sip:pbx03.lan:5060" } ``` ### Route Response (fallback — all nodes unhealthy) ```json { "fallback": true, "sip_gateway": "sip:operator.lan:5060", "reason": "all_nodes_unhealthy", "nodes": [...] } ``` ### Route Response (no nodes registered) ```json { "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: ```json { "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 ```bash # 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 ```