Create a route
POST/v1/catalog/routes
Creates a route, as New route and its editor would. A route is a day's walk through catalog places: stops in order, each a catalog place (punto) or activity (actividad) by placeId, with its own visit minutes (ownMinutes, null = the place's) and the LEG that arrives at it from the previous stop (mode: walking | transit | driving | bicycling, minutes, prose). intro and closing are the texts before and after. rules are how the planner uses it (structured: true lets it propose the route as a whole day). Everything but title is optional (an empty route is not suggested to the planner until it has stops and structured: true). Undo with POST /changes/{changeId}/revert, while nobody has saved it since.
Permissions: catalog:write · Kind: write · Cost: 1 unit · MCP tool create_route
Undoable: the response carries Bymundi-Change-Id; revert it with POST /v1/changes/{changeId}/revert.
Path parameters
None.
Body
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | yes | max 200 chars |
intro |
string | object[] | The text before the first stop. | |
closing |
string | object[] | The text after the last stop. | |
stops |
object[] | The whole stop list, in order. Absent = unchanged. max 40 items | |
stops[].placeId |
uuid | yes | A catalog place (punto) or activity (actividad), by id. |
stops[].ownMinutes |
integer | null | This route's own visit minutes for the stop; null or absent = the place's own. | |
stops[].leg |
object | How you get HERE from the previous stop (ignored on the first). Absent = keep the measured leg when the same pair was already on the route. | |
stops[].leg.mode |
"walking" | "transit" | "driving" | "bicycling" | null | ||
stops[].leg.minutes |
integer | null | Travel minutes from the previous stop. | |
stops[].leg.prose |
string | object[] | How to get here from the previous stop. | |
rules |
object | The route's planning rules; only the fields sent change. destinos is derived from the stops. |
|
rules.structured |
boolean | true: the planner may propose it as a whole day (needs at least two stops). | |
rules.base |
string | max 200 chars | |
rules.tipo |
"dia_entero" | "medio_dia" | "excursion" | "tematica" | "con_experiencias" | "llegada" | null | ||
rules.duracion |
"medio_dia" | "dia_entero" | "dia_entero_largo" | null | ||
rules.ritmo |
"relajado" | "equilibrado" | "intenso" | null | ||
rules.incompatibleDias |
"lunes" | "martes" | "miercoles" | "jueves" | "viernes" | "sabado" | "domingo"[] | Weekdays it cannot run. max 7 items | |
rules.requiereReserva |
boolean | ||
rules.notasUso |
string[] | max 20 items |
Response 201
| Field | Type | Required | Description |
|---|---|---|---|
object |
"route" | yes | |
id |
uuid | yes | |
title |
string | yes | |
externalId |
string | yes | |
intro |
object[] | yes | |
stops |
object[] | yes | |
stops[].placeId |
string | yes | |
stops[].name |
string | yes | The place's name — or the stop's stored title when its place no longer resolves. |
stops[].placeMinutes |
integer | null | yes | The place's own visit minutes; null when the place is gone. |
stops[].ownMinutes |
integer | null | yes | This route's override for the stop; null = the place's. |
stops[].leg |
object | null | yes | How you get here from the previous stop; null on the first stop, or when nothing is known. |
stops[].leg.mode |
"walking" | "transit" | "driving" | "bicycling" | null | yes | |
stops[].leg.minutes |
integer | null | yes | |
stops[].leg.prose |
object[] | yes | |
stops[].leg.source |
string | yes | Provenance: source (authored), routes (measured by Google), manual (typed). |
stops[].leg.stale |
boolean | yes | true: measured for a different previous stop — the minutes no longer describe this leg. |
closing |
object[] | yes | |
rules |
object | yes | |
rules.structured |
boolean | yes | |
rules.base |
string | yes | |
rules.tipo |
"dia_entero" | "medio_dia" | "excursion" | "tematica" | "con_experiencias" | "llegada" | null | yes | |
rules.duracion |
"medio_dia" | "dia_entero" | "dia_entero_largo" | null | yes | |
rules.ritmo |
"relajado" | "equilibrado" | "intenso" | null | yes | |
rules.incompatibleDias |
"lunes" | "martes" | "miercoles" | "jueves" | "viernes" | "sabado" | "domingo"[] | yes | |
rules.requiereReserva |
boolean | yes | |
rules.notasUso |
string[] | yes | |
destinos |
object[] | yes | Every destino the stops sit in (derived when the route is saved). |
destinos[].id |
uuid | yes | |
destinos[].name |
string | yes | |
durationMin |
integer | null | yes | The route's computed length in minutes; null while never computed. |
updatedAt |
string | 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 reachedidempotency_key_reused— Idempotency-Key reusedrequest_in_progress— Request in progress