OpenWeb PBXDeveloper Centre
Open source. Ready to connect.
Tenant API v1 · Hosted PBX provisioning

A new tenant.
A working place to call.

Register an organisation, verify its administrator and create an isolated PBX with its own managed openwebpbx.com address.

Build onboarding into your own service, reseller portal or internal tools. Each tenant keeps its own PBX configuration while using the shared installation’s current application files.

Hosted API base address
https://call.openweb.co.za/api/v1

One shared server, separate PBX services. The API creates a tenant’s PBX inside the hosted installation. It does not create a separate virtual machine, allocate a carrier number or activate a paid provider connection.

Debian 1.0.25 · Administration API v2

Build your own PBX service.

Compatibility: administration API v2 is live on hosted Debian 1.0.25. Public complete Debian and Windows downloads remain 1.0.23 and do not acquire these new endpoints automatically. Use API v1 for registration and onboarding.

Create an integration key in PBX → API Keys or Tenant Admin → API Keys. Choose its tenant, optional individual PBX, permissions and expiry. The full key appears once; revoke or replace it without changing phone passwords or QR codes.

Administration API · installation hostname
curl --fail-with-body \
  -H "Authorization: Bearer $OPENWEBPBX_API_KEY" \
  https://call.openweb.co.za/api/v2/services

API v2 covers users, SIP trunks, the single DID routing editor, office hours, ring groups, queues, call control, reports, voicemail and recordings, greetings, Call Me Back, hotel services, conferences, staff roles, phone recovery, provisioning, templates, resource packages and encrypted backups. Each key is checked against current ownership, permissions and tenant suspension on every request.

FeatureStart here
PBX configuration and routing/pbx/{domain_uuid}/users, /trunks, /dids and /actions/did-editor
Live calls and calling features/pbx/{domain_uuid}/actions/live and named actions in the OpenAPI document
Templates and reseller packages/templates, /services, /plans and /plans/assign
Tenant backup and restore/tenants/{tenant_uuid}/backups, then verify, download or confirmed restore
Full server recovery/instance/backups — instance administrator permission required
API key management/keys — delegation stays within the calling key’s boundary and permissions

Use Authorization: Bearer over HTTPS from your server. Keys allow 120 requests per minute and JSON up to 128 KiB. Separate binary backup import endpoints stream encrypted archives up to 2 GiB with an independently supplied backup password. Credentials and QR setup require explicit credential access; normal configuration reads remove passwords and server paths.

Hosted resources are controlled by the operator. Resellers define extension and concurrent call allowances, quote extra capacity and approve it after their billing process. This release provides resource orders and manual billing approval; it does not collect payments automatically. Self-hosted installations do not incur hosted resource charges.

API v2 requires Debian 1.0.25. API v1 registration and managed domain onboarding remain available separately. Windows support is qualified and released independently; a version number alone does not imply Windows feature parity.

Four steps

From registration to your tenant portal

01 / REGISTER

Create the account

Send company details and an administrator email.

02 / VERIFY

Confirm the email

Redeem the one-use code and save your API token.

03 / CREATE

Choose a PBX template

Create a service with a unique request key.

04 / CONNECT

Wait for the address

Poll readiness, then open the returned tenant URL.

1. Request a tenant account

Present the terms and privacy policy in your own onboarding flow before accepting registration. Choose a unique password of 12–72 UTF-8 bytes, including a letter and a number, and a short company label of 3–40 lower-case letters, numbers or hyphens.

Register · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/registrations' \
  --header 'Content-Type: application/json' \
  --data '{
    "tenant_name": "Example Company",
    "slug": "example-company",
    "email": "owner@example.com",
    "password": "replace-with-a-long-unique-password-42",
    "accept_terms": true
  }'

A valid request returns 202 Accepted with status: "verification_required". This generic response protects existing accounts; it does not reveal whether an email or label already exists and does not prove inbox delivery.

2. Redeem the email code

The global outgoing-mail service sends a code valid for 30 minutes. Send it with the same password you chose at registration in a POST body. Opening an email or following a link alone never activates a tenant.

Verify · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/registrations/verify' \
  --header 'Content-Type: application/json' \
  --data '{"token":"VERIFICATION_CODE_FROM_YOUR_EMAIL","password":"YOUR_ORIGINAL_REGISTRATION_PASSWORD"}'

Successful verification returns 201 Created with the tenant, a new access_token, its expiry and token metadata. Store the token securely in your integration’s secret store. Subsequent read endpoints never reveal it.

