Failing a stop now answers the question that matters most in the field: what happens
next? The fail endpoint accepts a recovery outcome that turns a failed delivery into
an explicit, auditable next step — send the goods back, try again, or accept the failure —
without juggling manual stop edits or custom integrations.
The outcomes
Fail stop accepts an optional outcome parameter on tour
stops:
return_now— appends a return stop to the booking, going back to the pickup’s address and contact, and dispatches it right onto the tour the stop failed on.return_later— appends the return stop without dispatching it, leaving the dispatcher in control of when and how it goes out.reattempt_now— appends a reattempt stop, a full copy of the failed stop, and dispatches it onto the failing tour for an immediate second attempt.reattempt_later— appends the reattempt copy undispatched.skip_recovery— the stop stays failed and no recovery stop is created. This is the default when no outcome is given, so existing integrations keep working unchanged.
Recovery stops are regular booking stops: they keep the booking active until they reach an outcome of their own, and the failed stop stays in place — the booking’s history reflects every attempt.
Choosing safely
Each outcome has pre-conditions — returns require the return stop type and a
pickup-then-drop-off booking shape, “now” variants require the tour to still be active,
and stop limits apply. To offer exactly the recovery choices that apply before making
the call, tour stop payloads (for example on Show Booking)
carry a failure_options object indicating, per outcome, whether the current user may
select it.
Each entry pairs an allowed flag with a localized reason explaining why an outcome is
currently unavailable (null when it is allowed), ready to be shown to the user as-is:
"failure_options": {
"outcomes": {
"skip_recovery": { "allowed": true, "reason": null },
"reattempt_later": { "allowed": true, "reason": null },
"reattempt_now": { "allowed": false, "reason": "The tour for this stop is no longer active." },
"return_later": { "allowed": true, "reason": null },
"return_now": { "allowed": false, "reason": "The tour for this stop is no longer active." }
}
}
The availability reflects everything that determines an outcome’s fate: the user’s role,
the stop’s status, the tour’s state, service configuration and capacity, and the booking’s
shape. The object renders on tour stops that can currently be failed (approaching or
arrived), and only for users that can run stop failure flows: administrators,
organization managers and the tour’s driver.
When a chosen outcome is not available after all, the fail request is rejected as a whole
with a 422 naming the reason — the stop is not failed in that case. A UI driven by
failure_options should rarely see these rejections.
failure_options also replaces the boolean fail indicator inside
completion_requirements, which is now deprecated —
failure_options.outcomes.skip_recovery.allowed carries the same signal, alongside the
richer per-outcome availability.
Webhooks
booking.stop_failednow carries the appliedoutcomealongside thefailure_reason, so downstream systems no longer need to diff the booking’s stop list to learn what recovery was chosen.- The new
booking.return_startedevent fires whenever a return stop is dispatched into a tour — the moment goods start moving back.
Why this matters
Together with terminal failed stops, this completes the structured recovery flow: every failure gets an explicit decision, every attempt is a first-class stop, and both the dashboard and your integrations see the same availability rules and the same audit trail.