openapi: 3.1.0
info:
  title: Hella Partner API
  version: 1.0.0-beta.1
  summary: Versioned, provider-neutral property discovery, availability and quote contract.
  description: |
    The Hella Partner API lets approved partners discover properties, check availability,
    request an indicative quote and hand the traveller off to Hella.

    Every operation requires a server-side partner credential. Browser CORS policy is not
    an authentication mechanism. Dates are calendar dates in the property's IANA timezone;
    a stay uses the half-open interval `[checkIn, checkOut)`, so check-out is not an occupied night.
    Monetary values are decimal strings in the response currency, never floating-point numbers.
  contact:
    name: Hella partner integration support
    url: https://www.hellastays.com/help#contact
  license:
    name: Proprietary
    identifier: LicenseRef-Hella-Proprietary
  x-api-status: beta
  x-last-updated: 2026-07-13
  x-changelog:
    - version: 1.0.0-beta.1
      date: 2026-07-13
      changes:
        - Introduced the provider-neutral Partner API v1 contract.
        - Standardised validation errors on RFC 9457 Problem Details.
        - Added server-generated handoff URLs with signed partner attribution.
servers:
  - url: https://www.app.hellastays.com/app/api
    description: Production
security:
  - PartnerBearer: []
x-max-stay-nights: 60
tags:
  - name: Properties
    description: Provider-neutral property catalogue and availability.
  - name: Quotes
    description: Indicative pricing and secure Hella handoff.
