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 the commerce feature. The community image will not read the key.
  • config.toml is 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 IDAddon
voicemailVoicemail Pro
ivr_editorIVR Editor
wholesaleWholesale
acmeACME
archiveArchive
transcriptTranscript
queueQueue
observabilityObservability

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

  1. Save config.toml.

  2. Validate it before restarting where possible:

    rustpbx --conf config.toml check-config
    

    check-config parses the configuration and exits without starting the server.

  3. 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:

SignalWhat to look for
Console → Addons → LicensesThe addon shows a valid license with plan, expiry date and scope.
Startup logsA 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 notificationUnlicensed 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 reasonMeaning and fix
License has expiredThe 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 deactivatedThe key was switched off, typically by support (refund, chargeback, abuse, or a migrated account). Contact sales.
License not authorized for this emailThe key is restricted to a specific email and the verification carried a different one.
License requires email authorizationThe 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

TopicSource
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 lookuprustpbx/src/config.rs:692-698
Verify request and cachingrustpbx/src/license.rs:126-183
Console Verify button writes the tablesrustpbx/src/console/handlers/licenses.rs:105-155
Reject reasonsmiuda.docs/src/handlers/licenses.rs::verify_license

Next: Pricing to buy a plan, or FAQ for operational questions.