Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Welcome

This guide is for the operator and its resellers: those who install the PBX, create customers and provide them with numbers and trunks. The customer guide, served under /guide/en/, covers what customers and their users see; in admin mode, you do there what they do.

RoleWhat they do
Reseller adminCreates and follows their customers, assigns them numbers, manages their sale rate plans.
Operator adminManages the whole installation: resellers, customers, number pool, trunks, diagnostics, settings.
Operator supportSees everything and acts on diagnostics, without changing anything else.

Signing in and the account are the same as for a customer: see Sign in and My account. The operator installing the PBX starts with Installation and operations: prerequisites, sizing, quick start, updates, backups.

This guide opens from the Documentation link in the portal menu, for the operator’s and resellers’ teams; so does the full API documentation (see Operator API).

Customers and users

The Customers page lists your customers. New customer creates one: its name and its domain, the customer’s SIP domain name (for example client.operateur.exemple). The domain can no longer change afterwards: the phones register to it.

A customer’s page brings together:

  • Users: Add a user with their name, email address, role (customer admin or user) and, if they have a phone, their extension number (2 to 8 digits). They receive their invitation by email; Resend invitation sends them a new one (expired link, forgotten password). Manage changes their name, address and role, disables them or deletes them along with their extension (see Team). Each extension shows its phone (connected or not), its caller ID and its restrictions (premium-rate, international, all outgoing).
  • Numbers: the customer’s numbers and their destination (see Numbers).
  • The customer’s Call flow and Call log, as its customer admin sees them.
  • Replay publication: rewrites the published call flow into the phone server, without a new version. Useful for diagnostics, when a call does not follow the call flow shown.
  • Subscription: see below.

A user who has lost their second factor: Manage, then Reset two-step verification; they will set up a new one at their next sign-in. Only an account of the level above can do it. The assistant does not do it: account security stays closed to it.

The other admins of your reseller are added from the Users page (see Resellers and team).

Customer’s space (admin mode)

Open the customer’s space, on its page, shows the portal as the customer’s administrator sees it: its menu (home, call flow, team members, call log, voicemail…) and its whole PBX, to understand what they see or help them. A red banner reminds you: you act with your own rights, and each action stays in your name in the audit log. Switch customer… goes to another customer’s space, on the same page; Leave the customer’s space goes back to its page.

A customer’s subscription

The Subscription card on the customer’s page:

  • Rename: the customer’s name (company name); its domain does not change.
  • Suspend: its accounts can no longer sign in, its API keys no longer work, its phones no longer register and its numbers no longer ring. It no longer counts towards the license. Everything stays in place: Reactivate restores the service as it was. In the customer list, it is marked “suspended”.
  • Terminate: permanent. Extensions, voicemail and messages, call flow, recordings, announcements, directory, accounts and API keys are deleted. Remove its numbers first (they go back to the pool); a customer with a trunk is terminated after the operator deletes the trunk. The audit log and the calls of past months remain available in the exports.

A customer admin can neither suspend nor terminate their own customer.

Numbers

The Numbers page shows the numbers the operator has reserved for you and those of your customers, with their status: available, assigned, porting in or porting out, terminated.

To give a number to a customer, from their page: Assign a number from stock, choose an available number, then Assign. Then choose its destination: an extension, or a module of their call flow. The customer admin can also change the destination from their call flow.

Remove returns the number to the pool; its calls are no longer routed to the customer.

Rate plans and usage

Each answered outbound call is rated on receipt, on three rate plans: the operator’s purchase price, its sale to the reseller, the sale to the customer. Inbound calls, internal calls and unanswered calls are not rated. A received call forwarded to an outside number is rated as an outbound call to that number, from the moment the other party answers to the hang-up.

Rate plans

The Rate plans page groups them by type: Purchase, Sale to resellers and Sale to customers for the operator; Sale to customers for a reseller, who never sees the operator’s purchase prices.

New rate plan: a name, an effective date (a purchase rate plan only applies to calls from that date, counted from midnight in the calling customer’s time zone; by default, today in yours) and its lines, in two ways:

  • CSV file: one line per prefix, prefixe;destination;prix_minute;frais;palier (prefix, destination, price per minute, connection fee, increment), for example +336;Mobiles;0,05;0;60/30. Prices in euros excluding VAT. The increment minimum/step in seconds: 1/1 per second, 60/60 per started minute, 60/30 first minute indivisible then per 30 seconds.
  • From another rate plan, with a margin: same lines, prices and fees increased by the percentage given.

The default rate plan of a type applies to any customer (or reseller) that has none assigned. Click a rate plan to see its lines and search for a prefix or a destination. Deleting a rate plan does not change calls already rated.

Assigning a rate plan

On a customer’s page, the Pricing card chooses its sale rate plan among those of its operator or reseller; on a reseller’s page, the operator chooses its sale-to-reseller rate plan. The change applies to the next calls.

The customer admin reads their sale rate plan, without being able to change it, in the Rates menu of their space: rate plan name, effective date, prices by destination. They see neither the purchase prices nor the sale-to-reseller rate plan.

Usage

The Customers list shows, for the chosen month (top of the page) and for each customer, the rated calls, the billed minutes and the amounts: purchase (operator only), sale to reseller and sale to customer, with the total. Months follow your organization’s time zone. Export calls (CSV) downloads the details of the outbound calls of the same month, the basis for billing.

Home

Your team (operator admin, support) lands on the Home page when signing in. It gathers what needs attention and the state of your network; each line leads to the page where to act.

To do:

  • FreeSWITCH not responding;
  • a customer’s trunk that is not registered, with the provider’s refusal (code and reason): a 403 usually means a wrong username or password;
  • banned IP addresses: a misconfigured phone may be blocking a whole site (see Diagnostics);
  • webhooks abandoned after several failures;
  • a licence that has expired, is blocked or expires in less than 30 days;
  • a new version available.

Your network: active and suspended customers, users (and the number the licence provides for), assigned numbers and free numbers in stock, calls in progress. Health, banned addresses and calls in progress refresh every 30 seconds.

This month: rated calls, sales to customers and purchases, excluding tax (see Rate plans and usage). Latest actions: the last five entries of the audit log.

A block your role cannot read does not appear. Resellers land on their customer list.

Resellers and team

Resellers

A reseller sells the hosted PBX to its own customers, on your installation. It sees only its customers; you see everything. The Resellers feature is turned on first in the Settings: without it, no menu and no creation.

The Resellers page lists them, with their number of customers. New reseller creates one: its name is enough. Its page shows:

  • Reseller admins: its accounts. Add an account with the name and email address; the invitation goes out by email (server set in the settings). They manage the reseller’s customers, their users and their extensions, and see the numbers you reserve for them.
  • Reseller’s customers.

To create a customer under a reseller: Customers page, New customer, then set Belongs to to the reseller. Without a reseller, the customer depends directly on you.

Team

The Users page lists the accounts of your organization. Add an account creates one, with its role:

  • Operator admin: the whole PBX, settings included;
  • Operator support: views everything and runs diagnostic actions (call trace, hanging up a call, reload), without creating or changing anything. They listen to neither customers’ voicemail messages nor their call recordings.

Each account receives its invitation by email; the second factor is required at its first sign-in. Resend invitation sends it a new link. Manage changes its name, address or role, disables it (no more sign-in) or deletes it; your own account is set up in My account.

A reseller admin also has a Users page, to add the other admins of their reseller.

On their Users page, a reseller admin also finds their Email alerts (see Instance settings); their fleet’s webhook and their API keys are in the API menu (see Operator API).

Number pool

The Numbers page manages the installation’s number pool.

Add to stock: one number per line in international format, with the country and the carrier if needed (+33123456789;FR;provider; FR by default for a +33), or a CSV file in the same format.

For each number: its country, its carrier, the customer using it, its Status (available, assigned, porting in, porting out, terminated) and its Porting date, editable from the list. A number reserved for a reseller is marked “reserved”; reservation is done through the API or the assistant. Filter by status finds, for example, ports in progress.

A number is assigned to a customer from the customer’s page, as for a reseller (Numbers).

Customer trunk

The trunk connects a customer’s PBX to the telephone network, through the carrier’s C5. Without a trunk, the customer can neither call out nor receive calls.

