Extension file reference
One file per extension, in <tenant dir>/<extensions_dir>/<number>.yaml. Paths
inside resolve against the tenant directory.
extension: "200"
name: "Support follow-me"
steps: [...]
| Key | Type | Required | Notes |
|---|---|---|---|
extension | string | yes | The number this plan answers for. |
name | string | no | Label shown in the editor and in logs. |
steps | list | yes | Ordered steps, run top to bottom. |
steps[]
Common keys on every step:
| Key | Type | Required | Notes |
|---|---|---|---|
id | string | no | Label used in logs. Defaults to the step type. |
type | string | yes | play, dial, moh or hangup. |
when | object | no | Time condition gating the step. |
Type-specific keys:
| Type | Key | Required | Default | Notes |
|---|---|---|---|---|
play | file | yes | — | 8 kHz mono 16-bit PCM WAV. Answers the call if needed. |
dial | target | yes | — | See below. |
dial | timeout | no | 30s | Ring time; for followme, the per-stage fallback. |
dial | moh | no | ringback | Music-on-hold class heard instead of ringback. |
moh | moh | no | default | Class name. Runs until the caller hangs up. |
hangup | — | — | — | No fields. |
target
target:
kind: endpoint | ring_group | followme | external
kind | Key | Required | Notes |
|---|---|---|---|
endpoint | endpoint | yes | A target reference. |
ring_group | group | yes | Name of a ring group in this tenant. |
external | number | yes | Number placed through the outbound routes. |
followme | stages | yes | At least one stage. |
stages[]
stages:
- targets: ["101"]
timeout: 15s
- targets: ["100", "external:0612345678"]
timeout: 25s
| Key | Type | Required | Notes |
|---|---|---|---|
targets | list | yes | At least one target reference. Rung in parallel. |
timeout | duration | no | Falls back to the step's timeout, then 30s. |
Stages run in order; the first answer wins and the rest are cancelled. A target that resolves to nothing at call time — an extension with no registered device — is skipped.
when
when:
days: [mon, tue, wed, thu, fri]
hours: "09:00-17:30"
dates: ["2026-12-25", "2026-12-30..2027-01-01"]
timezone: Europe/Amsterdam
not: false
| Key | Type | Format | Notes |
|---|---|---|---|
days | list | mon tue wed thu fri sat sun | Case-insensitive. |
hours | string | "HH:MM-HH:MM" | Start inclusive, end exclusive. Wraps midnight when start > end. |
dates | list | "YYYY-MM-DD" or "YYYY-MM-DD..YYYY-MM-DD" | Ranges are inclusive on both ends. |
timezone | string | IANA name | Overrides the tenant timezone for this condition. |
not | bool | Inverts the combined result. |
All configured parts must match. An omitted part is not a constraint. See
Time conditions for the evaluation details
and the traps around not.
A complete example
extension: "200"
name: "Support follow-me"
steps:
- id: greeting
type: play
file: ../../../sounds/welcome.wav
when:
days: [mon, tue, wed, thu, fri]
hours: "09:00-17:30"
- id: follow-me
type: dial
moh: default
target:
kind: followme
stages:
- targets: ["101"]
timeout: 15s
- targets: ["100", "external:0612345678"]
timeout: 25s
- id: nobody-home
type: play
file: ../../../sounds/closed.wav
- id: done
type: hangup
Validation
Every step is built and every time condition compiled at startup, so runtime
never encounters bad configuration. Rejected: an unknown step type; play
without file; dial without target; an invalid target kind; an unknown
ring group; an empty external number; followme with no stages, or a stage
with no targets; a music-on-hold class no level defines; and any invalid day,
hour range, date, date range or timezone in a when block.