Skip to main content

Extension reference

A dialplan. metadata.name is the number it answers for, which is why *78 is an ordinary extension and a feature code needs no special case.

configVersion: lyno/v1
kind: Extension
metadata:
name: "200"
tenant: acme
spec:
description: Support follow-me
steps: [...]
KeyTypeRequiredNotes
descriptionstringnoLabel shown in the editor and in logs.
stepslistyesOrdered steps, run top to bottom.

Paths inside resolve against the configuration root, like every other document — see Documents and paths.

steps[]

Common keys on every step:

KeyTypeRequiredNotes
idstringnoLabel used in logs. Defaults to the step type.
typestringyesOne of the twelve types below.
whenobjectnoTime condition gating the step.

Type-specific keys:

TypeKeyRequiredDefaultNotes
playfileyes8 kHz mono 16-bit PCM WAV. Answers the call if needed.
dialtargetyesSee below.
dialtimeoutno30sRing time; for followme, the per-stage fallback.
dialmohnoringbackMusic-on-hold class heard instead of ringback.
mohmohnodefaultClass name. Runs until the caller hangs up.
hangupNo fields.
gotoextensionyesA dialplan extension of this tenant. Checked for loops at startup.
ivrmenuyesA key of the tenant's ivr_menus.
queuequeueyesA key of the tenant's queues.
queuetimeoutnothe queue's maxWaitCan only shorten it, never lengthen it.
queuemohnothe queue's mohClass name.
voicemailmailboxnothis plan's own extensionA key of the tenant's mailboxes.
voicemailgreetingnothe mailbox's, then the shipped promptsA WAV path.
voicemail_checkmailboxnothe authenticated caller's own boxRequired in effect for a call from a trunk.
recordactionyesstart, stop or toggle.
recordconfirmnoA WAV played after the change.
dndactionyeson, off or toggle.
dndconfirmnoA WAV played after the change.
forwardactionyesset or clear.
forwardmodeyesalways or noanswer. busy is refused at startup.
forwardcollectfor setfalseMust be true when action: set.
forwardpromptnoAsks for the destination.
forwardconfirmnoPlayed once the change has been made.

The extension: field is the number, not the filename — every *.yaml in the directory is read and keyed by what it declares. That is what lets a plan answer on *78, which is how feature codes are built.

target

target:
kind: endpoint | ring_group | followme | external
kindKeyRequiredNotes
endpointendpointyesA target reference.
ring_groupgroupyesName of a ring group in this tenant.
externalnumberyesNumber placed through the outbound routes.
followmestagesyesAt least one stage.

stages[]

stages:
- targets: ["101"]
timeout: 15s
- targets: ["100", "external:0612345678"]
timeout: 25s
KeyTypeRequiredNotes
targetslistyesAt least one target reference. Rung in parallel.
timeoutdurationnoFalls 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
KeyTypeFormatNotes
dayslistmon tue wed thu fri sat sunCase-insensitive.
hoursstring"HH:MM-HH:MM"Start inclusive, end exclusive. Wraps midnight when start > end.
dateslist"YYYY-MM-DD" or "YYYY-MM-DD..YYYY-MM-DD"Ranges are inclusive on both ends.
timezonestringIANA nameOverrides the tenant timezone for this condition.
notboolInverts 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

configVersion: lyno/v1
kind: Extension
metadata:
name: "200"
tenant: acme
spec:
description: 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;
  • goto without extension; a goto or ivr jump to an unknown plan; a jump that forms a loop;
  • ivr without menu, or naming a menu the tenant does not define;
  • queue without queue, or naming a queue the tenant does not define;
  • voicemail or voicemail_check naming a mailbox the tenant does not define;
  • record without a valid action; dnd without a valid action;
  • forward without a valid action or mode, forward with mode: busy, and forward with action: set but collect unset;
  • a music-on-hold class no level defines;
  • any invalid day, hour range, date, date range or timezone in a when block.

Sound files are validated and cached in memory at startup as well — a play step's file, a prompt and a confirm, and a menu's prompt files. Two are not checked at startup: a voicemail step's greeting and a queue's announce. A bad file there surfaces as a warning during a call.