3. Choose a template and create a PBX

Read GET /templates, then use a returned template UUID. Send a new UUID in Idempotency-Key. Keep that key for retries of the same intended operation.

Create a PBX · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/services' \
  --header "Authorization: Bearer $OPENWEB_TOKEN" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: 9b484c7d-4e6f-48b2-b9fd-c55260d8b563' \
  --data '{
    "template_uuid": "TEMPLATE_UUID_FROM_THE_LIST",
    "service_name": "Main Office",
    "slug": "main-office",
    "subdomain": "example-company"
  }'

The response contains a new service and its subdomain status. Creation uses the current installed application files and the selected template’s current revision. Provider secrets are not exposed. Only shared published starter templates explicitly approved by the instance administrator are eligible, with no trunks, outbound rules or additional settings. If the list is empty, ask the instance administrator to publish an approved starter template. Configure your own provider after creating the PBX.

4. Poll the address until ready

Check tenant address · cURL
curl 'https://call.openweb.co.za/api/v1/services/SERVICE_UUID/subdomain' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"

Wait for ready: true, then use the returned url and admin_url. The address guide explains pending DNS, certificates and collision handling. Set up a provider and test incoming and outgoing calls before moving live numbers.

Authentication that stays with the tenant

Already verified? Send POST /auth/token with your owner email and password. Protected operations use Authorization: Bearer TOKEN. Browser sign-in cookies and phone passwords do not authenticate this API.

Authenticated request · cURL
curl 'https://call.openweb.co.za/api/v1/me' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"

Tokens start with owta_, followed by 64 hexadecimal characters. They expire after at most 90 days, with a default of 90 days. The server stores hashes, not usable token secrets. Each request checks that the owner and tenant remain active.

To change your account password, use POST /me/password with current_password and new_password. This revokes every previous owner API key, including the key making the request, and returns a replacement. Save that replacement before your next call. SIP, extension and phone-app passwords are unchanged.

Create a separate named token for each integration with POST /tokens, rotate it when needed and revoke it when no longer used. A tenant may have up to 10 active tokens. Rotation invalidates the old secret. A token-creation or rotation response is not saved for later secret recovery; if a response is lost, sign in again and review/revoke unused tokens.

A managed address for each PBX

Pass a single label such as example-company when creating the PBX, or use POST /services/{service_uuid}/subdomain for an existing owned service. Labels have 3–40 lower-case letters, numbers or hyphens and must start and end with a letter or number. Reserved names are refused. Omit subdomain during service creation to use the tenant slug.

GET /subdomains/availability?label=example-company is a local, advisory check. Its response has scope: "this_instance" and reservation_confirmed: false. It does not reserve global ownership or make a DNS request.

The central custodian confirms ownership and may assign a stable suffix for a label held elsewhere. Always use the final assigned hostname from status, rather than constructing it from your requested label. Once an address is queued or leased, it cannot be silently renamed. The initial custodian limit is 50 names per instance, including existing reserved names.

Address pending · example response
{
  "subdomain": {
    "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
    "requested_label": "example-company",
    "requested_hostname": "example-company.openwebpbx.com",
    "hostname": null,
    "url": null,
    "admin_url": null,
    "state": "pending_dns",
    "ready": false,
    "next_attempt": "2026-10-28T08:06:00+00:00",
    "error_category": ""
  }
}

url and admin_url remain null until both the certificate and local transport are verified ready. Registration runs asynchronously through the signed domain worker. Your integration never receives DNS credentials, instance signing keys or a wildcard certificate key. Existing SIP realms and desk-phone credentials are preserved.

After readiness, users can use the tenant portal and administrators its /admin address. Phone setup remains under the tenant’s normal Users workflow, with QR codes or app passwords. API tokens are not Android connection credentials.

Self-installed servers and Webhotel.cloud images

Your own server. One simple setup.

Installing OpenWeb PBX on your own server? Open the setup wizard and use the code displayed by the installed server. Confirm your account email, choose a business address and keep the server running while its secure connection is prepared. Your PBX, database, calls and recordings stay on your own installation.

Debian 1.0.23 includes guided registration, the first-boot consumer and mandatory registration for fresh installations. See the Debian installation guide and the qualified release scope. Windows Server 1.0.23 uses the same guided registration flow on Windows Server 2025 x64 with Desktop Experience. Signed Windows 1.0.23 upgrades require installed Windows 1.0.22 or newer; see its qualification record for actual component and reused-recovery scope. Follow the Windows installation guide for local setup and registration.