On the customer’s page, Set up the trunk. If the provider has a trunk profile, pick it in Profile: you then only enter the Username, Password and Main number (NDI). Otherwise, pick None: enter everything:

  • Provider and Server: the C5 address, host or host:port;
  • Username and Password of the SIP account, or IP-based trunk;
  • Number format: the called number, in France and international, and the presented number, in the format the carrier expects. Apply a template… prefills the formats of a known carrier;
  • Main number (NDI): the main line number, presented on any call that goes out without its own number (extension without a presented number, external member of a ring group, call forwarding to an outside number);
  • P-Asserted-Identity host: empty means the customer’s domain;
  • Provider networks: the addresses its calls come from, one network per line.

The page then shows the trunk status (registered or not) and the Last refusal from the provider, with its code: a 403 usually means a wrong username or password. The password is never shown again: leave the field empty to keep it.

Diagnostics

The Diagnostics page helps you understand and fix an incident without access to the server.

Health

The state of the installation: installed version, telephony server (responding or not, calls in progress), database, last call received, abandoned webhook deliveries, status of customer trunks.

When a new version is available, the health section says so (“update available”). It is installed on the server, by whoever administers it (apt upgrade): the portal does not install it itself.

  • Re-register restarts a trunk; its status updates in a few seconds.
  • Reload configuration reloads the telephony server’s configuration for the whole installation, without cutting calls in progress.

Live calls

Calls across the whole installation: customer, caller, called party, status. Hang up ends a stuck call. A customer’s call log shows, for each past call, its Trace: who hung up, who it was connected to, the media.

Live capture

To understand a failing call or a phone that does not register: choose the customer, Start the capture, then place the call or restart the phone. The SIP messages and telephony server logs that concern this customer are shown as they arrive, for five minutes at most. Stop ends the capture, Download saves the text to pass on to support. Nothing is kept on the server; each capture is recorded in the audit log.

SIP ladder draws the SIP messages as a ladder diagram: one column per address (FreeSWITCH and its peers: phones, trunk), one arrow per message, in red for a failure (4xx, 5xx, 6xx). Pick a dialog to follow a single call or registration; click an arrow to read the whole message. Download the pcap gives a file to open in Wireshark (Telephony, SIP Flows): the messages are as FreeSWITCH sent or received them, already decrypted for TLS; the IP and UDP or TCP headers are rebuilt, and the media (RTP) is not included.

Restart FreeSWITCH

As a last resort (operator admin only). By default, the restart waits for calls in progress to end and refuses new ones meanwhile; tick Right now, cutting ongoing calls to cut calls in progress. The server is back in about a minute.

Audit log

The Audit log page shows who did what, when and from where: sign-ins (successful and denied), creations, changes, diagnostic actions. Filter by action (start of the name: utilisateur., connexion) or on denials; click an action for its details. Entries can be neither changed nor deleted. Everyone sees their organization and what depends on it: the operator everything, a reseller its customers, a customer admin their company.

Banned IP addresses

After too many failed SIP registrations from an address (a misconfigured phone, or a bot looking for passwords), the address is banned for SIP and RTP only: the portal stays reachable. The ban ends on its own.

The list shows the reason, the account and the device involved: a misconfigured softphone at a customer blocks their whole site, and the diagnostics show it. Unban lifts the block right away, once the device is fixed.

Ban an address bans it by hand, for the duration chosen (the one in the settings by default): a bot spotted in the capture, a device to cut off. An address in the allow list or of a trunk cannot be banned.

Ban settings: number of failures, window and duration (by default 10 failures in 5 minutes, banned for one hour), and an Allow list of networks that are never banned. Trunk networks, addresses with a registered phone and those of a recent portal sign-in are already spared.

Support access

The vendor’s support team has an account on your instance, but it is closed by default: it only gets in if you open it. An admin of your team chooses a duration (from 4 hours to 7 days), then Open access; Close access cuts it at once. When the time is up, it closes by itself. Openings, closings and every action of the support team are in the audit log.

On the machine, the command centrex acces-assistance ouvrir|fermer|etat does the same.

Instance settings

These settings are on the Settings page: the operator admin changes them, support can view them. Those of the machine (database, public address, text-to-speech) are in the service file: see Server settings. Troubleshooting stays on the Diagnostics page.

Features

Two features are turned off on a new instance, so as not to clutter the portal:

  • Resellers: the Resellers menu, the reseller column in the customer list and reseller sale rate plans. It can no longer be turned off while a reseller exists.
  • AI assistant: the Assistant menu, for your team and customer admins, and the assistant settings. Turned off, its settings (key, budgets) are kept.

Turned off, a feature disappears from the portal for every account, and the API refuses what depends on it.

Trunk profiles

A profile gathers what all of a provider’s trunks share: its name (“Netwo”), the C5 server, the number format, the P-Asserted-Identity host and its networks. New profile, then Apply a template… to prefill the format of a known carrier.

On a customer’s page, a trunk based on a profile only asks for the username, password and main number. Changing a profile updates every trunk based on it: their gateway is reloaded if the server changes, their outbound routes if the format changes. A profile still in use cannot be deleted.

Usage cap

Protection against a hacked phone or SIP account: each customer’s outgoing usage, at purchase price excluding tax, is counted over a rolling hour and over a rolling day.

  • At 80% of a threshold, an alert e-mail goes to the operator’s and the customer’s reseller’s alert addresses, and to the customer’s admins.
  • At the threshold, the customer’s outgoing calls are restricted to French landlines and mobiles (01 to 05, 06, 07, 09); emergency numbers remain reachable, and call length is not limited. A second e-mail reports it.
  • The restriction stays until it is lifted, on the customer’s page (Lift the restriction): the hourly threshold is lifted by the customer admin or by you, the daily threshold by you only (not by the reseller). The window’s count starts again from the lift.

The Usage cap card sets the default thresholds (€5 per hour and €20 per day at installation). On a customer’s page, the same card shows its usage and thresholds, and lets you give it others; empty, the instance’s apply. Calls without a purchase price (no purchase rate plan) are not counted.

Brand

The name, color and logo your customers see: portal, sign-in page, phone installation page and mobile app.

  • Display name: empty means your organization’s name.
  • Accent color: the color of buttons and links. A color that is too light, on which the white text of buttons would be unreadable, is refused.
  • Logo: PNG, JPEG or WebP, 256 KiB at most, shown 28 pixels high on the dark background of the menu. Without a logo, a square in the accent color.

Changes apply immediately, for everyone. The portal installed on a phone (see Home) carries this name and an icon with the name’s initial, on the accent color.

Language and time zone

Language: French or English, for your team (portal, invitations, forgotten password) and, by default, for customers created afterwards. Each customer can set its own, and each account its own in My account. A customer’s language also sets the voice of its announcements, voicemail and conference rooms (French voice, or Callie in English). The English text-to-speech voice is optional: CENTREX_VOIX_PIPER_EN (an English Piper model); without it, a customer set to English uploads recordings.

Time zone: your organization’s, Europe/Paris by default, inherited by resellers and customers created afterwards; each can set its own. It sets the opening hours of call flows, the dates shown in the portal and announced by voicemail, how months are split (usage, fleet export, end-of-month webhook) and the day of the license report. A customer’s published opening hours switch to its new time zone at once.

Email alerts

The addresses that receive your fleet’s alerts: a customer’s trunk down (with the reason given by the carrier) then restored, license expired or blocked, and for the operator only, storage almost full (recordings volume at 80% then 95%, system disk below 20% then 10% free). One alert per change, not one per attempt. They go out through the email server below; each reseller sets its own for its customers (Users page).

E-mails (SMTP)

The PBX’s emails (voicemail, invitations, alerts) go out through this server: server, port, encryption (STARTTLS, TLS or local relay), username, password, sender address and sender name. Send the test checks the setting. Without a server, the PBX sends no email, invitations included: set it up before creating the first users.

Voicemail transcription

The service that transcribes messages in mailboxes where transcription is turned on: API address, model, language, key. Try checks that it responds. Leave the language empty so that each message is transcribed in its customer’s language (French or English); a language entered here applies to every customer. For a service installed on your premises, see Local transcription.

Mobile app

