{"openapi":"3.0.3","info":{"title":"Direct Travel API by Stay22","description":"Gateway access to live travel inventory from Booking.com, VRBO, Expedia, and Hotels.com. Starting with accommodations. Receive commissions in USD anytime a reservation is made in any of the brands' platforms via Stay22. Create your account and get your API key today at https://hub.stay22.com","version":"1.1.0","license":{"name":"Proprietary"}},"components":{"securitySchemes":{"apiKeyHeader":{"type":"apiKey","name":"X-API-KEY","in":"header","description":"API Key for accessing protected endpoints. Obtain your API key from the Stay22 dashboard. Include this header in all requests for higher rate limits and access to premium features."},"apiKeyQuery":{"type":"apiKey","name":"key","in":"query","description":"API Key passed as a query parameter. Convenient for quick testing and simple integrations. We recommend using the header method for enhanced security."}},"schemas":{}},"paths":{"/v1/accommodations":{"get":{"summary":"Search accommodations (v1, deprecated)","tags":["Accommodations"],"description":"> **Deprecated.** Use [`/v2/accommodations`](#tag/accommodations) — the unified, actively maintained API (all suppliers in one call, pagination, clustering). v1 remains available for backward compatibility but will not receive new features.\n\nSearch hotels and rentals across Booking.com, VRBO, Expedia, and Hotels.com.\n\n**Location (required):** Provide at least one — `address`, `lat`/`lng` (coordinate search), or all four bounding box params (`nelat`, `nelng`, `swlat`, `swlng`). For points of interest (e.g., \"Eiffel Tower\", \"Bell Centre\"), prefer geocoding the POI to coordinates first, then use `lat`/`lng` + `radius` for faster and more precise results.\n\n**Dates:** Default to 90–93 days from today if omitted (3-night stay). Explicit dates must be today or later and no more than 2 years in the future.\n\n**Pricing:** `price.total` in the response is the full stay amount (not per-night). Divide by `meta.nights` for per-night price. The `min`/`max` filters apply to the per-night price in USD.\n\n**Rate limits:** 5 req/min without an API key (demo), 100 req/min with a key. Sliding 60-second window. Contact us for higher limits.\n\n**Response headers:** `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (ms), `X-Cache` (HIT/MISS).\n\n**Authentication:** Pass your API key via `X-API-KEY` header (recommended) or `key` query parameter. No key needed for demo mode (lower rate limits). Get your key at [hub.stay22.com](https://hub.stay22.com).","parameters":[{"schema":{"type":"string","enum":["booking","vrbo","expedia","hotelscom"]},"in":"query","name":"provider","required":true,"description":"Provider to search. Must be one of: booking, vrbo, expedia, or hotelscom"},{"schema":{"type":"string"},"in":"query","name":"address","required":false,"description":"🔎 ADDRESS METHOD: Human-readable address, place name, or point of interest (e.g., \"Eiffel Tower\", \"Berliner Dom\", \"Colosseum\"). Example: \"Paris, France\" or \"Times Square, New York\". For POIs, lat/lng + radius might be more efficient."},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"lat","required":false,"description":"📍 LAT/LNG METHOD: Center point latitude for radial search. Range: -90 to 90. Use with lng. Example: 40.7128 for New York City."},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"lng","required":false,"description":"📍 LAT/LNG METHOD: Center point longitude for radial search. Range: -180 to 180. Use with lat. Example: -74.0060 for New York City."},{"schema":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"radius","required":false,"description":"📍 LAT/LNG METHOD: Search radius in meters. Default: 10000 (10km) when using lat/lng search."},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"nelat","required":false,"description":"🗺️ BOUNDING BOX METHOD: North-East corner latitude. Range: -90 to 90. Use all four coordinates (nelng, swlat, swlng) for precise map-based searches. Preferred for map viewports."},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"nelng","required":false,"description":"🗺️ BOUNDING BOX METHOD: North-East corner longitude. Range: -180 to 180. Use with nelat, swlat, swlng."},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"swlat","required":false,"description":"🗺️ BOUNDING BOX METHOD: South-West corner latitude. Range: -90 to 90. Use with nelat, nelng, swlng."},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"swlng","required":false,"description":"🗺️ BOUNDING BOX METHOD: South-West corner longitude. Range: -180 to 180. Use with nelat, nelng, swlat."},{"schema":{"type":"string"},"in":"query","name":"hotelids","required":false,"description":"Comma-separated list of accommodation IDs for direct lookup. When provided, location parameters are not required. Example: \"12345,67890,11223\"."},{"schema":{"type":"string","enum":["rental","hotel"]},"in":"query","name":"type","required":false,"description":"Accommodation type filter. \"rental\" returns vacation rentals, \"hotel\" returns traditional hotels. If omitted, both types are included."},{"schema":{"default":"2026-10-04","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"checkin","required":false,"description":"Check-in date in YYYY-MM-DD format. Example: 2024-01-31. Real calendar dates only. Explicit values must be today or later and no more than 2 years in the future. Default: today + 90 days when omitted, and the effective stay window must remain ascending after defaults are applied."},{"schema":{"default":"2026-10-07","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"checkout","required":false,"description":"Check-out date in YYYY-MM-DD format. Example: 2024-02-05. Real calendar dates only. Explicit values must be today or later, no more than 2 years in the future, and checkout must remain after checkin after defaults are applied. Default: today + 93 days when omitted."},{"schema":{"default":2,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"adults","required":false,"description":"Number of adult guests (18+ years old). Must be a positive integer. Default: 2. Affects pricing and availability."},{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"children","required":false,"description":"Number of children (under 18 years old). Must be zero or a positive integer. Default: 0. Some properties may have age restrictions."},{"schema":{"default":1,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"rooms","required":false,"description":"Number of rooms/accommodations needed. Must be a positive integer. Default: 1."},{"schema":{"default":0,"type":"number"},"in":"query","name":"min","required":false,"description":"Minimum per-night price filter in USD (before currency conversion). Only accommodations with per-night price >= this value are returned. Default: 0. Example: 100 for $100/night minimum."},{"schema":{"default":1000,"type":"number"},"in":"query","name":"max","required":false,"description":"Maximum per-night price filter in USD (before currency conversion). Only accommodations with per-night price <= this value are returned. Default: 1000. Example: 500 for $500/night maximum."},{"schema":{"default":"USD","type":"string"},"in":"query","name":"currency","required":false,"description":"Currency code for price display (ISO 4217 format). Prices are converted from USD to this currency. Examples: USD, EUR, GBP, JPY, CAD. Default: USD."},{"schema":{"type":"number","minimum":0,"maximum":5},"in":"query","name":"minstarrating","required":false,"description":"Minimum property star rating (hotel classification). Range: 0-5. Only applied if value ≤ 5. Example: 3 for 3-star hotels and above. Based on official property ratings. No default (filter not applied if omitted)."},{"schema":{"type":"number","minimum":0,"maximum":10},"in":"query","name":"minguestrating","required":false,"description":"Minimum guest review rating. Range: 0-10. Only applied if value ≤ 10. Example: 8 for highly rated properties. Based on aggregated guest reviews and feedback. No default (filter not applied if omitted)."},{"schema":{"default":10,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":100},"in":"query","name":"limit","required":false,"description":"Maximum number of results to return. Range: 1-100. Default: 10."},{"schema":{"default":"en","type":"string"},"in":"query","name":"lang","required":false,"description":"Language code for localized content (ISO 639-1 two-letter format). Default: \"en\". Examples: en (English), fr (French), es (Spanish), de (German), ja (Japanese). Affects property names and descriptions."},{"schema":{"type":"string"},"in":"query","name":"aid","required":false,"description":"Affiliate ID or Partner ID for tracking and attribution. Used for commission tracking and analytics. Provided by Stay22 upon partnership setup. No default (passed through from query only). When an API key is provided, the effective aid is determined by that key."},{"schema":{"type":"string"},"in":"query","name":"campaign","required":false,"description":"Campaign identifier for marketing attribution. Use to track performance of specific campaigns or traffic sources. Example: \"summer-promo-2024\". No default (passed through from query only)."}],"deprecated":true,"responses":{"200":{"description":"API response containing search results and metadata","content":{"application/json":{"schema":{"type":"object","properties":{"meta":{"type":"object","properties":{"message":{"description":"Error message, only present if success is false","type":"string"},"limit":{"type":"number","description":"Maximum number of results requested"},"count":{"type":"number","description":"Number of results included in this response"},"currency":{"type":"string","description":"Currency code used for prices (ISO 4217)"},"checkin":{"nullable":true,"description":"Effective check-in date used for the search, null if not set","type":"string"},"checkout":{"nullable":true,"description":"Effective check-out date used for the search, null if not set","type":"string"},"nights":{"nullable":true,"description":"Number of nights for the stay based on checkin/checkout dates","type":"number"}},"required":["limit","count","currency","checkin","checkout","nights"],"additionalProperties":false,"description":"Response metadata"},"_links":{"type":"object","properties":{"self":{"type":"object","properties":{"href":{"type":"string","description":"URL for this request"}},"required":["href"],"additionalProperties":false,"description":"Link to the current request"}},"required":["self"],"additionalProperties":false,"description":"HATEOAS navigation links"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the accommodation"},"provider":{"type":"string","description":"Provider name (e.g., booking, airbnb, vrbo)"},"name":{"type":"string","description":"Property name or title"},"type":{"type":"string","description":"Property type (e.g., hotel, apartment, house, villa)"},"location":{"type":"object","properties":{"address":{"type":"string","description":"Full formatted address of the property"},"coordinates":{"nullable":true,"description":"Geographic coordinates of the property. May be null if location is not precise.","type":"object","properties":{"lat":{"type":"number","description":"Latitude coordinate"},"lng":{"type":"number","description":"Longitude coordinate"}},"required":["lat","lng"],"additionalProperties":false},"distanceInMeters":{"type":"number","description":"Distance from search center point in meters"}},"required":["address","coordinates","distanceInMeters"],"additionalProperties":false,"description":"Property location information"},"rating":{"type":"object","properties":{"value":{"type":"number","maximum":10,"description":"Rating value up to 10"},"hotelStars":{"nullable":true,"description":"Official hotel star rating (1-5 stars), null if not applicable","type":"number","maximum":5},"count":{"type":"number","description":"Total number of reviews/ratings"}},"required":["value","hotelStars","count"],"additionalProperties":false,"description":"Property ratings and review metrics"},"price":{"type":"object","properties":{"total":{"type":"number","description":"Full amount payable for the entire stay, including taxes and non-conditional fees"}},"required":["total"],"additionalProperties":false,"description":"Pricing information in the requested currency"},"capacity":{"type":"object","properties":{"guests":{"type":"number","description":"Maximum number of guests the property can accommodate"},"bedrooms":{"type":"number","description":"Number of bedrooms"},"beds":{"nullable":true,"description":"Number of beds, null if not specified","type":"number"},"bathrooms":{"type":"number","description":"Number of bathrooms (may include half baths as 0.5)"}},"required":["guests","bedrooms","beds","bathrooms"],"additionalProperties":false,"description":"Property capacity and room configuration"},"policies":{"type":"object","properties":{"instantBook":{"type":"boolean","description":"Whether the property supports instant booking without host approval"},"freeCancellation":{"type":"boolean","description":"Whether the property offers free cancellation"}},"required":["instantBook","freeCancellation"],"additionalProperties":false,"description":"Booking policies and features"},"media":{"type":"object","properties":{"thumbnail":{"type":"string","description":"URL to property thumbnail image"},"logoSquare":{"type":"string","description":"URL to provider logo image"}},"required":["thumbnail","logoSquare"],"additionalProperties":false,"description":"Media assets for the property"},"links":{"type":"object","additionalProperties":{"type":"string"},"description":"HATEOAS links for booking, details, and other actions"}},"required":["id","provider","name","type","location","rating","price","capacity","policies","media","links"],"additionalProperties":false,"description":"Standardized accommodation object with normalized data across all providers"},"description":"Array of accommodations matching your search criteria"}},"required":["meta","_links","results"],"additionalProperties":false,"description":"API response containing search results and metadata"}}}},"400":{"description":"Validation error. Possible codes: `LOCATION_REQUIRED` (no location provided), `INVALID_PROVIDER` (unknown provider), `INVALID_DATE_FORMAT` (bad date), `INVALID_COORDINATES` (out of range), `VALIDATION_ERROR` (other validation failures).","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Validation error. Possible codes: `LOCATION_REQUIRED` (no location provided), `INVALID_PROVIDER` (unknown provider), `INVALID_DATE_FORMAT` (bad date), `INVALID_COORDINATES` (out of range), `VALIDATION_ERROR` (other validation failures)."}}}},"401":{"description":"Authentication error. Possible codes: `INVALID_API_KEY` (key is invalid or revoked), `MISSING_API_KEY` (no key provided for protected endpoint).","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Authentication error. Possible codes: `INVALID_API_KEY` (key is invalid or revoked), `MISSING_API_KEY` (no key provided for protected endpoint)."}}}},"429":{"description":"Rate limit exceeded. Code: `RATE_LIMIT_EXCEEDED`. Demo mode: 5 req/min per IP. Authenticated: 100 req/min per API key. Uses a 60-second sliding window. Use exponential backoff for retries. Contact us for higher limits.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Rate limit exceeded. Code: `RATE_LIMIT_EXCEEDED`. Demo mode: 5 req/min per IP. Authenticated: 100 req/min per API key. Uses a 60-second sliding window. Use exponential backoff for retries. Contact us for higher limits."}}}},"502":{"description":"Bad gateway. Possible codes: `UPSTREAM_ERROR` (provider returned an error), `BAD_GATEWAY` (failed to parse upstream response).","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Bad gateway. Possible codes: `UPSTREAM_ERROR` (provider returned an error), `BAD_GATEWAY` (failed to parse upstream response)."}}}},"503":{"description":"Service unavailable. Code: `SERVICE_UNAVAILABLE`. The upstream provider circuit breaker is open. Retry after 30 seconds.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Service unavailable. Code: `SERVICE_UNAVAILABLE`. The upstream provider circuit breaker is open. Retry after 30 seconds."}}}},"504":{"description":"Gateway timeout. Code: `UPSTREAM_TIMEOUT`. The upstream provider did not respond in time.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Gateway timeout. Code: `UPSTREAM_TIMEOUT`. The upstream provider did not respond in time."}}}}}}},"/v2/accommodations":{"get":{"summary":"Search accommodations (v2)","tags":["Accommodations"],"description":"Unified search for hotels and rentals across Booking.com, VRBO, Expedia, and Hotels.com. Each result carries a `suppliers.<name>` map with the supplier-specific id, deeplink, brand asset and (when pricing ran) quote.\n\n**Location (required):** Provide at least one — `address`, `lat`/`lng`, or all four bounding box params (`nelat`, `nelng`, `swlat`, `swlng`). For points of interest (e.g., \"Eiffel Tower\"), prefer geocoding to coordinates first, then use `lat`/`lng` + `radius`.\n\n**Provider filter (optional):** When omitted, all suppliers are returned. Pass `provider=booking` for one supplier, `provider=booking,vrbo` (CSV), or repeated params (`provider=booking&provider=vrbo`) to OR-combine multiple. Generated links use CSV. Accepted values: `booking`, `vrbo`, `expedia`, `hotelscom`. Counts, pagination and clustering all reflect the requested supplier set exactly (the filter runs natively upstream — not as a post-filter). Note: combining `provider=` with `hotelids=` is only valid for a single supplier (the id-to-supplier mapping is otherwise ambiguous) and returns 400 otherwise.\n\n**Dates (optional):** When omitted, results are static and the price filters are no-ops. Explicit dates must be today or later and no more than 2 years in the future.\n\n**Pricing:** Per-supplier quotes live under `suppliers.<name>.price` (`total` for the full stay in the response currency). The `min`/`max` filters apply to the per-night price in USD and only run when `checkin`/`checkout` are also supplied.\n\n**Pagination:** Use `page` (1-indexed) and `pageSize` (≤100). `meta.total` is a bounded count for this request: matching stays for **static** requests (no dates; saturates at ~100,000 on broad viewports), stays surviving availability/price filtering for **priced** requests, or H3 cells represented for **`cluster=top`** (a lower bound in dense viewports). `hasMore` is the source of truth — follow `_links.next` until it is absent or `meta.hasMore` is false.\n\n**Clustering (optional, off by default):** `cluster=false` (or omit) returns a flat list. `cluster=auto` summarizes dense viewports into H3 hex markers when the server judges it useful; `cluster=true` forces clustering whenever possible, clamping tight viewports to the deepest resolution (r8, ~460 m). Both return a separate `clusters[]` array — each with `count`, `location.bounds`, `location.coordinates`, and a `_links.expand` URL — disjoint from the sparse stays in `results[]`. `cluster=top` instead puts ONE representative (the best-rated stay) per cell directly in `results[]`, tagged with `cellId` and paginated over cells; `precision` (r0–r15) overrides cell size and clustering is kept down to ~r11 at tight zoom. Structural blockers (ID lookups, center+radius without a bbox, no resolvable viewport) always fall back to a flat list.\n\n**Rate limits:** 5 req/min without an API key (demo), 100 req/min with a key. Sliding 60-second window. Contact us for higher limits.\n\n**Response headers:** `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (ms), `X-Cache` (HIT/MISS).\n\n**Authentication:** Pass your API key via `X-API-KEY` header (recommended) or `key` query parameter. No key needed for demo mode. Get your key at [hub.stay22.com](https://hub.stay22.com).","parameters":[{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"in":"query","name":"provider","required":false,"description":"Optional supplier filter. REST accepts a single value (`?provider=booking`), comma-separated values (`?provider=booking,vrbo`), or repeated params (`?provider=booking&provider=vrbo`). MCP/tool callers should pass comma-separated values as a string. Accepted: booking, vrbo, expedia, hotelscom. Multiple values are OR-combined upstream — every returned listing is bookable by at least one of the requested suppliers. Omit the parameter to include every supplier. Note: combining `?provider=` with `?hotelids=` is only valid for a single supplier (the id-to-supplier mapping is otherwise ambiguous) and returns 400 otherwise."},{"schema":{"type":"string"},"in":"query","name":"address","required":false,"description":"🔎 ADDRESS METHOD: Human-readable address, place name, or point of interest (e.g., \"Eiffel Tower\", \"Berliner Dom\", \"Colosseum\"). Example: \"Paris, France\" or \"Times Square, New York\". For POIs, lat/lng + radius might be more efficient."},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"lat","required":false,"description":"📍 LAT/LNG METHOD: Center point latitude for radial search. Range: -90 to 90. Use with lng. Example: 40.7128 for New York City."},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"lng","required":false,"description":"📍 LAT/LNG METHOD: Center point longitude for radial search. Range: -180 to 180. Use with lat. Example: -74.0060 for New York City."},{"schema":{"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"radius","required":false,"description":"📍 LAT/LNG METHOD: Search radius in meters. Default: 10000 (10km) when using lat/lng search."},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"nelat","required":false,"description":"🗺️ BOUNDING BOX METHOD: North-East corner latitude. Range: -90 to 90. Use all four coordinates (nelng, swlat, swlng) for precise map-based searches. Preferred for map viewports."},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"nelng","required":false,"description":"🗺️ BOUNDING BOX METHOD: North-East corner longitude. Range: -180 to 180. Use with nelat, swlat, swlng."},{"schema":{"type":"number","minimum":-90,"maximum":90},"in":"query","name":"swlat","required":false,"description":"🗺️ BOUNDING BOX METHOD: South-West corner latitude. Range: -90 to 90. Use with nelat, nelng, swlng."},{"schema":{"type":"number","minimum":-180,"maximum":180},"in":"query","name":"swlng","required":false,"description":"🗺️ BOUNDING BOX METHOD: South-West corner longitude. Range: -180 to 180. Use with nelat, nelng, swlat."},{"schema":{"type":"string"},"in":"query","name":"hotelids","required":false,"description":"Comma-separated list of accommodation IDs for direct lookup. When provided, location parameters are not required. Example: \"12345,67890,11223\"."},{"schema":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"in":"query","name":"type","required":false,"description":"Property type filter. Single value or comma-separated list.\n\nAccepted values:\n  - hotel: traditional hotels only.\n  - rental: any non-hotel accommodation (the broad \"vacation rental\" bucket covering every concrete type below).\n  - Concrete types: palace, lodge, agritourism, riad, ryokan, villa, resort, pension, bed_and_breakfast, apartment, private_vacation_home, bungalow, tourist, all_inclusive, hostel, farm_stay, cabin, residence, house, condo, chalet, country_house, aparthotel, cottage.\n\nMultiple values are OR-combined (e.g. `villa,resort` returns villas OR resorts).\nWhen `rental` is combined with concrete non-hotel types it absorbs them, since `rental` already covers every non-hotel value.\nOmit the parameter to include every type."},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"checkin","required":false,"description":"Check-in date in YYYY-MM-DD format. Example: 2024-01-31. Real calendar dates only. Explicit values must be today or later and no more than 2 years in the future. Optional — no default applied today."},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"checkout","required":false,"description":"Check-out date in YYYY-MM-DD format. Example: 2024-02-05. Real calendar dates only. Explicit values must be today or later, no more than 2 years in the future, and (when both are supplied) must be after checkin. Optional — no default applied today."},{"schema":{"default":2,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"adults","required":false,"description":"Number of adult guests (18+ years old). Must be a positive integer. Default: 2. Affects pricing and availability."},{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"children","required":false,"description":"Number of children (under 18 years old). Must be zero or a positive integer. Default: 0. Some properties may have age restrictions."},{"schema":{"default":1,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"rooms","required":false,"description":"Number of rooms/accommodations needed. Must be a positive integer. Default: 1."},{"schema":{"type":"number"},"in":"query","name":"min","required":false,"description":"Minimum per-night price filter in USD (before currency conversion). Only applied when supplied AND both `checkin`/`checkout` are present. Omit to disable the lower bound. Example: 100 for $100/night minimum."},{"schema":{"type":"number"},"in":"query","name":"max","required":false,"description":"Maximum per-night price filter in USD (before currency conversion). Only applied when supplied AND both `checkin`/`checkout` are present. Omit to disable the upper bound. Example: 500 for $500/night maximum."},{"schema":{"default":"USD","type":"string"},"in":"query","name":"currency","required":false,"description":"Currency code for price display (ISO 4217 format). Prices are converted from USD to this currency. Examples: USD, EUR, GBP, JPY, CAD. Default: USD."},{"schema":{"type":"number","minimum":0,"maximum":5},"in":"query","name":"minstarrating","required":false,"description":"Minimum hotel star rating (0-5). Values greater than 0 return only properties at or above this star class; 0 is accepted as no filter."},{"schema":{"type":"number","minimum":0,"maximum":10},"in":"query","name":"minguestrating","required":false,"description":"Minimum guest review score (0-10). Values greater than 0 return only properties at or above this score; 0 is accepted as no filter."},{"schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"minratingcount","required":false,"description":"Minimum number of guest reviews. Integer values greater than 0 filter out properties with fewer reviews than this threshold; 0 is accepted as no filter."},{"schema":{"default":10,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":100},"in":"query","name":"pageSize","required":false,"description":"Page size: number of results returned per page. Range: 1-100. Default: 10."},{"schema":{"default":1,"type":"integer","minimum":0,"exclusiveMinimum":true,"maximum":9007199254740991},"in":"query","name":"page","required":false,"description":"1-indexed page number for pagination. Default: 1. Use with `pageSize` to walk through results."},{"schema":{"default":"en","type":"string"},"in":"query","name":"lang","required":false,"description":"Language code for localized content (ISO 639-1 two-letter format). Default: \"en\". Examples: en (English), fr (French), es (Spanish), de (German), ja (Japanese). Affects property names and descriptions."},{"schema":{"type":"string"},"in":"query","name":"aid","required":false,"description":"Affiliate ID or Partner ID for tracking and attribution. Used for commission tracking and analytics. Provided by Stay22 upon partnership setup. No default (passed through from query only). When an API key is provided, the effective aid is determined by that key."},{"schema":{"type":"string"},"in":"query","name":"campaign","required":false,"description":"Campaign identifier for marketing attribution. Use to track performance of specific campaigns or traffic sources. Example: \"summer-promo-2024\". No default (passed through from query only)."},{"schema":{"default":"false","type":"string","enum":["auto","true","false","top"]},"in":"query","name":"cluster","required":false,"description":"Spatial clustering for map UIs. Defaults to `false` (flat list). Four modes:\n- `false` (default; also when omitted) — flat list.\n- `auto` — server decides: aggregates dense viewports into H3 clusters when useful, flat list when the viewport is too tight (<5 km diagonal) or unsuitable.\n- `true` — force clustering whenever geometrically possible; clamps tight viewports to the deepest H3 resolution (r8, ~460 m hexes).\n- `top` — one representative (the best-rated stay) per H3 cell directly in `results[]`, tagged with `cellId`. With dates, only available representatives are returned (`total` then counts only those cells; without dates it is the candidate-cell count). Unlike `auto`/`true`, keeps clustering at tight zoom (up to r11) instead of going flat.\n\nStructural blockers (ID lookups, center+radius without a bbox, no resolvable viewport) always return a flat list. `auto`/`true` responses include a `clusters[]` array alongside sparse `results[]` (disjoint); each cluster has `count`, `location.bounds`, `location.coordinates`, and a `_links.expand` URL."},{"schema":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"in":"query","name":"precision","required":false,"description":"Optional H3 resolution override for `cluster=top` (ignored for other modes). Range r0 (~1,000 km hexes) to r15 (~0.5 m hexes); out-of-range values are rejected with a 400. Sets cell size and forces top mode at any zoom. When omitted, the server derives a resolution from the viewport, keeping clustering down to ~r11 rather than going flat."},{"schema":{"type":"string"},"in":"query","name":"cell","required":false,"description":"Optional H3 cell filter. Primarily used by cluster expand links."}],"responses":{"200":{"description":"API response containing search results and metadata","content":{"application/json":{"schema":{"type":"object","properties":{"meta":{"type":"object","properties":{"message":{"description":"Error message, only present if success is false","type":"string"},"pageSize":{"type":"number","description":"Page size used for this response (results per page)"},"count":{"type":"number","description":"Number of results included in this response"},"page":{"type":"number","description":"1-indexed page number echoed from the request (clamped to >= 1)"},"hasMore":{"type":"boolean","description":"Whether more pages exist beyond the current one. `meta.total` carries a bounded count of stays you can page through for this request; `hasMore` remains the source of truth for pagination depth."},"total":{"description":"Bounded count of stays you can page through for this request. Static (no dates): stays matching your geo + predicate filters (saturates at ~100,000 on broad viewports). Priced (with `checkin`/`checkout`): stays that survived availability and price filtering, so `total` and `hasMore` stay consistent with `_links.next`; for clustered responses it covers only the sparse `results[]` (cluster-summarized stays are in each `clusters[].count`). For `cluster=top`: the number of H3 cells represented — candidate cells when static, or only cells with an available representative when priced — a lower bound in very dense viewports.","type":"number"},"clustering":{"description":"Present on clustered responses, including `cluster=top`; absent on flat list responses.","type":"object","properties":{"strategy":{"type":"string","enum":["h3"],"description":"Clustering algorithm — currently always H3 hexagonal grid."},"precision":{"type":"number","description":"H3 resolution chosen for this response. Higher = smaller cells (4=~22km, 8=~460m)."},"mode":{"type":"string","enum":["auto","true","top"],"description":"The cluster mode that produced this response (echoes the request). `auto`/`true` → see `clusters[]`; `top` → `results[]` holds one representative stay per cell (each tagged with `cellId`)."}},"required":["strategy","precision","mode"],"additionalProperties":false},"currency":{"type":"string","description":"Currency code used for prices (ISO 4217)"},"checkin":{"nullable":true,"description":"Effective check-in date used for the search, null if not set","type":"string"},"checkout":{"nullable":true,"description":"Effective check-out date used for the search, null if not set","type":"string"},"nights":{"nullable":true,"description":"Number of nights for the stay based on checkin/checkout dates","type":"number"}},"required":["pageSize","count","page","hasMore","currency","checkin","checkout","nights"],"additionalProperties":false,"description":"Response metadata"},"_links":{"type":"object","properties":{"self":{"type":"object","properties":{"href":{"type":"string","description":"Fully-qualified URL"}},"required":["href"],"additionalProperties":false,"description":"Link to the current request"},"first":{"description":"Link to the first page (page=1).","type":"object","properties":{"href":{"type":"string","description":"Fully-qualified URL"}},"required":["href"],"additionalProperties":false},"prev":{"description":"Link to the previous page. Omitted on the first page.","type":"object","properties":{"href":{"type":"string","description":"Fully-qualified URL"}},"required":["href"],"additionalProperties":false},"next":{"description":"Link to the next page. Omitted when `meta.hasMore` is false (i.e. the current page is the last one we know about).","type":"object","properties":{"href":{"type":"string","description":"Fully-qualified URL"}},"required":["href"],"additionalProperties":false}},"required":["self"],"additionalProperties":false,"description":"HATEOAS navigation links following RFC 5988 / IANA link relation conventions. Clients should follow these rels rather than constructing pagination URLs manually. Note: `last` is intentionally absent — total page count is not exposed."},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Canonical Stay22 identifier for the accommodation"},"url":{"type":"string","description":"General provider-agnostic deeplink (Stay22 roam URL) for this listing. Resolves to the best available supplier at click time."},"suppliers":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string","description":"Supplier-specific property identifier"},"link":{"type":"string","description":"Deeplink (allez URL) to book this listing through the supplier"},"media":{"type":"object","properties":{"logoSquare":{"type":"string","description":"URL to this supplier's square brand logo"}},"required":["logoSquare"],"additionalProperties":false,"description":"Per-supplier brand assets"},"price":{"description":"This supplier's quote for the requested stay. Absent when no live pricing is available.","type":"object","properties":{"total":{"type":"number","description":"Full amount payable for the entire stay through this supplier, in the response currency"}},"required":["total"],"additionalProperties":false}},"required":["id","link","media"],"additionalProperties":false,"description":"Per-supplier identifier, deeplink, brand assets and (when priced) quote"},"description":"Map of suppliers that can fulfill this listing, keyed by supplier name (booking, expedia, hotelscom, vrbo). Each entry carries the supplier-specific id, a booking deeplink, brand media and (when pricing ran) the supplier's quote."},"name":{"type":"string","description":"Property name or title"},"type":{"type":"string","description":"Property type display string (e.g. \"Hotel\", \"Bed & breakfast\", \"Country House\"). Defaults to \"Accommodation\" when no value is available for the listing. To filter results by type use the `?type=` query parameter — note that its enum vocabulary uses lower_snake_case API values (e.g. `bed_and_breakfast`) rather than the display strings returned here."},"location":{"type":"object","properties":{"address":{"type":"string","description":"Full formatted address of the property"},"coordinates":{"nullable":true,"description":"Geographic coordinates of the property. May be null if location is not precise.","type":"object","properties":{"lat":{"type":"number","description":"Latitude coordinate"},"lng":{"type":"number","description":"Longitude coordinate"}},"required":["lat","lng"],"additionalProperties":false},"distanceInMeters":{"type":"number","description":"Distance from search center point in meters"}},"required":["address","coordinates","distanceInMeters"],"additionalProperties":false,"description":"Property location information"},"rating":{"type":"object","properties":{"value":{"type":"number","maximum":10,"description":"Rating value up to 10"},"hotelStars":{"nullable":true,"description":"Official hotel star rating (1-5 stars), null if not applicable","type":"number","maximum":5},"count":{"type":"number","description":"Total number of reviews/ratings"}},"required":["value","hotelStars","count"],"additionalProperties":false,"description":"Property ratings and review metrics"},"capacity":{"type":"object","properties":{"guests":{"type":"number","description":"Maximum number of guests the property can accommodate"},"bedrooms":{"type":"number","description":"Number of bedrooms"},"beds":{"nullable":true,"description":"Number of beds, null if not specified","type":"number"},"bathrooms":{"type":"number","description":"Number of bathrooms (may include half baths as 0.5)"}},"required":["guests","bedrooms","beds","bathrooms"],"additionalProperties":false,"description":"Property capacity and room configuration"},"policies":{"type":"object","properties":{"instantBook":{"type":"boolean","description":"Whether the property supports instant booking without host approval"},"freeCancellation":{"type":"boolean","description":"Whether the property offers free cancellation"}},"required":["instantBook","freeCancellation"],"additionalProperties":false,"description":"Booking policies and features"},"media":{"type":"object","properties":{"thumbnail":{"type":"string","description":"URL to property thumbnail image"}},"required":["thumbnail"],"additionalProperties":false,"description":"Media assets for the property"},"cellId":{"description":"H3 cell id this stay represents. Present only with `cluster=top`, where each `results[]` entry is the representative for one map cell. Stable across requests at the same precision.","type":"string"}},"required":["id","url","suppliers","name","type","location","rating","capacity","policies","media"],"additionalProperties":false,"description":"Standardized accommodation object with normalized data across all providers"},"description":"Array of accommodations matching your search criteria"},"clusters":{"description":"Clustered aggregations of stays. Present only when `cluster=auto` or `cluster=true` produced clusters. Disjoint from `results[]` — a stay never appears in both arrays.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Stable cluster id. Format: `cl_h3r{precision}_{cellId}`. Stable across requests for the same cell — useful as a React/MapLibre marker key for animation reconciliation."},"type":{"type":"string","enum":["Cluster"],"description":"Discriminator for client-side routing."},"location":{"type":"object","properties":{"coordinates":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"],"additionalProperties":false,"description":"Cell centroid — recommended marker placement."},"bounds":{"type":"object","properties":{"ne":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"],"additionalProperties":false},"sw":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}},"required":["lat","lng"],"additionalProperties":false}},"required":["ne","sw"],"additionalProperties":false,"description":"Axis-aligned envelope of the H3 hex — useful for fit-to-cluster zoom."}},"required":["coordinates","bounds"],"additionalProperties":false,"description":"Geometry for marker placement and zoom-to-fit."},"count":{"type":"number","description":"Number of stays summarized by this cluster."},"_links":{"type":"object","properties":{"expand":{"type":"object","properties":{"href":{"type":"string","description":"Pre-built `/v2/accommodations` URL for drilling into this cluster. The link filters to the cluster cell; max-precision clusters expand to a flat list."}},"required":["href"],"additionalProperties":false,"description":"Drill-in link to the underlying stays."}},"required":["expand"],"additionalProperties":false,"description":"HATEOAS navigation for this cluster."}},"required":["id","type","location","count","_links"],"additionalProperties":false,"description":"Aggregated marker representing N stays in a single H3 hex cell."}}},"required":["meta","_links","results"],"additionalProperties":false,"description":"API response containing search results and metadata"}}}},"400":{"description":"Validation error. Possible codes: `LOCATION_REQUIRED`, `INVALID_PROVIDER`, `INVALID_DATE_FORMAT`, `INVALID_COORDINATES`, `VALIDATION_ERROR`.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Validation error. Possible codes: `LOCATION_REQUIRED`, `INVALID_PROVIDER`, `INVALID_DATE_FORMAT`, `INVALID_COORDINATES`, `VALIDATION_ERROR`."}}}},"401":{"description":"Authentication error. Possible codes: `INVALID_API_KEY`, `MISSING_API_KEY`.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Authentication error. Possible codes: `INVALID_API_KEY`, `MISSING_API_KEY`."}}}},"429":{"description":"Rate limit exceeded. Code: `RATE_LIMIT_EXCEEDED`. Demo mode: 5 req/min per IP. Authenticated: 100 req/min per API key. Sliding 60-second window. Use exponential backoff for retries. Contact us for higher limits.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Rate limit exceeded. Code: `RATE_LIMIT_EXCEEDED`. Demo mode: 5 req/min per IP. Authenticated: 100 req/min per API key. Sliding 60-second window. Use exponential backoff for retries. Contact us for higher limits."}}}},"502":{"description":"Bad gateway.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Bad gateway."}}}},"503":{"description":"Service unavailable. Retry after 30 seconds.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Service unavailable. Retry after 30 seconds."}}}},"504":{"description":"Gateway timeout.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Gateway timeout."}}}}}}},"/v1/reporting/transactions":{"get":{"summary":"Retrieve partner transactions","tags":["Reporting"],"description":"Retrieve a paginated list of transactions for the authenticated partner.\n\n**Authentication:** Pass your API key via `X-API-Key` header (recommended) or `key` query parameter. Get your key at [hub.stay22.com](https://hub.stay22.com).\n\n**Date filtering:** By default filters on `lastUpdatedDate`. Use `dateFilter=bookedDate` or `dateFilter=startDate` to filter on those fields instead. Date range must not exceed 1 year. Defaults to the last year if omitted.\n\n**Formats:** Set `format=json` for JSON response or `format=csv` (default) for CSV download.\n\n**Pagination:** Use `page` (zero-indexed) and `limit` (max 500) to paginate results.","parameters":[{"schema":{"type":"string","minLength":1},"in":"query","name":"thirdParty","required":true,"description":"Your platform name as registered with Stay22 (e.g., \"inc\", \"helloworld\"). Required for identification."},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"startDate","required":false,"description":"Start of the date range. The field filtered depends on the dateFilter parameter (defaults to lastUpdatedDate). Defaults to 1 year before the current date. Format: YYYY-MM-DD."},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"endDate","required":false,"description":"End of the date range. The field filtered depends on the dateFilter parameter (defaults to lastUpdatedDate). Defaults to the current date. Format: YYYY-MM-DD."},{"schema":{"default":"lastUpdatedDate","type":"string","enum":["lastUpdatedDate","bookedDate","startDate"]},"in":"query","name":"dateFilter","required":false,"description":"Which date field the startDate/endDate range filters on. Default: lastUpdatedDate."},{"schema":{"default":"csv","type":"string","enum":["csv","json"]},"in":"query","name":"format","required":false,"description":"Response format. \"csv\" returns text/csv, \"json\" returns application/json. Default: csv."},{"schema":{"default":0,"type":"integer","minimum":0,"maximum":9007199254740991},"in":"query","name":"page","required":false,"description":"Zero-indexed page number for pagination. Use with limit. Default: 0."},{"schema":{"default":500,"type":"integer","minimum":1,"maximum":500},"in":"query","name":"limit","required":false,"description":"Number of records per page. Range: 1-500. Default: 500."}],"responses":{"200":{"description":"Transactions data. When format=csv, returns text/csv instead of JSON.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"partnerId":{"type":"string","description":"Partner identifier"},"data":{"type":"array","items":{"type":"object","properties":{"provider":{"nullable":true,"description":"Name of the transaction provider (e.g., \"Booking.com\")","type":"string"},"bookingId":{"nullable":true,"description":"Unique booking ID from the provider","type":"string"},"bookedDate":{"nullable":true,"description":"Date the transaction was made (ISO-8601)","type":"string"},"startDate":{"nullable":true,"description":"Stay start date (YYYY-MM-DD)","type":"string"},"endDate":{"nullable":true,"description":"Stay end date (YYYY-MM-DD)","type":"string"},"bookingStatus":{"nullable":true,"description":"Transaction status: APPROVED, PENDING, or CANCELED","type":"string"},"campaignId":{"nullable":true,"description":"Campaign identifier(s), separated by dash. Null when no campaign is associated.","type":"string"},"campaignIds":{"default":[],"description":"Array of individual campaign identifiers. Use this instead of campaignId when campaign names contain dashes.","type":"array","items":{"type":"string"}},"commission":{"nullable":true,"description":"Commission amount (e.g., \"5.21\")","anyOf":[{"type":"string"},{"type":"number"}]},"currency":{"nullable":true,"description":"Commission currency code (e.g., \"USD\")","type":"string"},"category":{"nullable":true,"description":"Booking category (e.g., \"ACCOMMODATION\")","type":"string"},"quantity":{"nullable":true,"description":"Length of stay (accommodation/cruise) or quantity ordered (retail)","type":"number"},"information":{"nullable":true,"description":"Property name (accommodation), airline (flight), agency (car), or item (retail)","type":"string"},"destination":{"nullable":true,"description":"Destination city and country (e.g., \"Paris, France\")","type":"string"},"userCountry":{"nullable":true,"description":"Country of the user who made the booking (e.g., \"US\")","type":"string"},"device":{"nullable":true,"description":"Device type used for the booking (e.g., \"desktop\")","type":"string"},"lastUpdatedDate":{"nullable":true,"description":"Date when the booking was last updated (ISO-8601)","type":"string"}},"required":["campaignIds"],"additionalProperties":{}},"description":"Array of transaction records"}},"required":["statusCode","partnerId","data"],"additionalProperties":{},"description":"Transactions data. When format=csv, returns text/csv instead of JSON."}}}},"401":{"description":"Authentication error. Possible codes: `INVALID_API_KEY` (key is invalid or revoked), `MISSING_API_KEY` (no key provided).","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Authentication error. Possible codes: `INVALID_API_KEY` (key is invalid or revoked), `MISSING_API_KEY` (no key provided)."}}}},"422":{"description":"Validation error from upstream. Possible causes: missing thirdParty, invalid date format, date range exceeds 1 year.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Validation error from upstream. Possible causes: missing thirdParty, invalid date format, date range exceeds 1 year."}}}},"502":{"description":"Bad gateway. Code: `UPSTREAM_ERROR`. Hub API returned an error.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Bad gateway. Code: `UPSTREAM_ERROR`. Hub API returned an error."}}}},"503":{"description":"Service unavailable. Code: `SERVICE_UNAVAILABLE`. Circuit breaker is open. Retry after 30 seconds.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Service unavailable. Code: `SERVICE_UNAVAILABLE`. Circuit breaker is open. Retry after 30 seconds."}}}},"504":{"description":"Gateway timeout. Code: `UPSTREAM_TIMEOUT`. Hub API did not respond in time.","content":{"application/json":{"schema":{"type":"object","properties":{"statusCode":{"type":"number","description":"HTTP status code"},"error":{"type":"string","description":"Error type/category"},"code":{"type":"string","description":"Machine-readable error code"},"message":{"type":"string","description":"Human-readable error message"},"requestId":{"type":"string","description":"Request ID for tracking"},"details":{"description":"Additional error details","type":"object","additionalProperties":{}},"provider":{"description":"Provider name if relevant","type":"string"}},"required":["statusCode","error","code","message","requestId"],"additionalProperties":false,"description":"Gateway timeout. Code: `UPSTREAM_TIMEOUT`. Hub API did not respond in time."}}}}}}}},"servers":[{"url":"/","description":"API Server"}],"security":[{"apiKeyHeader":[]},{"apiKeyQuery":[]}],"tags":[{"name":"Accommodations","description":"Stable accommodations search API."},{"name":"Reporting","description":"Partner transactions data."}],"externalDocs":{"url":"https://api.stay22.com/docs/","description":"Full API Documentation"}}