Public pairing checks cover real custodian DNS, Let’s Encrypt certificates, signed authorization and verified tenant HTTPS readiness. The release report records exact installer, upgrade, restore and provider-image acceptance. Test verification mail was intercepted privately; these checks do not establish new inbox delivery, physical handset or carrier acceptance.

Two integration paths

  • Browser or Webhotel image: use the dedicated /api/v1/wizard API and its 20-operation specification. The image receives a private device credential; the owner’s password and bearer token stay out of the guest. No custom hostname or certificate is needed before pairing.
  • Installed administrator wizard: sign in to the local PBX, confirm the owner email and use the seven installation operations below. The worker proves its P256 identity on its trusted canonical hostname and verifies the authority’s pinned RSA receipt.

Remote authorization creates no hosted /services row. A browser receives a connection link only after the actual tenant HTTPS proof and custodian ownership match. Always use the final assigned tenant hostname, including a collision suffix if one is assigned. Existing desk-phone domains, credentials and working connections are preserved.

Provider checkout and cancellation

Hosting integrations check global availability with POST /wizard/subdomains/availability, then reserve the chosen name for a verified owner before taking payment or creating a server. The reservation is bound to the installed server during claim. A separate encrypted cleanup credential stays in the provider backend and never enters the guest or browser.

To remove an instance, the provider first confirms its own virtual machine is stopped, then requests exact scoped cleanup through POST /wizard/servers/revoke. Only a fresh confirmed response permits address release. Conflicting ownership or uncertain DNS cleanup keeps those resources reserved for recovery. See the wizard specification for exact fields and retry rules.

Installation authorization sequence

  1. Register/verify or sign in to your existing owner account. An invited owner confirms their current email with /onboarding/email/request and /onboarding/email/verify.
  2. Request /installations/challenge with canonical server hostname, public P256 key, local tenant/domain UUIDs and the chosen label.
  3. The installed worker publishes the returned nonce proof and signs the exact challenge message. Submit POST /installations; verify the signed receipt against the independently pinned public key before storing it.
  4. Let the existing tenant-domain worker prepare DNS and trusted HTTPS. Authorization alone does not mean the tenant address is ready.

For exact signature bytes, provider-image preparation and local helper contracts, read the downloadable Webhotel and first-boot contract. The central API never receives a private instance key or exposes DNS provider credentials.

Endpoint reference

Expand an operation for its request, response and example. The OpenAPI file includes the machine-readable schemas. These examples use fictional data and never run API calls from this page.

GET/healthPublic

Check the API and registration status

Read safe public API/version metadata. No account details or credentials are returned. A disabled or uninstalled API returns 503.

Example request · cURL
curl 'https://call.openweb.co.za/api/v1/health'
HTTP 200 · example response
{
  "api": "OpenWeb PBX Tenant API",
  "version": "1",
  "software_version": "1.0.21",
  "registration_enabled": true,
  "documentation": "https://openwebpbx.com/api-docs"
}

Other responses: 403, 405, 503. See errors and retry guidance.

POST/registrationsPublic

Request a tenant account

Valid requests return a generic response without confirming whether an email or slug is already registered. No PBX, active owner or API token is created before email verification. The email code expires after 30 minutes. Public signup is enabled according to instance policy.

Body fieldTypePresenceRules
tenant_namestringRequiredMaximum 120 UTF-8 bytes. Minimum 1 characters. Maximum 120 characters.
slugstringRequired3–40 letters, numbers or hyphens; starts and ends with a letter or number. Reserved labels are refused. Minimum 3 characters. Maximum 40 characters.
emailemailRequiredMaximum 254 characters.
passwordpasswordRequired12–72 UTF-8 bytes, including a letter and a number. Never logged or returned.
accept_termsbooleanRequiredYour client must present the project terms and privacy policy. Must be true.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/registrations' \
  --header 'Content-Type: application/json' \
  --data '{
  "tenant_name": "Example Company",
  "slug": "example-company",
  "email": "owner@example.com",
  "password": "replace-with-a-long-unique-password-42",
  "accept_terms": true
}'
HTTP 202 · example response
{
  "status": "verification_required",
  "message": "If these details can be registered, a verification code will be emailed. Check your inbox and spam folder."
}

Other responses: 400, 403, 405, 413, 415, 429, 503. See errors and retry guidance.