The Acrobits Cloud ID of your Acrobits mobile app (Cloud Softphone, in your colors): extension installation links open it directly. In Acrobits’ Cloud Softphone portal, set “Initial external provisioning” to the address shown under the field, as is (it ends with ?p=%password%).

Without a Cloud ID, extensions are installed with the Acrobits Softphone app (paid) or, on a computer, with Linphone.

Sign-in from your portal

Your tool (customer portal, Odoo, extranet) can sign your customers in to the PBX without them retyping their password. Create the key shows a key, only once: copy it into your tool. Replace the key creates a new one (the old one stops working immediately); Remove the key closes this sign-in method.

Your tool sends the customer’s browser to the Sign-in link shown, followed by a JWT token signed with the key (HS256). The token carries:

FieldValue
audthe Audience (aud) shown: the PBX address
emailthe email address of the customer’s account
iat, expissued at and expiry (Unix seconds), ten minutes apart at most; one minute is enough
jtia unique identifier: a token can be used only once
clientoptional: the customer’s identifier (in the address of its page), to create the account if it does not exist and if the customer has allowed it
nameoptional: the name of the account to create

Example in Python (PyJWT library):

import time, uuid, jwt

maintenant = int(time.time())
jeton = jwt.encode(
    {"aud": AUDIENCE, "email": courriel, "iat": maintenant, "exp": maintenant + 60,
     "jti": str(uuid.uuid4())},
    CLE, algorithm="HS256")
redirection = f"{AUDIENCE}/api/v1/sso/jeton?jeton={jeton}"

Only customer accounts (customer admin, user) sign in this way: your tool’s key never opens an account of your team or of a reseller. The second factor is not asked again: your tool is responsible for it. A refused token (expired, already used, unknown or disabled account) brings the customer back to the sign-in page, with the reason; each sign-in and each refusal is recorded in the audit log.

Sign-in with your directory

If your team or your customers have a directory (Microsoft Entra, Google Workspace, Keycloak or any OpenID Connect provider), they can sign in with their account from that directory. First register an application with the directory, using the Redirect URI to declare in the directory shown on the card, then enter here the Issuer URL, the Application (client) ID and its Application secret, the Display name (“Microsoft”, “Google”) and, if you wish, the Accepted domains. The PBX checks the issuer when you save.

Set on the operator, the directory applies to all accounts of the installation that do not have their own: a customer can set its own directory in its settings, which takes precedence for its accounts. The account must already exist (same email address); two-step verification is the directory’s. On the sign-in page, the Use my company directory button starts from the address entered.

Configuration assistant

Once the AI assistant feature is turned on (see Features), to offer the assistant to administrators: a Claude API key, the model, a Monthly budget (€) for the installation and, if you wish, per customer. The amount used this month is shown; once the budget is reached, the assistant pauses until the following month.

License

Every day, the number of users is recorded and signed. Depending on your contract, the report is sent automatically to the vendor, or you download it and pass it on. The breakdown by customer and the reports of past days can be viewed here.

The vendor gives you back a license, valid until a date: it arrives automatically with the reply to the report, or you receive it as a file and upload it with Upload the vendor’s license. The card shows its end date. Once it has expired, you have 30 days, signaled by a banner at the top of the portal; after that, creating new users is blocked until renewal. Calls, however, are never cut.

Blocked destinations

International prefixes refused for all customers, call forwarding included (+882 Satellites). Emergency numbers always remain reachable.

GDPR

Retention periods

Retention periods section of the Settings page (and of each customer’s settings, as a customer can have its own). Beyond the period, the purge, which runs every hour, deletes:

  • Call recordings: 6 months at most, and by default; the call stays in the log. A recording whose call is not in the log is also deleted after 6 months.
  • Voicemail messages, with their transcription: no limit by default.
  • Call log (CDR): one year at least, the legal obligation to keep traffic data (French Postal and Electronic Communications Code, CPCE, art. L34-1); no limit by default.

An empty field takes the period of the level above (the customer that of the operator, the operator the default value).

Data subject rights

  • Access and portability: everyone downloads their data from My account (My data); an admin does it for a user (Account tab, Export their data). The JSON file contains the account, the extension, the sessions, the sign-ins and actions, the extension’s calls over twelve months and the voicemail messages.
  • Erasure: Erase (GDPR) deletes the account, the extension, the voicemail and the call recordings of the extension’s calls. What remains is the call log, kept for one year by legal obligation, and the audit log, protected against any change.

Each export and each erasure is recorded in the audit log.

Template processing record

To copy into your record of processing activities (GDPR Article 30).

ItemContent
ProcessingHosted business phone system (hosted PBX) provided to the operator’s customers
ControllerThe operator, for its accounts and billing; each customer, for its users and their communications (the operator is then its processor)
PurposesRouting calls; voicemail; call recording when the customer turns it on; billing; security and diagnostics
Data subjectsCustomers’ users; parties who call or are called
DataName, email address, extension, presented numbers; call log (numbers, timestamp, duration); voicemail messages and their transcription; call recordings; IP addresses used to sign in and to register phones; audit log
RecipientsThe operator and its resellers according to their rights; each customer’s admin; numbers are masked by default in the log, and showing them in full is restricted and logged
RetentionSee above; call log one year at least (CPCE L34-1, French traffic data retention rule)
TransfersNone outside the European Union by the PBX itself; transcription and assistant only if the operator sets up a provider, to be declared according to that provider
SecuritySecond factor mandatory for operator and reseller teams, enforceable by each customer; encrypted secrets; chained audit log; banning of abusive addresses; updates through the vendor’s repository
VendorThe software vendor has no access to any personal data: the license report only counts users per customer, identified by an identifier

Operator API

Everything the portal does goes through the public API: resellers, customers, numbers, rate plans, trunks, diagnostics and instance settings can be automated with an API key of your organisation.

API keys

API menu (operator admin, reseller admin): give the key a purpose (“Monitoring”), a role of your organisation and, if you wish, an expiry date, then Create the key. Copy it right away: it will not be shown again. A key never has more rights than you, and only a signed-in person creates or revokes a key. Revoke cuts off the tool using it. Customers have the same page in their space.

Fleet webhook

Your tool (monitoring, CRM, billing) receives your fleet’s events at an https address, signed by a secret shown once: cdr.month (calls of the past month), client.created, extension.created, trunk.down, trunk.up. The JSON body carries the event, its timestamp and the customer’s identifier (customer_id). The latest deliveries and their status are shown under the setting. A reseller has the same setting for its customers.

Monitoring

The Monitoring (Prometheus) card on the same page creates the token that opens /metrics to your Prometheus: see Monitoring.

Documentation

The PBX serves two documentations:

  • the full one, for you and your resellers: /api/docs/operateur/en (/api/docs/operateur in French; OpenAPI description: /api/v1/openapi.en.json and /api/v1/openapi.json);
  • the customer area one, which your customers see: /api/docs/en (description /api/v1/openapi-client.en.json), limited to their own operations.

The MCP server (/api/v1/mcp) exposes the same operations, with the rights of the key used: see AI assistants (MCP). An error carries a stable code and a detail; with the Accept-Language: en header, the detail is in English. Each key can send 300 requests in a row, then 10 per second; beyond that, the response is 429 (trop_de_requetes) with Retry-After.

AI assistants (MCP)

The centrex exposes an MCP (Model Context Protocol) server: an AI assistant, whether Claude, ChatGPT, Le Chat or any other MCP client, uses the API operations with the rights of an API key. You ask it in plain language “which trunks are failing?”, it calls the operations it needs and answers you.

Before you start

  • Server address: https://<portal address>/api/v1/mcp, over streamable HTTP. In the examples below, replace portal.example with your portal’s address.
  • API key: create one in the API menu (API keys). The MCP client sends it in the Authorization: Bearer <key> header.
  • Rights: the assistant has exactly the key’s rights. Choose the narrowest role that is enough, read-only to start with. Every write is recorded in the audit log, on the mcp channel, in the key’s name.
  • One key per tool: revoking it cuts off that tool alone.
  • Secret: don’t paste the key into a conversation. Put it in the tool’s configuration, or better in an environment variable (CENTREX_KEY below).
  • Data sent: API responses (call log, numbers, user names) go to the assistant’s provider. Check that this use is compatible with your GDPR commitments.
  • Network: tools installed on your computer (Claude Code, Claude Desktop, Codex, Vibe) reach the portal from your computer. Web assistants (claude.ai, Le Chat, OpenAI API) reach it from their provider’s servers: the portal must be reachable from the internet.

