Flows

Flow Examples

Three Flows taken from real Katexs deployments, trimmed to the parts that matter. Copy the shape, swap the copy, point the tools at your own systems.


Example 1: Appointment intake

A dental front desk line. The Flow qualifies new versus returning patients, collects the details each path needs, books against the calendar, and falls back to a callback request when nothing fits.

yaml
flow: appointment_intake
start: greet

nodes:
  greet:
    type: say
    text: "Thanks for calling {{practice_name}}. Are you a new or returning patient?"
    next: classify_patient

  classify_patient:
    type: branch
    edges:
      - when: utterance matches "new"        -> collect_new
      - when: utterance matches "returning"  -> collect_existing
      - fallback: collect_new

  collect_new:
    type: collect
    fields: [caller_name, phone, insurance_provider, reason_for_visit]
    next: find_slot

  collect_existing:
    type: collect
    fields: [caller_name, date_of_birth]
    next: find_slot

  find_slot:
    type: tool
    tool: calendar.find_availability
    inputs: { duration_minutes: 45, window_days: 14 }
    edges:
      success: offer_slot
      no_availability: request_callback
      error: transfer_desk

  offer_slot:
    type: collect
    variable: confirmed_slot
    prompt: "I have {{slot_one}} or {{slot_two}}. Which works better?"
    next: book

  book:
    type: tool
    tool: calendar.create_event
    edges:
      success: confirm
      error: transfer_desk

  confirm:
    type: say
    text: "You're all set for {{confirmed_slot}}. I'll text a confirmation to {{phone}}."
    next: end_booked

  end_booked:
    type: end
    outcome: booked
  • Why two collect paths โ€” New patients need insurance and reason for visit; returning patients only need identity. Splitting keeps both conversations short.
  • Why a separate find_slot โ€” Checking availability before asking for a preference avoids the worst pattern in phone booking โ€” asking, then apologising.
  • Why transfer on tool error โ€” A calendar outage should reach a human, not a retry loop.

Example 2: Intent router

A main business line that fields four recognisable requests and hands anything else to a freeform agent. This is the highest-leverage Flow most workspaces build, because it protects every downstream path from mis-routed calls.

yaml
flow: front_door
start: greet

nodes:
  greet:
    type: say
    text: "{{business_name}}, how can I help?"
    next: route

  route:
    type: intent
    labels:
      billing:     "questions about an invoice, payment, or refund"
      scheduling:  "booking, rescheduling, or cancelling an appointment"
      status:      "checking on an existing order or ticket"
      human:       "explicitly asking for a person"
    edges:
      billing:    -> handoff_billing
      scheduling: -> handoff_scheduling
      status:     -> lookup_status
      human:      -> transfer_human
      fallback:   -> general_agent
    retries: 1

  lookup_status:
    type: collect
    variable: order_number
    prompt: "Sure โ€” what's the order number?"
    next: fetch_status

  fetch_status:
    type: tool
    tool: crm.get_order
    edges:
      success: read_status
      error: transfer_human

  general_agent:
    type: agent
    agent: support_generalist
    return_on_complete: true
    next: end_handled
Give the router exactly one retry. Two failed classifications in a row means the caller is describing something your labels do not cover โ€” the fallback agent will handle it better than a third attempt.

Example 3: Outbound reactivation

A short outbound Flow used in campaigns. It opens with a compliant identification line, honours opt-outs immediately, and writes every outcome back to the CRM so the list stays clean.

yaml
flow: reactivation
start: identify

nodes:
  identify:
    type: say
    text: "Hi {{first_name}}, this is an automated assistant calling on behalf of {{business_name}}. Is now an okay time?"
    next: consent

  consent:
    type: branch
    edges:
      - when: utterance matches opt_out -> honour_opt_out
      - when: utterance matches "no"    -> schedule_callback
      - fallback: pitch

  honour_opt_out:
    type: tool
    tool: crm.set_do_not_contact
    next: end_opted_out

  pitch:
    type: say
    text: "We're reopening slots for {{service_name}} this month and wanted to hold one for you."
    next: offer

  end_opted_out:
    type: end
    outcome: opted_out
Outcome tagMeaningCRM action
bookedAppointment createdSet stage to scheduled
callbackCaller asked to be reached laterCreate task with requested time
opted_outDo-not-contact requestedSuppress from all future campaigns
no_answerNever connected to a humanIncrement attempt counter