License Activation
This page tells you exactly where and how to put a Miuda license key into RustPBX, how to confirm it was accepted, and how to diagnose a rejected key.
It applies to the Commerce (Enterprise) build. A Community build does not compile license support and ignores the [licenses] section entirely. See Editions if you are not sure which build you have.
1. Before you edit anything
- You need a license key. Get a 7-day key from the free trial page, or a paid key when you buy a plan.
- You need the commercial image (
docker.cnb.cool/miuda.ai/rustpbx:latest) or a binary built with thecommercefeature. The community image will not read the key. config.tomlis the same file you already use for the rest of the configuration. In the container it lives at/app/config.toml; with a bare-metal binary it is whatever you pass to--conf.
2. The exact config.toml schema
A license is configured with two tables under a top-level [licenses] section:
[licenses.addons]— maps each commercial addon ID to a key name (a label you choose).[licenses.keys]— maps that key name to the actual license key string.
Both tables are flat maps of string to string. The value in [licenses.addons] must be a key name that also exists in [licenses.keys]; the key name itself is arbitrary and can be shared by several addons.
[licenses]
[licenses.addons]
# addon id (left) -> key name (right)
voicemail = "enterprise"
ivr_editor = "enterprise"
[licenses.keys]
# key name (left) -> the license key you received
enterprise = "TRIAL-XXXXXXXXXXXXXXXXXXXX"
A complete minimal example for a trial key that should cover every commercial addon you run:
[licenses]
[licenses.addons]
voicemail = "trial"
ivr_editor = "trial"
wholesale = "trial"
[licenses.keys]
trial = "TRIAL-XXXXXXXXXXXXXXXXXXXX"
Replace TRIAL-XXXXXXXXXXXXXXXXXXXX with the key from the Try page or your purchase confirmation. Keep the quotes.
You do not have to hand-edit this. In the Commerce console, Addons → Licenses has a Verify button that checks a key and writes [licenses.addons] and [licenses.keys] into config.toml for you. Manual editing is fully supported and is often easier in GitOps workflows.
2.1 Addon IDs
The left-hand side of [licenses.addons] must match the addon’s registered ID exactly.
| Addon ID | Addon |
|---|---|
voicemail | Voicemail Pro |
ivr_editor | IVR Editor |
wholesale | Wholesale |
acme | ACME |
archive | Archive |
transcript | Transcript |
queue | Queue |
observability | Observability |
Addons from other bundles (for example endpoint manager, enterprise auth, SBC, telemetry) use their own IDs. Use the ID shown in Console → Addons rather than guessing.
ID spelling matters
Historically the same addon has appeared with two spellings in sample configs — for example endpoint_manager (underscore) in one file and endpoint-manager (hyphen) in another. A license is only found when the string matches the addon’s actual ID. If an addon still reports “unlicensed” after you add a key, check the ID spelling first.
2.2 How the lookup works
For a given addon, RustPBX looks up its ID in [licenses.addons] to get a key name, then looks up that name in [licenses.keys] to get the key value. If either lookup misses, the addon has no key and is treated as unlicensed. One key name can serve many addons, which is why a single Enterprise key is normally listed once in [licenses.keys] and referenced by each addon it covers.
3. Apply the change
-
Save
config.toml. -
Validate it before restarting where possible:
rustpbx --conf config.toml check-configcheck-configparses the configuration and exits without starting the server. -
Restart the service (or use the console’s reload action) so the new
[licenses]section is read at startup.
4. Confirm the license took effect
After startup, confirm the key is accepted in any of these places:
| Signal | What to look for |
|---|---|
| Console → Addons → Licenses | The addon shows a valid license with plan, expiry date and scope. |
| Startup logs | A line Verifying license key <prefix>... against https://miuda.ai/api/verify, followed by License verified for addon <id> with key <name>: valid=true, expiry=... for each licensed addon. |
| Console notification | Unlicensed commercial addons raise a flag in the Addons list; it clears once a valid key is present. |
Verification is online: RustPBX sends the key to https://miuda.ai/api/verify with a 5-second timeout. The result is cached in memory for the lifetime of the process, so the license server is contacted at startup, not on every call. If the network is unavailable but a cached result exists, RustPBX reuses the cached result; on a cold start with no network and no cache the verification fails.
The verify request currently sends only the license key ({"license_key": "..."}). It does not send an installation identifier or an email address.
5. Diagnosing a rejected license
All rejection reasons come from the license server’s response. The meanings are:
| Reject reason | Meaning and fix |
|---|---|
| License has expired | The key’s expiry date has passed (this is the normal end of a 7-day trial). Buy or renew a plan; renewal extends the same key. |
| License is deactivated | The key was switched off, typically by support (refund, chargeback, abuse, or a migrated account). Contact sales. |
| License not authorized for this email | The key is restricted to a specific email and the verification carried a different one. |
| License requires email authorization | The key is restricted to an email, but the verification carried none. |
| Maximum IP limit reached (N IPs allowed) | The key has already been seen from the maximum number of distinct IP addresses. See the FAQ. |
| License key not found (HTTP 404) | The key string does not exist on the server. Check for typos, truncation, or a key copied with surrounding whitespace. |
5.1 Checking the raw server response
If the console shows only a generic “invalid” message, verify the key directly against the license server:
curl -s https://miuda.ai/api/verify \
-H 'Content-Type: application/json' \
-d '{"license_key":"TRIAL-XXXXXXXXXXXXXXXXXXXX"}'
The JSON response contains valid, expiry, plan, scope, and — when rejected — reject_reason. That reject_reason is authoritative.
Two different vocabularies
The license server returns human-readable reasons such as "License has expired", while the RustPBX console’s friendly-message mapping is written against machine-style codes such as license_expired, ip_limit_exceeded, email_mismatch, email_required, key_not_found. When the two do not match, the console falls back to a generic message. Always trust the raw reject_reason from the endpoint above over the console text.
5.2 Common non-license causes
- Key accepted but the addon is still disabled — the addon must also be enabled (
[proxy] addons = [...]) and compiled into your build. A license unlocks an addon; it does not enable it. - Key accepted but the addon reports out of scope — the key’s plan scope does not include that addon. Check the plan in Pricing.
- Everything looks right but nothing changes — you are running a Community build, or the service was not restarted after the edit.
6. Known ambiguity: [licenses] email
The console’s error text for email-restricted keys tells you to “set [licenses] email in your config.toml”. That field does not exist in the current license configuration struct, which defines only addons and keys, and the verify request does not send an email. Setting email under [licenses] is therefore silently ignored rather than rejected.
The [licenses] section name itself is unambiguous — it is [licenses], matched by the licenses field of the top-level config. If you hold a key that is restricted to an authorized email, treat it as a sales/support matter rather than expecting a config field to satisfy the check.
If you are editing a config copied from sample files, note that some samples define [licenses.addons] without a matching [licenses.keys] table. That maps every addon to a name that has no value, so nothing is licensed. Always provide both tables.
7. Implementation references
| Topic | Source |
|---|---|
licenses field on config (commerce-only) | rustpbx/src/config.rs:557-560 |
LicenseConfig struct (addons, keys) | rustpbx/src/config.rs:684-689 |
| Addon-to-key lookup | rustpbx/src/config.rs:692-698 |
| Verify request and caching | rustpbx/src/license.rs:126-183 |
| Console Verify button writes the tables | rustpbx/src/console/handlers/licenses.rs:105-155 |
| Reject reasons | miuda.docs/src/handlers/licenses.rs::verify_license |
Next: Pricing to buy a plan, or FAQ for operational questions.