POST/registrations/verifyPublic

Verify email and activate the tenant

Redeem the single-use email code together with the original registration password. Returns the active tenant and a new bearer token once. Use POST; opening a link or GET request never activates an account.

Body fieldTypePresenceRules
tokenstringRequiredSingle-use 30-minute verification code received by email.
passwordpasswordRequiredThe same password supplied when registering. Binds verification to your original registration.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/registrations/verify' \
  --header 'Content-Type: application/json' \
  --data '{
  "token": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "password": "original-registration-password-42"
}'
HTTP 201 · example response
{
  "tenant": {
    "tenant_uuid": "d8da1fce-51ac-4e1a-a53a-91a4971e0e3c",
    "tenant_name": "Example Company",
    "slug": "example-company",
    "email": "owner@example.com",
    "service_limit": 1,
    "service_count": 0
  },
  "access_token": "owta_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "token_type": "Bearer",
  "expires_at": "2027-01-26T08:00:00+00:00",
  "token": {
    "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
    "name": "Tenant API",
    "created_at": "2026-10-28T08:00:00+00:00",
    "expires_at": "2027-01-26T08:00:00+00:00",
    "revoked_at": null,
    "last_used_at": null
  }
}

Other responses: 400, 401, 403, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

POST/auth/tokenPublic

Sign in as a verified tenant owner

A verified, enabled owner can obtain a fresh tenant-scoped token. Invalid credentials are reported generically. At most ten active tokens are allowed.

Body fieldTypePresenceRules
emailemailRequiredMaximum 254 characters.
passwordpasswordRequired
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/auth/token' \
  --header 'Content-Type: application/json' \
  --data '{
  "email": "owner@example.com",
  "password": "YOUR_ACCOUNT_PASSWORD"
}'
HTTP 200 · example response
{
  "tenant": {
    "tenant_uuid": "d8da1fce-51ac-4e1a-a53a-91a4971e0e3c",
    "tenant_name": "Example Company",
    "slug": "example-company",
    "email": "owner@example.com",
    "service_limit": 1,
    "service_count": 0
  },
  "access_token": "owta_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "token_type": "Bearer",
  "expires_at": "2027-01-26T08:00:00+00:00",
  "token": {
    "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
    "name": "Tenant API",
    "created_at": "2026-10-28T08:00:00+00:00",
    "expires_at": "2027-01-26T08:00:00+00:00",
    "revoked_at": null,
    "last_used_at": null
  }
}

Other responses: 400, 401, 403, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

GET/meBearer token

Read your tenant and token

Returns the current authenticated tenant and current token metadata. Token secrets are never returned by read endpoints.

Example request · cURL
curl 'https://call.openweb.co.za/api/v1/me' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "tenant": {
    "tenant_uuid": "d8da1fce-51ac-4e1a-a53a-91a4971e0e3c",
    "tenant_name": "Example Company",
    "slug": "example-company",
    "email": "owner@example.com",
    "service_limit": 1,
    "service_count": 0
  },
  "token": {
    "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
    "name": "Tenant API",
    "created_at": "2026-10-28T08:00:00+00:00",
    "expires_at": "2027-01-26T08:00:00+00:00",
    "revoked_at": null,
    "last_used_at": null
  }
}

Other responses: 401, 403, 405, 429, 503. See errors and retry guidance.

POST/me/passwordBearer token

Change your account password

Verify the current account password and choose a new 12–72-byte password with a letter and a number. All previous owner API keys, including the key making this request, are revoked. Store the returned replacement before making another request. Does not change SIP, extension or phone-app passwords. Limit five password-change attempts per owner per ten minutes.

Body fieldTypePresenceRules
current_passwordpasswordRequiredCurrent account password; this additional verification is mandatory.
new_passwordpasswordRequiredNew account password: 12–72 UTF-8 bytes, including a letter and a number. Does not change SIP, extension or phone-app passwords.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/me/password' \
  --header "Authorization: Bearer $OPENWEB_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
  "current_password": "YOUR_CURRENT_ACCOUNT_PASSWORD",
  "new_password": "replace-with-a-new-unique-password-73"
}'
HTTP 200 · example response
{
  "access_token": "owta_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "token_type": "Bearer",
  "expires_at": "2027-01-26T08:00:00+00:00",
  "token": {
    "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
    "name": "Tenant API",
    "created_at": "2026-10-28T08:00:00+00:00",
    "expires_at": "2027-01-26T08:00:00+00:00",
    "revoked_at": null,
    "last_used_at": null
  }
}

