ExoVoIPDeveloper guide

Authentication

Every request (except GET /health) requires an API key. The control panel uses the same API with the user's session.

API keys

Format: exo_live_<id><segreto> or exo_test_<id><segreto>. Two equivalent ways to send it:

# Entrambe le forme sono equivalenti
curl -s https://centralino.exovoip.it/api/v1/extensions -H "X-API-Key: $EXOPBX_API_KEY"
curl -s https://centralino.exovoip.it/api/v1/extensions -H "Authorization: Bearer $EXOPBX_API_KEY"

Each key has:

PropertyMeaning
ScopesGranted permissions (e.g. extensions:read, calendars:write).
Allowed IPsOptional: only these addresses or networks (CIDR). From other IPs: 403 IP_NOT_ALLOWED.
ExpiryOptional. After expiry: 401 API_KEY_EXPIRED.
TenantKeys created from a company's control panel operate only on that tenant.

The secret is stored as a hash (SHA-256): Exobyte cannot recover it. If you lose it, rotate the key.

Creating, rotating and revoking keys via the API

Requires the api_keys:read / api_keys:write scopes.

curl -s -X POST https://centralino.exovoip.it/api/v1/api-keys \
  -H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Integrazione CRM","scopes":["extensions:read","cdr:read","live:read"],
       "allowedIps":["203.0.113.10"],"expiresAt":"2027-12-31T23:00:00.000Z"}'

Rotation creates a new key with the same permissions; the previous one stays valid for the overlap period you specify, so you can update your systems without downtime.

# La vecchia chiave resta valida per 24 ore, poi scade
curl -s -X POST https://centralino.exovoip.it/api/v1/api-keys/12/rotate \
  -H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
  -d '{"overlapHours":24}'

curl -s -X POST https://centralino.exovoip.it/api/v1/api-keys/12/revoke -H "X-API-Key: $EXOPBX_API_KEY"
RequestScopeSDK method
GET /v1/api-keysapi_keys:readapiKeys.list() · py: api_keys.list()
POST /v1/api-keysapi_keys:writeapiKeys.create() · py: api_keys.create()
GET /v1/api-keys/:idapi_keys:readapiKeys.get() · py: api_keys.get()
PATCH /v1/api-keys/:idapi_keys:writeapiKeys.update() · py: api_keys.update()
POST /v1/api-keys/:id/rotateapi_keys:writeapiKeys.rotate() · py: api_keys.rotate()
POST /v1/api-keys/:id/revokeapi_keys:writeapiKeys.revoke() · py: api_keys.revoke()
GET /v1/api-keys/:id/usageapi_keys:readapiKeys.usage() · py: api_keys.usage()
GET /v1/api-keys/:id/usage/statsapi_keys:readapiKeys.usageStats() · py: api_keys.usage_stats()

Scopes

Format <resource>:<action>. <resource>:* grants all actions on the resource, * grants all tenant scopes.

ScopeWhat it allows
extensions:read / extensions:writeExtensions, SIP credentials, forwarding, feature codes
trunks:read / trunks:writeThe tenant's SIP trunks
numbers:read / numbers:writeNumbers and their destination
inbound_routes:read / inbound_routes:writeInbound routes
outbound_routes:read / outbound_routes:writeOutbound routes and blocks
calendars:read / calendars:writeBusiness hours, holidays, exceptions, overrides
ivrs:read / ivrs:writeIVR menus
ring_groups:read / ring_groups:writeRing groups
queues:read / queues:writeQueues and agents
media:read / media:writeVoice prompts and music on hold
recordings:read / recordings:writeRecordings, transcripts, rules and settings
voicemail:read / voicemail:writeVoicemail
contacts:read / contacts:writePhonebook
cdr:readCall log
live:readReal-time status and events
calls:controlCall control
notifications:read / notifications:writeEmail and Telegram notifications
webhooks:read / webhooks:writeWebhooks
api_keys:read / api_keys:writeAPI keys

Platform scopes (Exobyte global keys only): tenants:*, security:*, system:*. A tenant key can never obtain them, not even with *.

Control panel roles

Control panel users have permissions based on their role:

RolePermissions
Platform administratorEverything, across all tenants
Company administratorAll tenant scopes
SupervisorRead, real-time and call control
UserOwn extension only: details, voicemail, call log, real time

Global keys and X-Tenant-Id

Global keys (created by the platform administrator) operate across all tenants:

  • on tenant endpoints the X-Tenant-Id header is required (otherwise 400 TENANT_REQUIRED);
  • on endpoints with a global view (trunks, numbers, call log, keys, real time, webhooks), omitting the header returns data for all tenants, each with its own tenantId;
  • on SSE and WebSocket streams the tenant can also be specified with ?tenantId=.

For tenant keys the header is ignored; a value other than the key's own tenant returns 403.

# Chiave globale (piattaforma): azienda indicata con X-Tenant-Id
curl -s https://centralino.exovoip.it/api/v1/extensions \
  -H "X-API-Key: $EXOPBX_API_KEY" \
  -H "X-Tenant-Id: 3"

Suspended tenants

While a tenant is suspended its keys work in read-only mode: writes return 403 TENANT_SUSPENDED. When a tenant is deactivated, access is denied.

Best practices

  • One key per integration, with only the scopes it needs.
  • Restrict IPs when the integration runs on servers with a fixed address.
  • Rotate keys periodically and revoke unused ones immediately.
  • Never put keys in public web pages or distributed apps: use them server-side only.
  • Monitor each key's usage from the control panel (requests, errors, last access).