Claude

Claude Code: one command, then /mcp in Claude Code to check the connection.

claude mcp add --transport http centrex https://portal.example/api/v1/mcp \
  --header "Authorization: Bearer $CENTREX_KEY"

Claude Desktop: go through the mcp-remote bridge (Node.js required). Add this server to claude_desktop_config.json (Settings menu, Developer, Edit Config), then restart the app.

{
  "mcpServers": {
    "centrex": {
      "command": "npx",
      "args": ["mcp-remote", "https://portal.example/api/v1/mcp",
               "--header", "Authorization:${CENTREX_AUTH}"],
      "env": { "CENTREX_AUTH": "Bearer <key>" }
    }
  }
}

claude.ai (web and mobile): a custom connector accepts a fixed key only if your Claude organization has access to request headers (in beta). The organization owner then adds the connector with the Authorization header and the value Bearer <key>. Otherwise, use Claude Desktop or Claude Code.

OpenAI

ChatGPT: its connectors accept only OAuth or no authentication. It can’t use an API key for now.

Codex (command line and editor extension): add the server to ~/.codex/config.toml, export CENTREX_KEY before starting Codex, then check with codex mcp list.

[mcp_servers.centrex]
url = "https://portal.example/api/v1/mcp"
bearer_token_env_var = "CENTREX_KEY"

OpenAI API (Responses), for your own integrations: declare the server in the request’s tools. allowed_tools limits the operations offered to the model, and require_approval makes you approve each call.

"tools": [{
  "type": "mcp",
  "server_label": "centrex",
  "server_url": "https://portal.example/api/v1/mcp",
  "authorization": "<key>",
  "require_approval": "always"
}]

Mistral

Le Chat: a workspace administrator opens Connectors, Add Connector, Custom MCP Connector tab. They give a name without spaces (centrex) and the server address, then choose HTTP Bearer Token authentication and paste the key.

Vibe (command line): add the server to ~/.vibe/config.toml, and export CENTREX_KEY before starting Vibe.

[[mcp_servers]]
name = "centrex"
transport = "http"
url = "https://portal.example/api/v1/mcp"
api_key_env = "CENTREX_KEY"
api_key_header = "Authorization"
api_key_format = "Bearer {token}"

Other MCP clients

Any MCP client that sends a fixed header works. Usual configuration:

{
  "mcpServers": {
    "centrex": {
      "type": "http",
      "url": "https://portal.example/api/v1/mcp",
      "headers": { "Authorization": "Bearer <key>" }
    }
  }
}

Each API operation is a tool with the same name as in its documentation (listerClients, reglerRenvois…). With the Accept-Language: en header, tool descriptions and error details are in English.

Example requests

  • “Which trunks are failing, and since when?”
  • “List the clients of reseller Example with their number of extensions.”
  • “Why isn’t extension 100 of client Example registering?”
  • “Which IP addresses are banned, and for which account?”

To cut off an assistant’s access, revoke its key in the API menu.

Prerequisites

What to gather before installing the PBX: a virtual machine, its network, a name and a certificate, a SIP trunk and an email server.

Virtual machine

The PBX ships as a complete VM image: Debian 13, PostgreSQL, FreeSWITCH, FusionPBX, the PBX and nginx, ready to start (see Installing from the image).

HypervisorKVM, Proxmox or OpenStack (qcow2 image); VMware or VirtualBox (OVA image).
CPU and memory2 vCPUs and 4 GB by default, up to 200 simultaneous calls; see Sizing.
Disk12 GB at least; the image grows on first boot to the size given. Plan more for call recordings and voicemail (about 1 MB per minute in 8 kHz WAV).
AccessA public SSH key, given on first boot through cloud-init: the image has no password.

For high availability, two identical VMs on the same network, plus a free IP address (the floating IP): see Active/passive.

Network

A fixed public IP address, preferably carried by the VM itself: phones and the trunk provider reach it. Behind NAT, check that FreeSWITCH announces the public address (FusionPBX variables external_sip_ip and external_rtp_ip), otherwise audio only flows one way.

Ports to open towards the VM (the image installs no firewall: filter upstream, or on the VM):

PortProtocolFor
443TCPPortal and API (HTTPS).
80TCPRedirect to HTTPS, and Let’s Encrypt certificate validation.
5060UDP and TCPSIP from phones and softphones (internal profile).
5061TCPSIP over TLS from softphones (internal profile).
5080UDP and TCPSIP from the trunk: provider or C5 (external profile). Can be limited to the provider’s addresses.
16384 to 32768UDPAudio (RTP).
22TCPAdministration (SSH), from your own addresses only.

Between the two VMs of an active/passive pair: VRRP (IP protocol 112), PostgreSQL (5432) and SSH (22).

Phone SIP is in clear on 5060, like the current PBX, and over TLS on 5061, with the machine’s certificate: Linphone uses it for customers who choose encrypted calls (Settings). A phone that fails to authenticate too often gets its address banned (see Diagnostics): add your own addresses to the allow list right after installing.

The external profile also has TLS (5081): customers’ SIP connectors register over TLS with their service. These connections go out from the machine: port 5081 does not need to be open inbound.

Name and certificate

  • A fully qualified machine name (pbx.operator.example), with its DNS record pointing to the public address. It gives the portal address and the default domain.
  • A domain for customers, preferably as a wildcard (*.pbx.operator.example): each customer has its SIP domain (customer.pbx.operator.example), which its phones register to.
  • A TLS certificate for the machine name, for the portal and SIP over TLS: the operator’s, or Let’s Encrypt (certbot). On first boot, the VM uses a self-signed certificate, to be replaced: softphones reject it.

SIP trunk

An account with a SIP provider (or a C5) for each customer that calls out: username and password, or IP address authentication. Calls between customers also go out through the trunk. See A customer’s trunk.

Email

An SMTP server (address, port, credentials, sender): invitations, password links, voicemail and alerts are sent by email. It is set in the portal, before creating the first users.

Optional

  • Voicemail transcription: a service compatible with OpenAI’s transcription API (online service, or a local engine: see Local transcription).
  • Configuration assistant: a Claude API key (Anthropic).
  • Monitoring: Prometheus, for the metrics and the alerts shipped (Monitoring).
  • Backups: storage outside the VM to copy the archives to (Backup).

Sizing

What a VM handles, measured on the image: 50 customers with 20 extensions each, authenticated extension-to-extension calls, G.711 audio relayed by FreeSWITCH, quality computed on every call (MOS).

Guidelines

VMSimultaneous callsExtensions (guideline)
1 vCPU, 2 GB100700 to 1,000
2 vCPUs, 4 GB (image default)2001,300 to 2,000
  • Capacity follows the number of vCPUs: one vCPU handles 100 calls with headroom. Memory is not a limit (under 800 MB at 300 calls).
  • The extensions column assumes 10 to 15% of extensions on a call at peak time, the usual order of magnitude in companies; a call center needs much more. The number of customers matters little: simultaneous calls are what loads the machine.
  • Quality stays intact (MOS 4.48) as long as the CPU is not saturated: beyond 250 calls on 2 vCPUs, or 125 on 1 vCPU, it degrades.
  • SIP registrations are not a limit: 1,000 phones re-registering every minute make about 17 registrations per second, and a 2-vCPU VM absorbs 200.

When to grow

  • CPU above 75% at peak: the suggested alert threshold. Add vCPUs.
  • Costlier uses than extension-to-extension calls, not measured: transcoding (G.722, Opus), call recording, conferences, ring groups, call queues. Keep headroom if your customers use them heavily.
  • Recordings: about 16 MB per hour of recorded call, on their own volume (50 GB by default, about 3,000 hours): see Recordings volume.
  • Availability: a single VM is a single point of failure. Two VMs in active/passive restore service in under 2 minutes.

FreeSWITCH limits

  • 1,000 sessions at most (about 500 established calls).
  • 100 new sessions per second; beyond that, FreeSWITCH refuses new calls for a few minutes (503). An internal call costs about three, and a ring group one per extension: a VM accepts some thirty new internal calls per second.

Quick start