paths:
  /v1/properties:
    get:
      operationId: listPartnerProperties
      summary: Search partner-visible properties
      description: |
        Returns only partner-safe catalogue fields. Exact addresses and coordinates are not exposed.
        Without dates this operation is catalogue discovery. `checkIn` and `checkOut` must either
        both be omitted or both be supplied. With dates, `items` contains only properties available
        for `[checkIn, checkOut)` in each property's timezone and able to host `guests` using one
        eligible inventory type for the whole stay. Stays may contain at most 60 nights. Results are
        ordered by opaque `propertyId` before pagination. Unknown query parameters are rejected with
        `VALIDATION_ERROR`.
      tags:
        - Properties
      x-idempotency: idempotent
      x-cache-control: private, max-age=60
      x-required-scopes:
        - properties:read
      x-max-stay-nights: 60
      x-pagination-order:
        - propertyId
      x-error-codes:
        "400":
          - VALIDATION_ERROR
        "401":
          - PARTNER_AUTH_REQUIRED
          - INVALID_PARTNER_CREDENTIAL
          - PARTNER_ACCESS_REVOKED
        "403":
          - PARTNER_SCOPE_INSUFFICIENT
          - ORIGIN_NOT_ALLOWED
        "429":
          - RATE_LIMIT_EXCEEDED
        "500":
          - INTERNAL_ERROR
        "503":
          - PARTNER_API_UNAVAILABLE
      parameters:
        - $ref: "#/components/parameters/City"
        - $ref: "#/components/parameters/Country"
        - $ref: "#/components/parameters/CheckInOptional"
        - $ref: "#/components/parameters/CheckOutOptional"
        - $ref: "#/components/parameters/Guests"
        - $ref: "#/components/parameters/Page"
        - $ref: "#/components/parameters/PageSize"
        - $ref: "#/components/parameters/Locale"
      responses:
        "200":
          description: A page of matching partner-safe properties.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
            X-Correlation-ID:
              $ref: "#/components/headers/CorrelationId"
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerPropertyList"
              examples:
                roma:
                  $ref: "#/components/examples/PropertyListExample"
        "400":
          $ref: "#/components/responses/ValidationProblem"
        "401":
          $ref: "#/components/responses/AuthenticationProblem"
        "403":
          $ref: "#/components/responses/AuthorisationProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "500":
          $ref: "#/components/responses/InternalProblem"
        "503":
          $ref: "#/components/responses/PartnerApiUnavailableProblem"
  /v1/properties/{propertyId}:
    get:
      operationId: getPartnerProperty
      summary: Get a property
      description: |
        Returns the partner-safe detail for one opaque property identifier. Unknown query
        parameters are rejected with `VALIDATION_ERROR`.
      tags:
        - Properties
      x-idempotency: idempotent
      x-cache-control: private, max-age=60
      x-required-scopes:
        - properties:read
      x-error-codes:
        "400":
          - VALIDATION_ERROR
        "401":
          - PARTNER_AUTH_REQUIRED
          - INVALID_PARTNER_CREDENTIAL
          - PARTNER_ACCESS_REVOKED
        "403":
          - PARTNER_SCOPE_INSUFFICIENT
          - ORIGIN_NOT_ALLOWED
        "404":
          - PROPERTY_NOT_FOUND
        "429":
          - RATE_LIMIT_EXCEEDED
        "500":
          - INTERNAL_ERROR
        "503":
          - PARTNER_API_UNAVAILABLE
      parameters:
        - $ref: "#/components/parameters/PropertyId"
        - $ref: "#/components/parameters/Locale"
      responses:
        "200":
          description: Partner-safe property detail.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
            X-Correlation-ID:
              $ref: "#/components/headers/CorrelationId"
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerPropertyDetail"
              examples:
                property:
                  $ref: "#/components/examples/PropertyDetailExample"
        "400":
          $ref: "#/components/responses/ValidationProblem"
        "401":
          $ref: "#/components/responses/AuthenticationProblem"
        "403":
          $ref: "#/components/responses/AuthorisationProblem"
        "404":
          $ref: "#/components/responses/PropertyNotFoundProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "500":
          $ref: "#/components/responses/InternalProblem"
        "503":
          $ref: "#/components/responses/PartnerApiUnavailableProblem"
  /v1/properties/{propertyId}/availability:
    get:
      operationId: getPartnerPropertyAvailability
      summary: Check property availability
      description: |
        Checks every occupied local calendar night in `[checkIn, checkOut)` using the property's
        IANA timezone; check-out is exclusive. `available: true` guarantees that one eligible
        inventory type can host the requested guests for the entire interval, without combining
        capacity from different inventory types across nights. Stays may contain at most 60 nights.
        An unknown property returns 404, never an empty successful response. Unknown query parameters
        are rejected with `VALIDATION_ERROR`.
      tags:
        - Properties
      x-idempotency: idempotent
      x-cache-control: no-store
      x-required-scopes:
        - availability:read
      x-max-stay-nights: 60
      x-error-codes:
        "400":
          - VALIDATION_ERROR
        "401":
          - PARTNER_AUTH_REQUIRED
          - INVALID_PARTNER_CREDENTIAL
          - PARTNER_ACCESS_REVOKED
        "403":
          - PARTNER_SCOPE_INSUFFICIENT
          - ORIGIN_NOT_ALLOWED
        "404":
          - PROPERTY_NOT_FOUND
        "429":
          - RATE_LIMIT_EXCEEDED
        "500":
          - INTERNAL_ERROR
        "503":
          - PARTNER_API_UNAVAILABLE
      parameters:
        - $ref: "#/components/parameters/PropertyId"
        - $ref: "#/components/parameters/CheckIn"
        - $ref: "#/components/parameters/CheckOut"
        - $ref: "#/components/parameters/Guests"
      responses:
        "200":
          description: Availability for the requested stay.
          headers:
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
            X-Correlation-ID:
              $ref: "#/components/headers/CorrelationId"
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerAvailability"
              examples:
                available:
                  $ref: "#/components/examples/AvailabilityExample"
        "400":
          $ref: "#/components/responses/ValidationProblem"
        "401":
          $ref: "#/components/responses/AuthenticationProblem"
        "403":
          $ref: "#/components/responses/AuthorisationProblem"
        "404":
          $ref: "#/components/responses/PropertyNotFoundProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "500":
          $ref: "#/components/responses/InternalProblem"
        "503":
          $ref: "#/components/responses/PartnerApiUnavailableProblem"
  /v1/quotes:
    post:
      operationId: createPartnerQuote
      summary: Create an indicative quote
      description: |
        Creates an indicative quote for an available stay. The quoted price is not guaranteed and
        is not preserved during handoff; Hella revalidates availability and price before confirmation.
        The returned absolute `handoffUrl` contains a signed, opaque partner reference. Partners
        must use it unchanged and must not construct handoff links manually. Locale, stay, guests
        and attribution are bound inside the opaque `handoff` value rather than exposed as query fields.
        Unknown JSON fields are rejected. Malformed JSON returns `INVALID_JSON`; a body above the
        accepted request-size limit returns `PAYLOAD_TOO_LARGE`. Quote creation is not idempotent:
        a retry can create a new `quoteId`, and this version does not accept an idempotency key.
        The requested half-open stay may contain at most 60 local calendar nights.
      tags:
        - Quotes
      x-idempotency: non-idempotent
      x-cache-control: no-store
      x-required-scopes:
        - quotes:write
      x-max-stay-nights: 60
      x-error-codes:
        "400":
          - VALIDATION_ERROR
          - INVALID_JSON
        "401":
          - PARTNER_AUTH_REQUIRED
          - INVALID_PARTNER_CREDENTIAL
          - PARTNER_ACCESS_REVOKED
        "403":
          - PARTNER_SCOPE_INSUFFICIENT
          - ORIGIN_NOT_ALLOWED
        "404":
          - PROPERTY_NOT_FOUND
        "409":
          - STAY_UNAVAILABLE
          - QUOTE_UNAVAILABLE
        "413":
          - PAYLOAD_TOO_LARGE
        "429":
          - RATE_LIMIT_EXCEEDED
        "500":
          - INTERNAL_ERROR
        "503":
          - PARTNER_API_UNAVAILABLE
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreatePartnerQuoteRequest"
            examples:
              quote:
                $ref: "#/components/examples/CreateQuoteRequestExample"
      responses:
        "201":
          description: Indicative quote created.
          headers:
            Location:
              description: Absolute handoff URL for this quote.
              schema:
                type: string
                format: uri
            X-RateLimit-Limit:
              $ref: "#/components/headers/RateLimitLimit"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            X-RateLimit-Reset:
              $ref: "#/components/headers/RateLimitReset"
            X-Correlation-ID:
              $ref: "#/components/headers/CorrelationId"
            Cache-Control:
              $ref: "#/components/headers/CacheControl"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerQuote"
              examples:
                quote:
                  $ref: "#/components/examples/QuoteExample"
        "400":
          $ref: "#/components/responses/QuoteBadRequestProblem"
        "401":
          $ref: "#/components/responses/AuthenticationProblem"
        "403":
          $ref: "#/components/responses/AuthorisationProblem"
        "404":
          $ref: "#/components/responses/PropertyNotFoundProblem"
        "409":
          $ref: "#/components/responses/UnavailableProblem"
        "413":
          $ref: "#/components/responses/PayloadTooLargeProblem"
        "429":
          $ref: "#/components/responses/RateLimitProblem"
        "500":
          $ref: "#/components/responses/InternalProblem"
        "503":
          $ref: "#/components/responses/PartnerApiUnavailableProblem"
