Skip to content

Extensions

Manage SIP extensions on the PBX: list, create, update, delete, fetch available numbers, and rotate the SIP password.

GET /api/pbx/extensions — List

Query parameters

FieldTypeRequiredDescriptionValid values
searchstringoptionalMatch against extension_number or display_name (LIKE)
statusstringoptionalFilter by admin statusactive, disabled
pageintegeroptionalPage number≥ 1 (default 1)
per_pageintegeroptionalRecords per page1-200 (default 50)

Response fields (each item in data[])

FieldTypeDescriptionValues
extension_numberstringExtension number — SIP register name3-10 digits
display_namestring|nullDisplay name (caller ID name for internal calls)max 100 chars
voicemail_enabledboolWhether voicemail is enabledtrue / false
outbound_caller_idstring|nullDefault outbound caller IDMust exist in /caller-ids
recording_modestringRecording modenone, inbound, outbound, all
statusstringAdmin configuration statusactive, disabled
team_idinteger|nullTeam ID
department_idinteger|nullDepartment ID
max_concurrentintegerMaximum concurrent calls1-10
tls_enabledboolWhether SIPS/TLS is enabledtrue / false
created_atdatetimeCreation timestamp (ISO 8601 + offset)
updated_atdatetimeLast update timestamp

Response Body (JSON) — 200

json
{
  "current_page": 1,
  "data": [
    {
      "extension_number": "1001",
      "display_name": "Mac I5",
      "voicemail_enabled": true,
      "outbound_caller_id": null,
      "recording_mode": "all",
      "status": "active",
      "team_id": 2,
      "department_id": null,
      "max_concurrent": 2,
      "tls_enabled": false,
      "created_at": "2026-04-06T12:19:13+00:00",
      "updated_at": "2026-06-18T11:01:47+00:00"
    }
  ],
  "last_page": 2,
  "per_page": 10,
  "total": 17
}

GET /api/pbx/extensions/{ext} — Detail

Path parameter

FieldTypeDescription
extstringextension_number (NOT id)

Response = the 12 fields from the list + one extra realtime field sip_registration.

Response Body (JSON) — 200

json
{
  "data": {
    "extension_number": "1001",
    "display_name": "Mac I5",
    "voicemail_enabled": true,
    "outbound_caller_id": null,
    "recording_mode": "all",
    "status": "active",
    "team_id": 2,
    "department_id": null,
    "max_concurrent": 2,
    "tls_enabled": false,
    "created_at": "2026-04-06T12:19:13+00:00",
    "updated_at": "2026-06-18T11:01:47+00:00",
    "sip_registration": "registered"
  }
}
FieldTypeDescriptionValid values
statusstringAdmin configurationactive, disabled
sip_registrationstringRealtime SIP registration status — the CRM uses this to know whether the softphone is onlineregistered, unregistered, unknown

POST /api/pbx/extensions — Create

Request body fields

FieldTypeRequiredDescriptionValid values
extension_numberstringExtension number — unique per accountRegex ^[0-9]{3,10}$ (3-10 digits)
display_namestringoptionalDisplay namemax 100 chars
team_idintegeroptionalTeam IDMust exist in teams
sip_passwordstringoptionalSIP register password — if omitted, a 24-character alphanumeric password is auto-generatedmin 8, max 100 chars
voicemail_enabledbooloptionalEnable voicemailtrue / false (default false)
voicemail_pinstringoptionalVoicemail PINmax 20 chars
recording_modestringoptionalRecording modenone / inbound / outbound / all (default none)
max_concurrentintegeroptionalMaximum concurrent calls1-10 (default 2)
outbound_caller_idstringoptionalDefault outbound caller IDMust exist in /api/pbx/caller-ids (active)

Request Body (JSON)

json
{
  "extension_number": "9001",
  "display_name": "Sales 01",
  "voicemail_enabled": true,
  "recording_mode": "all",
  "max_concurrent": 2,
  "outbound_caller_id": "0900000020"
}

Response body fields (12 fields from list + 2 extra fields on creation)

FieldTypeDescription
sip_passwordstringSIP password — returned only once, copy into your softphone immediately
generated_passwordbooltrue if Zorio auto-generated, false if the client supplied it

Response Body (JSON) — 201