From the image to the first call, on one page. Each step is detailed in the following pages.

  1. Import the image into the hypervisor (OVA for VMware, qcow2 for Proxmox), with at least 2 vCPUs and 4 GB. Give the first boot an SSH key and the fully qualified name of the machine (pbx.operator.example): OVA properties, or cloud-init. See Installing from the image.

  2. Start the VM. On first boot, it generates its own secrets, sets up FusionPBX and FreeSWITCH, then starts the PBX and nginx. Connect over SSH with the debian account (OVA) or the one given to cloud-init, then check the machine: sudo sh -c 'set -a; . /etc/centrex-v3/centrex.env; centrex verifier'.

  3. Create the operator and its first administrator:

    sudo -u centrex sh -c 'set -a; . /etc/centrex-v3/centrex.env; centrex initialiser \
        --operateur "Operator name" --admin-courriel admin@operator.example \
        --admin-nom "First Last"'
    

    The command prints a single-use link: open it to choose the password, then turn on two-step verification (mandatory for the operator).

  4. Replace the self-signed certificate with yours, or with Let’s Encrypt: sudo apt install certbot python3-certbot-nginx, then sudo certbot --nginx -d pbx.operator.example --deploy-hook /usr/local/sbin/centrex-certificat-sip. The hook also gives it to SIP over TLS, on every renewal. A certificate of your own: sudo centrex-certificat-sip chain.pem key.pem for SIP, on top of nginx.

  5. Set up email: portal, Settings page, E-mails (SMTP) section, then Send the test. Without it, invitations are not sent.

  6. Open the network: HTTPS, SIP 5060, 5061 (TLS) and 5080, RTP 16384 to 32768 (see Prerequisites). Add your addresses to the ban allow list (Diagnostics page).

  7. Create a customer: Customers page, New customer, with its name and SIP domain (customer.pbx.operator.example). Give it a trunk and a number (A customer’s trunk, Number pool).

  8. Create a user with an extension on the customer’s page, then Open the customer’s space, Team, Phone tab: Email the link or Show the links (QR code) installs and sets up the phone (Users and extensions).

  9. Call: extension to extension, then out and in. The Diagnostics page shows the health of the instance, live calls and the trace of each call.

Next: declare the update repository, schedule backups, plug in monitoring.

Installing from the image

The image is a complete VM ready to start: Debian 13, PostgreSQL, FreeSWITCH and FusionPBX, the PBX and nginx in front of the portal, text-to-speech (Piper, French and English voices), plus keepalived and rsync for active/passive, idle until configured. It is the recommended way to install the PBX.

FileFor
centrex-v3-<version>.qcow2KVM, Proxmox, OpenStack.
centrex-v3-<version>.ovaVMware, VirtualBox (2 vCPUs, 4 GB, VMXNET3 NIC).
centrex-v3-<version>.sha256Checksums of both: check them before importing.

12 GB disk, grown on first boot to the size given to the VM. Add a second, empty disk for call recordings: see Recordings volume.

Granting access

The image has no open account and no password: access is granted on first boot, through cloud-init, as for Debian or Ubuntu images of this kind.

  • OVA (VMware): the import wizard asks for the VM properties.
    • Public SSH key: authorized for the debian account (administrator, sudo).
    • Machine name: fully qualified name (pbx.operator.example). It gives the portal address and the default domain.
    • cloud-init configuration (optional): a #cloud-config document in base64.
  • qcow2 (Proxmox, OpenStack): the hypervisor’s cloud-init tab or fields (user, SSH key, name), or a NoCloud source. Example:
#cloud-config
fqdn: pbx.operator.example
prefer_fqdn_over_hostname: true
users:
  - name: admin
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    ssh_authorized_keys:
      - ssh-ed25519 AAAA… admin@operator

Without a key (VirtualBox, OVA imported without filling in the key), nobody can log in: import again with the key, or attach a NoCloud CD.

First boot

Once, after cloud-init, the VM:

  1. generates its own secrets: passwords of the database, the event socket and the CDRs, self-signed certificate, first secrets encryption key;
  2. creates the FusionPBX database, with the machine name as the default domain;
  3. sets up FreeSWITCH: local event socket (sudo fs_cli without a password), CDRs to the PBX, French voice, 100 new sessions per second, SIP over TLS on 5061 with the self-signed certificate;
  4. fills in the PBX settings (/etc/centrex-v3/centrex.env), with https://<machine name> as the public address;
  5. puts call recordings on their own volume: the second disk if there is one, otherwise a 50 GB file (Recordings volume);
  6. starts PostgreSQL, FreeSWITCH, the PBX and nginx.

No secret is shared by two VMs made from the same image: SSH keys, machine ID and cloud-init state are erased when the image is built.

Next

  1. Create the operator and its first administrator:

    sudo -u centrex sh -c 'set -a; . /etc/centrex-v3/centrex.env; centrex initialiser \
        --operateur "Operator name" --admin-courriel admin@operator.example \
        --admin-nom "First Last"'
    

    It prints a single-use link to choose the password.

  2. Replace the self-signed certificate of nginx (/etc/nginx/sites-available/centrex) and of SIP over TLS: with certbot, with --deploy-hook /usr/local/sbin/centrex-certificat-sip, or with yours, which sudo centrex-certificat-sip chain.pem key.pem gives to SIP (Quick start).

  3. Fix the public address (CENTREX_URL_PUBLIQUE in /etc/centrex-v3/centrex.env, then sudo systemctl restart centrex) if the portal is not served under the machine name: the links in invitations and for phones use it.

  4. Set up email in the portal (Settings page), before creating users.

  5. Declare the update repository (Updates): without it, no new version is reported.

Free software sources

The sources of FreeSWITCH (MPL 1.1), sofia-sip and spandsp (LGPL-2.1) and libks, at the versions built, are in /usr/share/centrex-pbx/sources; FusionPBX is installed from source, unmodified, in /var/www/fusionpbx. Piper (GPL-3.0) is in /opt/centrex-v3/piper, its sources and those of espeak-ng, with the voice credits, in /usr/share/centrex-v3/sources/piper. The notices of the PBX’s libraries are in /usr/share/doc/centrex-v3/.

Installing the package

The PBX also ships as a Debian package, centrex-v3, for a machine where FusionPBX and FreeSWITCH are already installed. The image remains the recommended way: it sets up FreeSWITCH and FusionPBX as the PBX expects, which has to be done by hand here.

Expected machine

  • Debian 13, PostgreSQL (the postgresql-client of the same version, for backups);
  • FreeSWITCH 1.11 and FusionPBX 5.6.5, the tested versions;
  • nginx in front of the portal, with TLS.

What the package contains

PathRole
/usr/bin/centrexThe binary: API, portal, MCP server, background tasks, operations commands.
/usr/share/centrex-v3/portailThe portal and this guide (guide/, guide/en/).
centrex.serviceThe service, under the centrex account (www-data group, for FreeSWITCH files and the FusionPBX cache).
/etc/centrex-v3/centrex.envThe settings, created on first install from the template /usr/share/centrex-v3/centrex.env, never rewritten afterwards.
/etc/centrex-v3/clesThe keyring of the secrets encryption keys, created with a first key.
/usr/share/doc/centrex-v3/License, third-party library notices, reference Prometheus alerts.

