Destination Plan & Extend
Destination Plan & Extend
POST /v1/destination/plan and POST /v1/destination/extend — a per-member daily itinerary, code-dated.
Two calls build a multi-day itinerary for a member:
POST /v1/destination/plan— a fresh itinerary, starting at day 0.POST /v1/destination/extend— append more days to an itinerary your client is already holding.
Both are session-scoped — require the
X-Session-Id header.
Content-category safety on these two endpoints is prompt-guided only today — not
code-enforced. Every other age-gated surface in this API (catalog, suggestions, shopping,
ads) filters mature or excluded content in code, after the model runs, as a backstop. /plan
and /extend do not yet have that backstop: the itinerary items are drawn from the model’s own
knowledge (see Place metadata below), and only a
prompt instruction asks it to avoid nightlife, bars, casinos, and adult-entertainment venues. A
prompt instruction is not a guarantee.
Do not rely on /plan or /extend for Child, Tween, or Unknown-tier members until this
is resolved. /destination/search and /destination/replace
are unaffected — the model there only ever picks from an already-filtered candidate pool, which
is a mechanically different, safe design. Contact your partner manager before using /plan/
/extend in any context where a passenger’s tier may not be Adult.
Plan
Body (max 64 KiB):
heroImageis declared on the response but always omitted today — call/destination/hero-imageseparately for a banner image.days[].descriptionand each item’s descriptive text are model free text, output-guarded.
Extend
Body (max 64 KiB):
newDays carries only the newly generated days — never the full itinerary; your client
already holds the earlier days, and the server holds no itinerary state of its own between calls.
A newTotalDays that isn’t greater than existingDays is a valid, cost-free request: it
short-circuits to {"newDays": []} with zero model calls. If the model returns more days than
you asked for, the response is clipped to the exact count; if fewer, it’s left honestly short —
never padded.
Dates are entirely code-computed
Neither request has a date field. /plan sends tripDurationNights; /extend sends
existingDays/newTotalDays. The server resolves “today” from its own clock at request time, and
each day’s date is pure arithmetic from that anchor — the model is never asked for a date at
all.
/extend re-anchors “today” on every call — it does not remember the original plan’s start
date. If you call /extend on a different calendar day than the original /plan call, the
new days’ dates are computed from that call’s “now,” which may not line up with day 0 of the
itinerary you’re extending. This is fine for extending within the same session, but don’t assume
date continuity across a stale, multi-day-old plan.
Place metadata: provider-merged or null
Every itinerary item’s address, phone, hours, rating, reviewCount, imageUrl, and
sourceUrl start unset. They are filled in only when the model-asserted place name matches an
entry from the places provider’s results for that destination, fetched in parallel with the model
call — never guessed, never fabricated. The model itself generates the place names and
categories from its own knowledge; the places-provider pool is used only for this metadata
overlay, never shown to the model as a candidate list to choose from. This is the mechanism
behind the content-safety caveat at the top of this page: a model-invented place is composed into
the response regardless of category, and a places-provider match (or lack of one) only affects
whether address/phone/etc. get filled in — it does not filter anything.
Weather
weatherAvailable (also the field name on the dedicated /destination/weather
endpoint) reports whether the platform could resolve a forecast for
this trip: coordinates are resolved from lat/lon → airport → city/country, in that order,
against a small reference table. If resolution fails, or the weather provider call itself fails,
weatherAvailable is false and every day’s weather is null — never a fabricated
forecast. This is a separate code path from the dedicated weather endpoint, though the semantics
match.
If your environment’s places provider is disabled, expect every itinerary item’s provider-merged
fields to be null on every call — legitimate honest degradation, not a bug. The weather provider
is keyless and always on, so weatherAvailable should behave normally regardless.
Errors
Standard envelope. Both: 400 session.unresolved, 401 / 403, 500.
Plan additionally: 422 request.validation (blank seatId, blank destination.city/.country,
or tripDurationNights outside 1–7). Extend additionally: 422 request.validation (blank
seatId, blank destination.city/.country, a negative existingDays/newTotalDays, a delta
over 7, or more than 100 pinnedPlaceIds). There is no 429 for either endpoint today — a
policy/ceiling denial surfaces as 500 internal.error.