pub enum Node {
Greeting {
exits: Option<Exits>,
prompt: Prompt,
},
Hours {
exceptions: Vec<HoursException>,
exits: Option<Exits>,
schedule: WeeklySchedule,
timezone: String,
},
Menu {
exits: Option<Exits>,
options: HashMap<String, String>,
prompt: Prompt,
retries: u64,
timeout_secs: u64,
},
Ring {
exits: Option<Exits>,
timeout_secs: u64,
},
Message {
exits: Option<Exits>,
max_secs: u64,
prompt: Prompt,
tone: MessageTone,
},
Transfer {
exits: Option<Exits>,
target: String,
},
Hangup {
exits: Option<Exits>,
prompt: Option<Prompt>,
},
Book {},
}Expand description
One node: a component (kind + its config) plus its wired exits. An internally-tagged union on kind.
JSON schema
{
"description": "One node: a component (kind + its config) plus its wired exits. An internally-tagged union on `kind`.",
"oneOf": [
{
"description": "Speak a prompt, then continue. Exit: next.",
"type": "object",
"required": [
"kind",
"prompt"
],
"properties": {
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"greeting"
]
},
"prompt": {
"$ref": "#/definitions/Prompt"
}
}
},
{
"description": "Branch on the business's weekly schedule + holiday overrides. Exits: open, closed.",
"type": "object",
"required": [
"kind",
"schedule",
"timezone"
],
"properties": {
"exceptions": {
"type": "array",
"items": {
"$ref": "#/definitions/HoursException"
}
},
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"hours"
]
},
"schedule": {
"$ref": "#/definitions/WeeklySchedule"
},
"timezone": {
"description": "IANA zone (America/New_York). Validated to resolve at load.",
"type": "string"
}
}
},
{
"description": "Speak a prompt and collect a DTMF choice. Exits: one per digit in options, plus no_input and invalid.",
"type": "object",
"required": [
"kind",
"options",
"prompt"
],
"properties": {
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"menu"
]
},
"options": {
"description": "Digit key → human label. The exit for a digit is exits[digit]. Valid digit keys are enforced by the validator.",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"prompt": {
"$ref": "#/definitions/Prompt"
},
"retries": {
"default": 1,
"type": "integer",
"minimum": 0.0
},
"timeout_secs": {
"default": 5,
"type": "integer",
"minimum": 0.0
}
}
},
{
"description": "Ring the human for a window. answered is an implicit terminal; the only wired exit is no_answer.",
"type": "object",
"required": [
"kind",
"timeout_secs"
],
"properties": {
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"ring"
]
},
"timeout_secs": {
"type": "integer",
"minimum": 0.0
}
}
},
{
"description": "Voicemail: speak a prompt, record, transcribe, notify. Terminal.",
"type": "object",
"required": [
"kind",
"prompt"
],
"properties": {
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"message"
]
},
"max_secs": {
"default": 120,
"type": "integer",
"minimum": 0.0
},
"prompt": {
"$ref": "#/definitions/Prompt"
},
"tone": {
"default": "beep",
"allOf": [
{
"$ref": "#/definitions/MessageTone"
}
]
}
}
},
{
"description": "Blind-transfer to an external number. Terminal.",
"type": "object",
"required": [
"kind",
"target"
],
"properties": {
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"transfer"
]
},
"target": {
"type": "string"
}
}
},
{
"description": "Speak an optional goodbye and end the call. Terminal.",
"type": "object",
"required": [
"kind"
],
"properties": {
"exits": {
"$ref": "#/definitions/Exits"
},
"kind": {
"enum": [
"hangup"
]
},
"prompt": {
"$ref": "#/definitions/Prompt"
}
}
},
{
"description": "Offer the caller open appointment times and book the one they choose, before the call ends. New in schema_version 2. The bookable grid is described here (weekly schedule + timezone + how long an appointment is); which times are actually free comes from the connected calendar at call time, and the engine never sees a credential. Exits: booked, no_slots, no_input, unavailable. Three fields carry more meaning than their types show, and are described here because a `$ref` with a sibling description makes the type generators emit a duplicate type instead of reusing the named one. `prompt` is spoken once before the open times are offered (\"I can book you in — here are the next available times\"); the times themselves are never part of it, since they are not known until the call. `confirm_prompt` is spoken immediately after a successful booking and directly BEFORE the booked time (\"You're booked for\" → \"Tuesday\" → \"ten thirty a.m.\"); it deliberately takes no placeholder, because a prompt is frozen audio by the time a call runs and nothing can be interpolated into the middle of it — anything that should follow the time belongs on the booked exit. `schedule` is when this business takes appointments, the `hours` node's weekly shape reused verbatim; it is not the same question `hours` answers, because a business can answer the phone at times it will not book.",
"type": "object",
"required": [
"confirm_prompt",
"duration_mins",
"kind",
"prompt",
"schedule",
"timezone"
],
"properties": {
"buffer_mins": {
"description": "Clear time kept on BOTH sides of an appointment, so a slot is not offered flush against a busy interval.",
"default": 0,
"type": "integer",
"minimum": 0.0
},
"confirm_prompt": {
"$ref": "#/definitions/Prompt"
},
"duration_mins": {
"description": "How long one appointment runs. Bounds are the validator's (see each language's validate module), not this schema's.",
"type": "integer",
"minimum": 0.0
},
"exceptions": {
"description": "Single-date overrides of the weekly schedule (holidays, one-off clinics), as on `hours`.",
"type": "array",
"items": {
"$ref": "#/definitions/HoursException"
}
},
"exits": {
"$ref": "#/definitions/Exits"
},
"horizon_days": {
"description": "How far ahead to look for open times.",
"default": 14,
"type": "integer",
"minimum": 0.0
},
"kind": {
"enum": [
"book"
]
},
"lead_mins": {
"description": "Nothing sooner than this many minutes from now is offered — the caller cannot book the next five minutes.",
"default": 120,
"type": "integer",
"minimum": 0.0
},
"max_offers": {
"description": "How many times to offer the caller. Each offered time gets one keypad digit, counting from 1.",
"default": 3,
"type": "integer",
"minimum": 0.0
},
"prompt": {
"$ref": "#/definitions/Prompt"
},
"retries": {
"description": "Extra attempts to collect a keypress after the first, as on `menu`.",
"default": 1,
"type": "integer",
"minimum": 0.0
},
"schedule": {
"$ref": "#/definitions/WeeklySchedule"
},
"timeout_secs": {
"description": "How long to wait for a keypress on each attempt, as on `menu`.",
"default": 5,
"type": "integer",
"minimum": 0.0
},
"timezone": {
"description": "IANA zone (America/New_York) the schedule is written in. Validated to resolve at load.",
"type": "string"
}
}
}
]
}Variants§
Greeting
Speak a prompt, then continue. Exit: next.
Hours
Branch on the business’s weekly schedule + holiday overrides. Exits: open, closed.
Menu
Speak a prompt and collect a DTMF choice. Exits: one per digit in options, plus no_input and invalid.
Fields
Ring
Ring the human for a window. answered is an implicit terminal; the only wired exit is no_answer.
Message
Voicemail: speak a prompt, record, transcribe, notify. Terminal.
Transfer
Blind-transfer to an external number. Terminal.
Hangup
Speak an optional goodbye and end the call. Terminal.
Book
Offer the caller open appointment times and book the one they choose, before the call ends. New in schema_version 2. The bookable grid is described here (weekly schedule + timezone + how long an appointment is); which times are actually free comes from the connected calendar at call time, and the engine never sees a credential. Exits: booked, no_slots, no_input, unavailable. Three fields carry more meaning than their types show, and are described here because a $ref with a sibling description makes the type generators emit a duplicate type instead of reusing the named one. prompt is spoken once before the open times are offered (“I can book you in — here are the next available times”); the times themselves are never part of it, since they are not known until the call. confirm_prompt is spoken immediately after a successful booking and directly BEFORE the booked time (“You’re booked for” → “Tuesday” → “ten thirty a.m.”); it deliberately takes no placeholder, because a prompt is frozen audio by the time a call runs and nothing can be interpolated into the middle of it — anything that should follow the time belongs on the booked exit. schedule is when this business takes appointments, the hours node’s weekly shape reused verbatim; it is not the same question hours answers, because a business can answer the phone at times it will not book.
Fields
buffer_mins: u64Clear time kept on BOTH sides of an appointment, so a slot is not offered flush against a busy interval.
duration_mins: u64How long one appointment runs. Bounds are the validator’s (see each language’s validate module), not this schema’s.
exceptions: Vec<HoursException>Single-date overrides of the weekly schedule (holidays, one-off clinics), as on hours.
lead_mins: u64Nothing sooner than this many minutes from now is offered — the caller cannot book the next five minutes.
max_offers: u64How many times to offer the caller. Each offered time gets one keypad digit, counting from 1.
schedule: WeeklyScheduleImplementations§
Source§impl Node
impl Node
Sourcepub fn is_terminal(&self) -> bool
pub fn is_terminal(&self) -> bool
Whether a caller who reaches this node can have the call end here.
ring counts — its implicit answered hands the call to a human,
which ends the flow. Used by the “no caller is ever trapped” check.
Sourcepub fn required_exits(&self) -> Vec<String>
pub fn required_exits(&self) -> Vec<String>
The exit names this node must wire, given its config. Validation
checks the node’s exits keys are exactly this set — no missing exit
(a dead choice) and no stray exit (a typo the engine would never
follow).