Installing

  1. Declare the vendor’s repository (Updates), then sudo apt update && sudo apt install centrex-v3.

  2. Set up FreeSWITCH as the image’s first boot does:

    • event socket: listening on 127.0.0.1, loopback.auto ACL, a password of its own (event_socket.conf.xml);
    • CDRs: load mod_json_cdr and send to http://127.0.0.1:8085/cdr, with basic authentication and a username and password drawn for the purpose (json_cdr.conf.xml);
    • sessions: sessions-per-second at 100 (switch.conf.xml);
    • SIP over TLS: variables internal_ssl_enable set to true and sip_tls_version to tlsv1.2,tlsv1.3 (FusionPBX, Advanced > Variables), then in /etc/freeswitch/tls agent.pem (certificate, chain and key) and cafile.pem (the chain), readable by www-data; fs_cli -x reloadcert reloads them on every renewal;
    • files: www-data group and setgid bit on /var/lib/freeswitch/recordings, /var/lib/freeswitch/storage (and voicemail, json_cdr below it) and /var/cache/fusionpbx, where the PBX writes.
  3. Fill in /etc/centrex-v3/centrex.env (Server settings): FusionPBX’s PostgreSQL database, public address, event socket, CDR credentials.

  4. Create the operator and its first administrator, as for the image:

    sudo -u centrex sh -c 'set -a; . /etc/centrex-v3/centrex.env; centrex migrate; \
        centrex initialiser --operateur "…" --admin-courriel … --admin-nom "…"'
    
  5. Optional, text-to-speech: without it, announcements are uploaded as audio files. Install Piper and its voices like the image does, then set CENTREX_PIPER, CENTREX_VOIX_PIPER and CENTREX_VOIX_PIPER_EN:

    sudo apt install python3-venv
    sudo python3 -m venv /opt/centrex-v3/piper
    sudo /opt/centrex-v3/piper/bin/pip install piper-tts==1.8.0
    sudo mkdir -p /usr/share/centrex-v3/voix && cd /usr/share/centrex-v3/voix
    v=https://huggingface.co/rhasspy/piper-voices/resolve/main
    for f in fr/fr_FR/siwis/medium/fr_FR-siwis-medium en/en_US/ljspeech/medium/en_US-ljspeech-medium; do
        sudo curl -fsSLO "$v/$f.onnx" && sudo curl -fsSLO "$v/$f.onnx.json"
    done
    
  6. sudo systemctl start centrex, then nginx with TLS in front, to 127.0.0.1:8080, forwarding Host (with the port), X-Forwarded-For and X-Forwarded-Proto, and accepting 21 MB uploads (audio files).

  7. Give recordings their own volume (Recordings volume).

  8. In the portal: email (Settings page), then your team and your resellers (Resellers feature to turn on first, same page).

The service applies database migrations on every start. As long as the settings are empty, the package does not start it.

Removing

apt remove stops the service and removes the binary; apt purge also removes the settings, but keeps the keyring: without it, the database secrets would be unreadable.

Server settings

The service settings are in /etc/centrex-v3/centrex.env (root:centrex, 0640: it contains passwords). On a VM installed from the image, the first boot fills them in. After a change: sudo systemctl restart centrex.

Everything else (brand, language, time zone, email, transcription, SSO, assistant, license…) is set in the portal: see Instance settings.

Required

