Change the itinerary
POST/v1/trips/{tripId}/itinerary/ops
Applies a batch of block operations (add, update, move, delete) atomically, with the builder's own validation, and re-plans the route of every day it touches. Rules:
updateanddeletesend the block's currentversionasexpectedVersion; a stale one is a 409.update.datais MERGED into the block's data, so send only the fields you change. An unknown field is a 422. A stop's times on a planned route belong to the route and are ignored.- A later op in the same batch refers to a block added earlier by its
clientId(inparentIdoranchorId). The server assigns ids and returns them increated. diastructure ops are refused (422) on a trip whose days come from its design; change the design, then call /itinerary/sync.- Deleting more than 5 blocks, or more than 30 % of the itinerary, needs
confirmset to the trip's exact title. - A batch in which nothing changes is refused (422), and so is deleting a retired block type, which could not be undone.
With ?dryRun=true, returns the counts and the planner's notices without writing.
Permissions: trips:write · Kind: destructive · Cost: 1 unit · Dry run: ?dryRun=true
Undoable: the response carries Bymundi-Change-Id; revert it with POST /v1/changes/{changeId}/revert.
Path parameters
| Field | Type | Required | Description |
|---|---|---|---|
tripId |
uuid | yes |
Body
| Field | Type | Required | Description |
|---|---|---|---|
ops |
object | object | object | object[] | yes | max 200 items |
confirm |
string | max 300 chars |
Response 200
One of:
itinerary_change
| Field | Type | Required | Description |
|---|---|---|---|
object |
"itinerary_change" | yes | |
changeId |
uuid | yes | |
created |
object[] | yes | |
created[].clientId |
string | null | yes | |
created[].id |
uuid | yes | |
itinerary |
object | yes | |
itinerary.object |
"itinerary" | yes | |
itinerary.tripId |
uuid | yes | |
itinerary.title |
string | yes | |
itinerary.publication |
"draft" | "published" | yes | |
itinerary.hasDesignedRoute |
boolean | yes | |
itinerary.blocks |
object[] | yes | |
itinerary.blocks[].object |
"block" | yes | |
itinerary.blocks[].id |
uuid | yes | |
itinerary.blocks[].type |
"titulo" | "texto" | "imagen" | "agenda" | "dia" | "caja" | "desplegable" | "seccion" | "capitulo" | "derivado" | "actividad" | "alojamiento" | "vuelo" | "comida" | "transporte" | "tren" | "crucero" | "informacion" | "galeria" | "video" | "mapa" | "html" | "itinerario" | yes | |
itinerary.blocks[].parentId |
uuid | null | yes | |
itinerary.blocks[].rank |
string | yes | |
itinerary.blocks[].data |
object | yes | |
itinerary.blocks[].layout |
object | yes | |
itinerary.blocks[].version |
integer | yes | |
itinerary.blocks[].source |
object | null | yes |
itinerary_change_preview
| Field | Type | Required | Description |
|---|---|---|---|
object |
"itinerary_change_preview" | yes | |
added |
integer | yes | |
updated |
integer | yes | |
moved |
integer | yes | |
removed |
integer | yes | |
notices |
string[] | yes | |
requiresConfirmation |
boolean | yes |
Errors
Errors are problem details. Besides the refusals described above, any call like this one can return:
invalid_request— Invalid requestunauthorized— Missing or invalid API keyinsufficient_scope— Missing permissionrate_limited— Rate limit reachednot_found— Not foundidempotency_key_reused— Idempotency-Key reusedrequest_in_progress— Request in progress