Other responses: 400, 401, 403, 404, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

GET/templatesBearer token

List safe published templates

Returns current revisions visible to this tenant, without credentials or encrypted payloads. Only shared, published starter templates explicitly approved by the instance administrator are available. They must have no trunks, outbound rules or additional settings. Configure your own provider after creating the PBX.

Example request · cURL
curl 'https://call.openweb.co.za/api/v1/templates' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "templates": [
    {
      "template_uuid": "039b844b-7f52-40c6-a835-3db26a764d3c",
      "template_name": "Starter PBX",
      "description": "A starter office PBX.",
      "version": 1,
      "timezone": "Africa/Johannesburg",
      "extension_start": 100,
      "extension_count": 2,
      "extension_limit": 100
    }
  ]
}

Other responses: 401, 403, 405, 429, 503. See errors and retry guidance.

GET/servicesBearer token

List your PBX services

Returns only this tenant's services. Each service is an isolated PBX on the shared instance, not a separate virtual machine.

Example request · cURL
curl 'https://call.openweb.co.za/api/v1/services' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "services": [
    {
      "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
      "tenant_uuid": "d8da1fce-51ac-4e1a-a53a-91a4971e0e3c",
      "domain_uuid": "80d27531-dbb9-4ecb-8ed7-1c9295c2c6eb",
      "service_name": "Main Office",
      "template_uuid": "039b844b-7f52-40c6-a835-3db26a764d3c",
      "template_name": "Starter PBX",
      "template_version": 1,
      "sip_domain": "main-office.example-company.call.openweb.co.za",
      "software_version": "1.0.21",
      "created_at": "2026-10-28T08:05:00+00:00"
    }
  ]
}

Other responses: 401, 403, 405, 429, 503. See errors and retry guidance.

POST/servicesBearer token

Create a PBX service

Creates an isolated PBX from the selected current template and atomically queues its requested managed subdomain. New public tenants start with one service. No paid provider connection or carrier DID is automatically enabled. Retry the exact same request with the same Idempotency-Key after a timeout.

ParameterLocationRequiredMeaning
Idempotency-KeyheaderYesA UUID unique to this intended operation. Retry the same JSON request with the same key. A changed request with a reused key is rejected.
Body fieldTypePresenceRules
template_uuiduuidRequiredChoose a currently visible published starter template.
service_namestringRequiredMaximum 120 UTF-8 bytes. Minimum 1 characters. Maximum 120 characters.
slugstringRequiredMinimum 3 characters. Maximum 40 characters.
subdomainstringOptionalOptional single label. Defaults to your tenant slug. No scheme, dots, port or path. Minimum 3 characters. Maximum 40 characters.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/services' \
  --header "Authorization: Bearer $OPENWEB_TOKEN" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: 9b484c7d-4e6f-48b2-b9fd-c55260d8b563' \
  --data '{
  "template_uuid": "039b844b-7f52-40c6-a835-3db26a764d3c",
  "service_name": "Main Office",
  "slug": "main-office",
  "subdomain": "example-company"
}'
HTTP 201 · example response
{
  "service": {
    "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
    "tenant_uuid": "d8da1fce-51ac-4e1a-a53a-91a4971e0e3c",
    "domain_uuid": "80d27531-dbb9-4ecb-8ed7-1c9295c2c6eb",
    "service_name": "Main Office",
    "template_uuid": "039b844b-7f52-40c6-a835-3db26a764d3c",
    "template_name": "Starter PBX",
    "template_version": 1,
    "sip_domain": "main-office.example-company.call.openweb.co.za",
    "software_version": "1.0.21",
    "created_at": "2026-10-28T08:05:00+00:00"
  },
  "subdomain": {
    "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
    "requested_label": "example-company",
    "requested_hostname": "example-company.openwebpbx.com",
    "hostname": null,
    "url": null,
    "admin_url": null,
    "state": "pending_dns",
    "ready": false,
    "next_attempt": "2026-10-28T08:06:00+00:00",
    "error_category": ""
  }
}

Other responses: 400, 401, 403, 404, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

GET/services/{service_uuid}Bearer token

Read an owned PBX service

A valid UUID from another tenant is not disclosed. Existing service settings and the selected template revision remain attached to that service.

