Skip to content

Booking Flow

verify_member
--> resolve_address (pickup)
--> resolve_address (dropoff)
--> set_booking_details
--> review_trip
--> confirm_trip
sequenceDiagram participant Agent participant MCP Agent->>MCP: verify_member(member_id, dob) MCP-->>Agent: session_id + home_address + types Agent->>MCP: resolve_address(pickup) MCP-->>Agent: structured address + lat/lng Agent->>MCP: resolve_address(dropoff) MCP-->>Agent: structured address + lat/lng Agent->>MCP: set_booking_details(all fields) MCP-->>Agent: ready_to_confirm=true Agent->>MCP: review_trip(session_id) MCP-->>Agent: voice_script + summary Agent->>MCP: confirm_trip(session_id) Note over MCP: TP rate estimation (non-fatal) MCP-->>Agent: confirmation_number

Verify the member’s identity and create a booking session.

  • Input: member_id (Medicaid ID or UUID), name, dob, channel
  • Output: session_id, home_address (structured with coordinates), treatment_types[] (flat name list), service_types[] (human-readable with code)
  • DOB verification uses actual date comparison (minimum 2 date tokens required)
  • Creates a fresh BookingDraft in Valkey (24h TTL) – never reuses drafts from other sessions

Resolve the pickup address through the 4-source cascade.

  • Input: session_id, address (free text), type: "pickup"
  • Output: ranked candidates[] with street, city, state, zip, lat, lng, phone, source, confidence
  • For home address pickup, use the home_address object returned by verify_member and pass it directly as the structured pickup_address in set_booking_details

Resolve the dropoff address.

  • Input: session_id, address (free text), type: "dropoff"
  • Output: ranked candidates (facilities are boosted for dropoff)

Set booking fields incrementally. Can be called multiple times. Addresses must be structured objects.

  • 6 required fields: pickup_address, dropoff_address, appointment_time, trip_type, transport_mode (or service_type or service_type_id), treatment_type (or treatment_type_id)
  • Roundtrips also require return_type (and return_time if scheduled)
  • Addresses must be passed as structured objects: {street, city, state, zip_code, latitude, longitude}
  • Use coordinates from resolve_address candidates or the home_address returned by verify_member
  • Service type alias matching supported (e.g. wc -> wav, amb -> ambulatory)
  • Treatment type fuzzy matching supported (case-insensitive contains match + word-prefix/stem match)
  • Returns suggested_prompt for conversational UX and missing_fields list

Generate a trip summary and TTS voice script for member confirmation.

  • Validates all required fields are present
  • Sets draft.LastStep = "review" (sequence gate for confirm_trip)
  • Returns voice_script for AI to read aloud

Create the real trip — OR a standing order, depending on trip_type on the draft.

  • Validates draft.LastStep == "review" (must review first)
  • Routes by trip_type: standing_order → standing-order creator (folds in the removed create_standing_order tool); otherwise → trip creator
  • TP Rate Estimation: Automatically calls trip_estimates_general.Facade.Perform to get estimate IDs before trip creation. For roundtrips the outbound and return legs are estimated concurrently. Estimation failure is FATAL for roundtrips (the will-call return leg requires a customer estimate); for one-way it is non-fatal — the trip is still created without estimates.
  • Passenger counts (adult_count, pca_count) are set on both outbound and return legs
  • Calls nemt-trip-service TripsCreate()
  • Returns trip_id, trip_friendly_id, return_trip_id, confirmation_number, spellable_confirmation, plus pickup/dropoff/appointment/transport/return/treatment/companion/special-needs/notes echo fields for AI readback
  • Deletes only the draft on success — the session stays alive so send_confirmation can authorize the follow-up email
  • Populates session post-confirm fields: LastConfirmedDraft, LastTripIDs, LastFriendlyIDs, IsStandingOrder, StandingOrderID, StandingOrderMeta — send_confirmation reads these to prove the caller is sending an email for THIS session’s trip (cross-session PHI leak guard)

Email the trip confirmation to the member.

  • Pre-condition: session.LastConfirmedDraft must be populated (set by confirm_trip on success)
  • The supplied trip_id (or standing_order_id) MUST be in the session’s LastTripIDs / LastFriendlyIDs / StandingOrderID allowlist — anything else is rejected as unauthorized
  • For standing orders, the email includes a branded trip schedule PDF and a blank Aetna mileage reimbursement form
  • See the Trip Confirmation Flow for details
set_booking_details:
trip_type: "roundtrip"
return_type: "will_call"

Both legs are created in a single TripsCreate() call. The return leg is OnDemand (no scheduled time). Activate later with activate_will_call.

