StratusTalk Avion

API documentation

Versioned JSON API for the Avion control plane. The GUI uses same-origin session auth; MCP and external agents will use Bearer tokens (Sanctum personal access tokens) once issued for superadmins.

Base URL & versioning

All endpoints are under /api/v1. Current host: https://avion.stratustalk.com.

GET https://avion.stratustalk.com/api/v1/...

Authentication

Session cookie (Avion GUI — available now)

Log in to Avion in the browser, then call the API with credentials: 'same-origin' so the session cookie is sent. Same-origin only; not for cross-site agents.

Bearer tokens (MCP / external agents — coming soon)

Planned: Laravel Sanctum personal access tokens issued to superadmins. Send Authorization: Bearer <token>. Token issuance UI/API is not shipped in this release; use session auth for GUI integrations today.

Required headers

HeaderValue
Accept application/json (required)
X-XSRF-TOKEN / CSRF Required for cookie-auth mutations later; not needed for GET reads
Authorization Bearer … when token auth is available

Rate limits

60 requests per minute per authenticated user (or IP if unauthenticated).

Hostname rules

Path hostnames must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. Shell metacharacters are rejected (422).

Error envelope

Responses use a consistent envelope:

{
  "data": { ... } | null,
  "meta": { ... },
  "error": { "message": "...", "code": "..." } | null
}
StatusMeaning
401Not authenticated
403Authenticated but not allowed for this PBX
404PBX not found (or not visible)
406Missing Accept: application/json
422Invalid hostname
503Live collectors failed (partial DB data may still be in data)

Authorization scopes

Non-superadmins do not receive secret fields on PBX or provision records.

Example: GET PBX details

GET /api/v1/pbx/{hostname}
Accept: application/json

Example (browser / same-origin):

fetch('/api/v1/pbx/customer1.example', {
  credentials: 'same-origin',
  headers: { 'Accept': 'application/json' }
}).then(r => r.json())

data shape:

{
  "pbx": { "name", "hostname", "privip", "pop", "has_secret", "secret?", "contactCompany", "contactName", "contactEmail", "contactPhone", "tz" },
  "seats": [ { "ext", "name", "dssgroup", "paginggroup", "directory", "reception", "dss", "sortorder" } ],
  "peers": [ { "peer", "ip", "status" } ],
  "numbers": [ { "did", "forward", "pbx", "state", "comments", "gateway" } ],
  "provision": [ { "mac", "displayname", "sipname", "model", "..." } ],
  "softphones": [ { "meid", "number", "sip_callerid", "provision_login_username", "model" } ],
  "perf": { "uptime", "pod_uptime?", "pod_start_time?", "pod_status?", "alerts", "ifconfig", "routes" },
  "collectors": { "perf": "ok|error", "peers": "ok|error", "k8s": "ok|error|missing|skipped", "messages": [] }
}
Live collectors SSH to the PBX (getPBX.php / peers on port 22022). For pop=K8S, pod_uptime comes from stcontroller STATUS (container startedAt). DB sections still return when collectors partially fail. has_secret is always present; raw secret remains superadmin-only.

Example: Activate / cycle hosted PBX trunk

POST /api/v1/pbx/{hostname}/activate-trunk
Accept: application/json

Reseller+. K8S only. Generates a SIP secret, stores it on pbx.secret, and pushes it into FreePBX trunk id=1 over SSH (parity with dashboard activateTrunk.php). Border1 picks up the peer on the next SipAuto regen (~5 minutes).

{
  "hostname": "customer1",
  "activated": true,
  "has_secret": true,
  "message": "Trunk activated successfully. Allow up to 5 minutes before routes are available.",
  "secret?": "…superadmin only…"
}

Example: PUT PBX details

PUT /api/v1/pbx/{hostname}
Accept: application/json
Content-Type: application/json

Updates contact fields + timezone on pbx, and upserts/deletes seats on extensions (parity with putPBX.php). Soft-removed seats send removed: true (delete by originalExt or ext). Renames delete the old ext then upsert the new one.

// PUT body
{
  "contactCompany": "...",
  "contactName": "...",
  "contactEmail": "...",
  "contactPhone": "...",
  "tz": "America/Chicago",
  "seats": [
    {
      "ext": "101",
      "originalExt": "101",
      "name": "Front Desk",
      "dssgroup": "",
      "paginggroup": "",
      "directory": true,
      "reception": false,
      "dss": false,
      "removed": false
    }
  ]
}

Response data: refreshed pbx contact/tz fields and seats list.

Example: GET / PUT / POST / DELETE number

GET    /api/v1/numbers/{did}
PUT    /api/v1/numbers/{did}
POST   /api/v1/numbers
POST   /api/v1/numbers/{did}/release
DELETE /api/v1/numbers/{did}
Accept: application/json
Content-Type: application/json

GET and PUT return general number fields plus pbx_options (Target list: hosted PBX hostnames and SIP trunk usernames the actor may assign). PUT updates general fields; ingress gateway is set automatically (border1/CLUSTER for K8S PBXs and trunks, ast01/OLD otherwise). Rate decks are preserved. POST creates a DID (technician+). Release clears routing fields and sets pbx=AVAILABLE. DELETE removes the row.

// PUT body
{
  "forward": "...",
  "present": "...",
  "sms": "...",
  "pbx": "customer1",
  "comments": "...",
  "state": "ACTIVE"
}

// POST body (create)
{
  "did": "13125551212",
  "forward": "",
  "present": "",
  "sms": "",
  "pbx": "AVAILABLE",
  "comments": ""
}

Example: DNS records (ns1 zone update)

GET /api/v1/dns/records
Authorization: Bearer <WIREGUARD_SYNC_TOKEN>

Machine pull for ns1.stratustalk.com. Same shared secret as WireGuard peer sync (Authorization: Bearer or X-Avion-Token). Replaces dashboard getDNS.php and keeps that response shape. Each item is a PBX hostname and its privip. Rows with an empty or non-IP address (including PENDING) are omitted. Apply the list only when status is OK — an error returns data: [] and must not be treated as "delete every name."

{
  "status": "OK",
  "message": "DNS Info",
  "data": [
    { "hostname": "customer1", "privip": "10.200.1.20" }
  ]
}

Example: SMS destination lookup (sms webhook)

GET /api/v1/sms/destinations/{did}
Authorization: Bearer <WIREGUARD_SYNC_TOKEN>
Accept: application/json

Machine pull for the sms app. Same shared secret as WireGuard peer sync. Replaces dashboard getNumber.php. Returns only the destination configured on the DID (email address or webhook URL). Unknown DIDs return 404.

{
  "data": { "did": "13125551212", "sms": "alerts@example.com" },
  "meta": {},
  "error": null
}

Example: SMS alert email (sms webhook)

POST /api/v1/sms/alerts
Authorization: Bearer <WIREGUARD_SYNC_TOKEN>
Accept: application/json
Content-Type: application/json

{
  "to": "alerts@example.com",
  "from_number": "+15551212",
  "to_number": "13125551212",
  "text": "hello",
  "media": ""
}

Sends the inbound SMS alert through Avion's Settings → SMTP relay (Amazon SES). The From address is the relay's configured from address. Returns 503 when SMTP is not enabled.

← Back to login