json
{
  "data": {
    "extension_number": "9001",
    "display_name": "Sales 01",
    "voicemail_enabled": true,
    "recording_mode": "all",
    "status": "active",
    "max_concurrent": 2,
    "outbound_caller_id": "0900000020",
    "team_id": null,
    "department_id": null,
    "tls_enabled": false,
    "created_at": "2026-06-29T04:53:59+00:00",
    "updated_at": "2026-06-29T04:53:59+00:00",
    "sip_password": "<sip_password_shown_only_once>",
    "generated_password": true
  }
}

PUT /api/pbx/extensions/{ext} — Update

Request body fields (every field optional — send only what you want to change)

FieldTypeDescriptionValid values
display_namestring|nullDisplay namemax 100 chars
team_idinteger|nullTeam ID
voicemail_enabledboolEnable voicemailtrue / false
voicemail_pinstring|nullVoicemail PINmax 20
recording_modestringRecording modenone / inbound / outbound / all
max_concurrentintegerMaximum concurrent calls1-10
outbound_caller_idstring|nullOutbound caller IDMust exist in /api/pbx/caller-ids (active)
statusstringConfiguration statusactive / disabled

Note

extension_number and sip_password cannot be changed through PUT. To rotate sip_password, use the /change-password endpoint.

Request Body (JSON)

json
{
  "display_name": "Sales 01 (renamed)",
  "voicemail_enabled": false,
  "max_concurrent": 4,
  "recording_mode": "all",
  "outbound_caller_id": "0900000020"
}

Response Body (JSON) — 200

json
{
  "data": {
    "extension_number": "1001",
    "display_name": "Sales 01 (renamed)",
    "voicemail_enabled": false,
    "outbound_caller_id": "0900000020",
    "recording_mode": "all",
    "status": "active",
    "team_id": null,
    "department_id": null,
    "max_concurrent": 4,
    "tls_enabled": false,
    "created_at": "2026-04-06T12:19:13+00:00",
    "updated_at": "2026-06-29T07:55:12+00:00"
  }
}

DELETE /api/pbx/extensions/{ext} — Delete

No request body.

Response Body (JSON) — 200

json
{
  "data": { "deleted": true }
}

Soft delete + flush FS directory cache.

GET /api/pbx/caller-ids — List available numbers

Returns the outbound caller IDs with status=active for the customer — use this when picking outbound_caller_id for an extension or caller_id for a click-to-call.

Query parameters

FieldTypeDescriptionValues
searchstringMatch number or display_name
pageintegerPage number≥ 1
per_pageintegerRecords per page1-200 (default 50)

Response body fields

FieldTypeDescriptionValues
numberstringOutbound number (E.164 or Vietnam domestic)8-15 digits
display_namestring|nullInternal display name
carrierstringTelcoviettel / mobifone / vinaphone / vietnamobile / landline / other
cli_typestringNumber typemobile / fixed_02x / hotline_1900 / hotline_1800 / brandname
statusstringStatusactive (always, since the query filters this)

Response Body (JSON) — 200

json
{
  "current_page": 1,
  "data": [
    { "number": "0900000020", "display_name": "POC", "carrier": "landline", "cli_type": "mobile", "status": "active" },
    { "number": "0900000021", "display_name": "POC", "carrier": "landline", "cli_type": "mobile", "status": "active" }
  ],
  "last_page": 1, "per_page": 50, "total": 7
}

When you POST/PUT an extension with an outbound_caller_id that is not in the list → HTTP 422:

json
{ "errors": { "outbound_caller_id": ["The selected outbound caller id is invalid."] } }

POST /api/pbx/extensions/{ext}/change-password — Rotate SIP password

Request body fields

FieldTypeRequiredDescriptionValues
new_passwordstringoptionalNew SIP password — if omitted, a 24-character alphanumeric password is auto-generatedmin 8, max 100

Request Body (JSON) — auto-generate (empty body)

json
{}

Request Body (JSON) — set password manually

json
{
  "new_password": "MyNewStrongPwd123"
}

Response body fields

FieldTypeDescription
extension_numberstringExtension number that was updated
sip_passwordstringNew password — returned only once
generated_passwordbooltrue if Zorio auto-generated, false if the client supplied it

Response Body (JSON) — 200

json
{
  "data": {
    "extension_number": "1001",
    "sip_password": "AbC123...",
    "generated_password": true
  }
}

Cấp phép theo điều khoản sử dụng của Zorio.