sequenceDiagram participant Agent participant MCP Agent->>MCP: verify_member(member_id, dob) MCP-->>Agent: session_id Agent->>MCP: resolve_address(pickup) Agent->>MCP: resolve_address(dropoff) Agent->>MCP: set_booking_details(trip_type=roundtrip, return_type=will_call) MCP-->>Agent: ready_to_confirm=true Agent->>MCP: review_trip(session_id) Agent->>MCP: confirm_trip(session_id) MCP-->>Agent: confirmation_number (both legs) Note over Agent,MCP: Later, when member is ready... Agent->>MCP: verify_member or lightweight re-verification Agent->>MCP: get_active_trips(session_id) MCP-->>Agent: trips[] (is_will_call=true) Agent->>MCP: activate_will_call(trip_id) MCP-->>Agent: driver dispatched
set_booking_details:
trip_type: "roundtrip"
return_type: "scheduled"
return_time: "4:30 PM"
set_booking_details:
companion_name: "Jane Doe"
companion_type: "adult" # adult, child, or attendant

Test: /test:companion

MR trips require address validation AND driver selection before confirmation:

verify_member
--> resolve_address (pickup)
--> resolve_address (dropoff)
--> set_booking_details (transport_mode: "mileage_reimbursement", addresses, times)
--> get_member_drivers
--> assign_driver (stores enrollment_id on draft)
--> review_trip
--> confirm_trip (validates EnrollmentID present, passes as DriverID)
sequenceDiagram participant Agent participant MCP Agent->>MCP: verify_member(member_id, dob) MCP-->>Agent: session_id + home_address + types Agent->>MCP: resolve_address(pickup) MCP-->>Agent: structured address + lat/lng Agent->>MCP: resolve_address(dropoff) MCP-->>Agent: structured address + lat/lng Agent->>MCP: set_booking_details(transport_mode=mileage_reimbursement, addresses, times) MCP-->>Agent: ready fields set Agent->>MCP: get_member_drivers(session_id) MCP-->>Agent: drivers[] (enrollment_id, name, type) Note over Agent: Present drivers to member Agent->>MCP: assign_driver(session_id, enrollment_id) MCP-->>Agent: driver stored on draft Agent->>MCP: review_trip(session_id) MCP-->>Agent: voice_script (includes driver info) Agent->>MCP: confirm_trip(session_id) MCP-->>Agent: confirmation_number

Driver types:

  • self_drive — member drives themselves (relationship: “self”)
  • fnf — Friends & Family driver (relationship: “family”, “friend”)

Key: confirm_trip will FAIL with MISSING_PREREQUISITE if transport_mode=mileage_reimbursement but no driver selected. Always call get_member_drivers → assign_driver before confirm.

Test: /test:mr-booking, /convo:aboba-mr

Use the home_address object from the verify_member response and pass it as the structured pickup_address in set_booking_details:

set_booking_details:
pickup_address:
street: "123 Main St"
city: "Springfield"
state: "IL"
zip_code: "62701"
latitude: 39.7817
longitude: -89.6501

No resolve_address call needed when using the home address – coordinates are already included.

Error Recovery
TRIP_ALREADY_EXISTS Ask the member for a different date
INVALID_SCHEDULE Reconfirm date and time with the member
MISSING_PREREQUISITE Call the required prerequisite tool first
INELIGIBLE Inform the member, check benefits
TRIP_LIMIT_EXCEEDED Inform the member of their trip limit
Weekend appointment warning Confirm with the member that weekend is intended
graph LR subgraph Draft["BookingDraft"] D1["OrgID / MemberID"] D2["PickupAddress (lat,lng)"] D3["DropoffAddress (lat,lng)"] D4["AppointmentTime (local TZ)"] D5["PickupTime (auto-calc)"] D6["ServiceTypeID / TreatmentTypeID"] D7["EnrollmentID (MR)"] D8["TripType / ReturnType"] D9["Companion / Requester"] D10["AdultCount / PcaCount"] end subgraph EST["TP Estimation"] E1["trip_estimates_general.Facade.Perform"] E2["EstimateIDs (per leg)"] end subgraph SDK["Trip Service Request"] S1["OrganizationID / MemberID"] S2["Leg.Stops[0] pickup"] S3["Leg.Stops[1] dropoff"] S4["StartFromDate"] S5["ScheduledAtLocal"] S6["ServiceTypeID"] S7["DriverID"] S8["Legs[1] return leg"] S9["CallerName / PassengerCount"] S10["Leg.EstimateId (per leg)"] S11["Leg.Seats (both legs)"] end D1 --> S1 D2 --> S2 D3 --> S3 D4 --> S4 D5 --> S5 D6 --> S6 D7 --> S7 D8 --> S8 D9 --> S9 D10 --> S11 E1 --> E2 E2 --> S10 style Draft fill:#1a202c,color:#fff style SDK fill:#2d3748,color:#fff