Hero Images

POST /v1/shopping/hero-image and POST /v1/destination/hero-image — attributed banner images from a shared image provider.

Two endpoints resolve a banner/hero image for a subject — one for a shopping category, one for a destination city — through the same shared image-search provider and cache. Behavior, error handling, and the response shape are identical; only the request vocabulary differs.

Both are airline-scoped, model-free: no X-Session-Id (harmless if sent — simply ignored).

POST /v1/shopping/hero-image

Body (max 2 KiB):

FieldRequiredRules
subjectyesNon-blank, and must case-insensitively match one of the shopping category labels: Fashion, Fragrance, Beauty, Accessories, Electronics, Travel Essentials, Food & Beverage, Local & Destination, Kids & Family.
orientationnoFree string; no validation applied.
$curl -X POST "https://api.staging.westiq.ai/v1/shopping/hero-image" \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{ "subject": "Fashion" }'

POST /v1/destination/hero-image

Body (max 16 KiB):

FieldRequiredRules
cityyesNon-blank, 64 characters max.
countrynoIf supplied: non-blank, 64 characters max.

Both fields accept letters, digits, spaces, commas, periods, apostrophes, and hyphens only — anything else is rejected, never silently stripped.

$curl -X POST "https://api.staging.westiq.ai/v1/destination/hero-image" \
> -H "Authorization: Bearer $ACCESS_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{ "city": "Rome", "country": "Italy" }'

Response — shared shape

1{
2 "schemaVersion": "1",
3 "correlationId": "req_0197c0a1b2c3d4e5f60718293a4b5c6d",
4 "image": {
5 "url": "https://images.example/paris-hero.jpg",
6 "width": 1920,
7 "height": 1080,
8 "alt": "The Eiffel Tower at dusk",
9 "photographer": "Jane Doe",
10 "photographerUrl": "https://images.example/photographers/jane-doe"
11 }
12}
FieldNotes
imageOmitted (never null) when no image was resolved.
image.url, .width, .heightAlways present when image is present.
image.alt, .photographer, .photographerUrlOmitted individually when unknown.

A missing image is honest degradation, not an error — always a 200. It happens for any of three reasons: the image provider is disabled for your environment, the provider found no match for the subject, or the provider call itself failed. All three return the same shape with image omitted — never a fabricated URL, and never a 4xx/5xx for a legitimate “no image” outcome. Design your seatback UI to render cleanly with no hero image.

A positive result is cached for a short TTL keyed on the resolved query, so requesting the same subject repeatedly does not repeatedly call the provider. A miss or a provider fault is never negatively cached, so a transient hiccup self-heals on the next request.

Errors

Standard envelope. Both: 401 / 403, 500. Never 429 — these endpoints are model-free and never touch AI admission or ceilings.

Both endpoints also return 413 request.malformed over their body cap, and 400 request.malformed for a missing/malformed/empty JSON body — including when a required field (subject, or destination’s city) is absent from the JSON entirely, since that’s a binding failure, not a validation failure.

  • POST /v1/shopping/hero-image: 422 request.validation when subject is present but blank or outside the known category list.
  • POST /v1/destination/hero-image: 422 request.validation for a blank-but-present city, an over-length city/country, or an out-of-charset character in either. A blank country is not an error — it’s silently treated as “no country given” and the lookup falls back to city-only.

Whether a live call actually returns a populated image depends on your environment’s image provider being enabled — don’t assume every call returns an image while integrating; confirm with your partner manager if image is consistently absent and you expect otherwise.