components:
  securitySchemes:
    PartnerBearer:
      type: http
      scheme: bearer
      bearerFormat: opaque partner token
      description: |
        Revocable partner credential for server-to-server use. Never expose it in browser code.
  parameters:
    PropertyId:
      name: propertyId
      in: path
      required: true
      description: Opaque Hella property identifier. Do not parse or derive meaning from it.
      schema:
        $ref: "#/components/schemas/PropertyId"
    City:
      name: city
      in: query
      required: true
      description: Destination city name.
      schema:
        type: string
        minLength: 1
        maxLength: 120
      example: Roma
    Country:
      name: country
      in: query
      required: false
      description: ISO 3166-1 alpha-2 country code.
      schema:
        type: string
        pattern: ^[A-Z]{2}$
      example: IT
    CheckIn:
      name: checkIn
      in: query
      required: true
      description: First occupied local calendar date.
      schema:
        $ref: "#/components/schemas/LocalDate"
      example: 2027-05-12
    CheckOut:
      name: checkOut
      in: query
      required: true
      description: Exclusive local-calendar end of the stay; must be after check-in and no more than 60 nights later.
      schema:
        $ref: "#/components/schemas/LocalDate"
      example: 2027-05-15
    CheckInOptional:
      name: checkIn
      in: query
      required: false
      description: First occupied local calendar date. Must be supplied together with checkOut.
      schema:
        $ref: "#/components/schemas/LocalDate"
      example: 2027-05-12
    CheckOutOptional:
      name: checkOut
      in: query
      required: false
      description: Exclusive local-calendar end date. Must be supplied with checkIn, later than it and no more than 60 nights later.
      schema:
        $ref: "#/components/schemas/LocalDate"
      example: 2027-05-15
    Guests:
      name: guests
      in: query
      required: false
      description: Total number of travellers.
      schema:
        type: integer
        minimum: 1
        maximum: 30
        default: 2
    Page:
      name: page
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1
    PageSize:
      name: pageSize
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 50
        default: 20
    Locale:
      name: locale
      in: query
      required: false
      description: BCP 47 response locale.
      schema:
        type: string
        enum:
          - it-IT
          - en-GB
        default: it-IT
  headers:
    RateLimitLimit:
      description: Maximum requests available to this partner credential in the current 60-second window.
      schema:
        type: integer
        example: 120
    RateLimitRemaining:
      description: Requests remaining in the current window.
      schema:
        type: integer
        minimum: 0
        example: 119
    RateLimitReset:
      description: Unix timestamp in seconds when the current window resets.
      schema:
        type: integer
        format: int64
        example: 1810000060
    CorrelationId:
      description: Opaque request identifier for support and tracing.
      schema:
        type: string
        example: req_01JEXAMPLE00000000000000
    CacheControl:
      description: Cache policy for this response. Catalogue responses are private for 60 seconds; availability and quotes are not stored.
      schema:
        type: string
    RetryAfter:
      description: Seconds to wait before retrying the request.
      schema:
        type: integer
        minimum: 1
        example: 42
  responses:
    ValidationProblem:
      description: Request validation failed.
      headers:
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ValidationProblem"
          examples:
            invalidDate:
              value:
                type: https://www.hellastays.com/developers/problems/validation-error
                title: Invalid request
                status: 400
                detail: One or more request fields are invalid.
                code: VALIDATION_ERROR
                correlationId: req_01JEXAMPLE00000000000000
                errors:
                  - field: checkOut
                    code: DATE_ORDER_INVALID
                    detail: checkOut must be after checkIn.
    QuoteBadRequestProblem:
      description: The JSON body is malformed or one or more request fields are invalid.
      headers:
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            oneOf:
              - $ref: "#/components/schemas/ValidationProblem"
              - $ref: "#/components/schemas/Problem"
          examples:
            invalidFields:
              value:
                type: https://www.hellastays.com/developers/problems/validation-error
                title: Invalid request
                status: 400
                detail: One or more request fields are invalid.
                code: VALIDATION_ERROR
                correlationId: req_01JEXAMPLE00000000000000
                errors:
                  - field: stay.checkOut
                    code: DATE_ORDER_INVALID
                    detail: checkOut must be after checkIn.
            invalidJson:
              value:
                type: https://www.hellastays.com/developers/problems/invalid-json
                title: Invalid JSON
                status: 400
                detail: The request body is not valid JSON.
                code: INVALID_JSON
                correlationId: req_01JEXAMPLE00000000000000
    AuthenticationProblem:
      description: Partner credential is missing, invalid or revoked.
      headers:
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          examples:
            missingCredential:
              value:
                type: https://www.hellastays.com/developers/problems/partner-auth-required
                title: Authentication required
                status: 401
                detail: Supply a valid partner credential.
                code: PARTNER_AUTH_REQUIRED
                correlationId: req_01JEXAMPLE00000000000000
            invalidCredential:
              value:
                type: https://www.hellastays.com/developers/problems/invalid-partner-credential
                title: Invalid credential
                status: 401
                detail: The supplied Partner API credential is not valid.
                code: INVALID_PARTNER_CREDENTIAL
                correlationId: req_01JEXAMPLE00000000000000
            revokedCredential:
              value:
                type: https://www.hellastays.com/developers/problems/partner-access-revoked
                title: Partner access revoked
                status: 401
                detail: This Partner API credential has been revoked.
                code: PARTNER_ACCESS_REVOKED
                correlationId: req_01JEXAMPLE00000000000000
    AuthorisationProblem:
      description: The credential lacks the required scope or the browser origin is not authorized.
      headers:
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          examples:
            insufficientScope:
              value:
                type: https://www.hellastays.com/developers/problems/partner-scope-insufficient
                title: Access denied
                status: 403
                detail: This partner credential cannot perform the requested operation.
                code: PARTNER_SCOPE_INSUFFICIENT
                correlationId: req_01JEXAMPLE00000000000000
            originNotAllowed:
              value:
                type: https://www.hellastays.com/developers/problems/origin-not-allowed
                title: Origin not allowed
                status: 403
                detail: This browser origin is not authorized for the Partner API.
                code: ORIGIN_NOT_ALLOWED
                correlationId: req_01JEXAMPLE00000000000000
    PropertyNotFoundProblem:
      description: Property identifier is unknown or unavailable to this partner.
      headers:
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://www.hellastays.com/developers/problems/property-not-found
            title: Property not found
            status: 404
            detail: The requested property is not available.
            code: PROPERTY_NOT_FOUND
            correlationId: req_01JEXAMPLE00000000000000
    UnavailableProblem:
      description: The requested stay is no longer available.
      headers:
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          examples:
            stayUnavailable:
              value:
                type: https://www.hellastays.com/developers/problems/stay-unavailable
                title: Stay unavailable
                status: 409
                detail: The requested stay is not currently available.
                code: STAY_UNAVAILABLE
                correlationId: req_01JEXAMPLE00000000000000
            quoteUnavailable:
              value:
                type: https://www.hellastays.com/developers/problems/quote-unavailable
                title: Quote unavailable
                status: 409
                detail: An indicative quote cannot be created for this property.
                code: QUOTE_UNAVAILABLE
                correlationId: req_01JEXAMPLE00000000000000
    RateLimitProblem:
      description: The 300/minute source-IP ingress limit or the authenticated partner-credential quota was exceeded. Retry-After is authoritative; X-RateLimit-* headers, when present, describe the credential quota.
      headers:
        Retry-After:
          $ref: "#/components/headers/RetryAfter"
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://www.hellastays.com/developers/problems/rate-limit-exceeded
            title: Too many requests
            status: 429
            detail: Retry after the interval indicated by Retry-After.
            code: RATE_LIMIT_EXCEEDED
            correlationId: req_01JEXAMPLE00000000000000
    PayloadTooLargeProblem:
      description: The JSON request body exceeds the accepted request-size limit.
      headers:
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://www.hellastays.com/developers/problems/payload-too-large
            title: Payload too large
            status: 413
            detail: The request body exceeds the accepted size.
            code: PAYLOAD_TOO_LARGE
            correlationId: req_01JEXAMPLE00000000000000
    InternalProblem:
      description: An unexpected error occurred. The response never exposes internal details.
      headers:
        X-RateLimit-Limit:
          $ref: "#/components/headers/RateLimitLimit"
        X-RateLimit-Remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        X-RateLimit-Reset:
          $ref: "#/components/headers/RateLimitReset"
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://www.hellastays.com/developers/problems/internal-error
            title: Internal server error
            status: 500
            detail: The request could not be completed.
            code: INTERNAL_ERROR
            correlationId: req_01JEXAMPLE00000000000000
    PartnerApiUnavailableProblem:
      description: The Partner API is temporarily unavailable.
      headers:
        X-Correlation-ID:
          $ref: "#/components/headers/CorrelationId"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
          example:
            type: https://www.hellastays.com/developers/problems/partner-api-unavailable
            title: Partner API unavailable
            status: 503
            detail: The Partner API is temporarily unavailable.
            code: PARTNER_API_UNAVAILABLE
            correlationId: req_01JEXAMPLE00000000000000
  schemas:
    PropertyId:
      type: string
      minLength: 1
      maxLength: 160
      pattern: ^[A-Za-z0-9._~-]+$
      example: sandbox-roma-001
    LocalDate:
      type: string
      format: date
      pattern: ^\d{4}-\d{2}-\d{2}$
      description: A real Gregorian calendar date in YYYY-MM-DD format.
    DecimalMoney:
      type: string
      pattern: ^(0|[1-9]\d*)\.\d{2}$
      description: Non-negative decimal amount with exactly two fractional digits.
      example: "615.00"
    Currency:
      type: string
      pattern: ^[A-Z]{3}$
      description: ISO 4217 currency code.
      example: EUR
    PartnerLocation:
      type: object
      additionalProperties: false
      required:
        - city
        - country
        - area
      properties:
        city:
          type: string
          example: Roma
        country:
          type: string
          pattern: ^[A-Z]{2}$
          example: IT
        area:
          type:
            - string
            - "null"
          description: Approximate neighbourhood or area; never an exact address.
          example: Centro storico
    PartnerCapacity:
      type: object
      additionalProperties: false
      required:
        - guests
        - bathrooms
      properties:
        guests:
          type: integer
          minimum: 1
          example: 4
        bathrooms:
          type: number
          minimum: 0
          multipleOf: 0.5
          example: 1
    PartnerRating:
      type: object
      additionalProperties: false
      required:
        - score
        - count
      properties:
        score:
          type: number
          minimum: 0
          maximum: 5
          example: 4.8
        count:
          type: integer
          minimum: 0
          example: 24
    PartnerPropertySummary:
      type: object
      additionalProperties: false
      required:
        - propertyId
        - name
        - description
        - propertyType
        - location
        - capacity
        - amenities
        - rating
        - startingPrice
        - currency
        - timezone
      properties:
        propertyId:
          $ref: "#/components/schemas/PropertyId"
        name:
          type: string
          example: Casa Demo Roma
        description:
          type:
            - string
            - "null"
          example: Appartamento sintetico usato esclusivamente dal mock locale.
        propertyType:
          type: string
          example: apartment
        location:
          $ref: "#/components/schemas/PartnerLocation"
        capacity:
          $ref: "#/components/schemas/PartnerCapacity"
        amenities:
          type: array
          items:
            type: string
          example:
            - wifi
            - air-conditioning
        rating:
          oneOf:
            - $ref: "#/components/schemas/PartnerRating"
            - type: "null"
        startingPrice:
          oneOf:
            - $ref: "#/components/schemas/DecimalMoney"
            - type: "null"
        currency:
          $ref: "#/components/schemas/Currency"
        timezone:
          type: string
          description: IANA timezone used for calendar-date semantics.
          example: Europe/Rome
    PartnerPropertyDetail:
      allOf:
        - $ref: "#/components/schemas/PartnerPropertySummary"
    Pagination:
      type: object
      additionalProperties: false
      required:
        - page
        - pageSize
        - total
        - hasMore
      properties:
        page:
          type: integer
          minimum: 1
          example: 1
        pageSize:
          type: integer
          minimum: 1
          maximum: 50
          example: 20
        total:
          type: integer
          minimum: 0
          example: 1
        hasMore:
          type: boolean
          example: false
    PartnerPropertyList:
      type: object
      additionalProperties: false
      required:
        - items
        - pagination
      properties:
        items:
          type: array
          description: Properties ordered deterministically by opaque propertyId before pagination.
          items:
            $ref: "#/components/schemas/PartnerPropertySummary"
        pagination:
          $ref: "#/components/schemas/Pagination"
    Stay:
      type: object
      x-max-stay-nights: 60
      additionalProperties: false
      required:
        - checkIn
        - checkOut
        - nights
        - guests
        - timezone
      properties:
        checkIn:
          $ref: "#/components/schemas/LocalDate"
        checkOut:
          $ref: "#/components/schemas/LocalDate"
        nights:
          type: integer
          minimum: 1
          maximum: 60
          description: Number of occupied local calendar nights in the half-open stay interval.
          example: 3
        guests:
          type: integer
          minimum: 1
          maximum: 30
          description: Number of travellers used for the availability or quote decision.
          example: 2
        timezone:
          type: string
          description: IANA timezone defining the local-calendar interpretation of checkIn and checkOut.
          example: Europe/Rome
    AvailabilityDay:
      type: object
      additionalProperties: false
      required:
        - date
        - available
      properties:
        date:
          $ref: "#/components/schemas/LocalDate"
        available:
          type: boolean
          description: Whether at least one eligible inventory type is available on this local calendar night. Do not combine day flags to infer whole-stay availability.
    PartnerAvailability:
      type: object
      additionalProperties: false
      required:
        - propertyId
        - stay
        - available
        - days
      properties:
        propertyId:
          $ref: "#/components/schemas/PropertyId"
        stay:
          $ref: "#/components/schemas/Stay"
        available:
          type: boolean
          description: True only when one eligible inventory type can host the requested guests for every night in the interval without switching types.
        days:
          type: array
          description: One diagnostic entry per occupied local calendar night in [checkIn, checkOut), interpreted in stay.timezone; check-out is excluded.
          items:
            $ref: "#/components/schemas/AvailabilityDay"
    QuoteStayRequest:
      type: object
      x-max-stay-nights: 60
      description: Half-open local-calendar stay in the property's IANA timezone, with at most 60 occupied nights.
      additionalProperties: false
      required:
        - checkIn
        - checkOut
        - guests
      properties:
        checkIn:
          $ref: "#/components/schemas/LocalDate"
        checkOut:
          $ref: "#/components/schemas/LocalDate"
        guests:
          type: integer
          minimum: 1
          maximum: 30
          example: 2
    CreatePartnerQuoteRequest:
      type: object
      additionalProperties: false
      required:
        - propertyId
        - stay
        - locale
      properties:
        propertyId:
          $ref: "#/components/schemas/PropertyId"
        stay:
          $ref: "#/components/schemas/QuoteStayRequest"
        locale:
          type: string
          enum:
            - it-IT
            - en-GB
          example: it-IT
    PartnerPricing:
      type: object
      additionalProperties: false
      required:
        - mode
        - accommodation
        - cleaningFee
        - taxes
        - total
        - pricePreservedInHandoff
      properties:
        mode:
          type: string
          const: indicative
          description: Quotes are indicative and revalidated by Hella.
        accommodation:
          $ref: "#/components/schemas/DecimalMoney"
        cleaningFee:
          $ref: "#/components/schemas/DecimalMoney"
        taxes:
          $ref: "#/components/schemas/DecimalMoney"
        total:
          $ref: "#/components/schemas/DecimalMoney"
        pricePreservedInHandoff:
          type: boolean
          const: false
          description: The price is not preserved and is recalculated during handoff.
    PartnerQuote:
      type: object
      additionalProperties: false
      required:
        - quoteId
        - propertyId
        - stay
        - pricing
        - currency
        - expiresAt
        - handoffUrl
      properties:
        quoteId:
          type: string
          description: Opaque quote identifier.
          example: quote_sandbox_01
        propertyId:
          $ref: "#/components/schemas/PropertyId"
        stay:
          $ref: "#/components/schemas/Stay"
        pricing:
          $ref: "#/components/schemas/PartnerPricing"
        currency:
          $ref: "#/components/schemas/Currency"
        expiresAt:
          type: string
          format: date-time
          description: Expiry instant in UTC. Expiry does not make an indicative price guaranteed.
          example: 2027-05-01T12:15:00.000Z
        handoffUrl:
          type: string
          format: uri
          pattern: ^https://
          description: Absolute Hella URL containing one opaque signed handoff value. Use unchanged.
          example: https://www.app.hellastays.com/app/property/sandbox-roma-001?handoff=mock_signed_reference
    ProblemCode:
      type: string
      description: Stable machine-readable application error code. Branch on this value, never on prose.
      enum:
        - VALIDATION_ERROR
        - INVALID_JSON
        - PAYLOAD_TOO_LARGE
        - PARTNER_AUTH_REQUIRED
        - INVALID_PARTNER_CREDENTIAL
        - PARTNER_ACCESS_REVOKED
        - PARTNER_SCOPE_INSUFFICIENT
        - ORIGIN_NOT_ALLOWED
        - PROPERTY_NOT_FOUND
        - STAY_UNAVAILABLE
        - QUOTE_UNAVAILABLE
        - RATE_LIMIT_EXCEEDED
        - PARTNER_API_UNAVAILABLE
        - PARTNER_OPERATION_NOT_FOUND
        - HANDOFF_INVALID
        - INTERNAL_ERROR
    Problem:
      type: object
      additionalProperties: false
      required:
        - type
        - title
        - status
        - detail
        - code
        - correlationId
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
          minimum: 400
          maximum: 599
        detail:
          type: string
          description: Human-readable context; clients must branch on code, not this text.
        code:
          $ref: "#/components/schemas/ProblemCode"
        correlationId:
          type: string
          description: Opaque support identifier.
    ValidationIssue:
      type: object
      additionalProperties: false
      required:
        - field
        - code
        - detail
      properties:
        field:
          type: string
        code:
          type: string
          pattern: ^[A-Z][A-Z0-9_]+$
        detail:
          type: string
    ValidationProblem:
      type: object
      additionalProperties: false
      required:
        - type
        - title
        - status
        - detail
        - code
        - correlationId
        - errors
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
          const: 400
        detail:
          type: string
          description: Human-readable context; clients must branch on code, not this text.
        code:
          $ref: "#/components/schemas/ProblemCode"
          const: VALIDATION_ERROR
        correlationId:
          type: string
          description: Opaque support identifier.
        errors:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/ValidationIssue"
  examples:
    PropertyListExample:
      value:
        items:
          - &a1
            propertyId: sandbox-roma-001
            name: Casa Demo Roma
            description: Appartamento sintetico usato esclusivamente dal mock locale.
            propertyType: apartment
            location:
              city: Roma
              country: IT
              area: Centro
            capacity:
              guests: 4
              bathrooms: 1
            amenities:
              - wifi
              - air-conditioning
            rating:
              score: 4.8
              count: 24
            startingPrice: "205.00"
            currency: EUR
            timezone: Europe/Rome
        pagination:
          page: 1
          pageSize: 20
          total: 1
          hasMore: false
    PropertyDetailExample:
      value: *a1
    AvailabilityExample:
      value:
        propertyId: sandbox-roma-001
        stay:
          checkIn: 2027-05-12
          checkOut: 2027-05-15
          nights: 3
          guests: 2
          timezone: Europe/Rome
        available: true
        days:
          - date: 2027-05-12
            available: true
          - date: 2027-05-13
            available: true
          - date: 2027-05-14
            available: true
    CreateQuoteRequestExample:
      value:
        propertyId: sandbox-roma-001
        stay:
          checkIn: 2027-05-12
          checkOut: 2027-05-15
          guests: 2
        locale: it-IT
    QuoteExample:
      value:
        quoteId: quote_sandbox_01
        propertyId: sandbox-roma-001
        stay:
          checkIn: 2027-05-12
          checkOut: 2027-05-15
          nights: 3
          guests: 2
          timezone: Europe/Rome
        pricing:
          mode: indicative
          accommodation: "615.00"
          cleaningFee: "0.00"
          taxes: "0.00"
          total: "615.00"
          pricePreservedInHandoff: false
        currency: EUR
        expiresAt: 2027-05-01T12:15:00.000Z
        handoffUrl: https://www.app.hellastays.com/app/property/sandbox-roma-001?handoff=mock_signed_reference
x-rate-limit-policy:
  scope: partner credential
  defaultLimit: 120
  windowSeconds: 60
  authoritativeHeaders:
    - X-RateLimit-Limit
    - X-RateLimit-Remaining
    - X-RateLimit-Reset
  ingress:
    scope: source IP
    limit: 300
    windowSeconds: 60
    description: Safety limit applied before partner credential authentication; Retry-After remains authoritative when this limit returns 429.
  corsExposedHeaders:
    - X-Correlation-ID
    - X-RateLimit-Limit
    - X-RateLimit-Remaining
    - X-RateLimit-Reset
    - Retry-After
    - Location