ParameterLocationRequiredMeaning
service_uuidpathYesAn owned service UUID. Foreign resources are not disclosed.
Example request · cURL
curl 'https://call.openweb.co.za/api/v1/services/0d59d9f6-33a6-4328-8b31-dc63fe96d329' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "service": {
    "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
    "tenant_uuid": "d8da1fce-51ac-4e1a-a53a-91a4971e0e3c",
    "domain_uuid": "80d27531-dbb9-4ecb-8ed7-1c9295c2c6eb",
    "service_name": "Main Office",
    "template_uuid": "039b844b-7f52-40c6-a835-3db26a764d3c",
    "template_name": "Starter PBX",
    "template_version": 1,
    "sip_domain": "main-office.example-company.call.openweb.co.za",
    "software_version": "1.0.21",
    "created_at": "2026-10-28T08:05:00+00:00"
  }
}

Other responses: 400, 401, 403, 404, 405, 429, 503. See errors and retry guidance.

GET/subdomains/availabilityBearer token

Check a subdomain label

Advisory local-instance check only. It does not reserve the name or query central DNS ownership. Reserved labels are refused. The custodian may assign a stable suffix when a requested name is held elsewhere; poll the owned service's actual status.

ParameterLocationRequiredMeaning
labelqueryYesSingle lower-case label, 3–40 letters, numbers or hyphens.
Example request · cURL
curl 'https://call.openweb.co.za/api/v1/subdomains/availability?label=example-company' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "label": "example-company",
  "hostname": "example-company.openwebpbx.com",
  "available": true,
  "scope": "this_instance",
  "reservation_confirmed": false
}

Other responses: 400, 401, 403, 405, 429, 503. See errors and retry guidance.

GET/services/{service_uuid}/subdomainBearer token

Read subdomain preparation status

DNS, trusted HTTPS and local transport preparation run in the background. url/admin_url remain null until the assigned name is verified ready. The requested name is not necessarily the final assigned hostname.

ParameterLocationRequiredMeaning
service_uuidpathYesAn owned service UUID. Foreign resources are not disclosed.
Example request · cURL
curl 'https://call.openweb.co.za/api/v1/services/0d59d9f6-33a6-4328-8b31-dc63fe96d329/subdomain' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "subdomain": {
    "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
    "requested_label": "example-company",
    "requested_hostname": "example-company.openwebpbx.com",
    "hostname": null,
    "url": null,
    "admin_url": null,
    "state": "pending_dns",
    "ready": false,
    "next_attempt": "2026-10-28T08:06:00+00:00",
    "error_category": ""
  }
}

Other responses: 400, 401, 403, 404, 405, 429, 503. See errors and retry guidance.

POST/services/{service_uuid}/subdomainBearer token

Register an owned service's subdomain

Queue a managed subdomain on an owned active PBX service. Use a single label. Once queued or leased, the label cannot be silently renamed; changing it returns a conflict. Existing SIP realms and phone credentials are preserved. The custodian supports fifty names per instance, including reserved existing names.

ParameterLocationRequiredMeaning
service_uuidpathYesAn owned service UUID. Foreign resources are not disclosed.
Idempotency-KeyheaderYesA UUID unique to this intended operation. Retry the same JSON request with the same key. A changed request with a reused key is rejected.
Body fieldTypePresenceRules
labelstringRequiredMinimum 3 characters. Maximum 40 characters.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/services/0d59d9f6-33a6-4328-8b31-dc63fe96d329/subdomain' \
  --header "Authorization: Bearer $OPENWEB_TOKEN" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: 9b484c7d-4e6f-48b2-b9fd-c55260d8b563' \
  --data '{
  "label": "example-company"
}'
HTTP 200 · example response
{
  "subdomain": {
    "service_uuid": "0d59d9f6-33a6-4328-8b31-dc63fe96d329",
    "requested_label": "example-company",
    "requested_hostname": "example-company.openwebpbx.com",
    "hostname": null,
    "url": null,
    "admin_url": null,
    "state": "pending_dns",
    "ready": false,
    "next_attempt": "2026-10-28T08:06:00+00:00",
    "error_category": ""
  }
}

Other responses: 400, 401, 403, 404, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

GET/tokensBearer token

List integration token metadata

Returns up to the most recent 100 token metadata records, including revoked history, never raw token secrets. Use separate named keys for separate integrations.

Example request · cURL
curl 'https://call.openweb.co.za/api/v1/tokens' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 200 · example response
{
  "tokens": [
    {
      "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
      "name": "Tenant API",
      "created_at": "2026-10-28T08:00:00+00:00",
      "expires_at": "2027-01-26T08:00:00+00:00",
      "revoked_at": null,
      "last_used_at": null
    }
  ]
}