VariableValue
CENTREX_BASE_URLFusionPBX’s PostgreSQL database (postgres://user:password@127.0.0.1:5432/fusionpbx); the PBX creates its centrex schema in it.
CENTREX_URL_PUBLIQUEPublic address of the portal (https://pbx.operator.example): links sent by email, phone links, passkeys.
CENTREX_ESL_ADRESSEFreeSWITCH event socket (127.0.0.1:8021).
CENTREX_ESL_MOT_DE_PASSEIts password.
CENTREX_CDR_IDENTIFIANTSusername:password expected from mod_json_cdr; without them, no call reaches the log.
CENTREX_TROUSSEAUKeyring of the encryption keys (/etc/centrex-v3/cles).

Optional

VariableDefaultFor
CENTREX_PIPER, CENTREX_VOIX_PIPERabsent (set by the image)Text-to-speech (Piper program and French voice model); without it, announcements are uploaded as audio files.
CENTREX_VOIX_PIPER_ENabsent (set by the image)English voice model, for customers set to English.
CENTREX_HUB_URLabsentThe vendor’s Hub, which receives license reports; without it, the report is downloaded from the portal.
CENTREX_PARE_FEUnftablesAddress banning (nftables) or nothing (aucun, bans only recorded in the database).
CENTREX_ECOUTE127.0.0.1:8080Listening address of the portal and API, behind nginx.
CENTREX_CDR_ECOUTE127.0.0.1:8085Listening address for FreeSWITCH CDRs.
CENTREX_LOGinfoLog level (debug for troubleshooting: it may write signed links).

Commands

They run on the VM with the service’s settings: sudo sh -c 'set -a; . /etc/centrex-v3/centrex.env; centrex <command>'.

CommandFor
initialiser --operateur … --admin-courriel … --admin-nom …Create the operator and its first administrator, once.
inviter --courriel …Print a new link to choose one’s password (email not set up yet).
reinitialiser-second-facteur --courriel …Lost phone: remove an account’s two-step verification and close its sessions.
sauvegarder, restaurerBackup and restore.
verifierCheck the machine: settings, keyring, database and schema, FusionPBX, FreeSWITCH (SIP profiles, CDRs), files, portal, service. Each item ok, attention or ÉCHEC; exit code 1 on failure. Read-only.
migrateApply database migrations (the service does it on start).
bascule …Active/passive.
acces-assistance ouvrir --heures …, fermer, etatVendor support access, closed by default, 7 days at most.
audit verifierCheck that the audit log has not been tampered with (exit code 1 otherwise).
secrets nouvelle-cle, rechiffrer, etatRotation of the secrets encryption key.

Recordings volume

Call recordings live on their own fixed-size volume, mounted on /var/lib/freeswitch/recordings. When full, only new recordings fail: calls carry on normally, and the database, FreeSWITCH and voicemail, which are on the system disk, keep their space. Customer announcements are in the same folder: with a full volume, uploading a new announcement fails too.

At installation

The image sets it up on its first boot:

  • with a second, empty disk added to the VM (recommended in production): it is formatted and mounted whole; its size is the one you give it;
  • otherwise, a 50 GB file (/var/lib/centrex-v3/enregistrements.img), mounted in the same place. If the system disk lacks space, the volume is reduced (5 GB stay free on the system disk) and the first-boot log says so; if it is too small, the instance starts without a dedicated volume and the portal home page says so.

A disk is only used if it is entirely empty (no partition, no filesystem); a disk holding data is never formatted.

With the package, set it up yourself, with FreeSWITCH stopped (calls are cut during the operation):

sudo systemctl stop freeswitch
sudo centrex-volume-enregistrements creer --disque /dev/vdb    # a dedicated empty disk
sudo centrex-volume-enregistrements creer --taille 50G         # or a file
sudo systemctl start freeswitch

Recordings already there are copied into the volume.

How much space

FreeSWITCH records in stereo MP3: about 16 MB per hour of recorded call. 50 GB therefore hold about 3,000 hours. Retention (6 months at most) deletes the oldest every hour: count the hours recorded over that period.

Monitoring and growing

  • sudo centrex-volume-enregistrements etat: size, used and free space.
  • The portal home page warns at 80% usage, alerts at 95%, and flags recordings without a dedicated volume; the operator’s email alerts go out at the same thresholds, and below 20% then 10% free space on the system disk.
  • Prometheus: centrex_volume_taille_octets, centrex_volume_libre_octets and centrex_enregistrements_volume_dedie, with reference alerts (see Monitoring).

To grow it, live (calls and recordings carry on):

  • file: sudo centrex-volume-enregistrements agrandir 100G (the system disk must have the space);
  • second disk: grow it in the hypervisor, then sudo centrex-volume-enregistrements agrandir.

A volume cannot shrink. To free space without growing it, shorten recording retention (Settings page, or per customer).

Updates

The PBX is updated with apt, from the vendor’s signed repository. Nothing installs by itself: a new version is reported, and the operator chooses when to install it.

Declaring the repository

The vendor gives you the repository address (here https://repo.vendor.example):

sudo curl -fsSL https://repo.vendor.example/centrex-v3.gpg \
    -o /usr/share/keyrings/centrex-v3.gpg
sudo tee /etc/apt/sources.list.d/centrex-v3.sources >/dev/null <<'EOF'
Types: deb
URIs: https://repo.vendor.example
Suites: stable
Components: main
Signed-By: /usr/share/keyrings/centrex-v3.gpg
EOF
sudo apt update

Being notified

The package list refreshes every day. When a newer version is waiting:

  • the health card of the diagnostics shows the installed version and the waiting one;
  • the centrex_mise_a_jour_disponible metric goes to 1 (MiseAJourDisponible alert, see Monitoring).

Updating

  1. Back up first (Backup).
  2. sudo apt update && sudo apt upgrade. The service restarts and applies the database migrations; live calls are cut: pick a quiet time.
  3. Check the health in the portal (Diagnostics page).

Before any migration, the PBX takes a snapshot of its own database in /var/backups/centrex-v3 (the last three are kept). It is not a full backup: neither the FusionPBX tables, nor the keyring, nor the files are in it.

In active/passive: the passive first, then centrex bascule ceder on the active (the promoted passive migrates the database), then the former active.

Going back to an earlier version

sudo apt install --allow-downgrades centrex-v3=<version>

The package of the target version first brings the database back to its schema, using the binary still installed; if this fails, the installed version is kept and restarts. Going back destroys the data of the undone migrations (the snapshot keeps it): make a full backup first. In active/passive, go back on the active.

Settings after an update

/etc/centrex-v3/centrex.env is never rewritten by an update, and no question is asked. A new setting, always optional, appears commented out in the template /usr/share/centrex-v3/centrex.env: compare it with your settings after an update.

Backup and restore

Backups are the operator’s responsibility; the PBX provides the two commands. They run on the VM, as root, with the service’s settings.

What a backup contains

A .tar archive:

  • the whole database: the PBX and the FusionPBX tables;
  • the keyring of the encryption keys. Without it, the database secrets (trunk and email server passwords, webhook and two-step verification secrets…) would be unreadable; it is therefore included, and the archive is as sensitive as the machine: it is created readable by its owner only, to be kept outside the VM, encrypted or in a protected place;
  • the FreeSWITCH files: call recordings, voicemail, announcements, music on hold;
  • a manifest: PBX version, schema version, date.

The machine configuration (nginx and its certificate, /etc/freeswitch, systemd units) is not part of it: it comes from the installation.

Backing up

Live, services running:

sudo sh -c 'set -a; . /etc/centrex-v3/centrex.env; \
    centrex sauvegarder --sortie /srv/backups/centrex-$(date +%F).tar'

Schedule it daily (systemd timer or cron), copy the archive outside the VM, and keep several generations.

Restoring

On a VM installed with the same version (or a newer version) of the PBX:

  1. Describe the archive, without replacing anything:

    sudo sh -c 'set -a; . /etc/centrex-v3/centrex.env; centrex restaurer --archive centrex-2026-10-12.tar'
    
  2. Stop the PBX and FreeSWITCH (sudo systemctl stop centrex freeswitch), then restore, with --confirmer. The database is replaced in one transaction; keys and files are copied back.

  3. If the archive comes from an earlier version: centrex migrate. An archive from a newer version is refused.

  4. Restart FreeSWITCH, then the PBX.

Try a restore on a test VM from time to time: a backup never restored is not a backup.

Active/passive

Two identical VMs on the same network: the active one carries a floating IP and provides the service, the passive one keeps an up-to-date copy and takes over if the active one fails. Service comes back in under 2 minutes; live calls are lost.

What is copied

  • The database, whole, continuously (PostgreSQL replication): a few seconds behind at most. Phone registrations are part of it: after a failover, phones do not have to register again.
  • The files (voicemail, announcements, music, recordings) and the keyring: every minute. A failover may lose the last minute of voicemail.
  • The configuration: copied when pairing.

The floating IP is the service address: SIP, audio, portal, API and trunks. DNS, phones and providers only see it.

Setting up

Two VMs installed from the image, each with its fixed address, plus a free address for the floating IP. Between them: VRRP (IP protocol 112), PostgreSQL (5432) and SSH (22). As root:

# Active 192.0.2.11, passive 192.0.2.12, floating IP 192.0.2.10.
centrex bascule cle            # on the passive: prints its public SSH key
centrex bascule preparer --ip-flottante 192.0.2.10/24 --autre 192.0.2.12 \
    --cle-autre "ssh-ed25519 …"     # on the active
centrex bascule rejoindre --actif 192.0.2.11 --cle-actif "ssh-ed25519 …"   # on the passive

preparer prints the full rejoindre command. Options: --temoin (address each VM must reach to keep or take the IP; the gateway by default) and --routeur (VRRP identifier, 51 by default, unique on the network).

PostgreSQL replication between the two VMs is not encrypted: link them through a private network.

Failover

  • The active announces itself every second; after 3 seconds of silence, the passive takes the IP.
  • A health check (PostgreSQL, FreeSWITCH, PBX) runs on the active; three failures in a row, and it gives up the IP.
  • A VM that can no longer reach the witness gives up the IP and stops: an isolated VM never promotes itself.
  • A former active that comes back stays passive: no automatic failback.

Operating

CommandFor
centrex bascule etatRole of the VM, state of both databases, witness, services, replication lag, last file copy.
centrex bascule cederOn the active: the passive takes over (maintenance, update).
  • Log: /var/log/centrex-bascule.log.
  • Update: the passive first, then ceder on the active, then the former active (Updates).
  • Monitoring: on the active, centrex_bascule_passives_connectees (1 in steady state) and centrex_replication_retard_secondes (Monitoring).

Monitoring

The PBX exposes its metrics in Prometheus format, and ships reference alerts.

Plugging in Prometheus

Two ways to read /metrics:

  • From the VM, with nothing to set up: Prometheus, or a local agent that relays (Grafana Agent, node_exporter…), scrapes 127.0.0.1:8080.

    scrape_configs:
      - job_name: centrex
        static_configs:
          - targets: ["127.0.0.1:8080"]
    
  • From your Prometheus, over https, with a monitoring token: API menu of the portal, Monitoring (Prometheus) card, Create a token. The configuration to copy is shown only once, token included:

    scrape_configs:
      - job_name: centrex
        scheme: https
        authorization:
          credentials: cxs_…
        static_configs:
          - targets: ["centrex.operateur.exemple"]
    

    One token at a time: Replace the token invalidates the old one at once, Revoke makes /metrics local only again. The card shows Prometheus’s last read. Without a valid token, a request that goes through nginx is refused. Only an operator admin, signed in in person, manages this token: neither an API key nor the publisher’s support access.

Metrics

MetricMeaning
centrex_info{version, schema}Installed version and database schema.
centrex_mise_a_jour_disponible1 if a newer version is waiting in the repository.
centrex_freeswitch_joignable1 if FreeSWITCH answers.
centrex_appels_en_coursLive calls on the instance.
centrex_postes_inscritsRegistered phones and softphones.
centrex_licence_jours_restantsLicense days left; negative: expired for that many days.
centrex_licence_bloquee1 if the license expired more than 30 days ago (user creation blocked).
centrex_trunks{etat}Customer trunks by registration state (REGED, FAIL_WAIT, NOREG for an IP-authenticated trunk, INCONNU).
centrex_cdr_recusCalls kept in the log.
centrex_cdr_dernier_horodatage_secondesWhen the last call reached the log.
centrex_webhooks_en_attenteWebhook deliveries waiting.
centrex_webhooks_abandonnesDeliveries given up after all their attempts.
centrex_ip_banniesAddresses banned right now.
centrex_volume_taille_octets{volume}Volume size: enregistrements (theirs, see Recordings volume) or systeme (system disk).
centrex_volume_libre_octets{volume}Free bytes on the volume.
centrex_enregistrements_volume_dedie1 if recordings have their own volume, 0 if they share the system disk.
centrex_bascule_passives_connecteesActive/passive, on the active: passives replicating (1 in steady state).
centrex_replication_retard_secondesActive/passive, on the active: lag of the passive.

Reference alerts

/usr/share/doc/centrex-v3/supervision/alertes.yml, to load into Prometheus: PBX or FreeSWITCH unreachable, trunk not registered, no more calls in the log during the day, webhooks waiting or given up, too many banned addresses, update waiting for a week, active/passive without a passive or lagging, license about to expire or expired, sudden drop of registered phones, recordings volume at 80% then 95% or sharing the system disk, less than 10% free on the system disk. The thresholds are starting points, to adjust.

Add an alert on CPU (above 75% at peak, see Sizing) and on disk space, with your system agent.

In active/passive, the PBX is stopped on the passive: monitor both VMs, and only raise “PBX unreachable” if neither answers.

In the portal

The Diagnostics page shows the health of the instance (FreeSWITCH, database, call log, webhooks, trunks), live calls and banned addresses; email alerts (trunk down or restored, license) are set in the instance settings.

Local transcription

The PBX transcribes voicemail through the OpenAI-compatible transcription API (POST …/audio/transcriptions). The service can be online (OpenAI, Mistral…) or installed on your premises: messages then never leave your network. The PBX does not ship an engine; any server that exposes this API works.

Example: Speaches (faster-whisper)

Speaches serves Whisper models through faster-whisper behind this API. On a separate VM, with Docker:

# CPU only (replace latest-cpu with latest-cuda and add --gpus=all with an NVIDIA card)
docker run --detach --restart unless-stopped --name speaches \
  --publish 8000:8000 \
  --volume hf-hub-cache:/home/ubuntu/.cache/huggingface/hub \
  ghcr.io/speaches-ai/speaches:latest-cpu

# Download a multilingual model (French and English)
curl -X POST http://localhost:8000/v1/models/Systran/faster-whisper-small

Then, in the portal, Settings page, Voicemail transcription section:

FieldValue
Addresshttp://VM-ADDRESS:8000/v1
ModelSystran/faster-whisper-small
Languageempty: each message in its customer’s language
Keyempty, unless you protect the service

Try checks that the service responds.

  • Model: small runs on a 4-core CPU for a few hundred messages a day; medium or large-v3 transcribe better, preferably with a graphics card. Do not use the .en models, English only.
  • Network: the service has no authentication by default; open it to the PBX only (firewall), or put it behind a proxy that requires a key.
  • Outage: a transcription may take up to 7 minutes. On failure, the PBX retries after 1 minute then after 5 minutes, then sends the message’s email without the text.

These commands come from the Speaches documentation, to follow for recent versions.

Software security

The software has not been through an external security audit (penetration test by a firm). Instead, each version goes through:

  • static analysis of the code (compiler warnings treated as errors);
  • a dependency audit: known vulnerabilities, licences and origin of each library;
  • a secret scan of the whole code history;
  • isolation tests between customers: a customer, a reseller or an API key reads or changes nothing outside its scope;
  • tests on a real FreeSWITCH: calls, call flows, forwarding, encryption, on every change;
  • a written security review, redone at each major version.

If your business requires it, you can have a penetration test carried out at your own cost: let the vendor know, who will give you the scope and respond to the findings.

What protects your instance

  • Accounts: long passwords, lockout after 5 failures, second factor required for the operator’s and resellers’ teams, passkeys.
  • API: revocable keys, rights limited to their organisation.
  • SIP: banning of addresses that fail to authenticate too often (Diagnostics), SIP over TLS and SRTP offered.
  • Secrets (trunks, email server, keys) encrypted in the database; none is returned by the API.
  • Audit log of every change, readable in the portal.

Report a vulnerability

Write to the vendor, at the security address in your contract, with “Security” in the subject. Give the version (version in /api/v1/sante), what reproduces the problem and its effect. Answer within 3 working days. The fix is published in the apt repository and you are told directly, with the version that fixes it: apply it like an update.

Troubleshooting

Most incidents are handled from the portal, Diagnostics page (see Diagnostics), without SSH: health, live calls, live SIP capture, call trace, banned addresses, reloading and restarting FreeSWITCH. Each action there is recorded in the audit log, and also exists in the API and the MCP server.

Typical incidents

SymptomWhere to lookWhat to do
A customer has no more inbound or outbound callsHealth: its trunk is not REGED, with the provider’s last refusal401/403: fix the username or password on the trunk page; 404: account unknown to the provider; 408/503 or FAIL_WAIT: the provider does not answer (address, network). Then Re-register.
A whole site has no phones, the portal answersBanned IP addresses: the site’s address, the account targeted and the faulty deviceFix the device (password, domain), then Unban; an address that must never be banned goes in the allow list.
A bot is trying passwordsLive capture, Banned IP addressesBan an address by hand, for the duration chosen.
“My call didn’t go through”, “it cut off”The customer’s Call log, then the call’s TraceThe trace shows the steps followed, the cause and the SIP code. A call flow error: Replay publication on the customer’s page.
A phone does not register, a call fails without explanationLive captureThe customer’s SIP messages, as text, as a ladder diagram, or as a pcap for Wireshark.
A phone set to record has no recordingssudo centrex verifier (“FusionPBX” line) and the service logFusionPBX’s recording template must be readable (CENTREX_MODELES_FUSIONPBX, under /var/www/fusionpbx by default); the service adds it to the customers that lack it when it restarts. Full volume: see Recordings.
A call is stuckLive callsHang up.
Nothing works in FreeSWITCHHealthReload configuration, then as a last resort Restart FreeSWITCH.

On the VM

ForCommand
Check everything at oncecentrex verifier
Service statesystemctl status centrex freeswitch nginx postgresql
PBX logsjournalctl -u centrex (level set by CENTREX_LOG)
FreeSWITCH logs/var/log/freeswitch/freeswitch.log
Image first bootjournalctl -u centrex-premier-demarrage
Active/passivecentrex bascule etat, /var/log/centrex-bascule.log
Audit log integritycentrex audit verifier

The centrex commands run with the service’s settings: see Server settings.

Accounts

  • Forgotten password: the “Forgot your password?” link on the sign-in page, or Resend invitation on the customer’s page. Without email set up: centrex inviter --courriel … prints the link.
  • Two-step verification phone lost: an administrator resets it in the portal, or centrex reinitialiser-second-facteur --courriel … on the VM.
  • Account locked after 5 failures: it unlocks by itself after 15 minutes.

FusionPBX interface

The PBX writes into FusionPBX, but its interface is closed: nginx does not serve it. For troubleshooting, open it locally for the time of the intervention. Do not change anything from this interface: the PBX would not find its settings there. Its dialplans look incomplete there, which is expected.

  1. On the VM, install PHP-FPM and serve FusionPBX on the local address only:

    sudo apt install php8.4-fpm
    sudo tee /etc/nginx/sites-available/fusionpbx-local >/dev/null <<'NGINX'
    server {
    	listen 127.0.0.1:8081;
    	root /var/www/fusionpbx;
    	index index.php;
    	location / { try_files $uri $uri/ /index.php?$args; }
    	location ~ \.php$ {
    		include snippets/fastcgi-php.conf;
    		fastcgi_pass unix:/run/php/php8.4-fpm.sock;
    	}
    	location ~ /\. { deny all; }
    	location ~ \.(db|sql|conf)$ { deny all; }
    }
    NGINX
    sudo ln -s ../sites-available/fusionpbx-local /etc/nginx/sites-enabled/
    sudo nginx -t && sudo systemctl reload nginx
    
  2. Create a FusionPBX superadmin account, as its installer does, in the default domain (the one created on first boot and named after the machine:

    read -r -s -p "Password: " MDP; echo
    HASH=$(MDP="$MDP" php -r 'echo password_hash(getenv("MDP"), PASSWORD_DEFAULT);')
    sudo -u postgres psql -v ON_ERROR_STOP=1 -v hash="$HASH" -v domaine="$(hostname -f)" fusionpbx <<'SQL'
    with d as (select domain_uuid from v_domains where domain_name = :'domaine'),
    u as (insert into v_users (user_uuid, domain_uuid, username, password, salt, user_enabled)
          select gen_random_uuid(), domain_uuid, 'depannage', :'hash', '', 'true' from d
          returning user_uuid, domain_uuid)
    insert into v_user_groups (user_group_uuid, domain_uuid, group_name, group_uuid, user_uuid)
    select gen_random_uuid(), u.domain_uuid, 'superadmin', g.group_uuid, u.user_uuid
    from u, v_groups g where g.group_name = 'superadmin';
    SQL
    
  3. From your computer, open an SSH tunnel, then http://127.0.0.1:8081 in the browser:

    ssh -L 8081:127.0.0.1:8081 debian@VM
    

    Sign in as depannage@ followed by the machine name (hostname -f): the address 127.0.0.1 names no domain, the name after @ gives it.

  4. After the intervention, close it again:

    sudo rm /etc/nginx/sites-enabled/fusionpbx-local && sudo systemctl reload nginx
    sudo systemctl disable --now php8.4-fpm
    sudo -u postgres psql fusionpbx -c "update v_users set user_enabled = 'false' where username = 'depannage'"
    

Vendor support

For an intervention by the vendor, open time-limited access: portal, Diagnostics page, Support access section, or centrex acces-assistance ouvrir --heures 24. It is closed by default, can be closed at any time, and each of its actions is in the audit log.