Trip Edit Mode (Soft Lock)
Taxi & Private Hire › Products › Trip › Trip Edit Mode (Soft Lock)
Generally available
Time-bound soft indicator that an operator is currently editing a trip. Background workers (dispatch sweep, optimiser merge, recurring releaser, capacity sweep, health auto-resolve) check the flag and defer derived-state writes while it's active so the operator's edits aren't clobbered mid-flight. The lock auto-expires so a crashed FE never permanently stalls a trip.
Example request
POST /client/{clientId}/trip
{
"pricingMode": "METERED",
"tripSegments": [
{
"customer": {
"phone": "<E.164 phone>"
},
"tripSegmentStops": [
{
"type": "PICKUP",
"pickupAddress": "<address>",
"pickupLatitude": "<lat>",
"pickupLongitude": "<lng>"
},
{
"type": "DROPOFF",
"pickupAddress": "<address>",
"pickupLatitude": "<lat>",
"pickupLongitude": "<lng>"
}
],
"requestedPickupDate": "<unix-seconds>"
}
],
"maxSeatCapacity": "<seats>"
} Endpoints
| Method | Path | |
|---|---|---|
POST | /client/{clientId}/trip · primary | |
GET | /client/{clientId}/trip | |
POST | /client/{clientId}/trip/{tripId}/assign/{transporterId} | |
POST | /client/{clientId}/trip/{tripId}/reclaim | |
POST | /client/{clientId}/trip/{tripId}/designate | |
DELETE | /client/{clientId}/trip/{tripId}/designate | |
POST | /client/{clientId}/trip/{tripId}/status | |
POST | /client/{clientId}/trip/{tripId}/health/resolve | |
GET | /client/{clientId}/rejected-trip | |
POST | /client/{clientId}/trip/{tripId}/copy | |
GET | /client/{clientId}/trip/{tripId} | |
GET | /client/{clientId}/trip/{tripId}/dispatch-audit/{transporterId} | |
GET | /client/{clientId}/trip/{tripId}/dispatch-log | |
POST | /client/{clientId}/trip/{tripId}/edit/release | |
POST | /client/{clientId}/trip/{tripId}/guard-override | |
GET | /client/{clientId}/trip/{tripId}/guard-status | |
GET | /client/{clientId}/trip/{tripId}/health | |
GET | /client/{clientId}/trip/{tripId}/logs | |
POST | /client/{clientId}/trip/merge | |
POST | /client/{clientId}/trip/merge/analyze | |
POST | /client/{clientId}/trip/{tripId}/merge/candidates | |
POST | /client/{clientId}/trip/{tripId} | |
GET | /client/{clientId}/trip/count | |
GET | /client/{clientId}/trip/optimizable | |
GET | /client/{clientId}/trip/optimizable-accounts | |
GET | /client/{clientId}/trip/sync |
Full request/response schemas and an interactive explorer will live in the API reference (coming soon).
Use cases
Operator flows that exercise this feature.
action risk: low
Take a new ASAP booking — optionally quote first, then create the trip.
POST/client/{clientId}/quote/tripPOST/client/{clientId}/trip
action risk: low
Create a trip billed to a corporate account rather than the individual rider.
POST/client/{clientId}/trip
action risk: medium
Immediately assign a trip to a chosen driver (force-now) — distinct from designate, which reserves through allocation.
POST/client/{clientId}/trip/{tripId}/assign/{transporterId}
action risk: low
Detect a trip whose assigned driver has gone stale (lost GPS / disconnected) and reclaim it back to allocation so it can be re-dispatched.
POST/client/{clientId}/trip/{tripId}/health/resolvePOST/client/{clientId}/trip/{tripId}/reclaim
action risk: low
Operator manually dispatches a trip by designating a chosen driver, who then holds the trip through allocation (the engine reserves/offers to them rather than dispatching openly). Industry "manual dispatch" lives on the designate endpoint.
POST/client/{clientId}/trip/{tripId}/designate
action risk: low
Remove a manual driver designation so the trip returns to open dispatch.
DELETE/client/{clientId}/trip/{tripId}/designate
action risk: low
Cancel a trip. Cancel is NOT its own endpoint — it is a status transition to CANCELLED (Finding 1). If you only have a passenger name / destination, resolve the tripId first with find-trip.
POST/client/{clientId}/trip/{tripId}/status
action risk: low
Duplicate an existing trip to quickly re-book a repeat journey.
POST/client/{clientId}/trip/{tripId}/copy
read_diagnose risk: low
Explain why a trip wasn't allocated: read the dispatch audit + decision log.
GET/client/{clientId}/trip/{tripId}/dispatch-audit/{transporterId}GET/client/{clientId}/trip/{tripId}/dispatch-log
action risk: low
Release a stuck operator edit lock on a trip so others can edit it.
POST/client/{clientId}/trip/{tripId}/edit/release
action risk: high approval required
Override a capacity/time/proximity guard to push a trip past a block. Audit-logged; operator/system only.
POST/client/{clientId}/trip/{tripId}/guard-override
action risk: medium
Combine compatible trips into one shared-ride vehicle: analyse compatibility, then execute the merge.
POST/client/{clientId}/trip/merge/analyzePOST/client/{clientId}/trip/merge
read_diagnose risk: low
Suggest nearby trips that could merge with a given trip.
POST/client/{clientId}/trip/{tripId}/merge/candidates
action risk: low
Modify an existing booking (pickup, time, passengers).
POST/client/{clientId}/trip/{tripId}
read_diagnose risk: low
Look up live / scheduled trips by free-text keyword (passenger name, pickup/drop-off address, reference) to resolve a tripId — e.g. before cancelling, amending or diagnosing a trip. Reads the dispatch grid via trips_sync. The keyword is passed as a QUERY param, not a body: search is a JSON-encoded string (see trip-search-query note).
GET/client/{clientId}/trip/sync
Related
Fields
| Field | Type | Description |
|---|---|---|
creatorId | bigint | Actor that created the trip — the underlying record varies by `creator_type` (User, Driver, App, or System). Pinned for audit; never rewritten after the trip is built. |
appKeyId | bigint | API key that was used to create the trip. Identifies the originating integration (customer app, partner booking system, internal console) for support + analytics. |
bookingSource | BookingSource · PHONE | APP | WEB | OPERATOR | INTEGRATION | Channel the booking came through (OPERATOR / APP / WEB / PHONE / INTEGRATION), derived from the authenticated application (its app key's channel) at creation — never from the payload. Denormalised into TripSegmentArchive for customer-retention analytics. |
pickupDate | integer | Derived earliest pickup time across non-finished segments (Unix). Recomputed by `SetPickupDate` on segment edits; drives dispatch ordering, ETA, and the dispatch index. |
pickupH3Index | string | Resolution-8 H3 cell containing the primary pickup point. Used by RES-016 fleet metrics, scheduled-demand lookups, and the H3 heat map. |
releaseDate | integer | Time the trip becomes dispatch-eligible (Unix). ASAP trips ≈ creation time; pre-bookings stage until this fires. `effectiveReleaseDate` carries the priority-adjusted variant. |
dispatchedDate | integer | Unix timestamp the trip most recently entered the dispatch loop (ALLOCATION). Re-stamped on re-allocation cycles — pair with `initialDispatchedDate` for the original release moment. |
initialDispatchedDate | integer | Unix timestamp the trip first entered dispatch. Latched on first transition — never re-stamped on re-allocation so urgency + priority metrics see the original release moment. |
pausedDate | integer | Unix timestamp the trip was last moved to PAUSED. Stamped on each PAUSE; drives the stuck-dispatch failsafe auto-cancel (a trip left PAUSED beyond the threshold is cancelled). |
assignedDate | integer | Unix timestamp a transporter was assigned. Stamped inside `setAssignedTransporter`; cleared when assignment falls through (rejection, no-show, manual revoke). |
acceptDate | integer | Unix timestamp the driver accepted the assignment. Distinct from `assignedDate` — there is a brief offer window between the two for the driver to accept or reject. |
startDate | integer | Unix timestamp the first segment entered ENROUTE. The trip is "live" from this moment for monitoring, guards, and time-window enforcement. |
finishedDate | integer | Unix timestamp the trip reached a terminal state (FINISHED or CANCELLED). Anchors the archive cursor — trips older than the cursor get archived in batch. |
lastEditTouch | integer | Soft "edit mode" timestamp set when an operator opens the trip for editing. Background workers consult `isEditModeActive()` to defer derived-state writes during the EDIT_MODE_BUFFER_SECONDS window so concurrent edits do not clobber each other. |
status | TripLifecycleEnum · NONE | SCHEDULED | SCHEDULED_PAUSED | ALLOCATION | PAUSED | DESIGNATED | ASSIGNED | ASSIGNED_STAGING | INPROGRESS | FINISHED | CANCELLED | RECURRING_SCHEDULE | Trip lifecycle state: NONE → RECURRING_SCHEDULE / SCHEDULED → ALLOCATION → DESIGNATED → ASSIGNED → INPROGRESS → FINISHED, with PAUSED, ASSIGNED_STAGING, and CANCELLED branches. Single source of truth for dispatch eligibility, visibility, and pricing windows. |
cancellationActor | CancellationActor · CUSTOMER | OPERATOR | DRIVER | SYSTEM | Who initiated the cancellation (CUSTOMER / OPERATOR / DRIVER / SYSTEM), asserted at the cancel call — there is no CUSTOMER auth type, so a customer cancellation is one an operator marks on the customer's behalf. Drives customer-fault penalties (no-show / late-cancel miles). Null until the trip is cancelled. |
tripSegments | Collection | Child segments — the actual units of work. Trip is a coordinator that holds shared lifecycle, capacity, and trip-level pricing state across them; the segments carry customer, account, stops, quote, and price per-fare. |
assignedTransporter | Transporter | Transporter currently holding the trip (post-acceptance). Set by `setAssignedTransporter`, cleared on cancel/no-show. If a `designatedTransporter` is set, only that driver can be assigned. |
designatedTransporter | Transporter | Driver the trip is locked to (offer designation). Set by manual operator action or automated rescue (`ProblemTripDesignationService`); only this driver can accept. Distinct from `assignedTransporter` — designation is the offer, assignment is the acceptance. |
tripUnitOfWorks | Collection | Materialised driver itinerary — each UoW is one execution leg (co-located stops grouped by proximity). Recomputed by `UnitOfWorkMaterialiser` on every trip mutation; preserves INPROGRESS/FINISHED UoWs as anchors and rebuilds the remaining NONE UoWs. |
originatingFleet | Fleet | Fleet the trip was booked under — the trip's single ownership identity. Grouped reads may expand to a wider operating pool via `FleetContextResolver` + `FleetGroup`, but the trip itself always belongs to exactly one fleet. |
friendlyId | string | Human-readable short identifier (e.g. T-1234). Surfaced in the driver app, customer SMS, and support tools; never changes across the trip's lifetime. |
isSchedule | bool | True for a recurring master that spawns worker trips on schedule. The master itself never dispatches; `setIsSchedule()` swaps status between RECURRING_SCHEDULE and SCHEDULED. |
schedule | string | Schedule spec defining when the recurring master spawns worker trips (cron-like syntax). Only meaningful when `isSchedule=true`; ignored on one-off bookings. |
parentTripId | bigint | Id of the recurring master that spawned this worker trip. Null on one-off bookings; populated by `CopyTrip` when the scheduler releases a worker. |
pickupZone | Zone | Resolved dispatch zone containing the pickup point. Drives zone-scoped supply, capacity gates, and the operator dispatch board grouping. |
schedulePlan | TripSchedulePlan | Time-window plan for the trip — owns pickup/dropoff window targets, anchor metadata, and the projected itinerary timestamps used by the optimiser. |
tripQuote | TripQuote | Trip-level Quote. Carries the BILLING role when `singlePriced=true`; otherwise it carries a SHADOW fixed-fare alternate for comparison. See `Quote.role` and `BillingQuoteResolver` for the source-of-truth picker. |
tripPrice | TripPrice | Trip-level realised Price (mirrors `tripQuote` when active). FIXED-mode trips lock it at booking; METERED-mode would recompute at completion. Read through `BillingQuoteResolver`, not directly. |
isScheduleWorker | bool | True when this trip was spawned by a recurring master. Excluded from the FE's "Recurring Schedules" list (which only shows masters) and from re-scheduling UX. |
archivedDate | integer | Unix timestamp the trip was archived by `ArchiveTrips`. Archived trips are read-only and excluded from live dispatch queries; they remain visible in history. |
maxSeatCapacity | smallint | Upper bound on combined occupants across overlapping segments. Enforced consistently by the capacity sweep, UoW materialiser, and our optimisation engine optimiser — all three layers agree on this number. |
minSeatCapacity | smallint | Fixed seat requirement when set — anchors vehicle-size selection and skips the capacity sweep. Used for group bookings or trips where a specific seat count is contractually required. |
capacitySweepStatus | TripCapacitySweepStatus · PENDING | COMPLETED | BYPASSED | State of the async capacity sweep that re-evaluates plan feasibility across vehicle sizes after segment edits. BYPASSED when `minSeatCapacity` short-circuits or the trip is too small; PENDING during recompute; COMPLETED once a plan is locked in. |
autoDispatchEligibility | AutoDispatchEligibility · ELIGIBLE | OUT_OF_POLICY | Whether the auto-dispatch worker will consider this trip. Set to OUT_OF_POLICY by `PlanResolver` when the trip's seat requirement exceeds the client's `planningSweepCapacities`; affected trips stay in ALLOCATION for operator manual assignment. |
vehicleType | VehicleType | Resolved vehicle-type requirement (saloon, MPV, accessible, etc.). Derived from segment-level capabilities + capacity; narrows the eligible-driver pool during dispatch scoring. |
route | Route | Cached the routing service-derived route across the merged trip itinerary (duration, distance, polyline, provider/accuracy). Refreshed by `PlanResolver`; consumed by the optimiser and customer-facing ETAs. |
planCompressionPercentage | smallint | Legacy denormalised plan-compression metric. Stale-prone — the FE now computes this live from UnitOfWork routes via the Quote Roles work. Kept for backwards compatibility; do not consume in new code. |
bypassCapacityCheck | bool | Write-only operator override that suppresses the capacity sweep for this trip. Never serialised back to the client; only consumed during the create/update pipeline. |
requiredClientCapabilities | Collection | Tenant capabilities (wheelchair, child seat, etc.) the assigned vehicle must satisfy. Aggregated from segment-level requirements and frozen on the trip so dispatch filtering can run against a single bitmask. |
capabilityMask | string | 32-bit bitmask precomputed from `requiredClientCapabilities`. Lets the dispatch query filter eligible vehicles with a single bitwise AND instead of a join across the capability tables. |
bypassProximityGuards | bool | Operator override that disables arrival-proximity guards. Used for trips where the driver legitimately cannot pull right next to the address (gated communities, large venues, ferry terminals). |
bypassTimeGuards | bool | Operator override that disables time-window guards. Used for trips where late starts are expected (long wait scenarios, special events) and should not trigger health alerts. |
multiLoading | bool | True when the trip has been chained with others into a shared driver itinerary. Set by the merge analyser; flips on shared-pricing pathways and the multi-load UI affordances. |
singlePriced | bool | True when the trip uses trip-level pricing (one Quote/Price across all segments). False = per-segment pricing. Drives which discount model field is consulted (`singlePricedShareDiscountModel` vs `rideShareDiscountModel`). |
pricingMode | PricingMode · FIXED | METERED | Trip-level METERED vs FIXED. Only consulted when `singlePriced=true` (where Quote/Price live on the trip, not on each segment). Defaults from `Client.defaultPricingMode` at trip-create time; the column default is the fallback when no client context is available. |
operatorManagedModel | bool | When false, the backend re-matches the discount model via the decision tree on every save. When true, the backend uses the operator-attached model verbatim and the validator requires a non-null model for the active mode. |
operatorManagedShares | bool | When false, share percentages are recomputed by `ShareStrategyComputer` on every save. When true, the operator-supplied per-segment shares are used verbatim; validator requires sum=100 and every share > 0. |
singlePricedShareDiscountModel | SinglePricedShareDiscountModel | Active discount model when `singlePriced=true`. Snapshot resolved by `the system`; mutually exclusive with `rideShareDiscountModel`. |
rideShareDiscountModel | RideShareDiscountModel | Active discount model when `singlePriced=false` (ride-share / pool). Mutually exclusive with `singlePricedShareDiscountModel`; use `getActiveDiscountModel()` to pick the right one without branching. |
upliftPercentage | integer | Cost-basis multiplier applied to the trip total. 0 = no uplift; the resolver falls back to the active model's default uplift when this is 0 and the model carries one. The value itself carries the operator-vs-default intent — no separate source flag. |
hasHealthAlert | bool | Denormalised "any check in WARNING" flag set by the health pipeline. Drives the FE's amber health badge and the operator dashboard filter — the underlying checks live on TripHealthCheck. |
hasHealthCritical | bool | Denormalised "any check in CRITICAL" flag set by the health pipeline. Drives the FE's red health badge and surfaces the trip in the support escalation queue. |
allocationIssueType | string · HIGH_DEMAND | LOW_SUPPLY | CAPABILITY_MISMATCH | REJECTION_PATTERN | TIMING_MISMATCH | Classification of why a trip is stuck in allocation (LOW_SUPPLY, NO_ELIGIBLE_DRIVER, etc.) — set by `AllocationRiskCheck`. Powers the dispatch-issues panel and gates automated rescue designation. |
meta | json | Free-form JSON for service-specific annotations (allocation-radius snapshots, sweep diagnostics, projected dispatch radius for AllocationRiskCheck). Schema is convention-based; do not rely on a stable shape. |
prebooked | bool | True when the trip is in the pre-booking staging pipeline (ASSIGNED_STAGING). Drives driver self-scheduling UX and the confirm-window enforcement on the driver app. |
region | Region | Geographic region scope. `RegionFilter` restricts cross-tenant queries by this field; null = global. Set by `RegionResolver` at trip-create time based on the pickup location. |
tripProfiles | Collection | Operator-curated profiles applied to the trip (Corporate, VIP, etc.). Drive bespoke pricing/dispatch behaviour without modifying the booking itself; resolved at trip-create time from Customer/Account context. |
scheduleExclusionGroups | Collection | Operator-managed groups that can pause this recurring trip's release. Any linked group resolving to OFF blocks the next release; curated via the schedule editor's Admin column. Must NOT be `passive` — that would skip change-tracking on save. |
clientId | bigint | Tenant scope. Every tenant-aware entity carries this; `ClientFilter` enforces row-level isolation on read; the multi-tenancy routing layer (`/client/{clientId}`) sets it at create time. Surfaced only under `admin` / `tripLog` groups — never to end users. |
internalKey | string | Optional client-supplied external reference / idempotency key. When present, lets external systems correlate platform-side records back to their own source-of-truth ids. Not persisted to a column — populated by the request handler when the caller sets it. |
__objectType | string | Discriminator string (entity class short-name) emitted alongside the id in serialized output. Resolved at read time by `getObjectType()`; lets the FE dispatch entity-specific rendering without inspecting the URL. |
id | bigint | Snowflake-style primary key (unsigned BIGINT). Generated by `IdFactory` at create time; surfaced to the FE / API as a `G`-prefixed string and stripped back to plain bigint server-side before Doctrine lookup. |
createdDate | integer | Unix timestamp the row was first persisted. Set in the entity's PrePersist hook; never rewritten on subsequent updates. |
updatedDate | integer | Unix timestamp the row was last touched. Bumped on every commit that hits the Doctrine UoW for this entity; drives FE invalidation + the listing change cursor. |
passiveUpdatedDate | int | Read-through alias for `updatedDate` exposed under different serializer groups. Lets the FE distinguish "real edit" from "background touch" projections without changing the underlying column. |
listingUpdatedDate | int | Listing-projection timestamp surfaced only under the `listMode` group. Driven by `TripCache` and other listing-shape refreshers separately from `updatedDate` so a listing rebuild doesn't trigger detail-page invalidation. |