Other responses: 401, 403, 405, 429, 503. See errors and retry guidance.

POST/tokensBearer token

Create an integration token

Returns the new token secret once. The default and maximum lifetime are ninety days. At most ten active tokens are allowed per tenant. This operation does not retain a replayable copy of the token; avoid blindly retrying it.

Body fieldTypePresenceRules
namestringRequiredMaximum 80 UTF-8 bytes. Minimum 1 characters. Maximum 80 characters.
expires_in_daysintegerOptionalMinimum 1. Maximum 90.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/tokens' \
  --header "Authorization: Bearer $OPENWEB_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Billing integration",
  "expires_in_days": 30
}'
HTTP 201 · example response
{
  "access_token": "owta_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "token_type": "Bearer",
  "expires_at": "2027-01-26T08:00:00+00:00",
  "token": {
    "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
    "name": "Tenant API",
    "created_at": "2026-10-28T08:00:00+00:00",
    "expires_at": "2027-01-26T08:00:00+00:00",
    "revoked_at": null,
    "last_used_at": null
  }
}

Other responses: 400, 401, 403, 404, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

DELETE/tokens/{token_uuid}Bearer token

Revoke a token

Immediately invalidate the selected owned token. The response is 204 with no body. Revoking the current token also ends its ability to make subsequent requests.

ParameterLocationRequiredMeaning
token_uuidpathYesA token belonging to this tenant.
Example request · cURL
curl --request DELETE 'https://call.openweb.co.za/api/v1/tokens/17eae30b-1965-47ef-91bd-66e623b5ec99' \
  --header "Authorization: Bearer $OPENWEB_TOKEN"
HTTP 204 · example response
(No response body)

Other responses: 400, 401, 403, 404, 405, 429, 503. See errors and retry guidance.

POST/tokens/{token_uuid}/rotateBearer token

Rotate an integration token

Create a replacement secret and revoke the old secret atomically. Returns the replacement once; later read responses cannot recover it. Update the integration securely. Send an empty JSON object to use the default expiry.

ParameterLocationRequiredMeaning
token_uuidpathYesA token belonging to this tenant.
Body fieldTypePresenceRules
expires_in_daysintegerOptionalMinimum 1. Maximum 90.
Example request · cURL
curl --request POST 'https://call.openweb.co.za/api/v1/tokens/17eae30b-1965-47ef-91bd-66e623b5ec99/rotate' \
  --header "Authorization: Bearer $OPENWEB_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
  "expires_in_days": 30
}'
HTTP 201 · example response
{
  "access_token": "owta_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "token_type": "Bearer",
  "expires_at": "2027-01-26T08:00:00+00:00",
  "token": {
    "token_uuid": "17eae30b-1965-47ef-91bd-66e623b5ec99",
    "name": "Tenant API",
    "created_at": "2026-10-28T08:00:00+00:00",
    "expires_at": "2027-01-26T08:00:00+00:00",
    "revoked_at": null,
    "last_used_at": null
  }
}

Other responses: 400, 401, 403, 404, 405, 409, 413, 415, 429, 503. See errors and retry guidance.

Portable front controller: https://call.openweb.co.za/api/v1/index.php?route=/templates. Keep the same method, headers and body. Use &label=example-company when adding a query parameter to that form.

GET/onboarding/authorityPublic

Read the installation authority public identity

Public read-only metadata. Packages pin their own independent copy of this RSA3072 key; never replace that trust solely from an unauthenticated response.

Setup sequence · Full request/response schema · Public setup wizard

POST/onboarding/email/requestOwner bearer

Request confirmation for an existing owner email

Invited owners may sign in normally, then confirm their current administrator email before authorizing a remote installation. Queues a one-use code through global outgoing mail; no installation is activated.

Body: {}.

Setup sequence · Full request/response schema · Public setup wizard

POST/onboarding/email/verifyOwner bearer

Confirm the existing owner email

Requires the emailed code and current owner password. Keeps invitation/public-signup classification unchanged.

Required JSON fields: token, password.

Setup sequence · Full request/response schema · Public setup wizard

POST/installations/challengeOwner bearer

Request a server ownership challenge

Requires an active, email-confirmed tenant owner. Binds canonical hostname, P256 public key, local tenant/domain UUIDs and desired label. Expires after five minutes. Does not create a hosted PBX service.

