Open the chat on the booking that failed
A traveler got through the booking flow for Denver to Cancun, two passengers, next month, and the card was refused on the payment step. A minute later they are in chat. The assistant's first question asks which booking and what happened, and the traveler types out the route, the dates and the message they saw, then waits while it is looked up. The error code, the route, the dates and the fare were recorded when the payment failed. The chat can open with the booking on screen and the reason the card was refused, and move straight to retrying the payment.
last_payment_error- The error code from the most recent payment failure this visit
route_session- The route of the booking they were on last this visit
travel_dates_session- The departure date of that booking
fare_family_session- The fare family they picked, such as basic or flex
Opens the chat with the route, dates and fare on screen and the payment error named, and offers the payment retry as the first move when a payment has failed this visit. The retry runs through your payment provider and the assistant decides how to word it.
from snowplow_signals import (
Attribute, Event, EventProperty, StreamAttributeGroup, domain_sessionid,
)
booking_step = Event(name="booking_step", vendor="com.example", version="1-0-0")
payment_failed = Event(name="payment_failed", vendor="com.example", version="1-0-0")
last_payment_error = Attribute(
name="last_payment_error",
type="string",
events=[payment_failed],
aggregation="last",
property=EventProperty(vendor="com.example", name="payment_failed", major_version=1, path="code"),
)
route_session = Attribute(
name="route_session",
type="string",
events=[booking_step],
aggregation="last",
property=EventProperty(vendor="com.example", name="booking_step", major_version=1, path="route"),
)
travel_dates_session = Attribute(
name="travel_dates_session",
type="string",
events=[booking_step],
aggregation="last",
property=EventProperty(vendor="com.example", name="booking_step", major_version=1, path="depart_date"),
)
fare_family_session = Attribute(
name="fare_family_session",
type="string",
events=[booking_step],
aggregation="last",
property=EventProperty(vendor="com.example", name="booking_step", major_version=1, path="fare_family"),
)
# Keyed on the visit, so the chat widget asks about this session the moment it opens.
trip_support_payment = StreamAttributeGroup(
name="trip_support_payment",
version=1,
attribute_key=domain_sessionid,
owner="you@example.com",
attributes=[last_payment_error, route_session, travel_dates_session, fare_family_session],
)// In the handler that runs when the chat widget opens
const traveler = await signals.getServiceAttributes({
name: "trip_support_payment",
attribute_key: "domain_sessionid",
identifier: sessionId,
});
// { last_payment_error: "card_refused", route_session: "DEN-CUN", travel_dates_session: "2026-11-14", fare_family_session: "basic" }
if (traveler.last_payment_error) {
assistant.open({
intent: "retry_payment",
booking: { route: traveler.route_session, departs: traveler.travel_dates_session, fare: traveler.fare_family_session },
error: traveler.last_payment_error,
});
}