Voice and SIP
Portable voice configuration and inbound SIP routing.
A Package can answer phone calls. It brings how the line behaves. The speech providers, the carrier, the trunk and the number all belong to the installer. At most one voice configuration and one SIP routing template per Package.
Voice configuration
name: Phone Line
sttProvider: soniox
sttConfig:
languageHints: [en]
sttSlot: speech-in
ttsProvider: cartesia
ttsConfig:
language: en
speed: 1
ttsSlot: speech-out
callBehavior:
callFlow:
firstMessageMode: say
firstMessage: Thanks for calling. This is an automated assistant — how can I help?
recordCalls: false
consentMessage: null
agent: agents.call-handler| Field | Meaning |
|---|---|
name | The configuration's name in Studio. |
sttProvider, sttConfig | The speech-to-text provider the configuration was built for, and its settings (providers, keys). |
sttSlot | The stt Slot the installer fills with their own provider and credential. |
ttsProvider, ttsConfig, ttsSlot | The same for text-to-speech (keys). |
callBehavior | How the call runs: the first message, turn-taking, interruptions and voice detection (keys). |
recordCalls | Whether calls are recorded. |
consentMessage | What callers hear before anything else when calls are recorded. See Recording and consent. |
agent | The Agent that handles the call (agents.<name>). |
voice:
ref: ./voice/after-hours-line.yaml
slots:
- { id: speech-in, type: stt, required: true, allowedProviders: [soniox] }
- { id: speech-out, type: tts, required: true, allowedProviders: [cartesia] }Providers
The provider keys a Package may use for sttProvider and ttsProvider, and in an stt or tts
Slot's allowedProviders. They are the providers this platform runs. Write them in lowercase
(they are matched without regard to case).
| Side | Provider keys |
|---|---|
| Speech-to-text | soniox, cartesia, elevenlabs, google, azure |
| Text-to-speech | cartesia, elevenlabs, azure, google, soniox |
allowedProvidersnarrows which of these the installer may choose. Omit it to offer them all.- An
sttProviderorttsProvideroutside this list:validatewarns (VOICE_PROVIDER_UNSUPPORTED).validatedoes not checkallowedProvidersentries; the install refuses to bind a Slot to a key outside this list (SLOT_BINDING_UNRESOLVED), and a Slot whoseallowedProvidersare all outside it cannot be bound at all. - The installer chooses the provider. If they choose the one you built for, your
sttConfigorttsConfigis kept. If they choose another one, it is dropped (its keys mean nothing to the other provider) and they set that provider up in Studio after the install. - The credential comes from the Slot: the install writes it into the configuration as
apiKey. Never setapiKeyyourself;validatewarns (VOICE_CONFIG_KEY_IGNORED). - For
google(both sides) the Slot'sapiKeyis not used. Google authenticates with the operator's service account (serviceAccountJson, set up in Studio after the install) or the voice worker's own Google credentials. Never setserviceAccountJsonin a Package either;validatewarns.
How the settings are read
sttConfig, ttsConfig and callBehavior are free-form in the schema: validate and build
accept any key. A call reads only the keys listed below, for the provider in use, and drops every
other one. validate warns about each key a call would ignore (VOICE_CONFIG_KEY_IGNORED), so an
unknown key never fails a build, but it never does anything either.
- A value the platform cannot turn into one the provider accepts is dropped too, and the provider's own default applies. "If omitted" in the tables below is also what you get then.
- Language values take a language code (
en,en-GB) or an English language name (English).Automeans "detect", where the provider can. - No value may look like an identifier (a UUID). The install refuses a configuration that carries
one, because an id never means the same thing in another Organization. Cartesia voice ids are
UUIDs, so leave Cartesia's
voiceout; the installer picks the voice in Studio. region,location, voice ids and ElevenLabsdictionariesname resources in one provider account. They refer to the installer's own account, so leave them out unless the installer is expected to have the same ones.- Voices and models change often at every provider. The keys below are stable; which voice or model ids exist is the provider's catalog, not this page's.
sttConfig
soniox
sttProvider: soniox
sttConfig:
sttModel: stt-rt-v3 # a model id from Soniox
languageHints: [en, de]
context: Industrial pumps, valves and service contracts.
diarization: false
languageId: true| Key | Type | If omitted | Effect |
|---|---|---|---|
sttModel | string, a Soniox model id | Soniox's default | The recognition model. A value containing a space is dropped. |
languageHints | list of languages | none | The languages to expect. Auto and unknown names are dropped. |
context | string | none | Words and phrases that steer recognition (names, product terms). |
diarization | boolean | Soniox's default | Tell speakers apart. |
languageId | boolean | Soniox's default | Identify the language being spoken. |
cartesia
sttProvider: cartesia
sttConfig:
model: ink-2
language: en| Key | Type | If omitted | Effect |
|---|---|---|---|
model | ink-whisper or ink-2 | ink-whisper | The recognition model. Any other value is ink-whisper. |
language | language | detect | The language spoken. |
elevenlabs
sttProvider: elevenlabs
sttConfig:
language: en
tagAudioEvents: false| Key | Type | If omitted | Effect |
|---|---|---|---|
language | language | detect | The language spoken. |
tagAudioEvents | boolean | ElevenLabs' default | Mark non-speech sounds (laughter, music) in the transcript. |
google
sttProvider: google
sttConfig:
sttModel: telephony
languages: [en-GB, de-DE]
detectLanguage: false
addPunctuation: true
spokenPunctuation: false
location: eu
minConfidence: 0.6
keywords:
- { keyword: Elizon, boost: 5 }| Key | Type | If omitted | Effect |
|---|---|---|---|
sttModel | long, short, telephony, medical_dictation, medical_conversation, chirp, chirp_2, chirp_3, latest_long or latest_short | Google's default | The recognition model. Any other value is dropped. |
languages | list of locales (en-GB) or English names | Google's default | The languages to expect. |
detectLanguage | boolean | Google's default | Detect the language spoken. |
addPunctuation | boolean | Google's default | Punctuate the transcript. |
spokenPunctuation | boolean | Google's default | Turn spoken punctuation ("comma") into the mark. |
location | string, a Google Cloud location (global, eu, us-central1) | Google's default | Where recognition runs. Lowercased. |
minConfidence | number, 0 to 1 | Google's default | Results below this confidence are discarded. |
keywords | list of { keyword, boost } | none | Words to favour, each with a weight (boost, 1 when left out). |
The Google credential is the operator's service account, set up in Studio; the Slot's apiKey is
not used.
azure
sttProvider: azure
sttConfig:
region: westeurope
language: en-GB
profanity: Masked
segmentation: Semantic
phraseList: [Elizon, impeller]
explicitPunctuation: false
trueTextPostProcessing: true| Key | Type | If omitted | Effect |
|---|---|---|---|
region | string, an Azure region | none | The region of the installer's Azure Speech resource. |
language | locale (en-GB) or English name | Azure's default | The language spoken. |
profanity | Masked, Removed or Raw | Azure's default | How profanity appears in the transcript. |
segmentation | string, an Azure segmentation strategy | Azure's default | How speech is split into phrases. |
phraseList | list of strings | none | Words and phrases to favour. |
explicitPunctuation | boolean | Azure's default | Turn spoken punctuation into the mark. |
trueTextPostProcessing | boolean | Azure's default | Clean up the transcript (disfluencies, formatting). |
ttsConfig
cartesia
ttsProvider: cartesia
ttsConfig:
model: sonic-2
language: en
speed: 1
volume: 1
emotion: content| Key | Type | If omitted | Effect |
|---|---|---|---|
voice | string, a Cartesia voice id | Cartesia's default | Leave it out: Cartesia voice ids are UUIDs, which a Package cannot carry. |
model | string, a Cartesia model id | sonic-3 | The voice model. A value containing a space is sonic-3. |
language | language | detect | The language spoken. |
speed | number | Cartesia's default | Speaking speed. |
volume | number | Cartesia's default | Loudness. |
emotion | string, a Cartesia emotion | neutral | The tone of voice (neutral, content, happy, …). |
elevenlabs
ttsProvider: elevenlabs
ttsConfig:
voice: 21m00Tcm4TlvDq8ikWAM # a voice id from ElevenLabs
model: eleven_flash_v2_5
stability: 0.5
similarityBoost: 0.75
style: 0
speed: 1
useSpeakerBoost: true
latency: 1
audioEncoding: PCM 16000Hz
enableSsml: false
preferredAlignment: Original
dictionaries:
- { dictionaryId: pd-product-names, versionId: v1 }| Key | Type | If omitted | Effect |
|---|---|---|---|
voice | string, an ElevenLabs voice id | ElevenLabs' default | The voice. |
model | string, a model id or its name (Eleven Flash v2.5) | eleven_multilingual_v2 | The voice model. |
stability | number, 0 to 1 | ElevenLabs' default | How steady the delivery is. |
similarityBoost | number, 0 to 1 | ElevenLabs' default | How closely the output keeps to the original voice. |
style | number, 0 to 1 | ElevenLabs' default | How much style is exaggerated. |
speed | number | ElevenLabs' default | Speaking speed. |
useSpeakerBoost | boolean | ElevenLabs' default | Boost similarity to the original speaker. |
latency | number | ElevenLabs' default | Streaming latency optimisation level. |
audioEncoding | MP3 44.1kHz 128kbps, MP3 44.1kHz 192kbps, PCM 16000Hz, PCM 22050Hz, PCM 24000Hz or PCM 44100Hz | ElevenLabs' default | The audio format. Any other value is dropped. |
enableSsml | boolean | ElevenLabs' default | Read SSML tags in the text. |
preferredAlignment | string (Original, any case) | original | Which text word timings are aligned to. |
dictionaries | list of { dictionaryId, versionId } | none | Pronunciation dictionaries to apply. |
azure
ttsProvider: azure
ttsConfig:
region: westeurope
voice: en-GB-SoniaNeural
sampleRate: PCM 24 KHz 16 bit
voiceRate: 1
volume: 50
pitch: Medium
style: Friendly| Key | Type | If omitted | Effect |
|---|---|---|---|
region | string, an Azure region | none | The region of the installer's Azure Speech resource. |
voice | string, an Azure voice name | Azure's default | The voice. |
sampleRate | PCM 8 KHz 16 bit, PCM 16 KHz 16 bit, PCM 22.05 KHz 16 bit, PCM 24 KHz 16 bit, PCM 44.1 KHz 16 bit or PCM 48 KHz 16 bit | Azure's default | The audio sample rate. Any other value is dropped. |
voiceRate | number, 0.5 to 2 | Azure's default | Speaking rate. |
volume | number, 0 to 100 | Azure's default | Loudness. |
pitch | Extra Low, Low, Medium, High or Extra High | Azure's default | Pitch. |
style | string, an Azure speaking style (Friendly, Cheerful, …) | none | The speaking style. Neutral means none. |
google
Google text-to-speech uses no credential from the Slot: the Slot's apiKey is not used, and the
voice worker authenticates with its own Google credentials.
ttsProvider: google
ttsConfig:
voice: en-GB-Neural2-A
encoding: LINEAR16
rate: 1
pitch: 0
volume: 0| Key | Type | If omitted | Effect |
|---|---|---|---|
voice | string, a Google voice name | Google's default | The voice. |
encoding | LINEAR16, MP3, OGG_OPUS, MULAW or ALAW | Google's default | The audio encoding. Any other value is dropped. |
rate | number | Google's default | Speaking rate. |
pitch | number | Google's default | Pitch, in semitones. |
volume | number | Google's default | Volume gain, in dB. |
soniox
ttsProvider: soniox
ttsConfig:
voice: Aria # a voice from Soniox
model: tts-rt-v1 # a model id from Soniox
language: en
format: MP3
sampleRate: 24 kHz| Key | Type | If omitted | Effect |
|---|---|---|---|
voice | string, a Soniox voice | Soniox's default | The voice. |
model | string, a Soniox model id | Soniox's default | The voice model. A value containing a space is dropped. |
language | language | Soniox's default | The language spoken. |
format | MP3 or PCM (WAV) | Soniox's default | The audio format. Any other value is dropped. |
sampleRate | string in kHz (24 kHz) | Soniox's default | The audio sample rate. |
callBehavior
Four sections, each optional. Nothing else in callBehavior is read: a greeting key, for
example, does nothing. What callers hear first is callFlow.firstMessage.
callBehavior:
callFlow:
firstMessageMode: say
firstMessage: Thanks for calling. This is an automated assistant — how can I help?
userAwayTimeout: 20
userAwayAction: hangup
hangUpAwayTime: 30
minConsecutiveSpeechDelay: 0.2
preemptiveGeneration: true
filterMarkdown: true
filterEmoji: true
timing:
turnDetectionProvider: Multilingual Model
unlikelyThreshold: 0.2
minEndpointingDelay: 0.5
maxEndpointingDelay: 2
interruption:
allowInterruptions: true
minInterruptionDuration: 0.5
minInterruptionWords: 1
discardAudioIfUninterruptible: true
falseInterruptionTimeout: 2
resumeFalseInterruption: true
sensitivity:
vadProvider: silero
minSpeechDuration: 0.05
minSilenceDuration: 0.55
prefixPaddingDuration: 0.5
maxBufferedSpeech: 60
activationThreshold: 0.5
forceCpu: truecallFlow
| Key | Type | If omitted | Effect |
|---|---|---|---|
firstMessageMode | say, generate or none | none | say speaks firstMessage as written; generate has the Agent open the call, using firstMessage as its instructions. |
firstMessage | string | empty | The opening line (or, with generate, the instructions for it). Nothing is said while it is empty. |
userAwayTimeout | number, seconds | 15 | How long the caller can be silent before they count as away. |
userAwayAction | hangup, nothing, say or generate | hangup | What happens when the caller is away. say and generate need an away message the platform does not carry yet, so they do nothing. |
hangUpAwayTime | number, seconds | none | With say or generate, how long to wait after the away message before hanging up. Since those two do nothing yet, this has no effect today. |
minConsecutiveSpeechDelay | number, seconds | 0 | The shortest pause between two things the Agent says. |
preemptiveGeneration | boolean | false | Start the reply before the caller has finished, to answer faster. |
filterMarkdown | boolean | true | Strip Markdown from the text before it is spoken. Setting both this and filterEmoji to false does not turn stripping off: with neither, the worker falls back to stripping both. |
filterEmoji | boolean | true | Strip emoji from the text before it is spoken. See filterMarkdown for setting both to false. |
timing
| Key | Type | If omitted | Effect |
|---|---|---|---|
turnDetectionProvider | Auto, STT, VAD, Realtime LLM, Manual, Multilingual Model or English Model | Auto | How the end of the caller's turn is detected. Auto lets the platform choose. |
unlikelyThreshold | number, 0 to 1 | none | Below this end-of-turn likelihood, the Agent keeps waiting. |
minEndpointingDelay | number, seconds | 0.5 | The shortest wait after the caller stops before the Agent answers. |
maxEndpointingDelay | number, seconds | 3 | The longest wait, however unsure the turn detection is. |
interruption
| Key | Type | If omitted | Effect |
|---|---|---|---|
allowInterruptions | boolean | true | Whether the caller can talk over the Agent. |
minInterruptionDuration | number, seconds | 0.5 | How long the caller must speak to interrupt. |
minInterruptionWords | number | 0 | How many words the caller must say to interrupt. |
discardAudioIfUninterruptible | boolean | true | Drop what the caller says while the Agent cannot be interrupted. |
falseInterruptionTimeout | number, seconds | 2 | How long to wait before deciding an interruption was a false alarm. |
resumeFalseInterruption | boolean | true | After a false alarm, carry on with what the Agent was saying. |
sensitivity
| Key | Type | If omitted | Effect |
|---|---|---|---|
vadProvider | silero or builtin | no separate detector | Which voice activity detector listens for speech. Only silero builds one; with builtin or nothing set, the speech engine's own detection is used and the other sensitivity keys are ignored. |
minSpeechDuration | number, seconds | 0.05 | The shortest sound that counts as speech. |
minSilenceDuration | number, seconds | 0.55 | The silence that ends a stretch of speech. |
prefixPaddingDuration | number, seconds | 0.5 | Audio kept from just before speech starts. |
maxBufferedSpeech | number, seconds | 60 | The most speech held at once. |
activationThreshold | number, 0 to 1 | 0.5 | How confident the detector must be that a sound is speech. |
forceCpu | boolean | true | Run the detector on the CPU. |
Recording and consent
recordCalls: truerecords every call. When it is on andconsentMessageis not empty, callers hear the consent message first, before thefirstMessage. With recording off, the consent message is never played.validateandbuilddo not check the pair. Write the message yourself wheneverrecordCallsistrue;consentMessage: nullmeans nothing is said.- In the install wizard, a Package that records calls shows the installer the consent message. They
can edit it, and must confirm it is right before the Voice step is complete; the wizard will not
finish, or let them skip the step, with an empty message. The install refuses a reviewed message
that is empty (
PORTABLE_DEFINITION_INVALID). A Package that does not record calls has no consent step. - An install with no wizard (from the CLI) has no one to review the message, so your wording is installed as you wrote it.
These rules are pinned by the wizard's tests (apps/web/src/components/packages/steps.test.ts,
StepVoice.test.tsx) and the voice codec's (packages/contracts/src/packages/codec/capabilities.test.ts,
22.32-UNIT-003).
Recording and consent are legal decisions
The installer is shown your consent message and must confirm it's right for where their calls are recorded. The wizard won't set up recording with nothing said first. Write the message for the strictest jurisdiction you expect to be installed in.
SIP routing template
How the installer's inbound trunk handles a call: media encryption, which SIP headers it keeps, and
noise cancellation. npx @elizon/cli example sip prints a complete phone-line Package built around
this file.
sipTrunkSlot: phone-line
direction: inbound
inbound:
srtp: allow
sipHeaders: x_only
noiseCancellation: true| Field | Meaning |
|---|---|
sipTrunkSlot | The sip-trunk Slot the installer binds to one of their inbound trunks. See the note below the table. |
direction | Always inbound. See Inbound only. |
inbound.srtp | Media encryption for the call audio. See srtp. |
inbound.sipHeaders | Which SIP headers reach the call. See sipHeaders. |
inbound.noiseCancellation | true turns on noise cancellation for the caller's audio on the trunk; false leaves the audio as it is. |
All three inbound fields are required, and no other field is accepted: validate and build
refuse an unknown key, a value outside the tables below and a direction other than inbound, with
PORTABLE_DEFINITION_INVALID at the field's path (for example sip.inbound.srtp).
sipTrunkSlot must be the id of a type: sip-trunk Slot declared in slots. Use the same one
your inbound-call trigger names: validate and build check that the Slot is declared and is a
sip-trunk Slot, not that it matches the trigger. They refuse any other id with
PORTABLE_UNDECLARED_REFERENCE at sip.sipTrunkSlot: "Slot line is not declared in this
Package." for an undeclared id, or "Slot reasoning is a llm Slot, not a sip-trunk Slot." for
a Slot of another type. Copy the id from the trigger.
srtp
| Value | What it does | When to pick it |
|---|---|---|
disable | Call audio is never encrypted (plain RTP). | Only for a carrier that can't negotiate SRTP at all. Not recommended. |
allow | Encrypted and unencrypted calls are both accepted. | Recommended when you don't know the installer's carrier; also a new trunk's setting in Studio. |
require | Only encrypted calls are accepted; an unencrypted one is refused. | When the line handles sensitive calls and you're sure installers' carriers support SRTP. |
The value is passed to the carrier, and becomes the media-encryption setting of a number's call
route when that number is set up on the line (At install). require with a carrier
that doesn't offer SRTP means calls never connect. The wizard's test inbound call won't catch this: it uses the trunk's current setting, and the template is applied only at
Activate, so calls start failing after activation. Say in your Package's README that it needs an
SRTP-capable carrier.
sipHeaders
| Value | What it does | When to pick it |
|---|---|---|
none | No SIP headers reach the call. | Recommended unless you need a header; also a new trunk's setting in Studio. |
x_only | Only the custom X- headers reach the call. | When the installer's carrier adds X- headers you want, such as a caller reference. |
all | Every SIP header from the INVITE reaches the call. | Rarely; it passes on the most data about the caller. |
The value is applied to the number's call route (create, and every later refresh, such as the one
at Activate); the wizard's test inbound call uses the trunk's current value. The headers you choose arrive
on the caller's participant in the call room as sip.h.<header> attributes (for example
sip.h.x-customer-id).
Available on the call, not read by the Agent
The headers are available as participant attributes, but the Agent and the voice configuration
don't read them: nothing a Package defines can use them yet. Pick none unless you have a reason
to change it.
Inbound only
direction has one value, inbound. There is no outbound routing template: an outbound trunk's
settings (its SIP server, its transport) belong to the carrier account, so there is nothing a
Package could carry. A Package can't place calls through a trunk of its own.
In the manifest
The voice configuration and the routing template from this page, wired together. npx @elizon/cli example sip prints the same wiring as a complete project, with its own file, Slot and trigger names.
entryPoint: agents.call-handler
agents:
- name: call-handler
ref: ./agents/call-handler.yaml
voice:
ref: ./voice/after-hours-line.yaml
sip:
ref: ./sip/inbound.yaml
slots:
- { id: reasoning, type: llm, required: true }
- { id: speech-in, type: stt, required: true, allowedProviders: [soniox] }
- { id: speech-out, type: tts, required: true, allowedProviders: [cartesia] }
- {
id: phone-line,
type: sip-trunk,
required: true,
description: The trunk your after-hours number rings on.,
}
triggers:
- id: after-hours-line
type: inbound-call
sipTrunkSlot: phone-line
routing:
ref: ./sip/inbound.yaml
- id: run-manually
type: manual
compatibility:
requiredCapabilities: [voice, sip]sip:points at the routing template. A Package has at most one.- The
sip-trunkSlot is what the installer binds to their trunk. The template'ssipTrunkSlotand everyinbound-calltrigger name it by id. - The
inbound-calltrigger is what makes a call start the Package. It takessipTrunkSlot(a declaredsip-trunkSlot, orvalidaterefuses it withMANIFEST_TRIGGER_SLOT_UNRESOLVED) androuting.ref(the template).routing.refmust name the same file assip.ref, because install applies only thesip:template:validateandbuildrefuse a different file, or aninbound-calltrigger in a Package with nosip:, withMANIFEST_INVALID_FIELDattriggers[<i>].routing.ref. See Triggers →inbound-call. - The entry point. Like every trigger,
inbound-callfires the Package's oneentryPoint. Unlikescheduleandwebhook, it doesn't need a Workflow: an Agent, a Team or a Workflow entry point is wired the same way. The call itself is answered by the voice configuration'sagent, so the simplest Package makes that Agent the entry point, as above. The Triage demo uses a Workflow entry point instead. - Which voice configuration answers. The Package's own. At Activate, the bound number is routed to the Package's voice configuration, the same way Studio → Phone Configurations routes a number it adds. A number that already answers with another live voice configuration (one made in Studio, or by another Package) is not taken: that trigger is failed, and the installer removes the other routing first (see At install). A number that answers with this Package's own earlier voice configuration (from a previous version) is re-pointed.
requiredCapabilitiesmust listvoiceandsipwhen the Package has aninbound-calltrigger.validateandbuildrefuse each one that is missing withMANIFEST_CAPABILITY_DISAGREEMENTat the trigger, and the install's Review step refuses a platform without telephony withPKG_MISSING_CAPABILITY.- A
manualtrigger lets the installer test the Package from Studio before any call arrives.
There's no field that could hold a carrier account, credential or number: not in the template, not in
the Slot, not in the trigger. The installer supplies all three (see At install). The
template, the sip-trunk Slot and the trigger accept only the fields above: validate refuses
anything added to the Slot or the trigger (a number or trunk added to an inbound-call trigger gets
a hint saying where it comes from), and build refuses anything added to the template. At install,
a template that carries a vault reference, a secret-named field or an id is refused.
At install
The sip-trunk Slot binds to two things the installer already has:
- A trunk: one of their inbound SIP trunks (Studio → Phone Configurations), by its id. It must be
ready: inbound, provisioned without error, with its inbound settings saved and, for a managed
carrier, its carrier credentials linked. A trunk that isn't is refused with
SLOT_BINDING_UNRESOLVEDand the reason. - An inbound phone number on that trunk, optional: the number the Package answers, in E.164
(
+4930123456) or 7 to 15 digits. Without a number the trunk still binds, but noinbound-calltrigger can be wired to it: the trigger stays not answering yet until a number is given.
The installer also supplies, before installing, everything the Package can't carry: the carrier
account, its credentials, the trunk itself and the number (all set up in Studio → Phone
Configurations), and the speech-provider accounts for the stt and tts Slots.
In the wizard, the SIP Inbound step offers the installer's ready inbound trunks and the number to
answer. It then offers a test inbound call that confirms calls really reach Elizon before
activation. A number that silently never rings is the worst failure a phone Package can have. At
Activate, once an inbound-call trigger can be wired (a trunk and a number are bound):
- The routing template is written to the chosen trunk's inbound settings and the carrier step is re-run. Numbers on that trunk that are already set up get their call routes updated with the template's media encryption and noise cancellation. If one of them can't be updated, the error is shown on that number in Studio → Phone Configurations.
- The bound number is routed to the Package's voice configuration. A number with no call route gets one, with its own dispatch rule. A number that already has a route keeps it, and its dispatch rule is updated in place to answer with the Package's voice configuration, so it is never left without a route. A number already routed to the Package's voice configuration is left as it is.
The trigger is wired only when both steps worked. Otherwise it is failed with the reason, and the rest of the Package still activates. A trigger fails when:
- the template can't be applied (the trunk is no longer one of the Organization's inbound trunks, for example);
- the number isn't a number on the bound trunk (
+… is not a number on the bound trunk); - the number already answers with another voice configuration (
+… already answers with voice configuration <name>. Remove that routing first.). Nothing about that number is changed; - Elizon's SIP service can't set up the route. The error is stored on the number. A new route points the number at the Package's voice configuration but answers no calls yet; an existing route keeps answering with its old voice configuration.
To recover, fix the cause, then either:
- in Studio → Phone Configurations, remove the number and add it again with the Package's voice configuration. That runs the same routing as Activate; or
- apply an update of the Package, which wires its triggers again.
Activate runs once per installation, so activating again isn't one of the options.
A trunk bound without a number gets neither step. Apart from those call-route settings, nothing else about the trunk, the carrier account or the other numbers is changed: the other numbers keep the voice configuration they answer with. On uninstall, a number routed to the Package's voice configuration is un-routed and its call route and dispatch rule are removed. The number and the trunk stay.
From the CLI
npx @elizon/cli install ./my-package.pkg --slot.phone-line=<trunk-id> …
npx @elizon/cli packages bind <installation-id> --slot.phone-line=<trunk-id> --yesA sip-trunk Slot takes --slot.<id>=<trunk-id>, where the trunk id is the trunk's id from Studio →
Phone Configurations. The limits:
- No number. The CLI value is the trunk id and nothing else, so a CLI binding carries no inbound
number. The trunk binds, but the Package's
inbound-calltriggers are not wired, and the template'ssrtp,sipHeadersandnoiseCancellationare never applied to the trunk. Bind the line in the wizard or on the Package's page in Studio, where you pick the number, if the Package has aninbound-calltrigger. defer(--slot.phone-line=defer) leaves the Slot for later. The Package installs as installed-incomplete and the trigger is not live until the Slot is bound.- The trunk id must be the id of a trunk in the target Organization at that scope. Anything else is
refused with
SLOT_BINDING_UNRESOLVED.
These rules are pinned by the CLI's binding tests (packages/cli/src/install/install.test.ts,
22.29-UNIT-048 and 22.29-INT-001; packages/cli/src/packages/packages-bind.test.ts, 22.46-CLI-001).
See install and
packages bind.
See Voice and SIP for a walkthrough.