Destination Search & Replace
POST /v1/destination/search and POST /v1/destination/replace — POI discovery for a destination, member-scored.
Two calls curate points of interest (dining and experiences) for a member’s destination:
POST /v1/destination/search— a fresh, two-category candidate list.POST /v1/destination/replace— swap one place out for a new pick, without disturbing the rest of what you’re showing.
Both are session-scoped — require the
X-Session-Id header.
Search
Body (max 64 KiB):
Search takes destinationCity/destinationCountry as flat top-level fields. Replace, and
the plan/extend endpoints, take a nested destination: {city, country, …} object instead. The two shapes are genuinely different — don’t reuse one request
builder for both.
Each place carries an id/name plus provider-merged-or-null fields (category, neighborhood,
priceTier, address, phone, rating, reviewCount, imageUrl, sourceUrl — omitted
individually when the places provider has no data for that field), a code-clamped matchScore
(0–100 integer), and optional model free text in whyForYou.
On a search result, category is omitted whenever the places provider didn’t supply one —
don’t treat it as guaranteed. On a replace result, it always falls back to a generic label
when the provider gives nothing, so it’s always present there.
Replace
Body (max 64 KiB):
replacement (an itinerary-item shape carrying the same provider-merged-or-null fields as a
search result, plus hours when known) and reasoning are omitted together whenever no
replacement could be produced — never a fabricated place.
Business logic
- Search fetches dining and experience candidates independently; if both categories come
back empty, the platform makes no model call and returns the honest-empty shape
(
dining: [],experiences: []) at zero cost. - Replace’s candidate pool is the union of both categories, minus
pinnedPlaceIdsand thedismissedPlaceIditself, fetched fresh on every call (not cached). The model picks exactly one replacement from that offered pool; if it names something outside the pool, the response is the same honest-null shape — not a fabricated place, and not an error.
Places-provider degradation is the current default in the sandbox, not an edge case. If your
environment’s places provider is disabled, /search returns dining: [], experiences: [] and
/replace returns replacement omitted on every call — by design. Don’t read an empty result as
a bug during initial integration; confirm with your partner manager whether live place data is
enabled for your environment.
Place screening and categories
For Child, Tween and Unknown members, candidate screening checks every supplied usable category
for age-inappropriate venues, and checks names for explicit adult/nightlife terms.
If no usable category exists, the name also receives the broader venue screening.
Ordinary family venues such as golf clubs and kids clubs are not excluded merely for the word
“club”; nightlife venues remain excluded. The returned category uses the first usable category,
so an initial placeholder does not hide a later real category. This rule does not enable the places provider:
the disabled-provider empty results described above still apply.
Errors
Standard envelope. Both: 400 session.unresolved, 401 / 403, 500.
Search additionally: 422 request.validation (blank seatId, destinationCity, or
destinationCountry). Replace additionally: 422 request.validation (blank seatId, blank
destination.city/destination.country, blank dismissedPlaceId, an invalid timeBlockSlot, 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.