API Reference
ScaiDial exposes three HTTP surfaces:
- Admin (
/v1/modules/scaidial/) — tenant-wide configuration and observability. Requires module permissions documented in permissions. - Portal (
/v1/modules/scaidial/me/) — end-user self-service. No special permission; per-row access is gated byExtensionGrant. - Internal (
/v1/modules/scaidial/sip/...) — called by livekit-sip with a shared secret. Not exposed to callers; documented for completeness in the integration guide.
All routes return the ScaiGrid envelope: {"data": ..., "status": "ok"} or {"error": {"code": ..., "message": ..., "details": ...}}.
Trunks#
GET /trunks#
List trunks for the caller's tenant.
POST /trunks#
Create a trunk. Body:
| Field | Type | Notes |
|---|---|---|
name |
string | Operator-visible label |
sip_server_address |
string | FQDN or IP of the carrier's SIP server |
sip_server_port |
int | Usually 5060 (UDP/TCP) or 5061 (TLS) |
sip_transport |
string | udp / tcp / tls |
sip_auth_mode |
string | credentials or ip |
sip_auth_username |
string | When mode=credentials |
sip_auth_password |
string | When mode=credentials |
sip_inbound_numbers |
string[] | E.164 numbers the carrier delivers to this trunk |
sip_media_encryption |
string | disabled / allowed / required |
caller_id_e164 |
string | Default outbound caller-ID |
Returns the created trunk with sync_status="pending". Poll until synced (or error).
PATCH /trunks/{id}#
Update a trunk. Same body as POST; all fields optional. Changes trigger a re-sync.
DELETE /trunks/{id}#
Delete a trunk. Removes the registration on livekit-sip. Fails if any DID references the trunk.
POST /trunks/{id}/resync#
Force a re-sync after an error. Useful when you've fixed credentials on the carrier side.
POST /trunks/probe#
Test a trunk's connectivity without persisting it. Body is the same shape as POST. Returns {"ok": bool, "detail": string}.
DIDs#
GET /dids#
List DIDs for the caller's tenant.
POST /dids#
Create a DID. Body:
| Field | Type | Notes |
|---|---|---|
e164 |
string | E.164 number |
trunk_id |
UUID | Which trunk delivers this number |
dialplan_id |
UUID | Which dialplan handles incoming calls |
enabled |
bool | Default true |
PATCH /dids/{id}#
Partial update. enabled is the toggle the UI flips inline.
DELETE /dids/{id}#
Delete a DID.
Extensions#
GET /extensions#
List extensions for the caller's tenant.
POST /extensions#
Create an extension. Body:
| Field | Type | Notes |
|---|---|---|
number |
string | Internal number (e.g. 1001) |
type |
string | wave / bot / voicemail / external / sip_endpoint / desk_queue |
target_ref |
string | For wave: user ID; for bot: bot ID; for voicemail: box ID; for external: E.164; for sip_endpoint: SIP URI |
display_name |
string | Operator-visible label |
ring_timeout_s |
int | Default 30; max 600 |
timeout_action |
string | What happens on no-answer: voicemail / ring_extension / hangup |
timeout_forward_to |
string | When timeout_action=ring_extension, the next extension's number |
outbound_caller_id_e164 |
string | Override the trunk's default |
PATCH /extensions/{id}#
Partial update.
DELETE /extensions/{id}#
Delete an extension. Fails if any dialplan rule references it.
GET /extensions/{id}/grants#
List grants (who has access to this extension).
POST /extensions/{id}/grants#
Create a grant.
| Field | Type | Notes |
|---|---|---|
user_id |
UUID | Either this or group_id |
group_id |
UUID | Either this or user_id |
role |
string | owner / manage / answer / observe |
DELETE /extensions/{id}/grants/{grant_id}#
Remove a grant.
Dialplans#
GET /dialplans#
List dialplans for the caller's tenant.
POST /dialplans#
Create a dialplan. Body: {"name": string}.
PATCH /dialplans/{id}#
Rename a dialplan.
DELETE /dialplans/{id}#
Delete. Fails if any DID references it.
GET /dialplans/{id}/rules#
List rules for a dialplan, ordered by priority.
POST /dialplans/{id}/rules#
Create a rule. Body:
| Field | Type | Notes |
|---|---|---|
priority |
int | Lower fires first; 0-999 |
match_type |
string | See dialplans |
match_params |
object | Per-type — see dialplans concepts |
action_type |
string | See dialplans |
action_params |
object | Per-type — see dialplans concepts |
enabled |
bool | Default true |
PATCH /dialplans/{id}/rules/{rule_id}#
Partial update.
DELETE /dialplans/{id}/rules/{rule_id}#
Delete a rule.
GET /dialplan-pickers#
One fetch, multiple lists — used by the visual builder. Returns {match_types, action_types, dids, extensions, bots, users, voicemail_boxes}.
Active calls#
GET /calls/active#
List calls in state ringing, active, or held. Each call includes its current legs.
GET /calls/{id}#
Single call detail.
GET /calls/history#
Paginated history of ended calls. Filters:
extension_id— calls where any leg touched this extensionfrom_e164— substring match on callerend_reason— exact matchstarted_after/started_before— ISO-8601limit— capped at 500cursor_started_at— keyset pagination
Returns {items: [...], next_cursor_started_at: string | null}.
Per-leg controls#
POST /legs/{id}/hangup— end the legPOST /legs/{id}/hold— mute the leg's audio trackPOST /legs/{id}/unhold— restorePOST /legs/{id}/transfer— blind transfer. Body:{to_extension, mode: "blind"}. 409 on attended (not yet implemented) or carrier-rejected REFER.
Voicemail#
GET /voicemail#
List voicemail messages. Query: extension_id (filter), only_unheard (bool), limit.
GET /voicemail/{id}/audio#
Stream the recording bytes as audio/wav. Browsers can play this directly via <audio src>.
POST /voicemail/{id}/listened#
Mark listened. Idempotent.
DELETE /voicemail/{id}#
Delete a voicemail.
Forward rules#
GET /extensions/{id}/forward-rules#
List forward rules on an extension.
POST /extensions/{id}/forward-rules#
Create a rule. Body:
| Field | Type | Notes |
|---|---|---|
condition |
string | always / busy / no_answer / unavailable |
forward_to |
string | E.164 or SIP URI |
priority |
int | Lower fires first |
enabled |
bool |
DELETE /extensions/{id}/forward-rules/{rule_id}#
Delete.
Tenant policy#
GET /policy#
Read the tenant's policy. Returns defaults if no row exists.
1 | |
PATCH /policy#
Update. Only fields explicitly sent are touched. Stamps updated_by_user_id automatically.
Network info#
GET /network-info#
The four-tuple a carrier needs for whitelisting:
1 2 3 4 5 6 | |
End-user portal (/me/...)#
Distinct surface for tenant users without admin permission. Per-row access is gated by ExtensionGrant: read operations need any grant role (owner / manage / answer / observe); edit operations need owner.
| Route | What |
|---|---|
GET /me/extensions |
My extensions, each with my role |
PATCH /me/extensions/{id} |
Edit personal config (DND, ring timeout, display name, voicemail greeting). Owner-only. |
GET /me/voicemail |
Voicemail across my owned/managed extensions |
GET /me/voicemail/{id}/audio |
Stream a voicemail |
POST /me/voicemail/{id}/listened |
Mark listened |
DELETE /me/voicemail/{id} |
Delete a voicemail |
GET /me/extensions/{id}/forward-rules |
List my forward rules (owner-only) |
POST /me/extensions/{id}/forward-rules |
Add a forward rule (owner-only) |
DELETE /me/extensions/{id}/forward-rules/{rule_id} |
Delete a forward rule |
GET /me/calls/history |
Calls I placed or that touched any of my extensions |
POST /me/click-to-call |
Originate a call. Body {to, from_extension_id}. Returns {call_id, leg_id, livekit_url, livekit_token, room_name} — the connection bundle for the browser softphone. |