{
  "openapi" : "3.0.1",
  "info" : {
    "title" : "Accounts, Lookup, Search, Inventory, Content & Booking",
    "description" : "\n# Partner Integrator API\nAuthenticated, usage-metered access to bookable travel inventory for API integrators. Query\nproperties, availability and rates programmatically at scale over **gRPC**, calling the\n`wink.partner.v1` services below. Requests are counted and rate-limited per account: a free\ntier applies until you subscribe to the metered paid tier. See the Partner API pricing & SLA\npage for current limits, per-request pricing and terms.\n\nEvery call carries **two** gRPC metadata headers:\n\n| Header | Value |\n| --- | --- |\n| `authorization` | `Bearer <token>` — an OAuth2 client-credentials token, see Authentication |\n| `managing-entity-id` | The account you are calling on behalf of |\n\nAuthorization is per account: the account named in `managing-entity-id` is checked for `INVENTORY:READ`, and metered usage is billed to it. Omit the header and the call fails with `INVALID_ARGUMENT`; name an account you do not hold `INVENTORY:READ` on and it fails with `PERMISSION_DENIED`.\n\nThe account is a header rather than a request field so that it is known before the request body is read — which is what lets quota and rate-limit checks reject a call without decoding it. Request messages therefore have no account field, and setting one is not possible.\n\n### Generating a client\n\nYou call this API with a generated gRPC client, not with `curl`. Wink publishes the\nschema two ways:\n\n- **Server reflection** — the server describes itself, so `grpcurl`, Postman and Insomnia discover the schema at runtime with nothing to download. Reflection needs the same bearer token as any other call.\n- **The `.proto` files**, if you would rather vendor the schema and generate ahead of time.\n\n```bash\n# Reflection describes the schema and needs only the token — it is not account-scoped.\ngrpcurl -H \"authorization: Bearer $WINK_TOKEN\" partner.wink.travel:443 list\n\n# Every actual call additionally needs the account header.\ngrpcurl -H \"authorization: Bearer $WINK_TOKEN\" \\\n        -H \"managing-entity-id: $WINK_ACCOUNT_ID\" \\\n        -d '{\"urlName\": \"bangkok-thailand\", \"type\": \"CITY\"}' \\\n        partner.wink.travel:443 wink.partner.v1.Lookup/Get\n```\n\n[Generating a Wink gRPC client](https://wink.travel/developers/rest-and-grpc/) walks through it with our schema. For the language-specific toolchain, see the official [gRPC quick starts](https://grpc.io/docs/languages/).\n\n## Connection lifecycle\n\nThese are server settings you cannot discover from the schema, and each one will disconnect a client that guesses wrong. Configure your channel to match.\n\n| Setting | Value | What it means for you |\n| --- | --- | --- |\n| Minimum keepalive interval | **20s** | Do **not** send HTTP/2 keepalive pings more often than every 20 seconds. A faster client is disconnected with `GOAWAY(ENHANCE_YOUR_CALM)`. Most gRPC clients default to 10s or less, so this usually needs setting explicitly. |\n| Pings without active calls | **allowed** | You may keep an idle connection alive with pings; you do not have to hold a call open. |\n| Server keepalive | **60s** | We ping you every 60 seconds to detect a dead peer. Answer them — most clients do automatically. |\n| Maximum connection age | **30m (±10%)** | We send a graceful `GOAWAY` roughly every half hour and you should reconnect. **This is normal, not an error**: it is how traffic rebalances as we scale. Do not alert on it. |\n| Concurrent streams per connection | **200** | Beyond this, open a second connection. |\n\nThe jitter on connection age is deliberate — it stops every client in a fleet reconnecting in lockstep.\n\n```\n# Go, for example\ngrpc.WithKeepaliveParams(keepalive.ClientParameters{\n    Time:                30 * time.Second,  // >= our 20s minimum\n    PermitWithoutStream: true,\n})\n```\n\n> **This is a gRPC API, not a REST API.** Each operation is listed at the HTTP/2 path the transport really uses (`/package.Service/Method`, always `POST`), and the schemas are the [proto3 JSON mapping](https://protobuf.dev/programming-guides/json/) of each message — so the contract is readable and model generation is correct. But these endpoints are **not callable with an ordinary HTTP client**: a real request body is a length-prefixed protobuf frame. Use a generated gRPC client, or the `.proto` schema published via server reflection.\n\n### Errors\n\nFailures arrive as a gRPC status code in the response **trailers**, not as an HTTP status code:\n\n| Status | Meaning |\n| --- | --- |\n| `INVALID_ARGUMENT` | The request was malformed, or a required field was missing. |\n| `UNAUTHENTICATED` | Bearer token missing, expired or invalid. |\n| `PERMISSION_DENIED` | The token is valid but lacks the required grant on the account. |\n| `NOT_FOUND` | No record matched (only on operations that resolve a single record). |\n| `RESOURCE_EXHAUSTED` | Quota exceeded. Carries `retry-after` and `x-ratelimit-*` trailers — back off by the value given. |",
    "contact" : {
      "name" : "Contact",
      "email" : "hi@wink.travel"
    },
    "version" : "37.0.2",
    "x-logo" : {
      "backgroundColor" : "#FFFFFF",
      "altText" : "wink",
      "url" : "https://res.cloudinary.com/traveliko/image/upload/c_scale,h_129/v1653285543/wink/logo_text_white.svg"
    }
  },
  "tags" : [ {
    "name" : "Search",
    "description" : "Search Wink's bookable property inventory and get live prices — the paid Partner Search API.\n\nSix ways to find properties: by map position, by city, or ranked by agency, city, country or globally.\nAll six return the same page shape, so one result renderer serves every entry point.\n\nThese endpoints return PRICES, not property content. Each result carries just enough to render a list row\n— name, location, star rating, one image — plus the prices themselves. Descriptions, galleries, addresses,\nroom detail, amenities and policies come from the Content service, which you should cache per property and\nrefresh when `last_update` changes. That split keeps search responses small and fast, and it means\nproperty content has exactly one source of truth.\n\nEvery call needs an authenticated token with `INVENTORY:READ` on the account you pass, and is billed in\nhotel-day units: (properties actually returned) x min(itinerary nights, 90). You are charged for what comes\nback, not for what you asked for — a request for 50 results that matches 3 properties bills 3 properties.\n\nPrices are live and are not cached. Treat a result as valid for the length of a user's session."
  }, {
    "name" : "Booking",
    "description" : "Manage the bookings an agency facilitated — the Travel Agent Booking API.\n\nCreate bookings on behalf of the agency, then search, inspect, cancel, refund and re-send confirmation\nfor the bookings it produced. Every rpc is scoped to the agency in the `managing-entity-id` call\nmetadata; a booking facilitated by a different agency is not visible here at all.\n\nRequires an authenticated token whose account is a TRAVEL_AGENT and holds `BOOKING:READ` (reads) or\n`BOOKING:WRITE` (create, cancel, refund, resend)."
  }, {
    "name" : "Content",
    "description" : "Retrieve everything static about a property and its inventory — the paid Partner Content API.\n\nContent is the single source of truth for anything that does not change per search: names, descriptions,\naddresses, galleries, room detail, amenities, policies, rate plans and cancellation terms. The pricing\nendpoints deliberately do not return it, so fetch it here, cache it per property, and re-fetch only when the\n`content_version` on a search result stops matching the one you stored.\n\nAlready hold a copy? Send the `cache_token` you were given back in `known_versions` and any property that\nhas not changed returns CONTENT_STATUS_NOT_MODIFIED with no content, so a nightly catalogue sync transfers\nonly what actually moved. It does not reduce your bill — you are charged per call, not per property — but\nit cuts response size and the work on both ends. See `known_versions`.\n\nDescriptions come back in English plus the language you request, so you always have a usable fallback\nwhen a property has not translated a field.\n\nOne call, one shape. By default you get the property's basic identity; ask for more with `include` and it\narrives in the same response rather than costing you another round trip. Billing does not change with\nbreadth: 1 unit per property returned, whatever you included.\n\nRequires an authenticated token with `INVENTORY:READ` on the account you pass."
  }, {
    "name" : "Lookup",
    "description" : "Find the destinations you can query and sell through Wink: properties, cities, countries, islands\nand attractions.\n\nThe usual flow is two calls. Use **Search** as your user types to offer suggestions, then take\nthe `urlName` of whichever one they pick and pass it to **Get** to resolve the full record and\nconfirm you are allowed to sell it.\n\nBoth calls cost ONE unit each, whatever they return — not the per-night pricing that Inventory and Search\nbill. Driving an autocomplete from Search is therefore cheap, but it is not free: budget a unit per\nrequest you send and debounce your input accordingly."
  }, {
    "name" : "Inventory",
    "description" : "Look up live prices for a property you already know — the paid Partner Inventory API.\n\nSearch finds properties and returns each one's best offer. Inventory is the next step: given a property,\nreturn EVERY priced offer for the requested dates and occupancy, plus a rate calendar for planning across\na date range.\n\nThese endpoints return PRICES, not property content. Names, descriptions, galleries, addresses, amenities\nand policies come from the Content service — call it once per property, cache the answer, and refresh when\n`last_update` changes. That keeps these responses small and means property content has exactly one source\nof truth.\n\nThat split runs through the transactional inventory too. Ask for `INVENTORY_SCOPE_SPA` and you get the\nspa's PRICED ROWS keyed by `inventory_id`; the spa's name, photographs and opening hours are the same\n`inventory_id` in Content. Nothing is returned twice, and the half that changes by the minute is not\ndragging the half that changes by the month across the wire on every call.\n\nEvery call needs an authenticated token with `INVENTORY:READ` on the account you name in the\n`managing-entity-id` metadata header, and is billed in hotel-day units.\n\nPrices are live and are not cached. Treat a result as valid for the length of a user's session."
  }, {
    "name" : "Accounts",
    "description" : "Find out which Wink accounts you may act for.\n\nEvery other call on this API is scoped to one account: you send its identifier in the\n`managing-entity-id` metadata header, and your token must hold a permission on it. This service is\nwhere that identifier comes from.\n\nStart with **ListAccounts** to see everything your token reaches, then use the `account_id` of\nwhichever one you want on your subsequent Inventory, Search, Content and Booking calls. Reach for\n**SearchAccounts** instead when you manage enough accounts to need paging or filtering, and\n**GetAccount** when you already hold an identifier and want the current record.\n\nThese three calls are FREE. They are not metered and they consume none of your monthly units, so\nyou can call them at start-up without budgeting for them. Ordinary rate limits still apply.\n\nUnlike the rest of this API, these calls do NOT require the `managing-entity-id` header — they are\nwhat tells you which value to send."
  } ],
  "paths" : {
    "/wink.partner.v1.Search/SearchByGeo" : {
      "post" : {
        "tags" : [ "Search" ],
        "summary" : "SearchByGeo",
        "description" : "Search by map position\n\nFind properties inside a search area. Set `area` to a point for \"near here\", a polygon for \"everything\nin this viewport\", or a multi-polygon for several disjoint regions at once.\n\nA point search is ordered nearest first and reports `distance_in_meters` on every result. Polygon and\nmulti-polygon searches are containment tests with no distance and no distance ordering.\n\nReturns INVALID_ARGUMENT if `area` is unset, if its geometry is malformed, or if you pass\n`radius_in_meters` with anything other than a point.",
        "operationId" : "Search_SearchByGeo",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.SearchByGeoRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.SearchByGeoResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Search/SearchByCity" : {
      "post" : {
        "tags" : [ "Search" ],
        "summary" : "SearchByCity",
        "description" : "Search by city\n\nFind available properties in or around a city, identified by either its GeoNames id or its url name --\nboth are on the same `City` message the Lookup service and every pricing/content result return, so you\nnever need a lookup round trip just to convert one identifier to the other.\n\nReturns INVALID_ARGUMENT if you send neither.",
        "operationId" : "Search_SearchByCity",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.SearchByCityRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.SearchByCityResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Search/RankByAgency" : {
      "post" : {
        "tags" : [ "Search" ],
        "summary" : "RankByAgency",
        "description" : "Rank YOUR OWN managed properties\n\nReturn the properties in your own managed portfolio, ordered by the scoring type you choose --\n\"your own\" meaning the account you called with (`managing-entity-id`), not a request field: this is\nthe one Search RPC with no identifier for WHICH agency, because the account making the call IS the\nagency. Typical callers are a management company or hotel chain (`ManagingEntityType.MANAGEMENT_COMPANY`\nor `CHAIN`) that operates properties on behalf of their owners and calls the Partner API with its own\ncredentials to see everything it represents.\n\nThis is why it is the one ranked call with no `search_filters`: it is not a marketplace search, it is\na listing of what you already manage, so filters like star rating or lifestyle would be filtering your\nown portfolio rather than discovering new inventory. Optionally narrow it to one city or country with\n`scope`; omit it to rank everything you manage.",
        "operationId" : "Search_RankByAgency",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.RankByAgencyRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.RankByAgencyResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Search/RankByCity" : {
      "post" : {
        "tags" : [ "Search" ],
        "summary" : "RankByCity",
        "description" : "Rank a city's properties\n\nReturn properties in or around a city, identified by either its url name or its GeoNames id, ordered\nby the scoring type you choose.\n\nReturns INVALID_ARGUMENT if you send neither.",
        "operationId" : "Search_RankByCity",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.RankByCityRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.RankByCityResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Search/RankByCountry" : {
      "post" : {
        "tags" : [ "Search" ],
        "summary" : "RankByCountry",
        "description" : "Rank a country's properties\n\nReturn properties within a country, ordered by the scoring type you choose.",
        "operationId" : "Search_RankByCountry",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.RankByCountryRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.RankByCountryResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Search/RankGlobal" : {
      "post" : {
        "tags" : [ "Search" ],
        "summary" : "RankGlobal",
        "description" : "Rank properties worldwide\n\nReturn the best-scoring properties across the whole platform. Use this for editorial and discovery\nsurfaces rather than for a destination search.",
        "operationId" : "Search_RankGlobal",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.RankGlobalRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.RankGlobalResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/CreateBooking" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "CreateBooking",
        "description" : "Create a booking on behalf of the agency\n\nBooks inventory against the agency's balance and returns the resulting confirmations. This is the one\nrpc that MOVES MONEY on the way in.\n\nIDEMPOTENT ON `idempotency_key`. gRPC clients retry on UNAVAILABLE by default and a retried create is\na double booking and a double debit, so the key is REQUIRED rather than optional. Replaying a key\nreturns the ORIGINAL confirmations without booking again. Reuse a key only to retry the same intent —\na new booking needs a new key, and sending a used key with a different payload is FAILED_PRECONDITION\nrather than a silent no-op, because the alternative is an agent believing they booked something they\ndid not.\n\nA multi-room create produces SEVERAL bookings sharing a `group_id`, which is what makes them\nrefundable together via RefundBookings.\n\nReturns INVALID_ARGUMENT when `rooms` is empty, the request is malformed or `idempotency_key` is\nblank, FAILED_PRECONDITION\nwhen the key was used for a different request, and PERMISSION_DENIED when the account is not a\ntravel agent or lacks BOOKING:WRITE.",
        "operationId" : "Booking_CreateBooking",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.CreateBookingRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.CreateBookingResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/SearchBookings" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "SearchBookings",
        "description" : "Search the agency's bookings\n\nEvery filter is optional and combines with AND. Failed bookings are never returned — that is a server\nrule, not a filter you can turn off. Results are scoped to the calling agency automatically.\n\nReturns INVALID_ARGUMENT when a date is not ISO-8601 or `page.size` exceeds 100.",
        "operationId" : "Booking_SearchBookings",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.SearchBookingsRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.SearchBookingsResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/GetBooking" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "GetBooking",
        "description" : "Get one booking by id\n\nReturns NOT_FOUND for an unknown id, for a booking belonging to another agency, and for one still\nINITIALIZED — an in-flight booking is not yet a fact and is visible only to platform administrators.\nAll three are deliberately the same answer: distinguishing them would let a caller probe which booking\nids exist.",
        "operationId" : "Booking_GetBooking",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetBookingRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetBookingResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/CancelBooking" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "CancelBooking",
        "description" : "Cancel a booking\n\nApplies the agency's cancellation and returns the booking as it stands afterwards. Whether any money\ncomes back is decided by the rate's cancellation policy, not by this call — read `refund` on the\nresponse rather than assuming.\n\nReturns NOT_FOUND for an unknown or other-agency booking, and FAILED_PRECONDITION when the booking is\nin a status that cannot be cancelled.",
        "operationId" : "Booking_CancelBooking",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.CancelBookingRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.CancelBookingResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/ResendConfirmation" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "ResendConfirmation",
        "description" : "Re-send the traveller's confirmation email\n\nSide-effecting but not money-moving, and safe to repeat: the traveller receives another copy. No\nidempotency key for that reason.\n\nReturns NOT_FOUND for an unknown or other-agency booking.",
        "operationId" : "Booking_ResendConfirmation",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.ResendConfirmationRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.ResendConfirmationResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/RefundBooking" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "RefundBooking",
        "description" : "Immediately refund one booking\n\nFull refund inside the configured immediate-refund window. MOVES MONEY, so `idempotency_key` is\nREQUIRED with the same semantics as CreateBooking — a retried refund is a double refund.\n\nReturns NOT_FOUND for an unknown or other-agency booking, and FAILED_PRECONDITION when the booking is\noutside the immediate-refund window or already refunded.",
        "operationId" : "Booking_RefundBooking",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.RefundBookingRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.RefundBookingResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Booking/RefundBookings" : {
      "post" : {
        "tags" : [ "Booking" ],
        "summary" : "RefundBookings",
        "description" : "Immediately refund every booking in a group\n\nThe group equivalent of RefundBooking. MOVES MONEY; `idempotency_key` is REQUIRED and covers the whole\ngroup, so a retry cannot half-refund it.\n\nA booking that cannot be refunded does NOT fail the call — it comes back carrying an `error`, the same\njudgement GetProperties makes about an unknown id in a batch. One booking outside its window must not\ndeny the agency the refunds it is owed on the rest.\n\nReturns NOT_FOUND when the group has no bookings belonging to this agency.",
        "operationId" : "Booking_RefundBookings",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.RefundBookingsRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.RefundBookingsResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Content/GetProperties" : {
      "post" : {
        "tags" : [ "Content" ],
        "summary" : "GetProperties",
        "description" : "Get static content for one or more properties\n\nPass up to 50 property identifiers. Every property we hold comes back with a `status` of\nCONTENT_STATUS_OK, and unknown identifiers come back CONTENT_STATUS_NOT_FOUND — the call itself\nsucceeds, so a single bad identifier cannot fail the batch. A property you already hold an unchanged\ncopy of comes back CONTENT_STATUS_NOT_MODIFIED when you pass its token in `known_versions`.\n\nContent is not restricted by your sales channel; the BOOKABLE collections are. See ContentStatus.\n\nBilling is one unit per CALL, not per property, whatever you included. Fifty properties in one call\ncost what one does, so batch.\n\nReturns INVALID_ARGUMENT when `property_ids` is empty, exceeds 50, or repeats an identifier, and when\n`known_versions` holds more than 50 entries.",
        "operationId" : "Content_GetProperties",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetPropertiesRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetPropertiesResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Content/GetProperty" : {
      "post" : {
        "tags" : [ "Content" ],
        "summary" : "GetProperty",
        "description" : "Get one property by id\n\nThe single-property shape of GetProperties, for a deep link or a cache fill when you hold one\n`property_id` and have no batch to make. Same content, same `include` scopes, same cache semantics.\n\nCosts one unit — the SAME as a fifty-property GetProperties. Fetching fifty properties one at a time\ntherefore costs fifty times what one batched call would; reach for GetProperties whenever you hold\nmore than one id. This rpc exists for convenience, not economy.\n\nUnlike GetProperties, an unknown id is an ERROR here rather than a per-entry status: with one property\nin the response there is no batch left to keep succeeding, so NOT_FOUND is the honest answer. The\nreturned `status` is therefore only ever CONTENT_STATUS_OK or CONTENT_STATUS_NOT_MODIFIED.\n\nReturns NOT_FOUND for an unknown id, and INVALID_ARGUMENT when `property_id` is blank or\n`max_media_per_entity` is negative.",
        "operationId" : "Content_GetProperty",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetPropertyRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetPropertyResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Content/GetRoomType" : {
      "post" : {
        "tags" : [ "Content" ],
        "summary" : "GetRoomType",
        "description" : "Get one room type by id\n\nFor filling a cache or following a deep link when you already hold a `room_type_id` from\n`RoomTypeOffers`. To render a whole property, prefer GetProperties with CONTENT_SCOPE_ROOM_TYPE — it\nreturns every room type you can sell in one call, for the same one unit.\n\nCosts one unit, the same as a fifty-property GetProperties. Three deep links cost three times what one\nbatched call would; that is the intended pressure.\n\nReturns NOT_FOUND for an unknown id, and INVALID_ARGUMENT when `room_type_id` is blank.",
        "operationId" : "Content_GetRoomType",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetRoomTypeRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetRoomTypeResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Content/GetRatePlan" : {
      "post" : {
        "tags" : [ "Content" ],
        "summary" : "GetRatePlan",
        "description" : "Get one rate plan by id\n\nAs above, for a `rate_plan_id` from `RoomRate`.",
        "operationId" : "Content_GetRatePlan",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetRatePlanRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetRatePlanResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Content/GetCancellationPolicy" : {
      "post" : {
        "tags" : [ "Content" ],
        "summary" : "GetCancellationPolicy",
        "description" : "Get one cancellation policy by id\n\nAs above, for a `cancellation_policy_id` from `RoomRate`. The pricing surfaces already tell you whether\nan offer is refundable and until when; this tells you the terms behind that answer.",
        "operationId" : "Content_GetCancellationPolicy",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetCancellationPolicyRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetCancellationPolicyResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Lookup/Search" : {
      "post" : {
        "tags" : [ "Lookup" ],
        "summary" : "Search",
        "description" : "Search by term\n\nTypeahead search across destinations. Send the partial text your user has typed and render\nthe results as suggestions.\n\nTerms shorter than 3 characters return an empty list rather than an error, so it is safe to\ncall on every keystroke. At most 50 results are returned, most relevant first.\n\nSuggestions are not filtered by your sales channel — a property you cannot sell may still\nappear here. Get is where that is enforced.",
        "operationId" : "Lookup_Search",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.SearchRequest"
              },
              "example" : {
                "term" : "bangkok"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.SearchResponse"
                },
                "example" : {
                  "destinations" : [ {
                    "id" : "efb6edfd-7969-434e-8453-be3495a9ffb1",
                    "type" : "CITY",
                    "typeIdentifier" : "1609350",
                    "name" : "Bangkok",
                    "urlName" : "bangkok-thailand",
                    "cityName" : "Bangkok",
                    "countryName" : "Thailand",
                    "subCountryName" : "Bangkok",
                    "countryCode" : "TH",
                    "languageCode" : "en",
                    "longitude" : 100.5493,
                    "latitude" : 13.7412
                  }, {
                    "id" : "025a9f08-a528-4a24-a2ef-c0c3cf74e0d4",
                    "type" : "HOTEL",
                    "typeIdentifier" : "71f0ec54-c24b-463a-a72e-b060c6c7a6fe",
                    "name" : "Amayen Sanctuary",
                    "urlName" : "amayen-sanctuary-doi-saket-thailand",
                    "cityName" : "Doi Saket",
                    "countryName" : "Thailand",
                    "subCountryName" : "Chiang Mai",
                    "countryCode" : "TH",
                    "languageCode" : "en",
                    "longitude" : 99.24162894487381,
                    "latitude" : 18.907146707330355
                  } ]
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Lookup/Get" : {
      "post" : {
        "tags" : [ "Lookup" ],
        "summary" : "Get",
        "description" : "Look up by URL slug\n\nResolve one destination from its URL slug and type — for example `bangkok-thailand` plus\nDESTINATION_TYPE_CITY. Use it after Search, or whenever you already hold a Wink destination slug in your own\nrouting.\n\nBoth fields are required, because a slug is only unique within a type.\n\nReturns NOT_FOUND when nothing matches. For a destination that exists but sits outside your sales\nchannel it returns PERMISSION_DENIED instead, so you can tell \"not yours to sell\" apart from\n\"no such destination\".",
        "operationId" : "Lookup_Get",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetRequest"
              },
              "example" : {
                "urlName" : "bangkok-thailand",
                "type" : "CITY"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetResponse"
                },
                "example" : {
                  "destination" : {
                    "id" : "efb6edfd-7969-434e-8453-be3495a9ffb1",
                    "type" : "CITY",
                    "typeIdentifier" : "1609350",
                    "name" : "Bangkok",
                    "urlName" : "bangkok-thailand",
                    "cityName" : "Bangkok",
                    "countryName" : "Thailand",
                    "subCountryName" : "Bangkok",
                    "countryCode" : "TH",
                    "languageCode" : "en",
                    "longitude" : 100.5493,
                    "latitude" : 13.7412
                  }
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Inventory/GetPropertyInventory" : {
      "post" : {
        "tags" : [ "Inventory" ],
        "summary" : "GetPropertyInventory",
        "description" : "Prices for one property\n\nReturns every offer bookable at this property for the requested dates and occupancy, cheapest first.\nIdentify the property by `property_id` or by `url_name` — set exactly one.\n\nBilled as min(itinerary nights, 90) hotel-day units, whether or not anything is available.",
        "operationId" : "Inventory_GetPropertyInventory",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetPropertyInventoryRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetPropertyInventoryResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Inventory/ListPropertyInventory" : {
      "post" : {
        "tags" : [ "Inventory" ],
        "summary" : "ListPropertyInventory",
        "description" : "Prices for several properties\n\nThe same lookup for many properties at once, resolved in parallel. Each entry carries its own dates and\noccupancy, so you can price different stays in one call.\n\nBilled as the SUM over entries of min(that entry's nights, 90) — a batch costs exactly what the same\nentries would cost individually, so batching is a latency optimisation rather than a discount.",
        "operationId" : "Inventory_ListPropertyInventory",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.ListPropertyInventoryRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.ListPropertyInventoryResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Inventory/GetRatePeriod" : {
      "post" : {
        "tags" : [ "Inventory" ],
        "summary" : "GetRatePeriod",
        "description" : "Rate calendar\n\nThe best available rate for every check-in/check-out combination across a date range — what you need to\nrender a \"cheapest night to stay\" calendar without one call per date.\n\nBilled as `period_in_days` hotel-day units.",
        "operationId" : "Inventory_GetRatePeriod",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetRatePeriodRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetRatePeriodResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Accounts/ListAccounts" : {
      "post" : {
        "tags" : [ "Accounts" ],
        "summary" : "ListAccounts",
        "description" : "List the accounts you can act for\n\nReturns every account you can actually operate on — you are its owner, or your membership has been\naccepted. This is the normal starting point for an integration: call it once at start-up, keep the\n`account_id` values you care about, and send them as the `managing-entity-id` header on your other\ncalls. Every identifier returned here is one your token can genuinely use.\n\nAn account you have merely been INVITED to and have not accepted does not appear, and neither does\na removed one. Both would otherwise be handed to you here and then rejected with PERMISSION_DENIED\nby every call you made with them.\n\nThe response is scoped entirely by your token; there is nothing to pass. An empty list means your\ntoken reaches no account yet, not that something went wrong.\n\nAt most 200 accounts come back. If you hold more, `truncated` is true and you should switch to\nSearchAccounts, which pages. The list is never silently cut off without that flag being set.\n\nFree: this call is not metered.",
        "operationId" : "Accounts_ListAccounts",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.ListAccountsRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.ListAccountsResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Accounts/SearchAccounts" : {
      "post" : {
        "tags" : [ "Accounts" ],
        "summary" : "SearchAccounts",
        "description" : "Search the accounts you can act for\n\nThe paged, filterable form of ListAccounts, over exactly the same set of accounts, for when you\nhold too many to take in one response. Filter by lifecycle status, by account type, or by the\nstart of the name, and page with `skip` and `take`.\n\nEvery filter is optional; sending none is the same as ListAccounts with paging. Results are\nscoped to the accounts your token can reach — being a platform administrator does not widen it.\n\nFree: this call is not metered.",
        "operationId" : "Accounts_SearchAccounts",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.SearchAccountsRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.SearchAccountsResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    },
    "/wink.partner.v1.Accounts/GetAccount" : {
      "post" : {
        "tags" : [ "Accounts" ],
        "summary" : "GetAccount",
        "description" : "Get one account by id\n\nRetrieve the current record for a single account you already hold the identifier for — after a\nrename, say, or to refresh a copy you cached at start-up.\n\nRequires an authenticated token with `ACCOUNT:READ` on the account you ask for. Returns\nPERMISSION_DENIED when your token cannot reach it and NOT_FOUND when no such account exists, so\nyou can tell a revoked grant apart from a stale identifier.\n\nFree: this call is not metered.",
        "operationId" : "Accounts_GetAccount",
        "requestBody" : {
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/wink.partner.v1.GetAccountRequest"
              }
            }
          },
          "required" : true
        },
        "responses" : {
          "200" : {
            "description" : "The RPC completed with gRPC status OK.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/wink.partner.v1.GetAccountResponse"
                }
              }
            }
          }
        },
        "security" : [ {
          "oauth2ClientCredentials" : [ ]
        } ]
      }
    }
  },
  "components" : {
    "schemas" : {
      "wink.partner.v1.SearchByGeoRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context: dates, occupancy, language and currency. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "area" : {
            "description" : "Where to search. Required. A point, a polygon, or a multi-polygon — see GeoShape.\n\nThe geometry you choose decides how the search behaves: a point searches outward to\n`radius_in_meters` and orders results nearest first; a polygon or multi-polygon tests containment,\ntakes no radius, and has no distance to order by.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoShape"
            } ]
          },
          "radiusInMeters" : {
            "type" : "string",
            "description" : "How far to search from a point, in metres. Only valid when `area` holds a point; sending it with a\npolygon is rejected with INVALID_ARGUMENT rather than ignored, because a silently dropped radius\nreturns a plausible page that is not the search you asked for. Defaults to 1000 when omitted.",
            "format" : "int64"
          },
          "searchFilters" : {
            "description" : "Optional narrowing of the result set.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchFilters"
            } ]
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page number. Defaults to 0.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Results per page. Defaults to 20, which is also the maximum. A larger value is rejected with\nINVALID_ARGUMENT rather than clamped, so a caller never pages through a result set believing it is\nlarger than it is. Every property returned is billed, so the cap bounds cost as well as fan-out.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for SearchByGeo."
      },
      "wink.partner.v1.UserSession" : {
        "type" : "object",
        "properties" : {
          "itinerary" : {
            "description" : "Stay dates and occupancy. Required — searches without an itinerary cannot be priced.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Itinerary"
            } ]
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language, e.g. \"en\". Affects the little text this surface returns; for descriptions and other\nprose, pass the language to the Content service instead. Defaults to \"en\"."
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency for all returned prices, e.g. \"THB\". Exactly three characters. Defaults to the\nproperty's own currency."
          },
          "promotionalCodes" : {
            "type" : "array",
            "description" : "Promotional codes to apply. Codes your account is not entitled to are ignored, not rejected.",
            "items" : {
              "type" : "string"
            }
          }
        },
        "description" : "Who is travelling, when, and in what currency and language.\n\nThis drives pricing, not just presentation: `currency_code` selects the currency every Money on the response is\nconverted into, and `itinerary` determines both availability and how the call is billed."
      },
      "wink.partner.v1.Itinerary" : {
        "type" : "object",
        "properties" : {
          "startDate" : {
            "type" : "string",
            "description" : "Check-in date, ISO-8601 \"YYYY-MM-DD\". Required. Must be today or later -- a past date is rejected\nwith INVALID_ARGUMENT."
          },
          "nights" : {
            "type" : "integer",
            "description" : "Number of nights. Billing caps this at 90; a longer stay is rejected with INVALID_ARGUMENT.",
            "format" : "int32"
          },
          "room" : {
            "description" : "The room being searched for, and how many of it. Required.\n\nSINGULAR. This was once a list, on the promise that several occupancies could be priced in one call —\ntwo adults in one room and a family of four in another. The platform never delivered it: every\nconsumer read the FIRST entry and the room count came from the list's SIZE, so a second entry was\npriced as though it did not exist while still moving the count that volume discounts and availability\nkey off. Ask for several identical rooms with `RoomConfiguration.quantity`; ask for several DIFFERENT\noccupancies as separate calls, and assemble them in your own cart.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.RoomConfiguration"
            } ]
          }
        },
        "description" : "Stay dates and occupancy."
      },
      "wink.partner.v1.RoomConfiguration" : {
        "type" : "object",
        "properties" : {
          "adults" : {
            "type" : "integer",
            "description" : "Adults in this room. Defaults to 1.",
            "format" : "int32"
          },
          "children" : {
            "type" : "array",
            "description" : "Children in this room, by age. Ages are REQUIRED, not optional detail: properties price children in\nage bands, and an unpriceable age produces a rate the guest cannot actually book.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.ChildOccupancy"
            }
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many identical rooms of this configuration. Defaults to 1.",
            "format" : "int32"
          }
        },
        "description" : "The occupancy of one room."
      },
      "wink.partner.v1.ChildOccupancy" : {
        "type" : "object",
        "properties" : {
          "age" : {
            "type" : "integer",
            "description" : "Age at check-in, in years. Zero is valid — infants are priced differently from older children, not\nignored.",
            "format" : "int32"
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many children of this age. Defaults to 1.",
            "format" : "int32"
          }
        },
        "description" : "A number of children of the same age."
      },
      "wink.partner.v1.GeoShape" : {
        "maxProperties" : 1,
        "type" : "object",
        "properties" : {
          "point" : {
            "description" : "Search outward from this point, out to `radius_in_meters`.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPosition"
            } ]
          },
          "polygon" : {
            "description" : "Search inside this polygon.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPolygon"
            } ]
          },
          "multiPolygon" : {
            "description" : "Search inside any of these polygons.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoMultiPolygon"
            } ]
          }
        },
        "description" : "A search area: a point, a polygon, or several polygons.\n\nExactly one member is set, and the field you choose IS the geometry type — there is no separate `type`\nstring to keep in sync with the coordinates, and no way to describe a Point while supplying a ring.\n\nThe point here is a GeoPosition -- bare latitude and longitude -- rather than a message carrying its own\n`type` string. Inside this oneof the field name already settles the geometry type, so a `type` string\nwould be a second and weaker statement of what the case already guarantees, and it would serialize as\n`\"type\": \"\"` whenever a caller sensibly left it out, which reads like a defect.\n\nThe three are not interchangeable ways of saying the same thing. A `point` is a proximity search: it\ntakes a radius, and results come back nearest first. A `polygon` or `multi_polygon` is a containment\nsearch: a property is inside the area or it is not, there is no radius, and there is no distance to\norder by. Choose by which question you are asking, not by which shape is easier to build.\n\nAt most one of `point, polygon, multiPolygon` is set (protobuf `oneof geometry`)."
      },
      "wink.partner.v1.GeoPosition" : {
        "type" : "object",
        "properties" : {
          "longitude" : {
            "type" : "number",
            "description" : "Degrees east of the prime meridian, -180 to 180.",
            "format" : "double"
          },
          "latitude" : {
            "type" : "number",
            "description" : "Degrees north of the equator, -90 to 90.",
            "format" : "double"
          }
        },
        "description" : "A single position on the globe: latitude and longitude, and nothing else.\n\nNamed fields rather than a GeoJSON-style `[longitude, latitude]` array or `{type, coordinates}` object,\nfor two reasons that hold whether this is a request or a response field:\n\n  * proto3 has no ragged arrays, so a ring of positions cannot be a bare `repeated double` without\n    client and server silently disagreeing about where one vertex ends and the next begins.\n  * GeoJSON's array order is longitude-first, the reverse of the `lat,lng` ordering most mapping UIs\n    use -- a reversal that does not error, it just returns confident results from the wrong hemisphere.\n    Naming the fields makes that mistake unrepresentable.\n\nUSED ON BOTH SIDES OF THE WIRE. `GeoShape.point` and every polygon vertex use it on requests;\n`PropertySearchResult.geo_location`, `PropertyBasics.geo_location` and `InventoryItemContent.geo_location`\nuse it on responses. There used to be a separate `GeoPoint` message for responses, carrying a `type`\nstring so the value round-tripped as valid GeoJSON. It is gone: every caller that needed only the\ncoordinates paid for a field that was always the literal string `\"Point\"`, and a caller that wanted real\nGeoJSON to hand to a map library can wrap {@code {longitude, latitude}} in one line locally. If a genuine\nGeoJSON round-trip need reappears, reintroduce a dedicated type for it rather than growing this one --\nsee the SCOPE RULE at the top of this file."
      },
      "wink.partner.v1.GeoPolygon" : {
        "type" : "object",
        "properties" : {
          "rings" : {
            "type" : "array",
            "description" : "Outer ring first. Every ring after it is a hole punched out of that outer ring.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoRing"
            }
          }
        },
        "description" : "A polygon: one outer ring, plus a hole for each additional ring.\n\nGeoJSON winding applies — the outer ring counter-clockwise, holes clockwise. A ring wound the wrong way\ndescribes the complement of what you meant, which on a sphere is most of the planet, so a viewport\nwound backwards matches nearly every property rather than none."
      },
      "wink.partner.v1.GeoRing" : {
        "type" : "object",
        "properties" : {
          "positions" : {
            "type" : "array",
            "description" : "Vertices in order, first repeated last to close the ring.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPosition"
            }
          }
        },
        "description" : "One closed ring of a polygon.\n\nThe first and last position MUST be identical — that is what closes the ring, and Mongo rejects an\nunclosed one at query time. A ring needs at least four positions, since a triangle repeats its first\nvertex to close."
      },
      "wink.partner.v1.GeoMultiPolygon" : {
        "type" : "object",
        "properties" : {
          "polygons" : {
            "type" : "array",
            "description" : "The polygons. Each is independent; a property inside any one of them matches.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPolygon"
            }
          }
        },
        "description" : "Several disjoint polygons treated as one search area."
      },
      "wink.partner.v1.SearchFilters" : {
        "type" : "object",
        "properties" : {
          "nameSoundsLike" : {
            "type" : "string",
            "description" : "Fuzzy match on property name."
          },
          "lifestyle" : {
            "description" : "Restrict to properties oriented toward this lifestyle.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.LifestyleType"
            } ]
          },
          "starRating" : {
            "type" : "integer",
            "description" : "Minimum star rating, 1-5.",
            "format" : "int32"
          },
          "directOnly" : {
            "type" : "boolean",
            "description" : "Only properties bookable direct, excluding intermediated inventory."
          }
        },
        "description" : "Optional narrowing applied before scoring.\n\nEvery field here narrows WHICH properties match, on all six RPCs alike. The search radius used to sit\nhere too and did not belong: it is not a filter but half of a geometry — meaningless on the five\nnon-geo calls, and meaningless on SearchByGeo itself unless `area` holds a point. It lives on\nSearchByGeoRequest, next to the point it measures from."
      },
      "wink.partner.v1.SearchByGeoResponse" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "$ref" : "#/components/schemas/wink.partner.v1.PropertyPage"
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in — the one you asked for in\n`user_session.currency_code`, or the property's own when you asked for none. Stated once here rather than\nrepeated on every amount, which is why the amounts themselves are bare integers."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has: 2 for THB and USD, 0 for JPY, KRW and VND.\n\nAmounts are integers in MINOR UNITS, so the real value is `amount / 10^currency_exponent`. This is\npublished rather than assumed because a fixed divide-by-100 is wrong for a real slice of Wink's\ninventory — a JPY stay priced at 15000 is ¥15,000, not ¥150.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names and other text in this response are in — the one you asked for in\n`user_session.language_code`, or English when it was unavailable."
          }
        },
        "description" : "Response for SearchByGeo."
      },
      "wink.partner.v1.PropertyPage" : {
        "type" : "object",
        "properties" : {
          "content" : {
            "type" : "array",
            "description" : "The properties on this page.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertySearchResult"
            }
          },
          "number" : {
            "type" : "integer",
            "description" : "Zero-based index of this page.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Requested page size.",
            "format" : "int32"
          },
          "totalElements" : {
            "type" : "string",
            "description" : "Total matching properties across all pages.",
            "format" : "int64"
          },
          "totalPages" : {
            "type" : "integer",
            "description" : "Total number of pages.",
            "format" : "int32"
          },
          "numberOfElements" : {
            "type" : "integer",
            "description" : "Properties on this page. Differs from `size` on the last page.",
            "format" : "int32"
          },
          "first" : {
            "type" : "boolean",
            "description" : "True if this is the first page."
          },
          "last" : {
            "type" : "boolean",
            "description" : "True if this is the last page."
          }
        },
        "description" : "One page of priced properties.\n\nWire mirror of the REST PageResponse envelope: `number` is zero-based and `total_elements` counts across\nall pages, not this one."
      },
      "wink.partner.v1.PropertySearchResult" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "Stable property identifier. Your key for everything, including Content lookups."
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url-safe slug, stable and safe to build links from."
          },
          "uniqueId" : {
            "type" : "string",
            "description" : "The property's globally unique short code."
          },
          "name" : {
            "type" : "string",
            "description" : "Property name in the requested language. Enough to label a result row; full localized content comes\nfrom the Content service."
          },
          "geoLocation" : {
            "description" : "Where the property is, for map pins and distance sorting.\n\nBare latitude and longitude, not GeoJSON: this field carries only coordinates, and the surrounding\nJSON already tells you what kind of thing they belong to. Named `geo_location` rather than `location`\nso a generated client's field name says what it is without opening the type.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPosition"
            } ]
          },
          "city" : {
            "description" : "The city this property is in, with the identifiers the destination surfaces take.\n\nLets a result row offer \"more properties in this city\" without a Content call first: `geo_name_id`\ngoes straight back into SearchByCity, `url_name` into RankByCity. Absent when the property has no\ncity on record.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.City"
            } ]
          },
          "distanceInMeters" : {
            "type" : "number",
            "description" : "How far this property is from the point you searched from, in metres.\n\nSet only when `area` held a point — a polygon has no single origin to measure from, so this is\nunset on containment searches. On a point search the page is ordered by this value, nearest first.\n\nIt is the distance to the property's nearest bookable inventory item, which is the property's own\nlocation for all current inventory types.\n\n`optional` so a genuine near-zero distance (the property is essentially at the search coordinates)\nis distinguishable from \"not applicable\": a plain, non-optional double serializes identically to\nits own default at exactly 0, so proto3 JSON would otherwise omit both cases the same way.",
            "format" : "double"
          },
          "starRating" : {
            "type" : "integer",
            "description" : "Official star rating, 1-5. Zero when unrated.",
            "format" : "int32"
          },
          "heroImageUrl" : {
            "type" : "string",
            "description" : "A ready-to-use delivery URL for a single representative image of the property, scaled to 1024px wide\n(Cloudinary's medium variant), the same convention `SearchRoomTypeOffers.hero_image_url` already uses\nfor a room. The lowest-sorted published image; unset when the property has none.\n\nA plain URL rather than the full Multimedia object deliberately: this is a pricing surface, not a\ncontent surface, and the full object carries localized captions, camera angle and lifestyle tagging --\nreal content, not routing information for one image. The complete gallery is in Content."
          },
          "lastUpdate" : {
            "type" : "string",
            "description" : "When this property's record last changed, ISO-8601 \"YYYY-MM-DDThh:mm:ss\" in UTC.\n\nUse it as the cache key for your Content responses: store it alongside what you fetched, compare on\neach search, and re-fetch only when it has moved. It advances when the property or any inventory item\nbeneath it is added, removed or edited.\n\nEdits to child entities advance it too — room types, rate plans, master rates, cancellation policies and\nevery ancillary item, on create, edit, remove and translate alike.\n\nOne documented gap: PERKS. A perk is defined once for the whole platform rather than per property, so\nediting one cannot be attributed to a single property and does not move any property's value. Perk text\nyou have cached can therefore go stale silently. Perks are small and few — the pricing surfaces return\nevery perk referenced by a result inline — so read them from a search or inventory response rather than\ncaching them against this value, and re-sync content periodically regardless."
          },
          "roomTypes" : {
            "type" : "array",
            "description" : "Offers for this property, grouped by room type, cheapest room first. Search returns the single best\noffer per room type; use Inventory for every rate and occupancy combination.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchRoomTypeOffers"
            }
          },
          "perks" : {
            "type" : "array",
            "description" : "Every perk referenced by any `RoomRate.perk_ids` above, deduplicated — one entry per distinct perk,\nnot one per rate. Resolve a perk id here rather than calling Content; a handful of distinct perks\ntypically cover every rate in this response.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PerkContent"
            }
          },
          "commission" : {
            "type" : "number",
            "description" : "Commission rate your account earns on this property, as a fraction — 0.15 for 15%. A property-channel\nterm rather than a per-rate one: it describes your commercial relationship with the property, so it\nholds for every offer below.\n\nA double rather than a float because this multiplies money. 0.15 has no exact binary representation,\nand the wider type keeps the error far enough below a minor unit that `total * commission` does not\ndrift into a visible discrepancy on large bookings.",
            "format" : "double"
          },
          "direct" : {
            "type" : "boolean",
            "description" : "True if this is direct inventory, with no intermediary between you and the property."
          },
          "available" : {
            "type" : "boolean",
            "description" : "True if AT LEAST ONE room type at this property is bookable for the itinerary. Per-room-type\navailability is `RoomPrice.available`; this is the \"is anything here bookable at all\" flag. When false,\n`prices` still carries the unavailable offers rather than being empty, so you can show why."
          }
        },
        "description" : "One property and its live prices.\n\nCarries only what a result list needs to render, plus the prices. For anything else about the property —\ndescriptions, full gallery, address, amenities, room detail, policies — call the Content service with\n`property_id` and cache the answer against `last_update`."
      },
      "wink.partner.v1.City" : {
        "type" : "object",
        "properties" : {
          "geoNameId" : {
            "type" : "string",
            "description" : "GeoNames id, e.g. \"1609350\" for Bangkok. Pass to Search.SearchByCity.\n\nAlways set when this message is present: every property that has a city has this."
          },
          "name" : {
            "type" : "string",
            "description" : "The city's name.\n\nNOT localized, unlike every other name on this surface. The lightweight GeoNames record this comes\nfrom deliberately excludes alternate-name translations, so this is the canonical GeoNames name\nwhatever `language_code` you asked for. Said plainly because a caller would otherwise reasonably\nassume it follows the rest of the response."
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url-safe slug. Pass to Search.RankByCity, which takes the url name rather than the GeoNames id.\n\nEMPTY for a substantial minority of properties -- 601 of 3,230 in production, about 19% -- because the\nslug is populated separately from the GeoNames link. Check it before building a RankByCity call; an\nempty value here does not mean the property has no city, only that this particular handle is missing."
          },
          "countryCode" : {
            "type" : "string",
            "description" : "ISO 3166-1 alpha-2 country code of the city, e.g. \"TH\"."
          }
        },
        "description" : "The city a property is in, as a GeoNames record.\n\nThis is the JOIN between a property and the destination surfaces. `geo_name_id` is what\nSearch.SearchByCity takes; `url_name` is what Search.RankByCity and Lookup take. Carrying both means a\ncaller holding a property can ask \"what else is in this city\" without a lookup round trip first.\n\nDistinct from `Address.city`, which is the city NAME the property typed into its address and is free\ntext. This is the resolved GeoNames record, and only this one has identifiers you can call back with.\n\nThe whole message is absent when the property has no city on record. That is rare but real -- 17 of\n3,230 production properties, 13 of them live inventory -- so treat it as optional rather than assuming\nevery property resolves to a city."
      },
      "wink.partner.v1.SearchRoomTypeOffers" : {
        "type" : "object",
        "properties" : {
          "roomTypeId" : {
            "type" : "string",
            "description" : "The room type. Join to Content for the full gallery, amenities and long-form descriptions."
          },
          "name" : {
            "type" : "string",
            "description" : "The room type's name, in the requested language where a translation exists."
          },
          "heroImageUrl" : {
            "type" : "string",
            "description" : "A ready-to-use delivery URL for a single representative image of the room, scaled to 1024px wide\n(Cloudinary's medium variant — c_scale, dpr_auto, f_auto, q_auto:best, w_1024). The lowest-sorted\npublished image; unset when the room has none.\n\nA plain URL rather than the full Multimedia object deliberately: this is a rates surface, not a\ncontent surface, and shipping the full media object -- id, descriptions, every delivery-URL variant\n-- for every room type on every rate response would make the payload about images instead of about\nrates. The complete gallery is in Content."
          },
          "rates" : {
            "type" : "array",
            "description" : "The single best offer for this room -- one entry, not a list of every rate and occupancy combination.\nRepeated rather than singular so that a room whose rate WAS computed but is not bookable (occupancy\nmismatch, sold out) can still report it, rather than vanishing from the results.\n\nEMPTY, not a single unavailable entry, when the room type has no priced rate to report at all --\nmaster rates exist but none priced for these dates. That case has nothing meaningful to distinguish\none property's placeholder from another's on a page ranking hundreds of them, so it is omitted rather\nthan published as an all-blank entry.\n\nUnavailable-but-computed offers sort last, never first: they report 0 across all amounts, so ordering\npurely by price would put \"not priced\" ahead of the cheapest real rate. For every rate and occupancy\ncombination, call Inventory.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchRoomRate"
            }
          },
          "sort" : {
            "type" : "integer",
            "description" : "The property's own display order for its room types, ascending, when it has expressed one; 0 when it\nhas not. This describes the ROOM, which is why it is here rather than on each rate -- the domain\ndefines it as \"how the room types should be sorted and displayed\".\n\nIt is a merchandising hint, not the delivered order: `room_types` is already ordered by price.",
            "format" : "int32"
          }
        }
      },
      "wink.partner.v1.SearchRoomRate" : {
        "type" : "object",
        "properties" : {
          "roomRateId" : {
            "type" : "string",
            "description" : "Stable identifier for this offer, and the id you carry into a booking.\n\nThis IS the master rate identifier — it maps from `roomRateIdentifier`, whose own schema calls it\n\"Specified master rate identifier\". The name says `room_rate` for historical reasons and is kept rather\nthan duplicated: publishing a second `master_rate_id` field with the same value would be two\nrepresentations of one thing that can disagree, which this API forbids elsewhere for prices."
          },
          "ratePlanId" : {
            "type" : "string",
            "description" : "The rate plan this offer sells under. Resolve it with Content's GetRatePlan; a rate plan is shared by\nmany offers, so it is referenced rather than repeated on each one."
          },
          "available" : {
            "type" : "boolean",
            "description" : "True if THIS offer, at the requested occupancy, is bookable for the itinerary. Narrower than\n`PropertySearchResult.available`, which is true when ANY offer at the property is. An unavailable offer is\nstill returned rather than dropped, so you can show the traveller why it cannot be booked."
          },
          "startDate" : {
            "type" : "string",
            "description" : "Check-in date, ISO-8601 \"YYYY-MM-DD\"."
          },
          "endDate" : {
            "type" : "string",
            "description" : "Check-out date, ISO-8601 \"YYYY-MM-DD\"."
          },
          "roomNights" : {
            "type" : "integer",
            "description" : "Nights covered by this rate.",
            "format" : "int32"
          },
          "adults" : {
            "type" : "integer",
            "description" : "Adults the rate is priced for, after the property's occupancy policy is applied.",
            "format" : "int32"
          },
          "children" : {
            "type" : "integer",
            "description" : "Children the rate is priced for, after the property's occupancy policy is applied.",
            "format" : "int32"
          },
          "unitStrikethroughPrice" : {
            "type" : "string",
            "description" : "ONE room, whole stay, BEFORE discounts — step 1 above. The \"was\" price: render it struck through next\nto `unit_price`.\n\nGUARANTEED >= `unit_price`. Occupancy surcharges and rate premiums are already inside it, so the gap\nbetween the two is only ever the discounts. Equal to `unit_price` when nothing is discounted, in which\ncase show no strikethrough.",
            "format" : "int64"
          },
          "unitPrice" : {
            "type" : "string",
            "description" : "ONE room, whole stay, after every adjustment — step 2 above. The \"now\" price, and the figure the\ndiscount is measured against. Excludes the rate plan's fixed charges.\n\nNot named `net` or `source_total`: everywhere else in this platform \"source\" means the PROPERTY'S OWN\nCURRENCY, and this figure is in the caller's. Reusing the word would have the next mapper author reach\nfor the domain's `getSourceTotal()` and quote the wrong currency.",
            "format" : "int64"
          },
          "unitExtraCharges" : {
            "type" : "string",
            "description" : "ONE room, whole stay — fixed charges attached to the rate plan, a one-time cleaning fee and the like.\nReported separately so a traveller sees them as their own line rather than as an unexplained\ndifference. NEVER discounted, which is why they sit outside both prices above. Zero when the rate plan\nhas none.",
            "format" : "int64"
          },
          "total" : {
            "type" : "string",
            "description" : "What the guest pays for every room: `(unit_price + unit_extra_charges) x quantity`. This is the number\nto show and the number to charge.\n\nOnly meaningful when `available` is true. An unavailable offer reports 0 across every amount, which\nmeans \"not priced\" rather than \"free\" — show the offer with its reason, never with a price of zero.",
            "format" : "int64"
          },
          "meals" : {
            "description" : "Meals included in the rate. These change which offer a traveller picks, so they are here rather than\nbehind an identifier.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MealPlan"
            } ]
          },
          "refundable" : {
            "type" : "boolean",
            "description" : "True if the stay can be cancelled without charge before the policy's deadline."
          },
          "refundableUntilDays" : {
            "type" : "integer",
            "description" : "How many days from TODAY the traveller has left to cancel free of charge, and -1 when the rate is not\nrefundable at all. Zero means the free-cancellation window closes today.\n\nDerived as (days until check-in) − (the policy's days-before-arrival deadline), floored at 0 — the same\ncalculation `RoomTypeBestPriceForDateRange` publishes. It is relative to the moment of the response and\nwill drift as the stay approaches, so treat it as display data, not something to cache.",
            "format" : "int32"
          },
          "cancellationPolicyId" : {
            "type" : "string",
            "description" : "Cancellation policy identifier. Join to Content for the full terms and wording."
          },
          "perkIds" : {
            "type" : "array",
            "description" : "Identifiers of the perks bundled with this rate. Join to Content for their names and descriptions.\nThere is deliberately no monetary \"perk value\" on this surface.",
            "items" : {
              "type" : "string"
            }
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many identical rooms this offer is priced for — the `quantity` you set on the matching\n`RoomConfiguration`. At least 1. What the PROPERTY has left is `rooms_left`, which is a different\nnumber entirely.\n\nIt is echoed back because it is a MULTIPLIER on `total`, not merely a restatement of your request:\nwithout it you cannot tell whether `total` covers one room or five. It can also change the unit price\nitself, since a property may attach a rate to a room-count range — book three and every room may be\ncheaper than booking one.",
            "format" : "int32"
          },
          "discountPercent" : {
            "type" : "number",
            "description" : "The saving as a fraction of `unit_strikethrough_price` — 0.15 for 15%. Zero when nothing is\ndiscounted.\n\nPublished rather than left to you because there is one correct rounding of it and several plausible\nones, and a paid API where two integrators compute \"18%\" and \"17%\" from the same response is a support\nburden rather than a saving. Quantity-invariant: discounting every room equally does not change the\npercentage.",
            "format" : "double"
          },
          "adjustments" : {
            "type" : "array",
            "description" : "Every adjustment that moved this rate away from the property's plain price, one entry per kind, for\nshowing a traveller WHY the number is what it is. Empty when nothing applied.\n\nThese reconcile exactly: the signed amounts sum to `unit_price` minus the property's unadjusted price.\nThey are per-unit, like the prices they explain.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PriceAdjustment"
            }
          },
          "offerDetails" : {
            "type" : "array",
            "description" : "The property's own wording for the promotion or premium in force, in English plus your requested\nlanguage. Empty when neither applies.\n\nAt the RATE level, not on each adjustment, because that is how the platform stores it — one list of\ndescriptions covering the special rates that fired. Attaching a label to each `PriceAdjustment` would\nhave meant publishing a field nothing could ever fill.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "promotionalCodes" : {
            "type" : "array",
            "description" : "Promotional codes that were actually ACCEPTED and are reflected in the price above — not the codes you\nsent. A code your account is not entitled to is ignored rather than rejected, so comparing this against\nwhat you submitted is how you tell which ones bit.",
            "items" : {
              "type" : "string"
            }
          },
          "roomsLeft" : {
            "type" : "integer",
            "description" : "How many rooms the property still has for the WHOLE stay — the \"only 2 left\" number.\n\nThe SMALLEST nightly allotment across the stay, not the first night's and not an average. A three-night\nstay whose middle night has two rooms free has two rooms left, however many are free either side: you\ncannot sell a third room a guest would be evicted from on night two. The check-out date is excluded,\nsince nobody occupies a room that night.\n\nNOT the same as `quantity`, which is how many rooms YOU asked for. The relationship is\n`rooms_left >= quantity` whenever `available` is true — an offer that cannot house the party you\nrequested is not bookable and says so. Reading `quantity` as \"what the property has\" overstates\navailability, which is why they are named as differently as they are.\n\nZero means none are free for these dates, and only ever appears alongside `available = false`. It is\na real answer, not a missing one.\n\nThis is the property's true position, not a banded or capped figure. Treat it as commercially\nsensitive: it is exact enough to reconstruct a property's booking curve if sampled over time.",
            "format" : "int32"
          },
          "addOnCount" : {
            "type" : "integer",
            "description" : "How many extras -- add-ons plus in-room ancillaries -- can be bought alongside this offer. Zero when\nit sells none.\n\nA COUNT, not the items. Search is a list surface: the count is enough to render \"3 extras available\"\nand decide whether to show a badge, and that is the only add-on decision a result row makes. The items\nthemselves are per-offer objects with their own prices and booking identifiers, which is a detail-view\npayload -- fetch them from Inventory, where `RoomRate.add_on_ids` and `RoomRate.room_type_ancillaries`\ncarry the full picture.\n\nThe two kinds are summed rather than reported separately because nothing you can do with this number\non a search row distinguishes them. They ARE different at booking time -- an ancillary quotes this\nrate's `room_rate_id` as its inventory id, an add-on does not -- and that difference is exactly why\nresolving them belongs to the surface that can act on it.",
            "format" : "int32"
          }
        }
      },
      "wink.partner.v1.MealPlan" : {
        "type" : "object",
        "properties" : {
          "breakfast" : {
            "type" : "boolean",
            "description" : "Breakfast included."
          },
          "brunch" : {
            "type" : "boolean",
            "description" : "Brunch included."
          },
          "lunch" : {
            "type" : "boolean",
            "description" : "Lunch included."
          },
          "dinner" : {
            "type" : "boolean",
            "description" : "Dinner included."
          },
          "allInclusive" : {
            "type" : "boolean",
            "description" : "All meals included."
          },
          "allInclusivePlusAlcohol" : {
            "type" : "boolean",
            "description" : "All meals plus alcohol included."
          }
        },
        "description" : "Which meals a rate includes."
      },
      "wink.partner.v1.PriceAdjustment" : {
        "type" : "object",
        "properties" : {
          "type" : {
            "description" : "What kind of adjustment this is.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AdjustmentType"
            } ]
          },
          "unitAmount" : {
            "type" : "string",
            "description" : "How much it moved the price for ONE room over the whole stay, in minor units. Negative lowered the\nprice, positive raised it. Never zero — an adjustment that changed nothing is not returned.",
            "format" : "int64"
          }
        },
        "description" : "One reason this rate differs from the property's plain price.\n\nSIGNED, and the sign is the whole point: `unit_amount` is negative for anything that lowered the price\nand positive for anything that raised it, so a caller can render a list of lines and add them up without\nknowing which types are discounts. Amounts are per-unit minor units, matching the prices they explain.\n\nThe surcharges and premiums here are already inside `unit_strikethrough_price`, and the discounts are\nthe gap between it and `unit_price`. This message explains the arithmetic; it does not add to it."
      },
      "wink.partner.v1.Description" : {
        "type" : "object",
        "properties" : {
          "name" : {
            "type" : "string",
            "description" : "Short label for this description."
          },
          "description" : {
            "type" : "string",
            "description" : "The description body."
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language code of `name` and `description`."
          }
        },
        "description" : "A short piece of localized text.\n\nThe API returns descriptions already filtered to the language on your request, so you will normally see\nexactly one entry per collection. `language_code` is still present so you can tell which language you got when\nthe requested one was unavailable and the property's default was substituted."
      },
      "wink.partner.v1.PerkContent" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable perk identifier. Matches an entry in `RoomRate.perk_ids`."
          },
          "descriptions" : {
            "type" : "array",
            "description" : "Localized name and description, in English plus your requested language.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "guaranteed" : {
            "type" : "boolean",
            "description" : "Whether the perk is guaranteed rather than subject to availability."
          },
          "level" : {
            "type" : "integer",
            "description" : "Perk tier; higher is more valuable.",
            "format" : "int32"
          },
          "sort" : {
            "type" : "integer",
            "description" : "Display order, ascending.",
            "format" : "int32"
          }
        },
        "description" : "A perk bundled with a rate.\n\nLives here rather than in partner_content.proto because the PRICING surfaces return a deduplicated\ndictionary of these alongside their rates: `RoomRate.perk_ids` would otherwise be unresolvable without a\nsecond call, and inlining the perk on every rate would repeat a handful of distinct perks across every\nrate of every room type of every property in a search result.\n\n`descriptions` carries the display name and body together — Description has both."
      },
      "wink.partner.v1.SearchByCityRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "geoNameId" : {
            "type" : "string",
            "description" : "GeoNames id of the city, as returned by the Lookup service or `City.geo_name_id` on a\npricing/content result."
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url-safe city slug, as returned by the Lookup service or `City.url_name` on a pricing/content\nresult. NOTE: `City.url_name` is empty for roughly a fifth of properties -- check it is set before\nrelying on it."
          },
          "searchFilters" : {
            "description" : "Optional narrowing of the result set.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchFilters"
            } ]
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page number. Defaults to 0.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Results per page. Defaults to 20, which is also the maximum. A larger value is rejected with\nINVALID_ARGUMENT rather than clamped, so a caller never pages through a result set believing it is\nlarger than it is. Every property returned is billed, so the cap bounds cost as well as fan-out.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for SearchByCity.\n\nAt most one of `geoNameId, urlName` is set (protobuf `oneof city`)."
      },
      "wink.partner.v1.SearchByCityResponse" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "$ref" : "#/components/schemas/wink.partner.v1.PropertyPage"
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in — the one you asked for in\n`user_session.currency_code`, or the property's own when you asked for none. Stated once here rather than\nrepeated on every amount, which is why the amounts themselves are bare integers."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has: 2 for THB and USD, 0 for JPY, KRW and VND.\n\nAmounts are integers in MINOR UNITS, so the real value is `amount / 10^currency_exponent`. This is\npublished rather than assumed because a fixed divide-by-100 is wrong for a real slice of Wink's\ninventory — a JPY stay priced at 15000 is ¥15,000, not ¥150.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names and other text in this response are in — the one you asked for in\n`user_session.language_code`, or English when it was unavailable."
          }
        },
        "description" : "Response for SearchByCity."
      },
      "wink.partner.v1.RankByAgencyRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "cityGeoNameId" : {
            "type" : "string",
            "description" : "GeoNames id of the city, as returned by the Lookup service or `City.geo_name_id` on a\npricing/content result."
          },
          "cityUrlName" : {
            "type" : "string",
            "description" : "Url-safe city slug, as returned by the Lookup service or `City.url_name` on a pricing/content\nresult."
          },
          "countryCode" : {
            "type" : "string",
            "description" : "ISO 3166-1 alpha-2 country code, e.g. \"TH\"."
          },
          "sort" : {
            "description" : "How to order the results.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.ScoreSort"
            } ]
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page number. Defaults to 0.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Results per page. Defaults to 20, which is also the maximum. A larger value is rejected with\nINVALID_ARGUMENT rather than clamped, so a caller never pages through a result set believing it is\nlarger than it is. Every property returned is billed, so the cap bounds cost as well as fan-out.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for RankByAgency.\n\nAt most one of `cityGeoNameId, cityUrlName, countryCode` is set (protobuf `oneof scope`)."
      },
      "wink.partner.v1.RankByAgencyResponse" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "$ref" : "#/components/schemas/wink.partner.v1.PropertyPage"
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in — the one you asked for in\n`user_session.currency_code`, or the property's own when you asked for none. Stated once here rather than\nrepeated on every amount, which is why the amounts themselves are bare integers."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has: 2 for THB and USD, 0 for JPY, KRW and VND.\n\nAmounts are integers in MINOR UNITS, so the real value is `amount / 10^currency_exponent`. This is\npublished rather than assumed because a fixed divide-by-100 is wrong for a real slice of Wink's\ninventory — a JPY stay priced at 15000 is ¥15,000, not ¥150.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names and other text in this response are in — the one you asked for in\n`user_session.language_code`, or English when it was unavailable."
          }
        },
        "description" : "Response for RankByAgency."
      },
      "wink.partner.v1.RankByCityRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url name of the city, as returned by the Lookup service or `City.url_name` on a pricing/content\nresult."
          },
          "geoNameId" : {
            "type" : "string",
            "description" : "GeoNames id of the city, as returned by the Lookup service or `City.geo_name_id` on a\npricing/content result."
          },
          "sort" : {
            "description" : "How to order the results.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.ScoreSort"
            } ]
          },
          "searchFilters" : {
            "description" : "Optional narrowing of the result set.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchFilters"
            } ]
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page number. Defaults to 0.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Results per page. Defaults to 20, which is also the maximum. A larger value is rejected with\nINVALID_ARGUMENT rather than clamped, so a caller never pages through a result set believing it is\nlarger than it is. Every property returned is billed, so the cap bounds cost as well as fan-out.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for RankByCity.\n\nAt most one of `urlName, geoNameId` is set (protobuf `oneof city`)."
      },
      "wink.partner.v1.RankByCityResponse" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "$ref" : "#/components/schemas/wink.partner.v1.PropertyPage"
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in — the one you asked for in\n`user_session.currency_code`, or the property's own when you asked for none. Stated once here rather than\nrepeated on every amount, which is why the amounts themselves are bare integers."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has: 2 for THB and USD, 0 for JPY, KRW and VND.\n\nAmounts are integers in MINOR UNITS, so the real value is `amount / 10^currency_exponent`. This is\npublished rather than assumed because a fixed divide-by-100 is wrong for a real slice of Wink's\ninventory — a JPY stay priced at 15000 is ¥15,000, not ¥150.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names and other text in this response are in — the one you asked for in\n`user_session.language_code`, or English when it was unavailable."
          }
        },
        "description" : "Response for RankByCity."
      },
      "wink.partner.v1.RankByCountryRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "countryCode" : {
            "type" : "string",
            "description" : "ISO 3166-1 alpha-2 country code, e.g. \"TH\". Required."
          },
          "sort" : {
            "description" : "How to order the results.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.ScoreSort"
            } ]
          },
          "searchFilters" : {
            "description" : "Optional narrowing of the result set.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchFilters"
            } ]
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page number. Defaults to 0.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Results per page. Defaults to 20, which is also the maximum. A larger value is rejected with\nINVALID_ARGUMENT rather than clamped, so a caller never pages through a result set believing it is\nlarger than it is. Every property returned is billed, so the cap bounds cost as well as fan-out.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for RankByCountry."
      },
      "wink.partner.v1.RankByCountryResponse" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "$ref" : "#/components/schemas/wink.partner.v1.PropertyPage"
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in — the one you asked for in\n`user_session.currency_code`, or the property's own when you asked for none. Stated once here rather than\nrepeated on every amount, which is why the amounts themselves are bare integers."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has: 2 for THB and USD, 0 for JPY, KRW and VND.\n\nAmounts are integers in MINOR UNITS, so the real value is `amount / 10^currency_exponent`. This is\npublished rather than assumed because a fixed divide-by-100 is wrong for a real slice of Wink's\ninventory — a JPY stay priced at 15000 is ¥15,000, not ¥150.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names and other text in this response are in — the one you asked for in\n`user_session.language_code`, or English when it was unavailable."
          }
        },
        "description" : "Response for RankByCountry."
      },
      "wink.partner.v1.RankGlobalRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "sort" : {
            "description" : "How to order the results.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.ScoreSort"
            } ]
          },
          "searchFilters" : {
            "description" : "Optional narrowing of the result set.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.SearchFilters"
            } ]
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page number. Defaults to 0.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "description" : "Results per page. Defaults to 20, which is also the maximum. A larger value is rejected with\nINVALID_ARGUMENT rather than clamped, so a caller never pages through a result set believing it is\nlarger than it is. Every property returned is billed, so the cap bounds cost as well as fan-out.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for RankGlobal."
      },
      "wink.partner.v1.RankGlobalResponse" : {
        "type" : "object",
        "properties" : {
          "page" : {
            "$ref" : "#/components/schemas/wink.partner.v1.PropertyPage"
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in — the one you asked for in\n`user_session.currency_code`, or the property's own when you asked for none. Stated once here rather than\nrepeated on every amount, which is why the amounts themselves are bare integers."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has: 2 for THB and USD, 0 for JPY, KRW and VND.\n\nAmounts are integers in MINOR UNITS, so the real value is `amount / 10^currency_exponent`. This is\npublished rather than assumed because a fixed divide-by-100 is wrong for a real slice of Wink's\ninventory — a JPY stay priced at 15000 is ¥15,000, not ¥150.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names and other text in this response are in — the one you asked for in\n`user_session.language_code`, or English when it was unavailable."
          }
        },
        "description" : "Response for RankGlobal."
      },
      "wink.partner.v1.CreateBookingRequest" : {
        "type" : "object",
        "properties" : {
          "idempotencyKey" : {
            "type" : "string",
            "description" : "Idempotency key. REQUIRED — see the rpc comment for why this is not optional on a money-moving call.\nAny opaque string up to 128 characters that is unique per booking intent; a UUID is the obvious choice."
          },
          "bookingOriginUrl" : {
            "type" : "string",
            "description" : "Which of the agency's sites or apps the booking originated on, as an absolute URL.\n\nREQUIRED, and an explicit field rather than something sniffed from a header. Over REST this was read\noff the Origin/Host of the inbound request, which gRPC has no equivalent of — and which quietly\nrecorded whatever proxy happened to terminate the call. Stating it makes the value mean what it always\nclaimed to."
          },
          "rooms" : {
            "type" : "array",
            "description" : "What to book. At least one room; each entry may name a different property, so a multi-property\nitinerary is one call.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingRoomRequest"
            }
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency to quote totals back in, and ISO 639-1 language for anything human-readable.\nBOTH ARE REQUIRED — the domain rejects a blank either way, so omitting one is INVALID_ARGUMENT.\n\nCAREFUL: this is NOT the same `currency_code` as AgentBooking's, despite the shared name. This one is\nwhat the agency wants to SEE; that one is what the property SETTLES in, and the two differ whenever a\nbooking is sold in one currency and paid out in another. Every monetary field on the response is in\nthe settlement currency regardless of what is asked for here — see AgentBooking.currency_code."
          },
          "languageCode" : {
            "type" : "string"
          },
          "traceId" : {
            "type" : "string",
            "description" : "The agency's own correlation id for this attempt, carried into the platform's logs. Distinct from\n`idempotency_key`: this one identifies the REQUEST for tracing, that one identifies the INTENT for\ndeduplication, and reusing a trace id is harmless where reusing a key is not."
          }
        },
        "description" : "Request for CreateBooking.\n\nShaped from the domain's CreateAgentBookingRequest rather than from what a single-room booking looks\nlike. `rooms` is REPEATED because the domain takes a list and because that is how a group booking is\nmade — the `group_id` that RefundBookings operates on exists precisely because one create can produce\nseveral bookings. A one-room booking is a one-element list."
      },
      "wink.partner.v1.BookingRoomRequest" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "The property to book at. From a Search or Inventory result."
          },
          "checkIn" : {
            "type" : "string",
            "description" : "Local dates at the property, ISO-8601 (YYYY-MM-DD)."
          },
          "checkOut" : {
            "type" : "string"
          },
          "roomRateId" : {
            "type" : "string",
            "description" : "The rate to book. From a pricing result's `RoomRate`."
          },
          "bedroomConfigurationId" : {
            "type" : "string",
            "description" : "Which bedroom layout, when the rate offers more than one."
          },
          "occupancy" : {
            "description" : "Who is in the room, and how many identical rooms of it. Child AGES are required rather than a count:\nproperties price children in age bands, and an unpriceable age produces a rate that cannot be booked.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.RoomConfiguration"
            } ]
          },
          "promotionalCodes" : {
            "type" : "array",
            "description" : "Promotional codes to apply. Silently ignored when they do not apply to this rate, which is the same\nbehaviour the booking engine has.",
            "items" : {
              "type" : "string"
            }
          },
          "ancillaries" : {
            "type" : "array",
            "description" : "Extras to add to this room — spa treatments, restaurant sittings, activities, add-ons, and the\nin-room ancillaries sold with the rate itself.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingAncillaryRequest"
            }
          },
          "specialRequests" : {
            "type" : "string",
            "description" : "Free text for the property."
          },
          "guest" : {
            "description" : "Who is staying in this room.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingPerson"
            } ]
          },
          "externalBookingCode" : {
            "type" : "string",
            "description" : "The agency's own references, carried onto the booking and echoed back on reporting. These are the\nreason an agent booking is its own request type: an agency reconciling Wink against its own system\nneeds its identifiers to survive the round trip."
          },
          "externalTransactionId" : {
            "type" : "string"
          },
          "externalCustomerId" : {
            "type" : "string"
          },
          "externalFees" : {
            "type" : "array",
            "description" : "Fees the agency is adding on top of the rate. Amounts are in MINOR UNITS of `display_currency`.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingFee"
            }
          }
        },
        "description" : "One room to book.\n\nField-for-field from AgentBookingRequest, minus `nights` (derivable from the dates, and a second source\nof truth for the same fact) and `vendor_specific` (a vendor-keyed metadata map, excluded for the same\nreason its outbound twin is)."
      },
      "wink.partner.v1.BookingAncillaryRequest" : {
        "type" : "object",
        "properties" : {
          "inventoryId" : {
            "type" : "string",
            "description" : "The thing being sold from — see the table above. Never a room type."
          },
          "channelInventoryId" : {
            "type" : "string",
            "description" : "Your channel's gate onto it. Empty when the item reached you through Wink Network rather than one of\nyour own sales channels, which is the common case and not an error."
          },
          "offerId" : {
            "type" : "string",
            "description" : "The priced row you are buying."
          },
          "type" : {
            "description" : "Which kind of extra this is. INVENTORY_SCOPE_ANNOUNCEMENT is informational only and is rejected here.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryScope"
            } ]
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many. Bounded by the offer's `min_pax` and `max_pax` when it sets them; defaults to 1.",
            "format" : "int32"
          },
          "requestedStart" : {
            "type" : "string",
            "description" : "OPTIONAL, and only meaningful for appointment-style extras — a spa slot, a restaurant sitting, an\nactivity departure — or where the offer's `pricing_type` is per hour or per day and the span is what\nis being priced.\n\nIn-room extras and add-ons OMIT them: they are bundled with the stay, and sending times makes the\nplatform treat them as appointments and issue calendar invites nobody asked for.\n\nThese are REQUESTED, never confirmed. The property is not committing to them by accepting the booking.\nISO-8601 local date-time at the property."
          },
          "requestedEnd" : {
            "type" : "string"
          }
        },
        "description" : "One extra to add to a room.\n\nEvery field is quoted back from something the Inventory surface already handed you — there is nothing to\nconstruct and nothing to look up. Where you read them from depends on which kind of extra it is:\n\n  PROPERTY-LEVEL (restaurant, spa, meeting room, activity, attraction, place, add-on)\n    inventory_id          AncillaryInventory.inventory_id          — the restaurant or spa itself\n    channel_inventory_id  AncillaryInventory.channel_inventory_identifier\n    offer_id              AncillaryOffer.id                        — the sitting, the 1-hour massage\n    type                  AncillaryInventory.type\n\n  IN-ROOM (INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY)\n    inventory_id          RoomRate.room_rate_id                    — the RATE, not a room type\n    channel_inventory_id  leave blank — this is the common case and not an error\n    offer_id              RoomRate.room_type_ancillaries[].id\n    type                  INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY\n\nThe in-room case is the one to read twice: its `inventory_id` is a RATE because the gate for these is\nkeyed by the master rate rather than by an entity. Sending a room type id there matches nothing, and it\ndoes so SILENTLY rather than failing."
      },
      "wink.partner.v1.BookingPerson" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "email" : {
            "type" : "string"
          },
          "phone" : {
            "type" : "string"
          }
        },
        "description" : "A person on the booking. Deliberately not the platform's user model: an agency needs to contact somebody\nabout a stay, not to know their account."
      },
      "wink.partner.v1.BookingFee" : {
        "type" : "object",
        "properties" : {
          "feeId" : {
            "type" : "string"
          },
          "amount" : {
            "type" : "string",
            "description" : "MINOR UNITS of the enclosing request's `display_currency`.",
            "format" : "int64"
          },
          "type" : {
            "type" : "string"
          },
          "description" : {
            "type" : "string"
          }
        },
        "description" : "A fee the agency is adding on top of the rate."
      },
      "wink.partner.v1.CreateBookingResponse" : {
        "type" : "object",
        "properties" : {
          "bookings" : {
            "type" : "array",
            "description" : "One booking per room requested, in the order requested. A multi-room create produces several bookings\nsharing a `group_id` — that is what makes them refundable together.\n\nRepeated rather than singular because the domain returns BookingConfirmations, which is a LIST. A\nsingle-booking response would have quietly dropped every room after the first.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.AgentBooking"
            }
          },
          "idempotentReplay" : {
            "type" : "boolean",
            "description" : "True when this response replayed an earlier call rather than creating anything. Nothing was charged."
          }
        },
        "description" : "Response for CreateBooking."
      },
      "wink.partner.v1.AgentBooking" : {
        "type" : "object",
        "properties" : {
          "bookingId" : {
            "type" : "string",
            "description" : "Wink's identifier for this booking."
          },
          "bookingCode" : {
            "type" : "string",
            "description" : "The human-facing reference shown to the traveller, e.g. on the confirmation email."
          },
          "groupId" : {
            "type" : "string",
            "description" : "Present when this booking was made as part of a group; RefundBookings operates on this value."
          },
          "status" : {
            "description" : "Where the booking is in its lifecycle.\n\nLIFECYCLE ONLY — this does NOT report refunds. BOOKING_STATUS_PARTIALLY_REFUNDED and\nBOOKING_STATUS_FULLY_REFUNDED are retained for bookings written before the lifecycle and refund\naxes were separated, and are never emitted for anything refunded after that. They were always\nlossy: a refund settling used to overwrite a cancellation here, so only one of the two facts\nsurvived. Read `refund` (field 14) for whether money came back and how much; a refunded booking\nthat was not cancelled now correctly reports BOOKING_STATUS_PAYMENT_SUCCEEDED here.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingStatus"
            } ]
          },
          "creation" : {
            "description" : "How the booking came to exist.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingCreationType"
            } ]
          },
          "createdAt" : {
            "type" : "string",
            "description" : "When the booking was created. ISO-8601 instant, UTC."
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency every monetary field on this message is denominated in. This is the SOURCE currency\n— the one the property settles in — which is not necessarily the one the traveller was shown. See the\nnote on `net_total_sales_amount`."
          },
          "property" : {
            "description" : "The property and stay.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookedProperty"
            } ]
          },
          "stay" : {
            "$ref" : "#/components/schemas/wink.partner.v1.BookedStay"
          },
          "booker" : {
            "description" : "Who booked it, and who is staying. Frequently the same person, and deliberately still two fields: an\nagency books on behalf of a traveller far more often than not.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingPerson"
            } ]
          },
          "guest" : {
            "$ref" : "#/components/schemas/wink.partner.v1.BookingPerson"
          },
          "earnings" : {
            "description" : "What the agency earns and what the sale was worth.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AgentEarnings"
            } ]
          },
          "cancellation" : {
            "description" : "Present once cancelled.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingCancellation"
            } ]
          },
          "refund" : {
            "description" : "Present once any refund exists, whether from a cancellation or an immediate refund.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingRefund"
            } ]
          },
          "comment" : {
            "type" : "string",
            "description" : "Free text captured at booking time."
          },
          "specialRequests" : {
            "type" : "string"
          },
          "earlyCheckInCharge" : {
            "type" : "string",
            "description" : "Charges that apply outside the room rate — early check-in, late check-out and anything the property\nadds. `has_additional_charges` is the cheap check; `additional_charges_summary` is the human-readable\ndetail when it is true.",
            "format" : "int64"
          },
          "lateCheckOutCharge" : {
            "type" : "string",
            "format" : "int64"
          },
          "hasAdditionalCharges" : {
            "type" : "boolean"
          },
          "additionalChargesSummary" : {
            "type" : "string"
          },
          "paymentType" : {
            "description" : "How the traveller paid.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PaymentType"
            } ]
          },
          "customizationId" : {
            "type" : "string",
            "description" : "The white-label configuration this booking was made through. The id only — the configuration itself is\nthe agency's own setup and is managed elsewhere, not published here."
          },
          "statusHistory" : {
            "type" : "array",
            "description" : "Status changes in order, oldest first. Enough to answer \"when was this cancelled\" without a support\nticket.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingStatusChange"
            }
          }
        },
        "description" : "One booking, as the agency that sold it needs to see it.\n\nA deliberate projection, not a mirror of the internal model — see the note at the top of this file for\nwhat is excluded and why."
      },
      "wink.partner.v1.BookedProperty" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string"
          },
          "name" : {
            "type" : "string"
          },
          "address" : {
            "$ref" : "#/components/schemas/wink.partner.v1.Address"
          },
          "phone" : {
            "type" : "string",
            "description" : "Direct contact for the property, for when the agency needs to call about a booking."
          },
          "email" : {
            "type" : "string"
          },
          "timeZone" : {
            "type" : "string",
            "description" : "IANA zone, e.g. \"Asia/Bangkok\". Check-in and check-out below are LOCAL to the property, so without\nthis an agency cannot tell whether a same-day cancellation is still same-day."
          }
        },
        "description" : "Who and where the property is. Flattened from a 670-line aggregate on purpose: an agency servicing a\nbooking needs to identify and contact the property, not to re-render it."
      },
      "wink.partner.v1.Address" : {
        "type" : "object",
        "properties" : {
          "address1" : {
            "type" : "string",
            "description" : "Street line 1."
          },
          "address2" : {
            "type" : "string",
            "description" : "Street line 2, when the property supplies one."
          },
          "state" : {
            "type" : "string",
            "description" : "State, province or region."
          },
          "postalCode" : {
            "type" : "string",
            "description" : "Postal or ZIP code."
          },
          "city" : {
            "type" : "string",
            "description" : "City name."
          },
          "countryCode" : {
            "type" : "string",
            "description" : "ISO 3166-1 alpha-2 country code, e.g. \"TH\"."
          },
          "country" : {
            "type" : "string",
            "description" : "Country name in the requested language."
          },
          "fullAddress" : {
            "type" : "string",
            "description" : "The full address pre-formatted for display, newline-free."
          }
        },
        "description" : "A postal address."
      },
      "wink.partner.v1.BookedStay" : {
        "type" : "object",
        "properties" : {
          "checkIn" : {
            "type" : "string",
            "description" : "Local dates at the property, ISO-8601 (YYYY-MM-DD)."
          },
          "checkOut" : {
            "type" : "string"
          },
          "nights" : {
            "type" : "integer",
            "description" : "Nights between them, precomputed so nobody has to reimplement the date arithmetic.",
            "format" : "int32"
          },
          "roomTypeName" : {
            "type" : "string",
            "description" : "What was booked."
          },
          "ratePlanName" : {
            "type" : "string"
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many rooms of the above, and who is in each.",
            "format" : "int32"
          },
          "adults" : {
            "type" : "integer",
            "format" : "int32"
          },
          "childAges" : {
            "type" : "array",
            "description" : "One entry per child, carrying that child's age at check-in. Ages drive pricing, so an empty list means\nno children rather than unknown ages.",
            "items" : {
              "type" : "integer",
              "format" : "int32"
            }
          }
        },
        "description" : "The stay itself."
      },
      "wink.partner.v1.AgentEarnings" : {
        "type" : "object",
        "properties" : {
          "netTotalSalesAmount" : {
            "type" : "string",
            "description" : "Net sale value in `currency_code`, summed across SALE-type beneficiaries.\n\nThe SOURCE currency, meaning the one the property settles in. A booking sold in one currency and\nsettled in another carries an exchange spread, and that spread is intentional platform behaviour — do\nnot reconcile this against what the traveller was charged and expect equality.",
            "format" : "int64"
          },
          "netAmount" : {
            "type" : "string",
            "description" : "Net amount attributable to the booking after fees, in `currency_code`.",
            "format" : "int64"
          },
          "commissionPercent" : {
            "type" : "string",
            "description" : "The agency's commission on this booking, as a percentage at booking time. Percentages rather than\namounts because that is how they are agreed and how they survive a partial refund."
          },
          "agencyFeePercent" : {
            "type" : "string",
            "description" : "The agency's own agency-fee percentage at booking time."
          },
          "commissionPercentAfterRefund" : {
            "type" : "string",
            "description" : "Commission percentage restated after any refund. Equal to `commission_percent` until something is\nrefunded, and the value to reconcile against once something is."
          }
        },
        "description" : "What this booking is worth to the agency.\n\nThe agency's OWN economics only. The platform's fee, the supplier's agency fee and the payment\nprocessor's cut are all deliberately absent — they are somebody else's commercial terms and publishing\nthem here would make them a contract we cannot change."
      },
      "wink.partner.v1.BookingCancellation" : {
        "type" : "object",
        "properties" : {
          "cancelledAt" : {
            "type" : "string",
            "description" : "ISO-8601 instant, UTC."
          },
          "reason" : {
            "type" : "string",
            "description" : "Free text supplied by whoever cancelled."
          },
          "source" : {
            "description" : "Who cancelled. An agency cancelling on the traveller's behalf and a property cancelling on the agency\nare different conversations.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.CancellationSource"
            } ]
          },
          "type" : {
            "description" : "The classification the cancellation was filed under, which is not the same thing as `reason`: this is\nthe closed list the platform reports on, that is what a human typed.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.CancellationType"
            } ]
          }
        },
        "description" : "Why and when a booking was cancelled.\n\nField-for-field from the domain's BookingCancellation, minus `cancellerUserIdentifier` (an internal user\nid an agency cannot resolve) and `groupRefundNotified` (a notification bookkeeping flag)."
      },
      "wink.partner.v1.BookingRefund" : {
        "type" : "object",
        "properties" : {
          "amount" : {
            "type" : "string",
            "description" : "Amount returned, in `currency_code` minor units. This is the SOURCE amount — what the property\nsettles — matching every other monetary field on this message.",
            "format" : "int64"
          },
          "requestedAt" : {
            "type" : "string",
            "description" : "When the refund was requested, and when it actually settled. `processed_at` is empty while the status\nis PENDING or REQUIRES_ACTION, which is the honest answer: it has not been processed yet."
          },
          "processedAt" : {
            "type" : "string"
          },
          "status" : {
            "$ref" : "#/components/schemas/wink.partner.v1.RefundStatus"
          },
          "reason" : {
            "description" : "Why it was issued, from the closed list, plus whatever free text accompanied it.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.RefundReason"
            } ]
          },
          "description" : {
            "type" : "string"
          },
          "receiptUrl" : {
            "type" : "string",
            "description" : "Processor-hosted receipt for the traveller, when one exists. Published because an agency fielding\n\"where is my money\" needs something to forward; the processor's refund id and fee do not appear here\nfor the same reason no other payment plumbing does."
          }
        },
        "description" : "Money returned, if any.\n\nA cancellation does NOT imply a refund: a non-refundable rate cancels with nothing returned. Read this\nmessage rather than inferring from `cancellation`."
      },
      "wink.partner.v1.BookingStatusChange" : {
        "type" : "object",
        "properties" : {
          "status" : {
            "$ref" : "#/components/schemas/wink.partner.v1.BookingStatus"
          },
          "changedAt" : {
            "type" : "string"
          }
        },
        "description" : "One lifecycle transition."
      },
      "wink.partner.v1.SearchBookingsRequest" : {
        "type" : "object",
        "properties" : {
          "created" : {
            "description" : "When the booking was made.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.DateRange"
            } ]
          },
          "checkIn" : {
            "description" : "When the stay starts and ends. Both ranges are inclusive.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.DateRange"
            } ]
          },
          "checkOut" : {
            "$ref" : "#/components/schemas/wink.partner.v1.DateRange"
          },
          "bookingCode" : {
            "type" : "string",
            "description" : "Exact match on the traveller-facing reference."
          },
          "guestFirstName" : {
            "type" : "string",
            "description" : "Exact match, both optional and independent."
          },
          "guestLastName" : {
            "type" : "string"
          },
          "ratePlanNameContains" : {
            "type" : "string",
            "description" : "Substring match, case-insensitive."
          },
          "propertyNameContains" : {
            "type" : "string"
          },
          "customizationId" : {
            "type" : "string",
            "description" : "Exact match on the white-label configuration the booking came through."
          },
          "statuses" : {
            "type" : "array",
            "description" : "Restrict to these statuses. Empty means every status except the ones this service never returns.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingStatus"
            }
          },
          "page" : {
            "type" : "integer",
            "description" : "Zero-based page index and size. Size defaults to 25 and may not exceed 100. Named `page`/`size`\nto match every other paginated request on this API, not `page_number`/`page_size`.",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "format" : "int32"
          },
          "sortBy" : {
            "description" : "Defaults to CREATED_AT descending, which is what the agent grid opens on.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BookingSortField"
            } ]
          },
          "sortDirection" : {
            "$ref" : "#/components/schemas/wink.partner.v1.SortDirection"
          }
        },
        "description" : "Request for SearchBookings.\n\nEvery filter is optional and combines with AND. Failed bookings are excluded server-side and there is\ndeliberately no field to include them.\n\nThere is also no affiliate filter, and that is a fix rather than an omission: the REST grid offers one\nwhile the server independently pins the same field to the calling agency, so any value other than the\ncaller's own produces two contradictory equality filters and a permanently empty page. Scoping belongs\nto the server."
      },
      "wink.partner.v1.DateRange" : {
        "type" : "object",
        "properties" : {
          "from" : {
            "type" : "string",
            "description" : "ISO-8601 (YYYY-MM-DD)."
          },
          "to" : {
            "type" : "string"
          }
        },
        "description" : "An inclusive date range. Either bound may be omitted to leave that side open."
      },
      "wink.partner.v1.SearchBookingsResponse" : {
        "type" : "object",
        "properties" : {
          "content" : {
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.AgentBooking"
            }
          },
          "number" : {
            "type" : "integer",
            "format" : "int32"
          },
          "size" : {
            "type" : "integer",
            "format" : "int32"
          },
          "totalElements" : {
            "type" : "string",
            "format" : "int64"
          },
          "totalPages" : {
            "type" : "integer",
            "format" : "int32"
          },
          "numberOfElements" : {
            "type" : "integer",
            "format" : "int32"
          },
          "first" : {
            "type" : "boolean"
          },
          "last" : {
            "type" : "boolean"
          }
        },
        "description" : "Response for SearchBookings.\n\nWire mirror of the REST PageResponse envelope: `number` is zero-based and `total_elements` counts across\nall pages, not this one."
      },
      "wink.partner.v1.GetBookingRequest" : {
        "type" : "object",
        "properties" : {
          "bookingId" : {
            "type" : "string"
          }
        },
        "description" : "Request for GetBooking."
      },
      "wink.partner.v1.GetBookingResponse" : {
        "type" : "object",
        "properties" : {
          "booking" : {
            "$ref" : "#/components/schemas/wink.partner.v1.AgentBooking"
          }
        },
        "description" : "Response for GetBooking."
      },
      "wink.partner.v1.CancelBookingRequest" : {
        "type" : "object",
        "properties" : {
          "bookingId" : {
            "type" : "string"
          },
          "reason" : {
            "type" : "string",
            "description" : "Why the agency is cancelling, as free text. Carried onto the booking and, where the property supports\nit, onto the reservation."
          },
          "type" : {
            "description" : "The classification to file it under. Defaults to CANCELLATION when unspecified.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.CancellationType"
            } ]
          }
        },
        "description" : "Request for CancelBooking."
      },
      "wink.partner.v1.CancelBookingResponse" : {
        "type" : "object",
        "properties" : {
          "booking" : {
            "description" : "The booking after cancellation. Read its `refund` to find out whether any money came back — a\nnon-refundable rate cancels with nothing returned.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AgentBooking"
            } ]
          }
        },
        "description" : "Response for CancelBooking."
      },
      "wink.partner.v1.ResendConfirmationRequest" : {
        "type" : "object",
        "properties" : {
          "bookingId" : {
            "type" : "string"
          }
        },
        "description" : "Request for ResendConfirmation."
      },
      "wink.partner.v1.ResendConfirmationResponse" : {
        "type" : "object",
        "properties" : {
          "sent" : {
            "type" : "boolean",
            "description" : "True when the email was handed to the mail pipeline. It does not promise delivery."
          }
        },
        "description" : "Response for ResendConfirmation.\n\nWrapped rather than returning a bare bool, so this rpc can gain a field later without a wire break."
      },
      "wink.partner.v1.RefundBookingRequest" : {
        "type" : "object",
        "properties" : {
          "bookingId" : {
            "type" : "string"
          },
          "idempotencyKey" : {
            "type" : "string",
            "description" : "Idempotency key. REQUIRED — a retried refund is a double refund."
          },
          "reason" : {
            "type" : "string",
            "description" : "Why the refund is being issued. Recorded against the booking."
          }
        },
        "description" : "Request for RefundBooking."
      },
      "wink.partner.v1.RefundBookingResponse" : {
        "type" : "object",
        "properties" : {
          "booking" : {
            "description" : "The booking after the refund has been applied. Read `refund.status`: a PENDING refund is settling\nnormally and must NOT be retried.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AgentBooking"
            } ]
          },
          "idempotentReplay" : {
            "type" : "boolean",
            "description" : "True when this response replayed an earlier call. Nothing was refunded again."
          }
        },
        "description" : "Response for RefundBooking."
      },
      "wink.partner.v1.RefundBookingsRequest" : {
        "type" : "object",
        "properties" : {
          "groupId" : {
            "type" : "string",
            "description" : "The group to refund, from `AgentBooking.group_id`."
          },
          "idempotencyKey" : {
            "type" : "string",
            "description" : "Idempotency key. REQUIRED, and it covers the WHOLE group so a retry cannot half-refund it."
          },
          "reason" : {
            "type" : "string"
          }
        },
        "description" : "Request for RefundBookings."
      },
      "wink.partner.v1.RefundBookingsResponse" : {
        "type" : "object",
        "properties" : {
          "outcomes" : {
            "type" : "array",
            "description" : "One entry per booking in the group, including the ones that could not be refunded.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.RefundOutcome"
            }
          },
          "idempotentReplay" : {
            "type" : "boolean",
            "description" : "True when this response replayed an earlier call. Nothing was refunded again."
          }
        },
        "description" : "Response for RefundBookings."
      },
      "wink.partner.v1.RefundOutcome" : {
        "type" : "object",
        "properties" : {
          "booking" : {
            "description" : "The booking, refunded or not.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AgentBooking"
            } ]
          },
          "error" : {
            "type" : "string",
            "description" : "Empty when the refund succeeded. Human-readable when it did not."
          }
        },
        "description" : "What happened to one booking in a group refund.\n\nA booking that could not be refunded carries `error` and leaves `booking` populated with its unchanged\nstate, rather than failing the call — one booking outside its window must not deny the agency the\nrefunds it is owed on the rest."
      },
      "wink.partner.v1.GetPropertiesRequest" : {
        "type" : "object",
        "properties" : {
          "propertyIds" : {
            "type" : "array",
            "description" : "Properties to fetch, 1 to 50. Identifiers come from a search result's `property_id`.",
            "items" : {
              "type" : "string"
            }
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language for descriptions, e.g. \"th\". English is ALWAYS returned alongside it, so a missing\ntranslation never leaves you with nothing to display. Defaults to English only."
          },
          "include" : {
            "type" : "array",
            "description" : "What to include beyond the property's basic identity. Empty means basics only.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.ContentScope"
            }
          },
          "maxMediaPerEntity" : {
            "type" : "integer",
            "description" : "Cap on images and videos returned per entity. Zero means no cap, which is the default — you are\nexpected to cache the full bundle. Set this when you want a lighter response for a preview surface.",
            "format" : "int32"
          },
          "knownVersions" : {
            "type" : "object",
            "additionalProperties" : {
              "type" : "string"
            },
            "description" : "Content you already hold: property identifier to the `cache_token` that property's last response carried.\n\nA property whose token still matches comes back CONTENT_STATUS_NOT_MODIFIED carrying its\n`content_version` and `cache_token` and nothing else — keep serving what you cached. Anything that has\nchanged, and anything you did not list here, comes back in full as usual. Omit the field entirely and\nevery property is returned in full, which is the default.\n\nSend back the `cache_token`, NOT the `content_version`. The token is opaque: do not parse it, do not\nbuild one, and do not assume anything about its length or alphabet. It covers the property's version AND\nthe shape of the request that produced it — your sales channel, `language_code`, `include`,\n`max_media_per_entity`, `image_format` and `video_format` — which is precisely what makes it safe to\nchange a request. Ask for a scope you did not have last time, or a different media format, and the token\nstops matching, so you get the full response rather than a NOT_MODIFIED that would have left you\npermanently missing the thing you just asked for.\n\nBILLING DOES NOT CHANGE. You are charged one unit per call whatever comes back, so this saves response\nsize and processing on both ends, not money. Batching is what lowers a bill.\n\nEntries for properties absent from `property_ids` are ignored, so you may keep one map and reuse it\nacross batches. At most 50 entries, the same bound as `property_ids`."
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          },
          "videoFormat" : {
            "type" : "string",
            "description" : "IANA media type for the PLAYABLE video delivery URLs, `stream` and `preview`: video/mp4 (the\ndefault) or video/webm. Empty means video/mp4.\n\nThis does NOT affect `poster` and `thumbnail`. Those are still frames extracted from the video, so\nthey are images and follow `image_format`. An unrecognised value is rejected with INVALID_ARGUMENT."
          }
        },
        "description" : "Request for GetProperties."
      },
      "wink.partner.v1.GetPropertiesResponse" : {
        "type" : "object",
        "properties" : {
          "properties" : {
            "type" : "array",
            "description" : "One entry per requested property, in the order requested.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyContent"
            }
          }
        },
        "description" : "Response for GetProperties."
      },
      "wink.partner.v1.PropertyContent" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "The property this entry describes."
          },
          "status" : {
            "description" : "Whether content was returned, and if not, why.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.ContentStatus"
            } ]
          },
          "contentVersion" : {
            "type" : "string",
            "description" : "Version covering ALL static content for this property. The SAME value the pricing endpoints return on a\nsearch result as `last_update`. Store it with your cached copy; re-fetch when a search shows a different\none. Present on CONTENT_STATUS_OK and CONTENT_STATUS_NOT_MODIFIED.\n\nThis is about the PROPERTY, so it is comparable across surfaces — but for that same reason it says\nnothing about your channel, language or `include` set. To ask us whether your cached copy is current,\nsend `cache_token` rather than this."
          },
          "includedScopes" : {
            "type" : "array",
            "description" : "The scopes actually populated below. Echoed back so you can tell \"not requested\" from \"none exist\".",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.ContentScope"
            }
          },
          "property" : {
            "description" : "Basic identity — always present when status is CONTENT_STATUS_OK.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyBasics"
            } ]
          },
          "details" : {
            "description" : "Descriptions, address, policies, amenities and contacts. Present with CONTENT_SCOPE_PROPERTY_DETAILS.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyDetails"
            } ]
          },
          "restaurants" : {
            "type" : "array",
            "description" : "On- and off-premises restaurants. Present with CONTENT_SCOPE_RESTAURANT.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "spas" : {
            "type" : "array",
            "description" : "Spas. Present with CONTENT_SCOPE_SPA.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "meetingRooms" : {
            "type" : "array",
            "description" : "Meeting rooms. Present with CONTENT_SCOPE_MEETING_ROOM.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "activities" : {
            "type" : "array",
            "description" : "Activities. Present with CONTENT_SCOPE_ACTIVITY.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "attractions" : {
            "type" : "array",
            "description" : "Attractions. Present with CONTENT_SCOPE_ATTRACTION.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "places" : {
            "type" : "array",
            "description" : "Places of interest. Present with CONTENT_SCOPE_PLACE.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "addOns" : {
            "type" : "array",
            "description" : "Purchasable add-ons. Present with CONTENT_SCOPE_ADD_ON.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryItemContent"
            }
          },
          "cancellationPolicies" : {
            "type" : "array",
            "description" : "Cancellation policies referenced by `RoomRate.cancellation_policy_id`. Present with\nCONTENT_SCOPE_CANCELLATION_POLICY.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.CancellationPolicyContent"
            }
          },
          "perks" : {
            "type" : "array",
            "description" : "Perks referenced by `RoomRate.perk_ids`. Present with CONTENT_SCOPE_PERK.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PerkContent"
            }
          },
          "roomTypes" : {
            "type" : "array",
            "description" : "Room types the property sells, ONCE each — not repeated per rate. Present when you request\nCONTENT_SCOPE_ROOM_TYPE. `RoomTypeOffers.room_type_id` on the pricing surfaces points here.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.RoomTypeContent"
            }
          },
          "ratePlans" : {
            "type" : "array",
            "description" : "Rate plans, ONCE each — not repeated per rate. Present when you request CONTENT_SCOPE_RATE_PLAN.\n`RoomRate.rate_plan_id` on the pricing surfaces points here.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.RatePlanContent"
            }
          },
          "counts" : {
            "description" : "How many of each ancillary entity this property has. ALWAYS present, whatever you included, and the\nreason the default response is useful on its own: it tells you which scopes are worth requesting for\nthis property before paying for the round trip. A zero means the property has none, which is\ninformation — an empty `restaurants` list would not distinguish that from \"you did not ask\".",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyCounts"
            } ]
          },
          "cacheToken" : {
            "type" : "string",
            "description" : "Opaque token identifying THIS response for THIS property. Store it and send it back in\n`known_versions` to skip re-fetching content that has not changed. Present on CONTENT_STATUS_OK and\nCONTENT_STATUS_NOT_MODIFIED.\n\nUnlike `content_version`, this covers the request as well as the property: your sales channel,\n`language_code`, `include` and `max_media_per_entity` all feed it. Two callers holding the same property\nat the same version will hold different tokens if they asked different questions, which is why the token\nis per-response and the version is per-property.\n\nTreat it as bytes. It is not a timestamp, not a hash you should reproduce, and its format may change\nwithout notice — a token we no longer recognise simply yields a full response, never an error."
          }
        },
        "description" : "Everything requested about one property.\n\nPopulated fields depend on the `include` scopes you asked for. A scope you did not request is absent, not\nempty — proto3 cannot distinguish \"no restaurants\" from \"restaurants not requested\" on a repeated field,\nso check `included_scopes` rather than inferring from an empty list."
      },
      "wink.partner.v1.PropertyBasics" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "Stable property identifier."
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url-safe slug."
          },
          "name" : {
            "type" : "string",
            "description" : "Property name in the requested language."
          },
          "localName" : {
            "type" : "string",
            "description" : "Property name in its local language, when it differs."
          },
          "geoLocation" : {
            "description" : "Where the property is.\n\nBare latitude and longitude, not GeoJSON: this field carries only coordinates, and the surrounding\nJSON already tells you what kind of thing they belong to.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPosition"
            } ]
          },
          "starRating" : {
            "type" : "integer",
            "description" : "Official star rating, 1-5. Zero when unrated.",
            "format" : "int32"
          },
          "images" : {
            "type" : "array",
            "description" : "Published property images, lowest `sort` first. Capped by `max_media_per_entity` when you set one.\nUnpublished assets are never returned — a property that took an image down did so deliberately.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Multimedia"
            }
          },
          "videos" : {
            "type" : "array",
            "description" : "Published property videos, same ordering and cap as `images`.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Multimedia"
            }
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency the property trades in."
          },
          "numberOfRooms" : {
            "type" : "integer",
            "description" : "Total rooms at the property.",
            "format" : "int32"
          },
          "city" : {
            "description" : "The city this property is in, with the identifiers the destination surfaces take.\n\nHere rather than on PropertyDetails so that it arrives on every successful Content call, whatever\nscopes you asked for: pivoting from a property to its city should not require paying for the full\ndetails scope. Absent when the property has no city on record.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.City"
            } ]
          }
        },
        "description" : "A property's basic identity — the same fields a search result carries."
      },
      "wink.partner.v1.Multimedia" : {
        "type" : "object",
        "properties" : {
          "mediaId" : {
            "type" : "string",
            "description" : "Stable identifier for this asset. Use this as your cache key."
          },
          "entityId" : {
            "type" : "string",
            "description" : "Identifier of the inventory item this asset belongs to (the property, a guest room, and so on).\nNamed `entity_id` rather than `identifier` to match the `*_id` convention the rest of this surface\nuses, and because \"identifier\" said nothing about WHOSE identifier it is."
          },
          "kind" : {
            "description" : "MEDIA_TYPE_IMAGE or MEDIA_TYPE_VIDEO.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MultimediaKind"
            } ]
          },
          "source" : {
            "type" : "string",
            "description" : "Where the asset came from, e.g. the property's own upload or a syndicated source."
          },
          "sort" : {
            "type" : "integer",
            "description" : "Display order within its gallery, ascending.",
            "format" : "int32"
          },
          "angle" : {
            "type" : "string",
            "description" : "Camera angle or shot description, when the property supplied one."
          },
          "width" : {
            "type" : "integer",
            "description" : "Intrinsic width in pixels.",
            "format" : "int32"
          },
          "height" : {
            "type" : "integer",
            "description" : "Intrinsic height in pixels.",
            "format" : "int32"
          },
          "descriptions" : {
            "type" : "array",
            "description" : "Localized captions.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "lifestyleType" : {
            "description" : "The lifestyle this asset is meant to illustrate, when tagged.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.LifestyleType"
            } ]
          },
          "urls" : {
            "description" : "Ready-to-use delivery URLs, one per variant. Which variants are populated depends on whether the\nasset is an image, a Cloudinary video or an externally-hosted one — see MediaUrlSet.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlSet"
            } ]
          },
          "otaPicCategory" : {
            "type" : "string",
            "description" : "OpenTravel PIC (Picture Category) code, e.g. \"2\" = Lobby view, \"6\" = Guest room. What the asset depicts,\nwhich is what you want when laying out a gallery rather than dumping it in `sort` order. Empty when\nthe property never categorised the asset. Look codes up at https://wink.travel/developers/taxonomy."
          }
        },
        "description" : "One published image or video.\n\nKey anything you cache on `media_id`. It is stable for the life of the asset; URLs are not — they\ncarry transformation and delivery parameters that change without notice."
      },
      "wink.partner.v1.MediaUrlSet" : {
        "type" : "object",
        "properties" : {
          "original" : {
            "description" : "MULTIMEDIA_KIND_IMAGE only: unscaled, best-quality delivery URL.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "large" : {
            "description" : "MULTIMEDIA_KIND_IMAGE only: large variant, max width 1920px.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "medium" : {
            "description" : "MULTIMEDIA_KIND_IMAGE only: medium variant, max width 1024px.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "thumbnail" : {
            "description" : "MULTIMEDIA_KIND_IMAGE or MULTIMEDIA_KIND_VIDEO (Cloudinary): thumbnail variant, max width 320px. For video\nthis is a still frame extracted from the video, so its `format` is an image type and follows\n`image_format`, not `video_format`.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "stream" : {
            "description" : "MULTIMEDIA_KIND_VIDEO (Cloudinary) only: streaming delivery URL, unscaled. Delivered as `video_format`,\nwhich defaults to video/mp4.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "preview" : {
            "description" : "MULTIMEDIA_KIND_VIDEO (Cloudinary) only: preview variant, max width 1280px. Delivered as `video_format`.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "poster" : {
            "description" : "MULTIMEDIA_KIND_VIDEO (Cloudinary) only: poster frame at medium width. A still EXTRACTED from the video,\nso like `thumbnail` it is an image and follows `image_format`.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MediaUrlVariant"
            } ]
          },
          "externalUrl" : {
            "type" : "string",
            "description" : "MULTIMEDIA_KIND_VIDEO (YouTube) only: pass-through to the YouTube watch URL. Wink generates no variants for\nexternally-hosted media, so no format applies and every MediaUrlVariant field above is unset."
          }
        },
        "description" : "Ready-to-use delivery URLs for one asset, one field per variant.\n\nWhich fields are populated depends on the asset. Images carry `original`, `large`, `medium` and\n`thumbnail`; Cloudinary videos carry `stream`, `preview`, `poster` and `thumbnail`; externally-hosted\nvideo (YouTube) carries only `external_url`, because Wink generates no variants for media it does not\nhost. Always check the variant you want is present rather than assuming a fixed set.\n\nTHESE URLS MAY ROTATE. Wink's media-reorganization migration rewrites the underlying storage id of\nevery asset it touches, which changes every URL here. `Multimedia.media_id` does NOT change. Key your\nstorage on `media_id` and re-sync when `PropertySearchResult.content_version` moves; treating a URL as a\npermanent identifier will break."
      },
      "wink.partner.v1.MediaUrlVariant" : {
        "type" : "object",
        "properties" : {
          "url" : {
            "type" : "string",
            "description" : "Ready-to-use delivery URL for this variant."
          },
          "maxWidth" : {
            "type" : "integer",
            "description" : "Maximum pixel width this variant is scaled to. Unset for variants with no resize (image\n\"original\", video \"stream\") — explicit presence so \"not resized\" is distinguishable from a\nliteral 0px width.",
            "format" : "int32"
          },
          "format" : {
            "type" : "string",
            "description" : "The IANA media type this URL returns: one of image/jpeg, image/png, image/webp, image/* or video/mp4,\nvideo/webm. Feed it straight to a Content-Type header or use it to pick a decoder.\n\nThis ECHOES what was served, so it reflects the `image_format` and `video_format` you sent on the\nrequest. Send neither and images are image/jpeg and video is video/mp4.\n\nimage/* is the one value that is not a concrete type: it means the CDN negotiates per client from\nthe Accept header, so the same URL returns WebP, AVIF or JPEG to different callers. It is only ever\nreturned if you asked for it. Everything else is deterministic — the same URL returns the same bytes\nto every client, which is what makes it safe to cache and re-serve."
          }
        },
        "description" : "One delivery URL, what it is scaled to, and what it will actually return."
      },
      "wink.partner.v1.PropertyDetails" : {
        "type" : "object",
        "properties" : {
          "descriptions" : {
            "type" : "array",
            "description" : "Localized descriptions, in English plus your requested language.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "address" : {
            "description" : "Postal address.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Address"
            } ]
          },
          "chain" : {
            "type" : "string",
            "description" : "Property chain, when the property belongs to one."
          },
          "brand" : {
            "type" : "string",
            "description" : "Brand within the chain."
          },
          "locationCategory" : {
            "type" : "string",
            "description" : "Setting, e.g. beachfront or city centre."
          },
          "segmentCategory" : {
            "type" : "string",
            "description" : "Market segment, e.g. luxury or economy."
          },
          "propertyCategory" : {
            "type" : "string",
            "description" : "Property type, e.g. property, hostel or resort."
          },
          "architecturalStyle" : {
            "type" : "string",
            "description" : "Architectural style, when recorded."
          },
          "whenBuilt" : {
            "type" : "string",
            "description" : "Year or period built, when recorded."
          },
          "lifestyleTypes" : {
            "type" : "array",
            "description" : "Lifestyles this property is oriented toward.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.LifestyleType"
            }
          },
          "policy" : {
            "description" : "Check-in, check-out, children and pet rules.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyPolicy"
            } ]
          },
          "otaHacAmenityCodes" : {
            "type" : "array",
            "description" : "OpenTravel HAC (Hotel Amenity) codes. Look codes up at https://wink.travel/developers/taxonomy.",
            "items" : {
              "type" : "string"
            }
          },
          "otaPhyAccessibilityCodes" : {
            "type" : "array",
            "description" : "OpenTravel PHY (Accessibility Feature) codes.",
            "items" : {
              "type" : "string"
            }
          },
          "otaSecSecurityCodes" : {
            "type" : "array",
            "description" : "OpenTravel SEC (Security Feature) codes.",
            "items" : {
              "type" : "string"
            }
          },
          "aggregateReviewRating" : {
            "type" : "number",
            "description" : "Mean guest review rating.",
            "format" : "float"
          },
          "totalReviews" : {
            "type" : "integer",
            "description" : "Number of guest reviews behind the rating.",
            "format" : "int32"
          },
          "aggregateGreendexRating" : {
            "type" : "number",
            "description" : "Wink sustainability rating.",
            "format" : "float"
          }
        },
        "description" : "Everything else about the property itself."
      },
      "wink.partner.v1.PropertyPolicy" : {
        "type" : "object",
        "properties" : {
          "checkInTime" : {
            "type" : "string",
            "description" : "Local check-in time, \"HH:mm\"."
          },
          "checkOutTime" : {
            "type" : "string",
            "description" : "Local check-out time, \"HH:mm\"."
          },
          "childrenAllowed" : {
            "type" : "boolean",
            "description" : "Whether children are accepted."
          },
          "childrenMinimumAge" : {
            "type" : "integer",
            "description" : "Minimum age accepted, when the property sets one.",
            "format" : "int32"
          },
          "petsAllowed" : {
            "type" : "boolean",
            "description" : "Whether pets are accepted."
          },
          "petMaxWeightInKilos" : {
            "type" : "number",
            "description" : "Maximum pet weight in kilograms, when limited.",
            "format" : "double"
          },
          "internetConnectionType" : {
            "type" : "string",
            "description" : "How internet is provided, e.g. wired or wireless."
          },
          "internetConnectionLocation" : {
            "type" : "string",
            "description" : "Where internet is available, e.g. in-room or public areas."
          },
          "internetAvailability" : {
            "type" : "string",
            "description" : "Whether internet is free or paid."
          },
          "parkingAvailability" : {
            "type" : "string",
            "description" : "Whether parking is free, paid or unavailable."
          },
          "parkingAccess" : {
            "type" : "string",
            "description" : "How parking is accessed, e.g. valet or self."
          }
        },
        "description" : "Check-in, check-out and house rules."
      },
      "wink.partner.v1.InventoryItemContent" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable identifier for this item."
          },
          "name" : {
            "type" : "string",
            "description" : "Item name in the requested language."
          },
          "descriptions" : {
            "type" : "array",
            "description" : "Localized descriptions, in English plus your requested language.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "multimedias" : {
            "type" : "array",
            "description" : "Images and videos.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Multimedia"
            }
          },
          "sort" : {
            "type" : "integer",
            "description" : "Display order within its collection, ascending.",
            "format" : "int32"
          },
          "onPremises" : {
            "type" : "boolean",
            "description" : "Whether the item is on the property's premises."
          },
          "geoLocation" : {
            "description" : "Where the item is, when it differs from the property.\n\nBare latitude and longitude, not GeoJSON. Absent, not zeroed, when the item sits at the property --\nsee `PartnerContentContractMeasurementTest` for how that absence is verified rather than assumed.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.GeoPosition"
            } ]
          },
          "address" : {
            "description" : "Postal address, when the item has its own.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Address"
            } ]
          },
          "lifestyleType" : {
            "description" : "The lifestyle this item suits, when tagged. Singular, not a list: the platform records exactly one\nper item.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.LifestyleType"
            } ]
          },
          "otaRsiAmenities" : {
            "type" : "array",
            "description" : "OpenTravel RSI (Restaurant Service/Amenity) codes. Populated only for `restaurants`.",
            "items" : {
              "type" : "string"
            }
          },
          "otaSpaAmenities" : {
            "type" : "array",
            "description" : "OpenTravel SPA (Spa Feature) codes. Populated only for `spas`.",
            "items" : {
              "type" : "string"
            }
          },
          "otaMrcAmenities" : {
            "type" : "array",
            "description" : "OpenTravel MRC (Meeting Room Code) codes. Populated only for `meeting_rooms`.",
            "items" : {
              "type" : "string"
            }
          },
          "otaRecAmenities" : {
            "type" : "array",
            "description" : "OpenTravel REC (Recreation) codes. Populated only for `activities`.",
            "items" : {
              "type" : "string"
            }
          },
          "otaPrxProximityCode" : {
            "type" : "string",
            "description" : "OpenTravel PRX (Proximity) code, e.g. \"1\" = On premises, \"3\" = Nearby. Five values, so do NOT read\nit as a boolean -- `on_premises` above is the two-way answer if that is all you need."
          },
          "otaAqcMinAgeAppropriateCode" : {
            "type" : "string",
            "description" : "OpenTravel AQC (Age Qualifying) code, e.g. \"3\" = Under 2, \"8\" = Child, \"1\" = Over 21. An age\nCATEGORY, not a numeric minimum age -- parsing it as a number yields nonsense for most values."
          },
          "otaSecSecurityFeatures" : {
            "type" : "array",
            "description" : "OpenTravel SEC (Security Feature) codes.",
            "items" : {
              "type" : "string"
            }
          },
          "otaPhyAccessibilityFeatures" : {
            "type" : "array",
            "description" : "OpenTravel PHY (Accessibility Feature) codes.",
            "items" : {
              "type" : "string"
            }
          }
        },
        "description" : "A non-room inventory item: restaurant, spa, meeting room, activity, attraction, place or add-on.\n\nThese share a shape deliberately. They differ in what they mean, not in what they carry, and one message\nkeeps the contract small and lets a client render any of them with the same code. Where a type needs\nfields the others do not, those arrive as new field numbers on this message rather than a new message."
      },
      "wink.partner.v1.CancellationPolicyContent" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable policy identifier."
          },
          "refundable" : {
            "type" : "boolean",
            "description" : "Whether the stay can be cancelled without charge before the deadline. The single field to branch on."
          },
          "policyCode" : {
            "type" : "string",
            "description" : "Two-character summary: \"R\" refundable, \"NR\" non-refundable."
          },
          "freeCancellationDaysBefore" : {
            "type" : "integer",
            "description" : "Days before arrival by which a free cancellation must be made. Zero means the deadline falls ON the\narrival day — read `advance_cancellation_free_of_charge` for the hour, which is the only place that\ndistinction survives. Absent on a non-refundable policy, which has no free window to count down.",
            "format" : "int32"
          },
          "advanceCancellationFreeOfCharge" : {
            "type" : "string",
            "description" : "The free-cancellation window as the property set it, e.g. \"SEVEN_DAYS_BEFORE_ARRIVAL\" or\n\"UNTIL_EIGHTEEN_HUNDRED_HOURS_ON_DAY_OF_ARRIVAL\". The two day-of-arrival values both report\n`free_cancellation_days_before` = 0 and differ only here."
          },
          "refundableCancellationCharge" : {
            "type" : "string",
            "description" : "What a refundable policy charges when the free window has closed, e.g. \"FIFTY_PERCENT\"."
          },
          "noShowCharge" : {
            "type" : "string",
            "description" : "What is charged when the guest does not arrive at all, e.g. \"SAME_AS_CANCELLATION_FEE\"."
          },
          "nonRefundableCancellationCharge" : {
            "type" : "string",
            "description" : "What a non-refundable policy charges on cancellation, e.g. \"SEVENTY_PERCENT\"."
          },
          "nonRefundableDeadline" : {
            "type" : "string",
            "description" : "The point after which a non-refundable policy charges more, e.g. \"SEVEN_DAYS_BEFORE_ARRIVAL\"."
          },
          "nonRefundableAfterDeadlineCancellationCharge" : {
            "type" : "string",
            "description" : "What a non-refundable policy charges once `non_refundable_deadline` has passed, e.g.\n\"ONE_HUNDRED_PERCENT\"."
          }
        },
        "description" : "A cancellation policy: the deadline, and what is charged on each side of it.\n\nJoin to a pricing result through `RoomRate.cancellation_policy_id`. The pricing surface already tells you\nwhether a rate is refundable and until when; this tells you the terms behind that answer.\n\nA policy has no name and no descriptions — it is a set of rules, not prose, and the platform stores no\nlocalized text for one. Render it from the fields below; `policy_code` is the short label to show when\nyou have room for two characters and not a sentence.\n\nThe charge fields are enum NAMES, not free text, and the set of names is stable. Treat an unrecognised\nvalue as \"charge applies, terms unknown\" rather than as an error: new members are added to these ladders\nover time and an unknown one must not break your rendering."
      },
      "wink.partner.v1.RoomTypeContent" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable room type identifier. Matches `RoomPrice.room_type_id` on the pricing endpoints, which you can\nuse to group several rates for the same room."
          },
          "name" : {
            "type" : "string",
            "description" : "Room name in the requested language."
          },
          "descriptions" : {
            "type" : "array",
            "description" : "Localized descriptions, in English plus your requested language.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "multimedias" : {
            "type" : "array",
            "description" : "Room images and videos.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Multimedia"
            }
          },
          "sort" : {
            "type" : "integer",
            "description" : "Display order within the property, ascending.",
            "format" : "int32"
          },
          "maxOccupancy" : {
            "type" : "integer",
            "description" : "Total guests, adults plus children.",
            "format" : "int32"
          },
          "minOccupancy" : {
            "type" : "integer",
            "description" : "Minimum guests required.",
            "format" : "int32"
          },
          "maxAdultOccupancy" : {
            "type" : "integer",
            "description" : "Maximum adults.",
            "format" : "int32"
          },
          "maxChildOccupancy" : {
            "type" : "integer",
            "description" : "Maximum children.",
            "format" : "int32"
          },
          "quantity" : {
            "type" : "integer",
            "description" : "Rooms of this type at the property.",
            "format" : "int32"
          },
          "size" : {
            "type" : "number",
            "description" : "Floor area in square metres.",
            "format" : "double"
          },
          "nonSmoking" : {
            "type" : "boolean",
            "description" : "Whether the room is non-smoking."
          },
          "otaSegRoomCategory" : {
            "type" : "string",
            "description" : "OpenTravel SEG (Segment Category) code, e.g. \"4\" = Deluxe, \"16\" = Standard. Look codes up at\nhttps://wink.travel/developers/taxonomy."
          },
          "otaRvtRoomViewCode" : {
            "type" : "string",
            "description" : "OpenTravel RVT (Room View Type) code, e.g. \"11\" = Ocean view, \"16\" = Garden view."
          },
          "otaRmaAmenities" : {
            "type" : "array",
            "description" : "OpenTravel RMA (Room Amenity) codes, e.g. \"2\" = Air conditioning, \"7\" = Balcony.",
            "items" : {
              "type" : "string"
            }
          },
          "otaPhyAccessibilityFeatures" : {
            "type" : "array",
            "description" : "OpenTravel PHY (Accessibility Feature) codes, e.g. \"110\" = Roll-in shower available.",
            "items" : {
              "type" : "string"
            }
          },
          "otaRltRoomLocationCode" : {
            "type" : "string",
            "description" : "OpenTravel RLT (Room Location Type) code, e.g. \"1\" = Away from elevator, \"8\" = High floor."
          },
          "otaGriRoomClassificationCode" : {
            "type" : "string",
            "description" : "OpenTravel GRI (Guest Room Info) code, e.g. \"44\" = Bungalow, \"45\" = Villa, \"82\" = Standard."
          },
          "otaArcRoomArchitectureCode" : {
            "type" : "string",
            "description" : "OpenTravel ARC (Architectural Style) code, e.g. \"7\" = Modern, \"11\" = Victorian."
          },
          "bedConfigurations" : {
            "type" : "array",
            "description" : "The bed layouts this room can be booked in, each with a stable id.\n\nPass the id of the one the guest chose as `BookingRoomRequest.bedroom_configuration_id`. A room with\none layout still returns it; send the id anyway rather than relying on the default, which is\n\"whichever the property happens to list first\".",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.BedConfiguration"
            }
          }
        },
        "description" : "A bookable room type. Returned once per property in `PropertyContent.room_types`, never inline on a\nrate; `RoomTypeOffers.room_type_id` points here. Only room types reachable from a master rate you can\nsee are ever returned by a property-scoped call."
      },
      "wink.partner.v1.BedConfiguration" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable identifier for this layout. THIS is what `BookingRoomRequest.bedroom_configuration_id` wants."
          },
          "name" : {
            "type" : "string",
            "description" : "The property's own label for the layout, e.g. \"Master Bedroom\". Untranslated, and frequently verbose."
          },
          "bedrooms" : {
            "type" : "array",
            "description" : "The bedrooms in this layout, and the beds in each.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Bedroom"
            }
          }
        },
        "description" : "One bookable bed layout for a room type.\n\nSuppliers routinely put an entire room description in `name` (\"Suite - 1 Bedroom, 1 Double Bed,\nNon-Smoking, Balcony…\"), so render from `bedrooms` when you want to show what a guest actually gets.\nWink's own booking confirmations stopped trusting `name` for exactly that reason."
      },
      "wink.partner.v1.Bedroom" : {
        "type" : "object",
        "properties" : {
          "type" : {
            "description" : "Which bedroom this is within the layout.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.BedroomType"
            } ]
          },
          "beds" : {
            "type" : "array",
            "description" : "The beds in this bedroom.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Bed"
            }
          }
        },
        "description" : "One bedroom within a layout."
      },
      "wink.partner.v1.Bed" : {
        "type" : "object",
        "properties" : {
          "otaBedTypeCode" : {
            "type" : "string",
            "description" : "OpenTravel BED (Bed Type) code, e.g. \"3\" = King, \"5\" = Queen, \"8\" = Twin. Look codes up at\nhttps://wink.travel/developers/taxonomy."
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many beds of this type, at least one.",
            "format" : "int32"
          }
        },
        "description" : "A quantity of one bed type."
      },
      "wink.partner.v1.RatePlanContent" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable rate plan identifier. Matches `RoomRate.rate_plan_id` on the pricing endpoints."
          },
          "name" : {
            "type" : "string",
            "description" : "Rate plan name as the property wrote it, e.g. \"Best Available Rate\". Untranslated."
          },
          "prepaid" : {
            "type" : "boolean",
            "description" : "Whether the rate must be paid in full at booking rather than at the property."
          },
          "breakfast" : {
            "type" : "boolean",
            "description" : "Breakfast is included in the rate."
          },
          "brunch" : {
            "type" : "boolean",
            "description" : "Brunch is included in the rate."
          },
          "lunch" : {
            "type" : "boolean",
            "description" : "Lunch is included in the rate."
          },
          "dinner" : {
            "type" : "boolean",
            "description" : "Dinner is included in the rate."
          },
          "allInclusive" : {
            "type" : "boolean",
            "description" : "Everything is included except alcohol."
          },
          "allInclusivePlusAlcohol" : {
            "type" : "boolean",
            "description" : "Everything is included, alcohol as well."
          },
          "cancellationPolicyId" : {
            "type" : "string",
            "description" : "The cancellation policy governing this rate plan, when it sets one. Join to\n`PropertyContent.cancellation_policies` or fetch it with GetCancellationPolicy."
          }
        },
        "description" : "A rate plan and what it includes. Returned once per property in `PropertyContent.rate_plans`, never\ninline on a rate; `RoomRate.rate_plan_id` points here. Only rate plans reachable from a master rate you\ncan see are ever returned by a property-scoped call.\n\nA rate plan has no localized descriptions — `name` is a single untranslated label the property chose,\nand `language_code` on your request does not affect it. What a rate plan actually carries is what it\nINCLUDES, which is the meal booleans below."
      },
      "wink.partner.v1.PropertyCounts" : {
        "type" : "object",
        "properties" : {
          "restaurants" : {
            "type" : "integer",
            "description" : "Restaurants on and off the premises.",
            "format" : "int32"
          },
          "spas" : {
            "type" : "integer",
            "description" : "Spas.",
            "format" : "int32"
          },
          "meetingRooms" : {
            "type" : "integer",
            "description" : "Meeting and event rooms.",
            "format" : "int32"
          },
          "activities" : {
            "type" : "integer",
            "description" : "Activities.",
            "format" : "int32"
          },
          "attractions" : {
            "type" : "integer",
            "description" : "Attractions.",
            "format" : "int32"
          },
          "places" : {
            "type" : "integer",
            "description" : "Places of interest.",
            "format" : "int32"
          },
          "addOns" : {
            "type" : "integer",
            "description" : "Purchasable add-ons.",
            "format" : "int32"
          }
        },
        "description" : "How many ancillary entities a property has, per kind.\n\nCheap to produce — these are stored counts on the property aggregate, not a count of loaded entities —\nso they come back on every response regardless of `include`. Use them to decide which CONTENT_SCOPE\nvalues are worth asking for: requesting CONTENT_SCOPE_SPA for a property with zero spas costs a round\ntrip and returns an empty list."
      },
      "wink.partner.v1.GetPropertyRequest" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "Property to fetch. Identifier comes from a search result's `property_id`."
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language for descriptions, e.g. \"th\". English is ALWAYS returned alongside it, so a missing\ntranslation never leaves you with nothing to display. Defaults to English only."
          },
          "include" : {
            "type" : "array",
            "description" : "What to include beyond the property's basic identity. Empty means basics only.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.ContentScope"
            }
          },
          "maxMediaPerEntity" : {
            "type" : "integer",
            "description" : "Cap on images and videos returned per entity. Zero means no cap, which is the default.",
            "format" : "int32"
          },
          "knownVersion" : {
            "type" : "string",
            "description" : "Content you already hold: the `cache_token` this property's last response carried.\n\nMatches and the response comes back CONTENT_STATUS_NOT_MODIFIED carrying its `content_version` and\n`cache_token` and nothing else — keep serving what you cached. Omit it and the property is returned in\nfull, which is the default.\n\nSend back the `cache_token`, NOT the `content_version`. The same opacity rules as GetProperties apply:\ndo not parse it, do not build one. It covers the property's version AND the shape of the request that\nproduced it — `include`, `max_media_per_entity`, `image_format` and `video_format` among them — so\nwidening a scope or changing a media format correctly stops it matching rather than leaving you\npermanently missing the thing you just asked for.\n\nBILLING DOES NOT CHANGE. One unit per call whatever comes back."
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          },
          "videoFormat" : {
            "type" : "string",
            "description" : "IANA media type for the PLAYABLE video delivery URLs, `stream` and `preview`: video/mp4 (the\ndefault) or video/webm. Empty means video/mp4.\n\nThis does NOT affect `poster` and `thumbnail`. Those are still frames extracted from the video, so\nthey are images and follow `image_format`. An unrecognised value is rejected with INVALID_ARGUMENT."
          }
        },
        "description" : "Request for GetProperty.\n\nThe single-property mirror of GetPropertiesRequest. Field names match it exactly so moving between the\ntwo is a shape change and never a semantic one — the only differences are that `property_ids` becomes a\nsingle `property_id`, and the `known_versions` map becomes the one `known_version` it would have held."
      },
      "wink.partner.v1.GetPropertyResponse" : {
        "type" : "object",
        "properties" : {
          "property" : {
            "description" : "The property. Its `status` is CONTENT_STATUS_OK or CONTENT_STATUS_NOT_MODIFIED — an unknown id is a\nNOT_FOUND error on the call itself, not a status here.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyContent"
            } ]
          }
        },
        "description" : "Response for GetProperty."
      },
      "wink.partner.v1.GetRoomTypeRequest" : {
        "type" : "object",
        "properties" : {
          "roomTypeId" : {
            "type" : "string",
            "description" : "Room type to fetch, from `RoomTypeOffers.room_type_id` on a pricing result."
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language for descriptions, e.g. \"th\". English is ALWAYS returned alongside it."
          },
          "maxMedia" : {
            "type" : "integer",
            "description" : "Cap on images and videos returned. Zero means no cap, which is the default.",
            "format" : "int32"
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          },
          "videoFormat" : {
            "type" : "string",
            "description" : "IANA media type for the PLAYABLE video delivery URLs, `stream` and `preview`: video/mp4 (the\ndefault) or video/webm. Empty means video/mp4.\n\nThis does NOT affect `poster` and `thumbnail`. Those are still frames extracted from the video, so\nthey are images and follow `image_format`. An unrecognised value is rejected with INVALID_ARGUMENT."
          }
        },
        "description" : "Request for GetRoomType."
      },
      "wink.partner.v1.GetRoomTypeResponse" : {
        "type" : "object",
        "properties" : {
          "roomType" : {
            "description" : "The room type.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.RoomTypeContent"
            } ]
          }
        },
        "description" : "Response for GetRoomType."
      },
      "wink.partner.v1.GetRatePlanRequest" : {
        "type" : "object",
        "properties" : {
          "ratePlanId" : {
            "type" : "string",
            "description" : "Rate plan to fetch, from `RoomRate.rate_plan_id` on a pricing result."
          }
        },
        "description" : "Request for GetRatePlan.\n\nNo `language_code`: a rate plan carries a single untranslated `name` and no descriptions, so there would\nbe nothing for it to select. Accepting one anyway would imply a translation that never arrives."
      },
      "wink.partner.v1.GetRatePlanResponse" : {
        "type" : "object",
        "properties" : {
          "ratePlan" : {
            "description" : "The rate plan.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.RatePlanContent"
            } ]
          }
        },
        "description" : "Response for GetRatePlan."
      },
      "wink.partner.v1.GetCancellationPolicyRequest" : {
        "type" : "object",
        "properties" : {
          "cancellationPolicyId" : {
            "type" : "string",
            "description" : "Cancellation policy to fetch, from `RoomRate.cancellation_policy_id` on a pricing result."
          }
        },
        "description" : "Request for GetCancellationPolicy.\n\nNo `language_code`: a policy is a set of rules with no stored prose to translate. See\nCancellationPolicyContent."
      },
      "wink.partner.v1.GetCancellationPolicyResponse" : {
        "type" : "object",
        "properties" : {
          "cancellationPolicy" : {
            "description" : "The cancellation policy.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.CancellationPolicyContent"
            } ]
          }
        },
        "description" : "Response for GetCancellationPolicy."
      },
      "wink.partner.v1.SearchRequest" : {
        "type" : "object",
        "properties" : {
          "term" : {
            "type" : "string",
            "description" : "What the user has typed so far. Fewer than 3 characters (after trimming) returns an empty\nlist rather than an error."
          }
        },
        "description" : "Your Wink account identifier. Every call is scoped to one account, and your token must hold\nINVENTORY:READ on it."
      },
      "wink.partner.v1.SearchResponse" : {
        "type" : "object",
        "properties" : {
          "destinations" : {
            "type" : "array",
            "description" : "Matching destinations, most relevant first. At most 50.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Destination"
            }
          }
        }
      },
      "wink.partner.v1.Destination" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Wink's identifier for this destination entry."
          },
          "type" : {
            "description" : "What kind of place this is. Determines what `type_identifier` refers to.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.DestinationType"
            } ]
          },
          "typeIdentifier" : {
            "type" : "string",
            "description" : "Identifier of the underlying resource — a property ID for DESTINATION_TYPE_PROPERTY, a geoname ID for DESTINATION_TYPE_CITY and\nDESTINATION_TYPE_COUNTRY. This is the id you pass to the other Wink endpoints that take a property or place."
          },
          "name" : {
            "type" : "string",
            "description" : "Display name, in the language given by `language_code`."
          },
          "urlName" : {
            "type" : "string",
            "description" : "URL slug. Pass this back to the lookup-by-slug call to resolve this destination later."
          },
          "ownerName" : {
            "type" : "string",
            "description" : "Display name of the supplier that created this entry, where one applies."
          },
          "cityName" : {
            "type" : "string",
            "description" : "Nearest city."
          },
          "countryName" : {
            "type" : "string"
          },
          "subCountryName" : {
            "type" : "string",
            "description" : "State or province."
          },
          "subSubCountryName" : {
            "type" : "string",
            "description" : "County or district."
          },
          "countryCode" : {
            "type" : "string",
            "description" : "ISO 3166-1 alpha-2 country code."
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 code for the language `name` is written in."
          },
          "longitude" : {
            "type" : "number",
            "description" : "Longitude and latitude in decimal degrees. Both are omitted together when the location is\nunknown — which is why they are optional rather than defaulting to 0/0, a real place in the\nGulf of Guinea.",
            "format" : "double"
          },
          "latitude" : {
            "type" : "number",
            "format" : "double"
          }
        },
        "description" : "A place you can search for and sell through Wink."
      },
      "wink.partner.v1.GetRequest" : {
        "type" : "object",
        "properties" : {
          "urlName" : {
            "type" : "string",
            "description" : "URL slug of the destination, e.g. `bangkok-thailand`. Required."
          },
          "type" : {
            "description" : "Type of the destination you are resolving. Required — a slug is only unique within a type,\nso leaving this unset is rejected rather than guessed.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.DestinationType"
            } ]
          }
        },
        "description" : "Your Wink account identifier. Every call is scoped to one account, and your token must hold\nINVENTORY:READ on it."
      },
      "wink.partner.v1.GetResponse" : {
        "type" : "object",
        "properties" : {
          "destination" : {
            "description" : "The resolved destination.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Destination"
            } ]
          }
        }
      },
      "wink.partner.v1.GetPropertyInventoryRequest" : {
        "type" : "object",
        "properties" : {
          "userSession" : {
            "description" : "Traveller context: dates, occupancy, language and currency. Required.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.UserSession"
            } ]
          },
          "propertyId" : {
            "type" : "string",
            "description" : "Stable property identifier, as returned by Search and Lookup."
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url-safe slug, as returned by Search."
          },
          "include" : {
            "type" : "array",
            "description" : "Transactional inventory to price alongside the room offers. Leave it empty — the default — and you get\nroom offers only, which is the smallest response and what most booking flows need.\n\nEach scope you add prices one more class of bookable item. Unrecognised values are ignored rather than\nrejected, so a client built against a newer version of this file still works.\n\nThis does NOT change what you are billed: the meter counts hotel-days from the itinerary, not sections\nof the response.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryScope"
            }
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for GetPropertyInventory.\n\nAt most one of `propertyId, urlName` is set (protobuf `oneof property`)."
      },
      "wink.partner.v1.GetPropertyInventoryResponse" : {
        "type" : "object",
        "properties" : {
          "property" : {
            "description" : "The property and its offers.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyInventory"
            } ]
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has — amounts are `amount / 10^currency_exponent`.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names in this response are in."
          }
        },
        "description" : "Response for GetPropertyInventory."
      },
      "wink.partner.v1.PropertyInventory" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "Stable property identifier. Your key for everything, including Content lookups."
          },
          "urlName" : {
            "type" : "string",
            "description" : "Url-safe slug, stable and safe to build links from."
          },
          "uniqueId" : {
            "type" : "string",
            "description" : "The property's globally unique short code."
          },
          "lastUpdate" : {
            "type" : "string",
            "description" : "When this property's record last changed, ISO-8601 \"YYYY-MM-DDThh:mm:ss\" in UTC. Use it as the cache key\nfor your Content responses — the same value Search returns."
          },
          "available" : {
            "type" : "boolean",
            "description" : "True if AT LEAST ONE offer is bookable for the requested stay. When false, `room_types` still carries\nthe unavailable offers rather than being empty, so you can show why."
          },
          "commission" : {
            "type" : "number",
            "description" : "Commission rate your account earns on this property, as a fraction — 0.15 for 15%. A property-channel\nterm, so it holds for every offer below.",
            "format" : "double"
          },
          "heroImageUrl" : {
            "type" : "string",
            "description" : "A ready-to-use delivery URL for a single representative image of the PROPERTY (as opposed to\n`RoomTypeOffers.hero_image_url`, one per room), scaled to 1024px wide (Cloudinary's medium variant —\nc_scale, dpr_auto, f_auto, q_auto:best, w_1024). The lowest-sorted published image; unset when the\nproperty has none."
          },
          "roomTypes" : {
            "type" : "array",
            "description" : "Offers grouped by room type. `room_types[0].rates[0]` is the best available price, the same figure\nSearch would have shown for this property.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.RoomTypeOffers"
            }
          },
          "perks" : {
            "type" : "array",
            "description" : "Every perk referenced by any `RoomRate.perk_ids` below, deduplicated — one entry per distinct perk,\nnot one per rate. Resolve a perk id here rather than calling Content; a handful of distinct perks\ntypically cover every rate in this response.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PerkContent"
            }
          },
          "addOns" : {
            "type" : "array",
            "description" : "Every add-on referenced by any `RoomRate.add_on_ids` above, deduplicated the same way perks are.\n\nEmpty unless the request carried `INVENTORY_SCOPE_ADD_ON`. Unlike the collections below, add-ons are\nNOT property-scoped: they are matched per room configuration, which is why the association lives on\neach rate and only the priced rows are gathered here.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.AncillaryInventory"
            }
          },
          "ancillaries" : {
            "type" : "array",
            "description" : "Bookable non-room inventory at this property, one entry per item — a restaurant, a spa, a meeting\nroom. Each carries only its identifiers and its priced rows; everything describing the item comes\nfrom Content, joined on `inventory_id`.\n\nContains only the types you asked for in `include`, and is empty when you asked for none.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.AncillaryInventory"
            }
          },
          "announcements" : {
            "type" : "array",
            "description" : "Announcements the property is running for the REQUESTED STAY — already filtered against your\nitinerary, so everything here is meant to be shown. Empty unless the request carried\n`INVENTORY_SCOPE_ANNOUNCEMENT`.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Announcement"
            }
          }
        },
        "description" : "One property and every priced offer available for the requested stay.\n\nFor anything else about the property — name, descriptions, gallery, address, amenities, policies, and its\nrestaurants, spas, meeting rooms and activities — call the Content service with `property_id` and cache the\nanswer against `last_update`."
      },
      "wink.partner.v1.RoomTypeOffers" : {
        "type" : "object",
        "properties" : {
          "roomTypeId" : {
            "type" : "string",
            "description" : "The room type. Join to Content for the full gallery, amenities and long-form descriptions."
          },
          "name" : {
            "type" : "string",
            "description" : "The room type's name, in the requested language where a translation exists."
          },
          "heroImageUrl" : {
            "type" : "string",
            "description" : "A ready-to-use delivery URL for a single representative image of the room, scaled to 1024px wide\n(Cloudinary's medium variant — c_scale, dpr_auto, f_auto, q_auto:best, w_1024). The lowest-sorted\npublished image; unset when the room has none.\n\nA plain URL rather than the full Multimedia object deliberately: this is a rates surface, not a\ncontent surface, and shipping the full media object -- id, descriptions, every delivery-URL variant\n-- for every room type on every rate response would make the payload about images instead of about\nrates. The complete gallery is in Content."
          },
          "rates" : {
            "type" : "array",
            "description" : "Every offer for this room, BOOKABLE FIRST and cheapest first within each group. On Search that is the\nsingle best offer; on Inventory it is every rate and occupancy combination for the requested dates.\n\nUnavailable offers sort last, never first: they report 0 across all amounts, so ordering purely by\nprice would put \"not priced\" ahead of the cheapest real rate.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.RoomRate"
            }
          },
          "sort" : {
            "type" : "integer",
            "description" : "The property's own display order for its room types, ascending, when it has expressed one; 0 when it\nhas not. This describes the ROOM, which is why it is here rather than on each rate -- the domain\ndefines it as \"how the room types should be sorted and displayed\".\n\nIt is a merchandising hint, not the delivered order: `room_types` is already ordered by price.",
            "format" : "int32"
          }
        },
        "description" : "The offers for one room type, with the room's own details carried once.\n\nThis grouping mirrors the pricing engine's own shape (a room type holding its price configurations) and\nthe way a rate table is actually rendered. It exists because `name` and `hero_image_url` describe the\nROOM, not a rate: carried on RoomRate they were repeated across every rate x occupancy combination, which\non the Inventory surface is the whole cross-product for the property."
      },
      "wink.partner.v1.RoomRate" : {
        "type" : "object",
        "properties" : {
          "roomRateId" : {
            "type" : "string",
            "description" : "Stable identifier for this offer, and the id you carry into a booking.\n\nThis IS the master rate identifier — it maps from `roomRateIdentifier`, whose own schema calls it\n\"Specified master rate identifier\". The name says `room_rate` for historical reasons and is kept rather\nthan duplicated: publishing a second `master_rate_id` field with the same value would be two\nrepresentations of one thing that can disagree, which this API forbids elsewhere for prices."
          },
          "ratePlanId" : {
            "type" : "string",
            "description" : "The rate plan this offer sells under. Resolve it with Content's GetRatePlan; a rate plan is shared by\nmany offers, so it is referenced rather than repeated on each one."
          },
          "available" : {
            "type" : "boolean",
            "description" : "True if THIS offer, at the requested occupancy, is bookable for the itinerary. Narrower than\n`PropertySearchResult.available`, which is true when ANY offer at the property is. An unavailable offer is\nstill returned rather than dropped, so you can show the traveller why it cannot be booked."
          },
          "startDate" : {
            "type" : "string",
            "description" : "Check-in date, ISO-8601 \"YYYY-MM-DD\"."
          },
          "endDate" : {
            "type" : "string",
            "description" : "Check-out date, ISO-8601 \"YYYY-MM-DD\"."
          },
          "roomNights" : {
            "type" : "integer",
            "description" : "Nights covered by this rate.",
            "format" : "int32"
          },
          "adults" : {
            "type" : "integer",
            "description" : "Adults the rate is priced for, after the property's occupancy policy is applied.",
            "format" : "int32"
          },
          "children" : {
            "type" : "integer",
            "description" : "Children the rate is priced for, after the property's occupancy policy is applied.",
            "format" : "int32"
          },
          "unitStrikethroughPrice" : {
            "type" : "string",
            "description" : "ONE room, whole stay, BEFORE discounts — step 1 above. The \"was\" price: render it struck through next\nto `unit_price`.\n\nGUARANTEED >= `unit_price`. Occupancy surcharges and rate premiums are already inside it, so the gap\nbetween the two is only ever the discounts. Equal to `unit_price` when nothing is discounted, in which\ncase show no strikethrough.",
            "format" : "int64"
          },
          "unitPrice" : {
            "type" : "string",
            "description" : "ONE room, whole stay, after every adjustment — step 2 above. The \"now\" price, and the figure the\ndiscount is measured against. Excludes the rate plan's fixed charges.\n\nNot named `net` or `source_total`: everywhere else in this platform \"source\" means the PROPERTY'S OWN\nCURRENCY, and this figure is in the caller's. Reusing the word would have the next mapper author reach\nfor the domain's `getSourceTotal()` and quote the wrong currency.",
            "format" : "int64"
          },
          "unitExtraCharges" : {
            "type" : "string",
            "description" : "ONE room, whole stay — fixed charges attached to the rate plan, a one-time cleaning fee and the like.\nReported separately so a traveller sees them as their own line rather than as an unexplained\ndifference. NEVER discounted, which is why they sit outside both prices above. Zero when the rate plan\nhas none.",
            "format" : "int64"
          },
          "total" : {
            "type" : "string",
            "description" : "What the guest pays for every room: `(unit_price + unit_extra_charges) x quantity`. This is the number\nto show and the number to charge.\n\nOnly meaningful when `available` is true. An unavailable offer reports 0 across every amount, which\nmeans \"not priced\" rather than \"free\" — show the offer with its reason, never with a price of zero.",
            "format" : "int64"
          },
          "meals" : {
            "description" : "Meals included in the rate. These change which offer a traveller picks, so they are here rather than\nbehind an identifier.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.MealPlan"
            } ]
          },
          "refundable" : {
            "type" : "boolean",
            "description" : "True if the stay can be cancelled without charge before the policy's deadline."
          },
          "refundableUntilDays" : {
            "type" : "integer",
            "description" : "How many days from TODAY the traveller has left to cancel free of charge, and -1 when the rate is not\nrefundable at all. Zero means the free-cancellation window closes today.\n\nDerived as (days until check-in) − (the policy's days-before-arrival deadline), floored at 0 — the same\ncalculation `RoomTypeBestPriceForDateRange` publishes. It is relative to the moment of the response and\nwill drift as the stay approaches, so treat it as display data, not something to cache.",
            "format" : "int32"
          },
          "cancellationPolicyId" : {
            "type" : "string",
            "description" : "Cancellation policy identifier. Join to Content for the full terms and wording."
          },
          "perkIds" : {
            "type" : "array",
            "description" : "Identifiers of the perks bundled with this rate. Join to Content for their names and descriptions.\nThere is deliberately no monetary \"perk value\" on this surface.",
            "items" : {
              "type" : "string"
            }
          },
          "addOnIds" : {
            "type" : "array",
            "description" : "Identifiers of the add-ons purchasable ALONGSIDE this offer, resolved against\n`PropertyInventory.add_ons` in the same response — the same dictionary pattern as `perk_ids`.\n\nAdd-ons hang off the OFFER, not the property: the pricing engine matches them per room configuration\n(`RoomConfigurationPrice.addOnOffers`), so which add-ons are available genuinely differs between two\nrates on the same room. Flattening them to a property-level list would discard that.\n\nINVENTORY ONLY, and only when you ask for it. Empty unless the request carried\n`INVENTORY_SCOPE_ADD_ON`, and always empty on Search, which does not price add-ons at all.",
            "items" : {
              "type" : "string"
            }
          },
          "quantity" : {
            "type" : "integer",
            "description" : "How many identical rooms this offer is priced for — the `quantity` you set on the matching\n`RoomConfiguration`. At least 1. What the PROPERTY has left is `rooms_left`, which is a different\nnumber entirely.\n\nIt is echoed back because it is a MULTIPLIER on `total`, not merely a restatement of your request:\nwithout it you cannot tell whether `total` covers one room or five. It can also change the unit price\nitself, since a property may attach a rate to a room-count range — book three and every room may be\ncheaper than booking one.",
            "format" : "int32"
          },
          "discountPercent" : {
            "type" : "number",
            "description" : "The saving as a fraction of `unit_strikethrough_price` — 0.15 for 15%. Zero when nothing is\ndiscounted.\n\nPublished rather than left to you because there is one correct rounding of it and several plausible\nones, and a paid API where two integrators compute \"18%\" and \"17%\" from the same response is a support\nburden rather than a saving. Quantity-invariant: discounting every room equally does not change the\npercentage.",
            "format" : "double"
          },
          "adjustments" : {
            "type" : "array",
            "description" : "Every adjustment that moved this rate away from the property's plain price, one entry per kind, for\nshowing a traveller WHY the number is what it is. Empty when nothing applied.\n\nThese reconcile exactly: the signed amounts sum to `unit_price` minus the property's unadjusted price.\nThey are per-unit, like the prices they explain.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PriceAdjustment"
            }
          },
          "offerDetails" : {
            "type" : "array",
            "description" : "The property's own wording for the promotion or premium in force, in English plus your requested\nlanguage. Empty when neither applies.\n\nAt the RATE level, not on each adjustment, because that is how the platform stores it — one list of\ndescriptions covering the special rates that fired. Attaching a label to each `PriceAdjustment` would\nhave meant publishing a field nothing could ever fill.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "promotionalCodes" : {
            "type" : "array",
            "description" : "Promotional codes that were actually ACCEPTED and are reflected in the price above — not the codes you\nsent. A code your account is not entitled to is ignored rather than rejected, so comparing this against\nwhat you submitted is how you tell which ones bit.",
            "items" : {
              "type" : "string"
            }
          },
          "roomTypeAncillaries" : {
            "type" : "array",
            "description" : "Extras sold with THIS rate — in-room ancillaries. Empty when the rate sells none.\n\nTo book one, quote its `id` as BookingAncillaryRequest.offer_id and this rate's `room_rate_id` as\n`inventory_id`. Unlike every other ancillary the inventory id is the RATE, because the gate for\nthese is keyed by the master rate rather than by an entity. Leave\nBookingAncillaryRequest.channel_inventory_id blank — that is the common case and not an error.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.AncillaryOffer"
            }
          },
          "roomsLeft" : {
            "type" : "integer",
            "description" : "How many rooms the property still has for the WHOLE stay — the \"only 2 left\" number.\n\nThe SMALLEST nightly allotment across the stay, not the first night's and not an average. A three-night\nstay whose middle night has two rooms free has two rooms left, however many are free either side: you\ncannot sell a third room a guest would be evicted from on night two. The check-out date is excluded,\nsince nobody occupies a room that night.\n\nNOT the same as `quantity`, which is how many rooms YOU asked for. The relationship is\n`rooms_left >= quantity` whenever `available` is true — an offer that cannot house the party you\nrequested is not bookable and says so. Reading `quantity` as \"what the property has\" overstates\navailability, which is why they are named as differently as they are.\n\nZero means none are free for these dates, and only ever appears alongside `available = false`. It is\na real answer, not a missing one.\n\nThis is the property's true position, not a banded or capped figure. Treat it as commercially\nsensitive: it is exact enough to reconstruct a property's booking curve if sampled over time.",
            "format" : "int32"
          }
        },
        "description" : "One priced, bookable offer for the itinerary.\n\nEverything here either is a price or changes which offer a traveller would choose. The prose behind the\nidentifiers — rate plan names, perk descriptions, cancellation policy wording — lives in Content, reached\nvia `room_rate_id`.\n\nHOW THE PRICE IS BUILT, and what each field means:\n\n  1. The property's price for the room over these dates, with occupancy surcharges (an extra adult, an\n     extra child), any single-occupancy reduction, and any rate PREMIUM already applied. That is\n     `unit_strikethrough_price` — the \"was\" price, for ONE room.\n  2. Discounts come off it: promotions, and your channel or membership terms. The result is\n     `unit_price` — the cost of ONE ROOM, everything applied. The \"now\" price.\n  3. Fixed charges attached to the rate plan are added — a one-time cleaning fee and the like. Never\n     discounted, so they are reported separately as `unit_extra_charges` rather than folded in.\n  4. Multiply by `quantity`, the number of identical rooms you asked for.\n\n  total = (unit_price + unit_extra_charges) x quantity\n\nWHY THE SURCHARGES SIT INSIDE THE STRIKETHROUGH. A strikethrough is a promise that the traveller is\nsaving the difference, so the two prices must differ ONLY by things that are actually discounts. Put an\nextra-adult surcharge outside it and the \"was\" price falls BELOW the \"now\" price for any room with a\nthird guest — which is why `unit_strikethrough_price >= unit_price` ALWAYS holds here, and why you can\nrender the strikethrough unconditionally instead of testing which number is larger first.\n\n`discount_percent` is published rather than left to you, because there is exactly one correct rounding\nof it and three plausible ones. The absolute saving is deliberately NOT published: it is\n`unit_strikethrough_price - unit_price`, and a stored field that must always equal a subtraction is a\nfield that can one day disagree with it.\n\nPER UNIT vs TOTAL. Fields prefixed `unit_` are for ONE room over the WHOLE stay. `total` is the only\namount covering every room, and it is the number to charge. Nothing here is per NIGHT — divide by\n`room_nights` if you need a nightly figure, and expect it to be an average, since rates vary by date.\n\nMONEY CONVENTION. Every monetary field is an INTEGER IN MINOR UNITS of the response's `currency_code` —\ndivide by 10^`currency_exponent` to get the real value. The currency and its exponent are stated once\nper response instead of on every amount, so amounts are directly comparable and directly addable with\nexact integer arithmetic and no rounding.\n\nThe same vocabulary means the same things on the ancillary side — see `AncillaryOffer`."
      },
      "wink.partner.v1.AncillaryOffer" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable identifier for this priced row. Distinct from `AncillaryInventory.inventory_id`: one\nrestaurant sells several sittings, and this identifies the sitting."
          },
          "name" : {
            "type" : "string",
            "description" : "The row's name, as the property wrote it — \"1 hour session\", \"Sunset departure\". Untranslated;\nthe localized descriptions are on Content."
          },
          "pricingType" : {
            "description" : "What one unit of `unit_price` buys, and therefore how to multiply it up. See the table above.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.PricingType"
            } ]
          },
          "unitStrikethroughPrice" : {
            "type" : "string",
            "description" : "Price of ONE unit BEFORE discounts — the \"was\" price. Show it struck through next to `unit_price`.\n\nGUARANTEED >= `unit_price`, exactly as on `RoomRate`, and equal to it when nothing is discounted.",
            "format" : "int64"
          },
          "unitPrice" : {
            "type" : "string",
            "description" : "Price of ONE unit after the property's discount and your channel terms. This is the figure to\nmultiply up. Means the same thing `RoomRate.unit_price` means — the post-adjustment price of the thing\nitself — and there is deliberately no `total` here, because the quantity is yours to choose.",
            "format" : "int64"
          },
          "minPax" : {
            "type" : "integer",
            "description" : "Smallest party this row can be booked for; 0 when the property set no minimum.",
            "format" : "int32"
          },
          "maxPax" : {
            "type" : "integer",
            "description" : "Largest party this row can be booked for; 0 when the property set no maximum.",
            "format" : "int32"
          },
          "promotion" : {
            "type" : "string",
            "description" : "Promotional code that produced the discount, when one did. Empty when the price is simply the\nproperty's own discounted rate."
          },
          "discountPercent" : {
            "type" : "number",
            "description" : "The saving as a fraction of `unit_strikethrough_price` — 0.15 for 15%. Zero when nothing is\ndiscounted. Same definition and the same reason for existing as `RoomRate.discount_percent`.\n\nQuantity-invariant, which is what makes it safe here even though this message has no total: the\npercentage a traveller sees does not change with how many they buy.",
            "format" : "double"
          }
        },
        "description" : "One priced, bookable row of non-room inventory — a spa treatment, a table sitting, a tour departure.\n\nPRICES ARE PER UNIT, and there is no total. `RoomRate` can publish one because the room count is in your\nrequest; here the quantity is a choice the traveller has not made yet. An ancillary is quoted for ONE of\nwhatever `pricing_type` says it is sold by, and YOU multiply. Publishing a \"total\" that assumed one unit\n— or assumed the whole party — would be a number that is wrong for most bookings but looks\nauthoritative.\n\nHOW TO COMPUTE THE TOTAL. Take `unit_price`, then multiply by the quantity the traveller chooses and by\nwhatever `pricing_type` says it repeats over:\n\n  PER_STAY, PER_USE                      total = unit_price x quantity\n  PER_NIGHT, PER_DAY                     total = unit_price x quantity x nights\n  PER_HOUR                               total = unit_price x quantity x hours\n  PER_PERSON, PER_ADULT, PER_CHILD       total = unit_price x that headcount\n  PER_PERSON_PER_NIGHT                   total = unit_price x headcount x nights\n  PER_ADULT_PER_NIGHT                    total = unit_price x adults x nights\n  PER_CHILD_PER_NIGHT                    total = unit_price x children x nights\n  PER_PERSON_PER_HOUR                    total = unit_price x headcount x hours\n  PER_ADULT_PER_HOUR                     total = unit_price x adults x hours\n  PER_CHILD_PER_HOUR                     total = unit_price x children x hours\n\n`nights` is the itinerary you asked to be priced; the occupancy is likewise yours to apply. Honour\n`min_pax`/`max_pax` before quoting: a row is not bookable outside them.\n\nMONEY. Minor units of the response's `currency_code`, exactly as on `RoomRate`, so ancillary and room\namounts are addable without conversion."
      },
      "wink.partner.v1.AncillaryInventory" : {
        "type" : "object",
        "properties" : {
          "inventoryId" : {
            "type" : "string",
            "description" : "Stable identifier of the item — the restaurant, spa, activity or add-on itself. Join to Content's\n`InventoryItemContent.id` for everything descriptive."
          },
          "type" : {
            "description" : "Which kind of item this is, so a single flat list can be grouped without inspecting ids.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.InventoryScope"
            } ]
          },
          "channelInventoryIdentifier" : {
            "type" : "string",
            "description" : "Your channel's identifier for carrying this item. Present when the item reached you through a\ndistribution channel rather than directly."
          },
          "commissionable" : {
            "type" : "boolean",
            "description" : "True if this item earns your account commission."
          },
          "commission" : {
            "type" : "number",
            "description" : "Commission rate on this item, as a fraction — 0.15 for 15%. Zero when `commissionable` is false.",
            "format" : "double"
          },
          "offers" : {
            "type" : "array",
            "description" : "The bookable, priced rows for this item, CHEAPEST FIRST by `unit_price`. An item with no rows at all\nis not returned, since it cannot be sold through this surface — Content is where you learn the\nproperty merely HAS a spa.\n\nOrdered for the same reason room rates are: a caller rendering \"from ฿X\" takes the first row, and an\narbitrary order would make that figure depend on the property's data-entry sequence.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.AncillaryOffer"
            }
          }
        },
        "description" : "One non-room item the property sells, and its priced rows.\n\nDeliberately thin. `inventory_id` is the SAME identifier Content returns as `InventoryItemContent.id`\nfor this item, so the name, descriptions, gallery, address, amenities and opening hours are one cached\nContent call away and are never repeated here. What this message adds is the part Content cannot know:\nwhat it costs, on your channel, right now."
      },
      "wink.partner.v1.Announcement" : {
        "type" : "object",
        "properties" : {
          "id" : {
            "type" : "string",
            "description" : "Stable announcement identifier."
          },
          "descriptions" : {
            "type" : "array",
            "description" : "Localized title and body together, in English plus your requested language. Whether the title should\nbe rendered is `show_title`.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Description"
            }
          },
          "showTitle" : {
            "type" : "boolean",
            "description" : "True when the property wants the title shown above the body; false to render the body alone."
          }
        },
        "description" : "A message the property is running for the dates you asked to be priced.\n\nAlready resolved against your itinerary: a property's announcements each carry either a date window or\nan \"always show\" flag, and both are evaluated server-side. Everything returned here is meant to be\ndisplayed, so there is no window and no flag to re-check — publishing them would only invite each\nintegrator to re-implement the filter and get it subtly wrong."
      },
      "wink.partner.v1.ListPropertyInventoryRequest" : {
        "type" : "object",
        "properties" : {
          "entries" : {
            "type" : "array",
            "description" : "The properties to price, each with its own dates and occupancy. At least one, at most 20.\n\nEvery entry must ask for the SAME `user_session.currency_code` and `user_session.language_code`, because the\nresponse declares one display currency for all of them. A batch that mixes currencies is rejected with\nINVALID_ARGUMENT rather than answered in a currency half the entries did not ask for.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.GetPropertyInventoryRequest"
            }
          }
        },
        "description" : "Request for ListPropertyInventory."
      },
      "wink.partner.v1.ListPropertyInventoryResponse" : {
        "type" : "object",
        "properties" : {
          "properties" : {
            "type" : "array",
            "description" : "One entry per request entry, in the order requested — match results back to your request by position.\n\nThe batch is ATOMIC. If any property cannot be resolved the whole call fails rather than returning a\nshort or reordered list, because a partial response that still looked well-formed would have you\nattribute one property's prices to another. Nothing is billed for a failed batch.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.PropertyInventory"
            }
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has — amounts are `amount / 10^currency_exponent`.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names in this response are in."
          }
        },
        "description" : "Response for ListPropertyInventory."
      },
      "wink.partner.v1.GetRatePeriodRequest" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "Stable property identifier. Required."
          },
          "startDate" : {
            "type" : "string",
            "description" : "First check-in date to price, ISO-8601 \"YYYY-MM-DD\". Required."
          },
          "periodInDays" : {
            "type" : "integer",
            "description" : "How many days forward to price from `start_date`. Between 2 and 30. Required.\n\nThis is also the billed unit count: a 30-day calendar costs 30 hotel-day units.",
            "format" : "int32"
          },
          "adults" : {
            "type" : "integer",
            "description" : "Adults per room, between 1 and 10. Omit it to take the default of 2.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language for the room names, e.g. \"en\". Required: room names are translated against it,\nand with no language supplied every name falls back to its untranslated form."
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency for the returned prices. Exactly three characters."
          }
        },
        "description" : "Request for GetRatePeriod."
      },
      "wink.partner.v1.GetRatePeriodResponse" : {
        "type" : "object",
        "properties" : {
          "period" : {
            "description" : "The rate calendar.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.RatePeriod"
            } ]
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency EVERY amount in this response is denominated in."
          },
          "currencyExponent" : {
            "type" : "integer",
            "description" : "How many decimal places `currency_code` has — amounts are `amount / 10^currency_exponent`.",
            "format" : "int32"
          },
          "languageCode" : {
            "type" : "string",
            "description" : "ISO 639-1 language the names in this response are in."
          }
        },
        "description" : "Response for GetRatePeriod."
      },
      "wink.partner.v1.RatePeriod" : {
        "type" : "object",
        "properties" : {
          "propertyId" : {
            "type" : "string",
            "description" : "Stable property identifier."
          },
          "name" : {
            "type" : "string",
            "description" : "The property's name, in the requested language."
          },
          "adults" : {
            "type" : "integer",
            "description" : "Adults every rate in this calendar is priced for — the occupancy you asked for, so it is the same for\nevery entry and is stated once here rather than repeated on each.",
            "format" : "int32"
          },
          "internetIncluded" : {
            "type" : "boolean",
            "description" : "True if internet is included across the priced rates."
          },
          "parkingIncluded" : {
            "type" : "boolean",
            "description" : "True if parking is included across the priced rates."
          },
          "entries" : {
            "type" : "array",
            "description" : "One entry per check-in/check-out combination in the range.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.RatePeriodEntry"
            }
          }
        },
        "description" : "Best available rate for each check-in date across the requested range."
      },
      "wink.partner.v1.RatePeriodEntry" : {
        "type" : "object",
        "properties" : {
          "roomTypeId" : {
            "type" : "string",
            "description" : "Identifier of the room type this rate is for. Join to Content for its description, images and amenities."
          },
          "name" : {
            "type" : "string",
            "description" : "The room type's name, in the requested language."
          },
          "startDate" : {
            "type" : "string",
            "description" : "Check-in date, ISO-8601 \"YYYY-MM-DD\"."
          },
          "endDate" : {
            "type" : "string",
            "description" : "Check-out date, ISO-8601 \"YYYY-MM-DD\"."
          },
          "roomNights" : {
            "type" : "integer",
            "description" : "Nights covered.",
            "format" : "int32"
          },
          "available" : {
            "type" : "boolean",
            "description" : "True if this combination is bookable."
          },
          "refundable" : {
            "type" : "boolean",
            "description" : "True if it can be cancelled without charge before the policy's deadline."
          },
          "refundableUntilDays" : {
            "type" : "integer",
            "description" : "Days from TODAY until free cancellation closes; -1 when not refundable, 0 when it closes today. Same\ncalculation as `RoomRate.refundable_until_days`.",
            "format" : "int32"
          },
          "breakfastIncluded" : {
            "type" : "boolean",
            "description" : "True if breakfast is included."
          },
          "total" : {
            "type" : "string",
            "description" : "What the guest pays for this combination, in minor units of `currency_code`.\n\nOnly meaningful when `available` is true. An unavailable combination reports 0, which means \"no price\"\nrather than \"free\" — do not render it as a price.",
            "format" : "int64"
          }
        },
        "description" : "The best available rate for one check-in/check-out combination."
      },
      "wink.partner.v1.ListAccountsRequest" : {
        "type" : "object",
        "properties" : {
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        },
        "description" : "Request for ListAccounts.\n\nWhich accounts come back is scoped by your token alone; there is nothing to filter on. The only field\nhere shapes how the accounts' logos are DELIVERED, not which ones you get."
      },
      "wink.partner.v1.ListAccountsResponse" : {
        "type" : "object",
        "properties" : {
          "accounts" : {
            "type" : "array",
            "description" : "The accounts you can act for, up to 200. May be empty.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Account"
            }
          },
          "truncated" : {
            "type" : "boolean",
            "description" : "True when you hold more than the 200 returned, so this list is incomplete. Switch to\nSearchAccounts, which pages. It is false in every ordinary case; the list is never shortened\nwithout setting it."
          }
        }
      },
      "wink.partner.v1.Account" : {
        "type" : "object",
        "properties" : {
          "accountId" : {
            "type" : "string",
            "description" : "Stable identifier for this account. Send it as the `managing-entity-id` metadata header on every\nother call in this API."
          },
          "ownerUserId" : {
            "type" : "string",
            "description" : "Identifier of the user who owns this account."
          },
          "provisionedById" : {
            "type" : "string",
            "description" : "Identifier of the application that provisioned this account — normally Wink itself."
          },
          "ownerType" : {
            "description" : "Whether the account is held by a company or by an individual.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AccountOwnerType"
            } ]
          },
          "type" : {
            "description" : "What kind of business this account is.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AccountType"
            } ]
          },
          "name" : {
            "type" : "string",
            "description" : "Display name."
          },
          "legalName" : {
            "type" : "string",
            "description" : "Registered legal name, when it differs from the display name."
          },
          "urlName" : {
            "type" : "string",
            "description" : "URL-friendly slug identifying the account in public profiles and links, e.g.\n`the-siam-residences-bangkok`."
          },
          "uniqueId" : {
            "type" : "string",
            "description" : "Short human-friendly identifier, used where a UUID would be unwieldy."
          },
          "status" : {
            "description" : "Where the account sits in its lifecycle. Only ACCOUNT_STATUS_ACTIVE accounts transact.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AccountStatus"
            } ]
          },
          "currencyCode" : {
            "type" : "string",
            "description" : "ISO 4217 currency the account accounts and settles in, e.g. \"THB\"."
          },
          "description" : {
            "type" : "string",
            "description" : "Short description of the business."
          },
          "url" : {
            "type" : "string",
            "description" : "Primary website or social URL."
          },
          "address" : {
            "description" : "Business address.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Address"
            } ]
          },
          "city" : {
            "description" : "City the account is registered in.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AccountCity"
            } ]
          },
          "logo" : {
            "description" : "The account's logo, when it has published one.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Multimedia"
            } ]
          }
        },
        "description" : "One Wink account — the business entity that owns properties, receives payouts and controls team\naccess.\n\nThis is an identity projection: enough to recognise an account, route a call to it and render it in\na picker. It deliberately carries no banking, payout or team-roster detail."
      },
      "wink.partner.v1.AccountCity" : {
        "type" : "object",
        "properties" : {
          "geoNameId" : {
            "type" : "string",
            "description" : "Wink's identifier for this city."
          },
          "name" : {
            "type" : "string",
            "description" : "City name."
          },
          "urlName" : {
            "type" : "string",
            "description" : "URL slug, e.g. `bangkok-thailand`. Accepted by Lookup's Get with DESTINATION_TYPE_CITY."
          },
          "countryCode" : {
            "type" : "string",
            "description" : "ISO 3166-1 alpha-2 country code, e.g. \"TH\"."
          }
        },
        "description" : "The city an account is registered in.\n\nA projection of Wink's geographic record, not a free-text city name — `url_name` is the same slug\nthe destination Lookup service accepts, so you can resolve the full destination from it."
      },
      "wink.partner.v1.SearchAccountsRequest" : {
        "type" : "object",
        "properties" : {
          "skip" : {
            "type" : "integer",
            "description" : "How many records to skip. Omit it to start at the beginning.\n\nPaging is page-aligned, so this must be a whole multiple of `take` — 0, then `take`, then\n`2 * take`, and so on. A partial offset is rejected with INVALID_ARGUMENT rather than quietly\nrounded down, so you can never be handed a page you did not ask for.",
            "format" : "int32"
          },
          "take" : {
            "type" : "integer",
            "description" : "How many records to return. Between 1 and 180; omit it to take the default of 30.",
            "format" : "int32"
          },
          "status" : {
            "description" : "Return only accounts with this lifecycle status. Omit it to return every status.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AccountStatus"
            } ]
          },
          "type" : {
            "description" : "Return only accounts of this type. Omit it to return every type.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.AccountType"
            } ]
          },
          "nameQuery" : {
            "type" : "string",
            "description" : "Return only accounts whose name STARTS WITH this text, case-insensitively. Matched literally —\nregular-expression characters carry no special meaning. At most 100 characters.\n\nPrefix rather than substring on purpose: a substring match cannot use the name index and so reads\nevery account on the platform on a call that costs you nothing, which is not a load we are willing\nto publish. Search for \"Siam\" to find \"Siam Residences\", not for \"Residences\"."
          },
          "sortBy" : {
            "type" : "string",
            "description" : "Field to sort on. One of `name`, `urlName`, `status`, `type`, `createdDate` or `lastUpdate`;\nanything else is rejected. Omit it to sort by `name`."
          },
          "sortDirection" : {
            "description" : "Direction for `sort_by`. Omit it to sort ascending. Declared in partner_common.proto and shared\nwith the Booking surface, so ordering reads the same wherever you page.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.SortDirection"
            } ]
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        }
      },
      "wink.partner.v1.SearchAccountsResponse" : {
        "type" : "object",
        "properties" : {
          "accounts" : {
            "type" : "array",
            "description" : "The matching accounts for this page.",
            "items" : {
              "$ref" : "#/components/schemas/wink.partner.v1.Account"
            }
          },
          "skip" : {
            "type" : "integer",
            "description" : "Records skipped to produce this page — echoes the `skip` you sent.",
            "format" : "int32"
          },
          "take" : {
            "type" : "integer",
            "description" : "Page size used — echoes the `take` you sent, or the default that was applied.",
            "format" : "int32"
          },
          "totalCount" : {
            "type" : "string",
            "description" : "Total accounts matching the filters, across all pages.",
            "format" : "int64"
          }
        }
      },
      "wink.partner.v1.GetAccountRequest" : {
        "type" : "object",
        "properties" : {
          "accountId" : {
            "type" : "string",
            "description" : "Identifier of the account to retrieve. Required. Your token must hold `ACCOUNT:READ` on it."
          },
          "imageFormat" : {
            "type" : "string",
            "description" : "IANA media type for image delivery URLs: image/jpeg (the default), image/png, image/webp, or image/*\nto let the CDN negotiate per client from its Accept header. Empty means image/jpeg.\n\nAn unrecognised value is rejected with INVALID_ARGUMENT rather than quietly falling back, so a typo\nlike \"image/jpg\" (not a media type) fails loudly instead of serving you JPEG while you believe you\nasked for something else. Whatever is served is echoed back on `MediaUrlVariant.format`."
          }
        }
      },
      "wink.partner.v1.GetAccountResponse" : {
        "type" : "object",
        "properties" : {
          "account" : {
            "description" : "The account matching `account_id`.",
            "allOf" : [ {
              "$ref" : "#/components/schemas/wink.partner.v1.Account"
            } ]
          }
        }
      },
      "wink.partner.v1.LifestyleType" : {
        "type" : "string",
        "description" : "The traveller lifestyle a property, room or media asset is oriented toward.\n\nValues are additive: a property may carry several. Treat an unrecognized value as \"some lifestyle this\nclient does not know about yet\" rather than an error — proto3 enums are open, and new lifestyles are\nadded without a version bump.\n\n- `LIFESTYLE_TYPE_UNSPECIFIED`: Not set.\n- `LIFESTYLE_TYPE_HEALTH_FITNESS`: Health and fitness focused.\n- `LIFESTYLE_TYPE_RELAX`: Relaxation focused.\n- `LIFESTYLE_TYPE_ADULT_ONLY`: Adults only.\n- `LIFESTYLE_TYPE_ADVENTURE`: Adventure travel.\n- `LIFESTYLE_TYPE_BUSINESS`: Business travel.\n- `LIFESTYLE_TYPE_LGBT`: LGBT focused.\n- `LIFESTYLE_TYPE_SINGLE_PARENT`: Single parent travel.\n- `LIFESTYLE_TYPE_SOLO_FEMALE`: Solo female travel.\n- `LIFESTYLE_TYPE_BEAUTY`: Beauty and wellness.\n- `LIFESTYLE_TYPE_FOODIE`: Food and dining focused.\n- `LIFESTYLE_TYPE_FAMILY`: Family travel.\n- `LIFESTYLE_TYPE_ROMANCE`: Romantic travel.\n- `LIFESTYLE_TYPE_COUPLE`: Couples travel.\n- `LIFESTYLE_TYPE_SOLO`: Solo travel.\n- `LIFESTYLE_TYPE_BACKPACKER`: Backpacking.\n- `LIFESTYLE_TYPE_SHOPPING`: Shopping focused.\n- `LIFESTYLE_TYPE_SPORTS`: Sports focused.\n- `LIFESTYLE_TYPE_MOUNTAIN`: Mountain destinations.\n- `LIFESTYLE_TYPE_BEACH`: Beach destinations.\n- `LIFESTYLE_TYPE_CITY`: City destinations.\n- `LIFESTYLE_TYPE_COUNTRY`: Countryside destinations.\n- `LIFESTYLE_TYPE_CULTURE`: Culture and heritage.\n- `LIFESTYLE_TYPE_ECO`: Eco and sustainable travel.",
        "enum" : [ "LIFESTYLE_TYPE_UNSPECIFIED", "LIFESTYLE_TYPE_HEALTH_FITNESS", "LIFESTYLE_TYPE_RELAX", "LIFESTYLE_TYPE_ADULT_ONLY", "LIFESTYLE_TYPE_ADVENTURE", "LIFESTYLE_TYPE_BUSINESS", "LIFESTYLE_TYPE_LGBT", "LIFESTYLE_TYPE_SINGLE_PARENT", "LIFESTYLE_TYPE_SOLO_FEMALE", "LIFESTYLE_TYPE_BEAUTY", "LIFESTYLE_TYPE_FOODIE", "LIFESTYLE_TYPE_FAMILY", "LIFESTYLE_TYPE_ROMANCE", "LIFESTYLE_TYPE_COUPLE", "LIFESTYLE_TYPE_SOLO", "LIFESTYLE_TYPE_BACKPACKER", "LIFESTYLE_TYPE_SHOPPING", "LIFESTYLE_TYPE_SPORTS", "LIFESTYLE_TYPE_MOUNTAIN", "LIFESTYLE_TYPE_BEACH", "LIFESTYLE_TYPE_CITY", "LIFESTYLE_TYPE_COUNTRY", "LIFESTYLE_TYPE_CULTURE", "LIFESTYLE_TYPE_ECO" ]
      },
      "wink.partner.v1.AdjustmentType" : {
        "type" : "string",
        "description" : "The kinds of adjustment a rate can carry.\n\nMirrors the pricing engine's own buckets one for one. There is deliberately no \"other\": a new bucket\nshould appear here as its own member rather than being folded into a catch-all nobody can render.\n\n- `ADJUSTMENT_TYPE_UNSPECIFIED`: Not set; ignored.\n- `ADJUSTMENT_TYPE_EXTRA_ADULT`: Surcharge for adults beyond the rate's included occupancy. Raises the price.\n- `ADJUSTMENT_TYPE_EXTRA_CHILD`: Surcharge for children beyond the rate's included occupancy. Raises the price.\n- `ADJUSTMENT_TYPE_SINGLE_OCCUPANCY`: Reduction for a room occupied by a single guest. Lowers the price.\n- `ADJUSTMENT_TYPE_PREMIUM`: A rate premium the property attached to these dates or conditions. Raises the price.\n- `ADJUSTMENT_TYPE_PROMOTION`: A promotion or promotional code. Lowers the price. This is the bucket a room-count deal lands in — a rate that only applies when you book three or more rooms is a promotion, not a separate kind.\n- `ADJUSTMENT_TYPE_CHANNEL`: Your channel or membership terms. Lowers the price.",
        "enum" : [ "ADJUSTMENT_TYPE_UNSPECIFIED", "ADJUSTMENT_TYPE_EXTRA_ADULT", "ADJUSTMENT_TYPE_EXTRA_CHILD", "ADJUSTMENT_TYPE_SINGLE_OCCUPANCY", "ADJUSTMENT_TYPE_PREMIUM", "ADJUSTMENT_TYPE_PROMOTION", "ADJUSTMENT_TYPE_CHANNEL" ]
      },
      "wink.partner.v1.ScoreSort" : {
        "type" : "string",
        "description" : "How ranked results are ordered.\n\nFour orderings, and they are the only four the platform actually has. Wink previously scored properties\non separate member, popularity, experience, perk, loyalty and add-on dimensions; those collapsed into a\nsingle demand score in 2026-07 and their backing fields no longer exist. The REST enum keeps them as\ndeprecated aliases so persisted documents keep loading — every one silently resolves to RECOMMENDED —\nbut this contract is new, so it publishes only what it can honour. A sort you can select and that does\nnothing is worse than one you cannot select.\n\n- `SCORE_SORT_UNSPECIFIED`: Not set; the server picks RECOMMENDED.\n- `SCORE_SORT_RECOMMENDED`: Wink's demand score — bookings and reviews blended, 0-100. The default.\n- `SCORE_SORT_PRICE_LOW_TO_HIGH`: Cheapest first.\n- `SCORE_SORT_PRICE_HIGH_TO_LOW`: Most expensive first.\n- `SCORE_SORT_ECO`: Sustainability, highest first — backed by the green-index rating, which is maintained separately from the demand score.",
        "enum" : [ "SCORE_SORT_UNSPECIFIED", "SCORE_SORT_RECOMMENDED", "SCORE_SORT_PRICE_LOW_TO_HIGH", "SCORE_SORT_PRICE_HIGH_TO_LOW", "SCORE_SORT_ECO" ]
      },
      "wink.partner.v1.InventoryScope" : {
        "type" : "string",
        "description" : "Optional transactional sections of a property-inventory response.\n\nEvery one of these prices something BOOKABLE. The names, descriptions, galleries, opening hours and\naddresses behind these identifiers are static content and come from the Content service — call it once\nper property and cache it. That split is the whole reason this surface stays small.\n\n- `INVENTORY_SCOPE_UNSPECIFIED`: Not set; ignored.\n- `INVENTORY_SCOPE_ADD_ON`: Add-ons purchasable alongside a room offer. Populates `RoomRate.add_on_ids` and the `PropertyInventory.add_ons` dictionary it resolves against.\n- `INVENTORY_SCOPE_RESTAURANT`: Bookable restaurant inventory — tables, sittings, set menus.\n- `INVENTORY_SCOPE_SPA`: Bookable spa inventory — treatments and sessions.\n- `INVENTORY_SCOPE_MEETING_ROOM`: Bookable meeting and event room inventory.\n- `INVENTORY_SCOPE_ACTIVITY`: Bookable activities.\n- `INVENTORY_SCOPE_ATTRACTION`: Bookable attractions.\n- `INVENTORY_SCOPE_PLACE`: Bookable places of interest.\n- `INVENTORY_SCOPE_ANNOUNCEMENT`: Property announcements for the requested stay. Not priced, but itinerary-dependent, which is why it lives here rather than on Content: Content has no itinerary to resolve them against.\n- `INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY`: Extras sold with a ROOM RATE rather than by the property at large — the in-room ancillaries on RoomRate.room_type_ancillaries. Its `inventory_id` on a booking is the RATE's id, not a room type and not an entity. That is the one place this vocabulary is not entity-keyed, and getting it wrong produces an empty match rather than an error, so read BookingAncillaryRequest before using it.",
        "enum" : [ "INVENTORY_SCOPE_UNSPECIFIED", "INVENTORY_SCOPE_ADD_ON", "INVENTORY_SCOPE_RESTAURANT", "INVENTORY_SCOPE_SPA", "INVENTORY_SCOPE_MEETING_ROOM", "INVENTORY_SCOPE_ACTIVITY", "INVENTORY_SCOPE_ATTRACTION", "INVENTORY_SCOPE_PLACE", "INVENTORY_SCOPE_ANNOUNCEMENT", "INVENTORY_SCOPE_ROOM_TYPE_ANCILLARY" ]
      },
      "wink.partner.v1.BookingStatus" : {
        "type" : "string",
        "description" : "Where a booking is in its lifecycle.\n\nMirrors the domain enum exactly. `PAYMENT_SUCCEEDED` is the \"booked and paid\" state — there is no\nseparate CONFIRMED, and no COMPLETED: a stay that has happened is still PAYMENT_SUCCEEDED.\n\n- `BOOKING_STATUS_INITIALIZED`: In flight. Never returned by this API — see GetBooking.\n- `BOOKING_STATUS_TEST_PAYMENT_SUCCEEDED`: Paid against a test payment method. Returned so an agency can tell its own test bookings apart from real ones rather than discovering the difference in a reconciliation.\n- `BOOKING_STATUS_PAYMENT_SUCCEEDED`: Booked and paid. The normal terminal state for a stay that goes ahead.\n- `BOOKING_STATUS_FAILED`: Never returned: failed bookings are excluded from every response on this service.",
        "enum" : [ "BOOKING_STATUS_UNSPECIFIED", "BOOKING_STATUS_INITIALIZED", "BOOKING_STATUS_TEST_PAYMENT_SUCCEEDED", "BOOKING_STATUS_PAYMENT_SUCCEEDED", "BOOKING_STATUS_PAYMENT_FAILED", "BOOKING_STATUS_FAILED", "BOOKING_STATUS_CANCELLED" ]
      },
      "wink.partner.v1.BookingCreationType" : {
        "type" : "string",
        "description" : "How a booking came to exist.\n\nMirrors the domain enum (`travel.wink.booking.api.BookingCreationType`) name for name, the same\nconvention `PaymentType` below documents. It previously did not: values 1-3 were\nAGENT/TRAVELLER/CHANNEL_MANAGER, a channel taxonomy that has no domain field behind it. The domain\nonly ever produces NORMAL, TEST or FAILED, so `Enum.valueOf` on the mismatched name threw for every\nnon-FAILED booking, was swallowed, and `creation` silently stayed at its wire default — i.e. every\nreal and test booking published BOOKING_CREATION_TYPE_UNSPECIFIED, unconditionally. Renamed rather\nthan reserved: this service has never been released, so no client can hold the old numbers.\n\n- `BOOKING_CREATION_TYPE_FAILED`: Never returned: failed bookings are excluded from every response on this service.",
        "enum" : [ "BOOKING_CREATION_TYPE_UNSPECIFIED", "BOOKING_CREATION_TYPE_NORMAL", "BOOKING_CREATION_TYPE_TEST", "BOOKING_CREATION_TYPE_FAILED" ]
      },
      "wink.partner.v1.CancellationSource" : {
        "type" : "string",
        "description" : "Who cancelled.\n\nMirrors the domain's BookingCancellerType. Its `HOTEL` member is published as `PROPERTY` — the wire\nvocabulary is deliberately generic and the naming guard enforces it, so the mapper is where the two\nspellings meet.",
        "enum" : [ "CANCELLATION_SOURCE_UNSPECIFIED", "CANCELLATION_SOURCE_PROPERTY", "CANCELLATION_SOURCE_GUEST", "CANCELLATION_SOURCE_ADMIN", "CANCELLATION_SOURCE_AGENT", "CANCELLATION_SOURCE_ACQUIRER", "CANCELLATION_SOURCE_SALES_CHANNEL", "CANCELLATION_SOURCE_SYSTEM" ]
      },
      "wink.partner.v1.CancellationType" : {
        "type" : "string",
        "description" : "Why a booking was cancelled. Mirrors the domain's CancellationType.",
        "enum" : [ "CANCELLATION_TYPE_UNSPECIFIED", "CANCELLATION_TYPE_DUPLICATE", "CANCELLATION_TYPE_CANCELLATION", "CANCELLATION_TYPE_NO_SHOW", "CANCELLATION_TYPE_CC_INVALID", "CANCELLATION_TYPE_CC_INSUFFICIENT", "CANCELLATION_TYPE_DISCRETIONARY" ]
      },
      "wink.partner.v1.RefundStatus" : {
        "type" : "string",
        "description" : "Where a refund has got to. Mirrors the domain's BookingRefundStatus.\n\n- `REFUND_STATUS_PENDING`: Requested and settling. NOT a failure, and NOT to be retried — retrying a PENDING refund is how you refund twice.\n- `REFUND_STATUS_CANCELED`: Spelled with one L, matching the payment processor and the domain enum.\n- `REFUND_STATUS_REQUIRES_ACTION`: Needs intervention before it can settle; not terminal.",
        "enum" : [ "REFUND_STATUS_UNSPECIFIED", "REFUND_STATUS_PENDING", "REFUND_STATUS_SUCCEEDED", "REFUND_STATUS_CANCELED", "REFUND_STATUS_REQUIRES_ACTION", "REFUND_STATUS_FAILED" ]
      },
      "wink.partner.v1.RefundReason" : {
        "type" : "string",
        "description" : "Why a refund was issued. Mirrors the domain's BookingRefundReason.",
        "enum" : [ "REFUND_REASON_UNSPECIFIED", "REFUND_REASON_DUPLICATE", "REFUND_REASON_FRAUDULENT", "REFUND_REASON_REQUESTED_BY_CUSTOMER", "REFUND_REASON_GUEST_COMPLAINT", "REFUND_REASON_GUEST_BOOKED_INCORRECT_DATE", "REFUND_REASON_GUEST_SICK", "REFUND_REASON_OVERBOOKING", "REFUND_REASON_EMERGENCY", "REFUND_REASON_FLIGHT_CANCELLED", "REFUND_REASON_DEATH_IN_FAMILY", "REFUND_REASON_OTHER", "REFUND_REASON_EXPIRED_UNCAPTURED_CHARGE", "REFUND_REASON_AVAILABILITY_CHECK_FAILED" ]
      },
      "wink.partner.v1.PaymentType" : {
        "type" : "string",
        "description" : "How the booking was paid for.\n\nMirrors the domain enum. Two values, not a payment-method taxonomy: either the card was charged through\nStripe, or it came off the agency's own balance.",
        "enum" : [ "PAYMENT_TYPE_UNSPECIFIED", "PAYMENT_TYPE_STRIPE", "PAYMENT_TYPE_AGENT" ]
      },
      "wink.partner.v1.BookingSortField" : {
        "type" : "string",
        "description" : "What SearchBookings may sort by.\n\nA closed list rather than a free-text field name, so the sortable surface is a contract instead of\nwhatever the storage layer happens to index today. These are exactly the columns the agent booking grid\noffers.",
        "enum" : [ "BOOKING_SORT_FIELD_UNSPECIFIED", "BOOKING_SORT_FIELD_CREATED_AT", "BOOKING_SORT_FIELD_CHECK_IN", "BOOKING_SORT_FIELD_CHECK_OUT", "BOOKING_SORT_FIELD_BOOKING_CODE", "BOOKING_SORT_FIELD_GUEST_LAST_NAME", "BOOKING_SORT_FIELD_GUEST_FIRST_NAME", "BOOKING_SORT_FIELD_RATE_PLAN_NAME", "BOOKING_SORT_FIELD_PROPERTY_NAME", "BOOKING_SORT_FIELD_STATUS" ]
      },
      "wink.partner.v1.SortDirection" : {
        "type" : "string",
        "description" : "Direction to sort a result set in.\n\nShared by every paged surface on this API, so one ordering vocabulary covers them all.\n\n- `SORT_DIRECTION_UNSPECIFIED`: Never sent by the server; treated as ascending on a request.\n- `SORT_DIRECTION_ASC`: Ascending.\n- `SORT_DIRECTION_DESC`: Descending.",
        "enum" : [ "SORT_DIRECTION_UNSPECIFIED", "SORT_DIRECTION_ASC", "SORT_DIRECTION_DESC" ]
      },
      "wink.partner.v1.ContentScope" : {
        "type" : "string",
        "description" : "Which content to return.\n\n- `CONTENT_SCOPE_UNSPECIFIED`: Not set; ignored.\n- `CONTENT_SCOPE_PROPERTY_DETAILS`: Descriptions, address, check-in/out policy, amenity and accessibility codes, contacts, categories.\n- `CONTENT_SCOPE_RESTAURANT`: Restaurants on and off the premises.\n- `CONTENT_SCOPE_SPA`: Spas.\n- `CONTENT_SCOPE_MEETING_ROOM`: Meeting and event rooms.\n- `CONTENT_SCOPE_ACTIVITY`: Activities.\n- `CONTENT_SCOPE_ATTRACTION`: Attractions.\n- `CONTENT_SCOPE_PLACE`: Places of interest.\n- `CONTENT_SCOPE_ADD_ON`: Purchasable add-ons.\n- `CONTENT_SCOPE_CANCELLATION_POLICY`: Cancellation policies and their deadlines.\n- `CONTENT_SCOPE_PERK`: Perks and their descriptions.\n- `CONTENT_SCOPE_ROOM_TYPE`: Room types the property sells, once each. Join from `RoomTypeOffers.room_type_id`.\n- `CONTENT_SCOPE_RATE_PLAN`: Rate plans and their meal inclusions and terms, once each. Join from `RoomRate.rate_plan_id`.",
        "enum" : [ "CONTENT_SCOPE_UNSPECIFIED", "CONTENT_SCOPE_PROPERTY_DETAILS", "CONTENT_SCOPE_RESTAURANT", "CONTENT_SCOPE_SPA", "CONTENT_SCOPE_MEETING_ROOM", "CONTENT_SCOPE_ACTIVITY", "CONTENT_SCOPE_ATTRACTION", "CONTENT_SCOPE_PLACE", "CONTENT_SCOPE_ADD_ON", "CONTENT_SCOPE_CANCELLATION_POLICY", "CONTENT_SCOPE_PERK", "CONTENT_SCOPE_ROOM_TYPE", "CONTENT_SCOPE_RATE_PLAN" ]
      },
      "wink.partner.v1.ContentStatus" : {
        "type" : "string",
        "description" : "Whether a property's content could be returned.\n\nThere is no \"not entitled\" member, and its absence is deliberate. Content is not gated on your sales\nchannel — every property we hold is described to you. What your channel determines is which BOOKABLE\ninventory comes back: `room_types`, `rate_plans`, `cancellation_policies` and `perks` are derived from\nthe master rates your channel carries, so a property you cannot sell returns CONTENT_STATUS_OK with its\ndescriptions, address and galleries, and empty bookable collections. That is a normal answer, not an\nerror — you are looking at a property you cannot currently sell, and `counts` still tells you what it\nhas.\n\n- `CONTENT_STATUS_UNSPECIFIED`: Not set.\n- `CONTENT_STATUS_OK`: Content returned.\n- `CONTENT_STATUS_NOT_FOUND`: No property with that identifier.\n- `CONTENT_STATUS_NOT_MODIFIED`: Your cached copy is still current — you sent a matching `cache_token` in `known_versions`. `content_version` and `cache_token` come back; every other field is absent, INCLUDING `counts` and `included_scopes`. Absent here means \"unchanged, look at what you stored\", not \"empty\" — do not overwrite your cache from this entry, and do not read `included_scopes` as the scopes narrowing to none. The token echoed back is the one to store for next time. You will only ever see this for a property whose token you supplied. It costs the same as any other entry: billing is per call.",
        "enum" : [ "CONTENT_STATUS_UNSPECIFIED", "CONTENT_STATUS_OK", "CONTENT_STATUS_NOT_FOUND", "CONTENT_STATUS_NOT_MODIFIED" ]
      },
      "wink.partner.v1.MultimediaKind" : {
        "type" : "string",
        "description" : "Whether a Media entry is a still image or a video.\n\n- `MULTIMEDIA_KIND_UNSPECIFIED`: Not set.\n- `MULTIMEDIA_KIND_IMAGE`: A still image.\n- `MULTIMEDIA_KIND_VIDEO`: A video.",
        "enum" : [ "MULTIMEDIA_KIND_UNSPECIFIED", "MULTIMEDIA_KIND_IMAGE", "MULTIMEDIA_KIND_VIDEO" ]
      },
      "wink.partner.v1.BedroomType" : {
        "type" : "string",
        "description" : "Which bedroom a Bedroom is within its layout.\n\n- `BEDROOM_TYPE_UNSPECIFIED`: Not set.\n- `BEDROOM_TYPE_MASTER`: The principal bedroom.\n- `BEDROOM_TYPE_QUEEN`: A queen bedroom.\n- `BEDROOM_TYPE_JUNIOR`: A junior bedroom.\n- `BEDROOM_TYPE_DORMITORY`: A shared dormitory. Typically hostels.\n- `BEDROOM_TYPE_BEDROOM_1`: Numbered bedrooms, for layouts that simply count them.",
        "enum" : [ "BEDROOM_TYPE_UNSPECIFIED", "BEDROOM_TYPE_MASTER", "BEDROOM_TYPE_QUEEN", "BEDROOM_TYPE_JUNIOR", "BEDROOM_TYPE_DORMITORY", "BEDROOM_TYPE_BEDROOM_1", "BEDROOM_TYPE_BEDROOM_2", "BEDROOM_TYPE_BEDROOM_3", "BEDROOM_TYPE_BEDROOM_4", "BEDROOM_TYPE_BEDROOM_5", "BEDROOM_TYPE_BEDROOM_6", "BEDROOM_TYPE_BEDROOM_7", "BEDROOM_TYPE_BEDROOM_8", "BEDROOM_TYPE_BEDROOM_9", "BEDROOM_TYPE_BEDROOM_10" ]
      },
      "wink.partner.v1.DestinationType" : {
        "type" : "string",
        "description" : "What kind of place a destination is.\n\n- `DESTINATION_TYPE_UNSPECIFIED`: Never sent by the server, and rejected on a request: pick a real type.",
        "enum" : [ "DESTINATION_TYPE_UNSPECIFIED", "DESTINATION_TYPE_CITY", "DESTINATION_TYPE_ISLAND", "DESTINATION_TYPE_PROPERTY", "DESTINATION_TYPE_SPA", "DESTINATION_TYPE_MEETING_ROOM", "DESTINATION_TYPE_RESTAURANT", "DESTINATION_TYPE_ATTRACTION", "DESTINATION_TYPE_ACTIVITY", "DESTINATION_TYPE_PLACE", "DESTINATION_TYPE_COUNTRY" ]
      },
      "wink.partner.v1.PricingType" : {
        "type" : "string",
        "description" : "How a non-room item is sold, and therefore how to multiply `AncillaryOffer.unit_price` up to a total.\n\nMirrors the platform's own pricing granularities one-for-one. Several combinations the names suggest\n(per person per stay, per adult per use) do not exist because the platform does not offer them.\n\n- `PRICING_TYPE_UNSPECIFIED`: Not set; ignored. Treat a row carrying this as sold per stay.\n- `PRICING_TYPE_PER_STAY`: Priced once for the entire stay.\n- `PRICING_TYPE_PER_DAY`: Priced per day.\n- `PRICING_TYPE_PER_NIGHT`: Priced per night.\n- `PRICING_TYPE_PER_USE`: Priced per use or visit.\n- `PRICING_TYPE_PER_HOUR`: Priced per hour.\n- `PRICING_TYPE_PER_PERSON`: Priced per person.\n- `PRICING_TYPE_PER_PERSON_PER_NIGHT`: Priced per person, per night.\n- `PRICING_TYPE_PER_PERSON_PER_HOUR`: Priced per person, per hour.\n- `PRICING_TYPE_PER_ADULT`: Priced per adult.\n- `PRICING_TYPE_PER_ADULT_PER_NIGHT`: Priced per adult, per night.\n- `PRICING_TYPE_PER_ADULT_PER_HOUR`: Priced per adult, per hour.\n- `PRICING_TYPE_PER_CHILD`: Priced per child.\n- `PRICING_TYPE_PER_CHILD_PER_NIGHT`: Priced per child, per night.\n- `PRICING_TYPE_PER_CHILD_PER_HOUR`: Priced per child, per hour.",
        "enum" : [ "PRICING_TYPE_UNSPECIFIED", "PRICING_TYPE_PER_STAY", "PRICING_TYPE_PER_DAY", "PRICING_TYPE_PER_NIGHT", "PRICING_TYPE_PER_USE", "PRICING_TYPE_PER_HOUR", "PRICING_TYPE_PER_PERSON", "PRICING_TYPE_PER_PERSON_PER_NIGHT", "PRICING_TYPE_PER_PERSON_PER_HOUR", "PRICING_TYPE_PER_ADULT", "PRICING_TYPE_PER_ADULT_PER_NIGHT", "PRICING_TYPE_PER_ADULT_PER_HOUR", "PRICING_TYPE_PER_CHILD", "PRICING_TYPE_PER_CHILD_PER_NIGHT", "PRICING_TYPE_PER_CHILD_PER_HOUR" ]
      },
      "wink.partner.v1.AccountOwnerType" : {
        "type" : "string",
        "description" : "Whether an account is held by a company or an individual.\n\n- `ACCOUNT_OWNER_TYPE_UNSPECIFIED`: Never sent by the server.\n- `ACCOUNT_OWNER_TYPE_COMPANY`: A corporate entity.\n- `ACCOUNT_OWNER_TYPE_INDIVIDUAL`: A sole proprietor.",
        "enum" : [ "ACCOUNT_OWNER_TYPE_UNSPECIFIED", "ACCOUNT_OWNER_TYPE_COMPANY", "ACCOUNT_OWNER_TYPE_INDIVIDUAL" ]
      },
      "wink.partner.v1.AccountType" : {
        "type" : "string",
        "description" : "What kind of business an account is.\n\n- `ACCOUNT_TYPE_UNSPECIFIED`: The account's type is not one this API publishes. Sent rather than omitting the account, so a new internal type can never make an account you manage disappear from your list.\n- `ACCOUNT_TYPE_PROPERTY`: A hotel, resort, vacation rental or other bookable property.\n- `ACCOUNT_TYPE_APPLICATION`: A software application integrating with Wink.\n- `ACCOUNT_TYPE_TRAVEL_AGENT`: A travel agency.\n- `ACCOUNT_TYPE_CORPORATE`: A corporate travel buyer.\n- `ACCOUNT_TYPE_INFLUENCER`: A content creator monetising travel recommendations.\n- `ACCOUNT_TYPE_BLOGGER`: A travel blogger.\n- `ACCOUNT_TYPE_DESTINATION`: A destination marketing organisation.\n- `ACCOUNT_TYPE_CHANNEL_MANAGER`: A channel manager distributing inventory on behalf of properties.\n- `ACCOUNT_TYPE_PROPERTY_MANAGEMENT_SYSTEM`: A property management system.\n- `ACCOUNT_TYPE_CENTRAL_RESERVATION_SYSTEM`: A central reservation system.\n- `ACCOUNT_TYPE_GOOGLE_HOTEL_API`: A Google Hotel API integration.\n- `ACCOUNT_TYPE_MANAGEMENT_COMPANY`: A company managing properties on their owners' behalf.\n- `ACCOUNT_TYPE_CHAIN`: A hotel chain.\n- `ACCOUNT_TYPE_BRAND`: A hotel brand.\n- `ACCOUNT_TYPE_EVENT_ORGANIZER`: An event organiser.\n- `ACCOUNT_TYPE_FACILITATOR`: A payment facilitator.\n- `ACCOUNT_TYPE_DIRECT`: A direct-booking account.\n- `ACCOUNT_TYPE_OTHER`: Anything that fits none of the above.",
        "enum" : [ "ACCOUNT_TYPE_UNSPECIFIED", "ACCOUNT_TYPE_PROPERTY", "ACCOUNT_TYPE_APPLICATION", "ACCOUNT_TYPE_TRAVEL_AGENT", "ACCOUNT_TYPE_CORPORATE", "ACCOUNT_TYPE_INFLUENCER", "ACCOUNT_TYPE_BLOGGER", "ACCOUNT_TYPE_DESTINATION", "ACCOUNT_TYPE_CHANNEL_MANAGER", "ACCOUNT_TYPE_PROPERTY_MANAGEMENT_SYSTEM", "ACCOUNT_TYPE_CENTRAL_RESERVATION_SYSTEM", "ACCOUNT_TYPE_GOOGLE_HOTEL_API", "ACCOUNT_TYPE_MANAGEMENT_COMPANY", "ACCOUNT_TYPE_CHAIN", "ACCOUNT_TYPE_BRAND", "ACCOUNT_TYPE_EVENT_ORGANIZER", "ACCOUNT_TYPE_FACILITATOR", "ACCOUNT_TYPE_DIRECT", "ACCOUNT_TYPE_OTHER" ]
      },
      "wink.partner.v1.AccountStatus" : {
        "type" : "string",
        "description" : "Where an account sits in its lifecycle.\n\n- `ACCOUNT_STATUS_UNSPECIFIED`: Never sent by the server, and ignored on a request: omit the field instead of setting this.\n- `ACCOUNT_STATUS_PENDING_APPROVAL`: Created and awaiting compliance review; cannot transact yet.\n- `ACCOUNT_STATUS_ACTIVE`: Fully onboarded and operating. The only status that transacts.\n- `ACCOUNT_STATUS_INACTIVE`: Deactivated; no longer visible or transacting.\n- `ACCOUNT_STATUS_UNDER_REVIEW`: Under compliance or quality review; some operations may be limited.\n- `ACCOUNT_STATUS_SUSPENDED`: Temporarily suspended for policy violations or abuse.\n- `ACCOUNT_STATUS_REJECTED`: Onboarding was rejected.\n- `ACCOUNT_STATUS_REMOVED`: Permanently removed from the platform.",
        "enum" : [ "ACCOUNT_STATUS_UNSPECIFIED", "ACCOUNT_STATUS_PENDING_APPROVAL", "ACCOUNT_STATUS_ACTIVE", "ACCOUNT_STATUS_INACTIVE", "ACCOUNT_STATUS_UNDER_REVIEW", "ACCOUNT_STATUS_SUSPENDED", "ACCOUNT_STATUS_REJECTED", "ACCOUNT_STATUS_REMOVED" ]
      }
    },
    "securitySchemes" : {
      "oauth2ClientCredentials" : {
        "type" : "oauth2",
        "description" : "Every endpoint is protected with OAuth2 **client credentials**. Create an\n**Application** in your account to obtain a `clientId` and `clientSecret`, then\nexchange them for a short-lived bearer token.\n\n### 1. Request an access token\n`POST` to the **Token URL** below with `Content-Type: application/x-www-form-urlencoded`\nand a body of `grant_type=client_credentials` (optionally `scope=<space-separated scopes>`).\nSend your credentials as HTTP Basic auth:\n`Authorization: Basic base64(clientId + \":\" + clientSecret)`.\n\n### 2. Call the API\nPass the returned token on every request: `Authorization: Bearer <access_token>`.\n\nTokens carry only the scopes granted to your Application — request the least privilege\nyou need. There is no interactive authorize step and no refresh token for this flow;\nrequest a new token when the current one expires.",
        "name" : "Client Credentials",
        "flows" : {
          "clientCredentials" : {
            "tokenUrl" : "https://iam.wink.travel/oauth2/token",
            "scopes" : {
              "inventory.read" : "View your inventory & rates.",
              "inventory.write" : "Create and update your inventory & rates.",
              "inventory.remove" : "Delete your inventory & rates."
            }
          }
        }
      }
    }
  }
}