Appearance
pbx
6 endpoints.
GET /api/v1/pbx/call-flow
Call Flow
The full call-flow graph for one domain: inbound numbers, their live routing, and every destination they can point at.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
domain | query | no | string | pbx domain_name; defaults to the org's SIP domain |
200 — Successful Response
json
{
"type": "object",
"additionalProperties": true,
"title": "Response Call Flow Api V1 Pbx Call Flow Get"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/pbx/route
Route
Route an inbound number to a destination (POST /phone-numbers).
Idempotent and non-destructive: portal_pbx.route_number treats an already-correct route as done and REFUSES to re-point a number that's routed elsewhere (the portal can't delete the old route, so a re-point would stack a duplicate) — that refusal comes back as a 409 so the UI can tell the operator to clear the old route on the pbx first.
Request body
json
{
"properties": {
"domain": {
"type": "string",
"title": "Domain",
"description": "pbx domain_name"
},
"number": {
"type": "string",
"title": "Number",
"description": "the inbound DID"
},
"destination": {
"type": "string",
"title": "Destination",
"description": "target extension/ring-group/IVR number"
}
},
"type": "object",
"required": [
"domain",
"number",
"destination"
],
"title": "RouteIn"
}200 — Successful Response
json
{
"additionalProperties": true,
"type": "object",
"title": "Response Route Api V1 Pbx Route Post"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/pbx/extension-enabled
Extension Enabled
Enable/disable one extension (PUT /api/v1/extensions/{uuid} via the pbx platform key). Resolves the uuid domain-scoped first.
Request body
json
{
"properties": {
"domain": {
"type": "string",
"title": "Domain"
},
"extension": {
"type": "string",
"title": "Extension"
},
"enabled": {
"type": "boolean",
"title": "Enabled"
}
},
"type": "object",
"required": [
"domain",
"extension",
"enabled"
],
"title": "ExtToggleIn"
}200 — Successful Response
json
{
"additionalProperties": true,
"type": "object",
"title": "Response Extension Enabled Api V1 Pbx Extension Enabled Post"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/pbx/cdrs
Cdrs
Recent call-detail records for one domain (newest first, first page).
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
domain | query | no | string | pbx domain_name; defaults to the org's SIP domain |
days | query | no | integer |
200 — Successful Response
json
{
"type": "object",
"additionalProperties": true,
"title": "Response Cdrs Api V1 Pbx Cdrs Get"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/pbx/billing-metrics
Billing Metrics
The pbx platform's call-billing rollup — the same figures the pbx admin's own /billing-dashboard page shows (fusionpbx .get_billing_metrics), rendered in-app at /pbx/billing.
Superuser only, deliberately unscoped. Unlike every other route here this one takes no domain and applies no DomainScope: it is platform-wide wholesale data — sell revenue, gross profit, GP% and the supplier cost-anomaly/margin-alert queues. A reseller seeing its own scoped slice would still be seeing OUR markup on ITS traffic, so there is no "scoped" version of this to fall back to for a non-superuser; the admin dependency is just the app-access floor, the real gate is below. The menu row is gated to match (menu_seed .reconcile_pbx_billing_menu_min_role).
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
period | query | no | string | today|7d|month|30d|90d; server default month |
200 — Successful Response
json
{
"type": "object",
"additionalProperties": true,
"title": "Response Billing Metrics Api V1 Pbx Billing Metrics Get"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}POST /api/v1/pbx/assistant
Assistant
Natural-language PBX changes. Loads the domain config into the model, asks it for a reply + a list of actions, VALIDATES each action against the real config, executes the supported ones, and returns what happened.
Operator-only ("Einstein AI"). require_role("admin") is NOT a staff test on the portal: PORTAL_ROLES tier-alias service_user -> admin (app.api.deps._PORTAL_ROLE_TIER_ALIASES), so every ordinary portal customer passed it and could re-route their own numbers by asking the model. deny_customer_facing_non_staff narrows that to a platform superuser on a customer_facing site, and is a no-op on the internal sites.
Request body
json
{
"properties": {
"domain": {
"type": "string",
"title": "Domain",
"description": "pbx domain_name; defaults to the org's SIP domain",
"default": ""
},
"message": {
"type": "string",
"minLength": 1,
"title": "Message"
}
},
"type": "object",
"required": [
"message"
],
"title": "AssistantIn"
}200 — Successful Response
json
{
"additionalProperties": true,
"type": "object",
"title": "Response Assistant Api V1 Pbx Assistant Post"
}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}