Catalog Serve

The age-gated catalog read — unscored (GET) and per-member scored (POST).

/v1/catalog/serve returns your airline’s visible catalog, age-gated. It has two verbs:

  • GET — the unscored read: the full visible catalog in a stable order, age-gated to a seat/member if you name one.
  • POST — the scored serve: the same age-gated catalog ranked for one member by their bound persona and affinity.

Both apply the age-tier model and the fail-closed locator rules: an unknown or unbound member is served the most-restrictive (child-safe) catalog, never the full one, and never an error.

GET — unscored read

Airline-scoped — no X-Session-Id. All query parameters are optional:

ParamRules
kindMovie or TV (any casing). Anything else → 422 request.validation. Omitted = both.
limitInteger 1–500. Omitted = no cap (the full page). Out of range → 422.
offsetInteger ≥ 0. Omitted = 0. Negative → 422.
seatIdSeat locator. Untrusted — an absent or unbound seat resolves to the child-safe tier, never a 4xx.
memberSlotMember locator within the seat. Omit for a solo traveler.
$curl "https://api.staging.westiq.ai/v1/catalog/serve?kind=Movie&limit=50&seatId=12A" \
> -H "Authorization: Bearer $ACCESS_TOKEN"
1{
2 "schemaVersion": "1",
3 "correlationId": "req_0197c0a1b2c3d4e5f60718293a4b5c6d",
4 "items": [
5 {
6 "titleId": 2041,
7 "kind": "Movie",
8 "title": "The Quiet Coast",
9 "year": 1998,
10 "genres": ["Drama"],
11 "cast": ["A. Rivera"],
12 "rating": "PG",
13 "synopsis": "A retired lighthouse keeper…"
14 }
15 ],
16 "totalItems": 45,
17 "ageExcludedCount": 7,
18 "ageTierApplied": "Child"
19}
  • titleId is an integer (see the note on ids). year: 0 means an unknown year; rating and synopsis are omitted when unknown (the API never sends a field as null).
  • totalItems is the visible-catalog size before age exclusion; ageExcludedCount is how many the gate removed — reported honestly, never papered over.
  • ageTierApplied is the tier the server resolved for your locator (Unknown is shown as-is and behaves as Child).

POST — scored serve

The keystone personalization read: the age-gated catalog ranked for one member. This is session-scoped — it requires the X-Session-Id header. Body (max 4 KiB):

FieldRequiredRules
seatIdyesNon-blank, or 422 request.validation.
memberSlotnoOmit for a solo traveler.
kindnoMovie / TV / omit (422 otherwise).
limitno1–500, or no cap.
offsetno≥ 0.
$curl -X POST "https://api.staging.westiq.ai/v1/catalog/serve" \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -H "X-Session-Id: seat-12A-f3d9c1" \
> -H "Content-Type: application/json" \
> -d '{ "seatId": "12A", "kind": "Movie", "limit": 20 }'
1{
2 "schemaVersion": "1",
3 "correlationId": "req_0197c0a1b2c3d4e5f60718293a4b5c6d",
4 "items": [
5 {
6 "titleId": 2041,
7 "kind": "Movie",
8 "title": "The Quiet Coast",
9 "year": 1998,
10 "genres": ["Drama"],
11 "cast": ["A. Rivera"],
12 "rating": "PG",
13 "score": 82,
14 "matchReason": "Matches your Drama affinity"
15 }
16 ],
17 "totalItems": 45,
18 "ageExcludedCount": 7,
19 "ageTierApplied": "Adult",
20 "personaBound": true
21}

Everything from the GET item, plus:

  • score — a deterministic 0–100 affinity score. It is code-computed, not model-generated; items are returned in descending score order.
  • matchReason — a short deterministic explanation; omitted when the scorer has no signal (e.g. cold-start).
  • personaBoundthe honest cold-start signal. false means no persona or affinity is bound for this member yet, so ordering is affinity-neutral rather than a fabricated ranking. Bind a persona with /v1/personas/generate and drive affinity with events to move it to true.

Scoring never overrides child-safety. The age gate excludes mature titles regardless of affinity — a high-affinity mature title is still excluded for a Child/Tween/Unknown member, and still counted in ageExcludedCount.

Errors

Standard envelope. The GET returns 401 / 403 / 422 / 500; the POST additionally returns 400 session.unresolved when the X-Session-Id header is missing or invalid (see Sessions). Note that a bad seatId is not an error on either verb — it fails closed to the child-safe tier.