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 code-enforced. Like every other
age-gated surface in this API (catalog, suggestions, shopping, ads), /plan and /extend
filter the composed output in code, after the model runs: for a restricted member —
Child, Tween, or Unknown tier, with Unknown treated as Child, per the
age-tier model — any nightlife, bar, casino, gambling, or
adult-category item is dropped from the response, regardless of what the model composed.
You may serve /plan and /extend to members of every tier.
Two consequences to design for: a restricted member’s time blocks and arrival recommendations can honestly contain fewer items than an adult’s for the same trip — sparser by design, never padded with substitutes — and adult members’ itineraries are unchanged by this filter.
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. A places-provider match
(or lack of one) only affects whether address/phone/etc. get filled in — the overlay itself
filters nothing. Content-category filtering for restricted members is a separate, code-enforced
step on the composed output (the note at the top of this page).
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). Both endpoints enforce the
per-client request-rate limit: over it, 429 rate.limited with a
Retry-After header. Other policy/ceiling denials still surface as 500 internal.error.