Required JSON fields: server_host, public_key, local_tenant_uuid, domain_uuid, label.

Setup sequence · Full request/response schema · Public setup wizard

GET/installationsOwner bearer

List this owner’s authorized installations

Returns only this verified owner’s installation metadata, without private keys or another account’s grants.

Setup sequence · Full request/response schema · Public setup wizard

POST/installationsOwner bearer

Authorize or renew an independently hosted PBX

Verifies the exact challenge signature and fresh trusted canonical HTTPS nonce proof, then issues a signed one-year RSA authorization bound to owner/local IDs/key/hostname/label. The installed server independently verifies it before activation. Renewal increments generation and keeps bindings immutable. No central hosted PBX is created.

Required JSON fields: challenge_uuid, signature.

Setup sequence · Full request/response schema · Public setup wizard

GET/installations/{installation_uuid}Owner bearer

Read one owned authorization and its signed receipt

Owner-scoped. The signed receipt is usable only by the exact local instance identity. Tenant address DNS and trusted HTTPS readiness are separate from authorization.

Setup sequence · Full request/response schema · Public setup wizard

Safe retries and clear limits

PBX creation and subdomain registration require a UUID Idempotency-Key. Retrying the same JSON request with the same key returns its saved result and Idempotent-Replayed: true. Poll the subdomain resource for its current readiness; the original saved response can still show a pending state. Reusing that key with changed details returns 409. Use a new key only for a genuinely new intended operation.

New public tenants begin with one PBX service. The instance operator controls admission and quotas; the default public-signup cap is 25 tenants. This is not an offer of unlimited hosted capacity.

OperationDefault limit
Register5 per source IP/hour; 3 per email/hour; 100 globally/day.
Verify30 per source IP/10 minutes.
Sign in20 per source IP/10 minutes; 8 per email/10 minutes.
Change account password5 attempts per owner/10 minutes.
Authenticated API120 requests per tenant per minute, shared by its tokens.

On 429, honor Retry-After. On a timeout or temporary 503, use bounded exponential backoff and keep the original idempotency key for a write. Correct validation, authentication and conflict errors before retrying. Do not retry forever or repeatedly create new tokens after losing a response.

Errors you can act on

Use the HTTP status and error.code for application decisions. A request_id is safe to include in a support report; passwords and tokens are not.

Error envelope
{
  "error": {
    "code": "invalid_request",
    "message": "Check the supplied details."
  },
  "request_id": "example-request-reference"
}
HTTP statusMeaning
400Invalid request.
401Missing, invalid, expired or revoked bearer token, or invalid owner sign-in.
403Secure transport, request origin, account or tenant restriction.
404Route or owned resource unavailable.
405Incorrect method; inspect Allow.
409Domain, quota, ownership or idempotency conflict.
413Request body exceeds the supported size.
415JSON body requires application/json.
429Rate limit exceeded; honor Retry-After.
503A required dependency is temporarily unavailable.

What your integration can control

Public self-service tenants use the modern Desktop and Tenant Services portals. Direct legacy administration pages remain unavailable to these accounts; existing invited tenants keep their current permissions.

Every protected operation is scoped to its verified tenant owner. Clients cannot choose arbitrary tenant identities, grant platform roles, supply DNS credentials, take another company’s name, read trunk passwords or execute operating-system commands. An unavailable or foreign-owned service is not disclosed.

Use a server-side integration and normal HTTPS certificate validation. Browser-cookie authentication and cross-origin browser requests are not supported. JSON request bodies are limited to 32 KiB and unknown fields are refused. Keep tokens in a secret store and send them only in the Authorization header, never in URLs.

There is no destructive tenant-installation or recording-deletion operation in this API. For account or personal-data removal, use the deletion procedure. Registering a tenant does not activate paid provider trunks or assign carrier numbers.

Verification uses the installation’s global outgoing-mail service. A queued message or SMTP acceptance does not prove inbox delivery. DNS and certificate readiness can take time; preserve a working connection while an address is pending.

Build with the reference beside you

For instance administrators: under Tenant Services → Templates, publish a shared starter and select Allow new tenants to use this starter template through the API. The template must have no trunks, outbound rules or additional settings. Tenant-owned templates and existing services are not changed.

For hosted account assistance, contact hello@openwebpbx.com. Include the route, approximate time and request reference. Remove tokens, passwords, verification codes, phone numbers and customer records before sharing a report.