> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dev.gojinko.com/llms.txt
> Use this file to discover all available pages before exploring further.

# hotel-cancel-commit

> Cancel a hotel booking against a quote the customer accepted

Start the cancellation the preview described. The body is the `quote` handle and the `acknowledged` figure PLUS the same auth mode the preview took, booking\_ref + last\_name, or provider\_reference: ownership is re-checked here rather than carried by the handle, so the mode has to be sent again. `acknowledged` is the refund the customer was shown, copied from the preview as it arrived (`value` and `decimal_places` included): if the refund has moved since the preview, the call is refused with 409 `quote_drift`, carrying a fresh `requote` handle and the current figure, and nothing is cancelled, so nobody is cancelled into a number they never saw. The other refusals are 409 `quote_expired` (run hotel\_cancel\_preview again), `active_operation_exists` (poll the `active_operation` it names instead of starting a second one), `not_cancellable` and `funds_unavailable`. Success does NOT mean the money has moved: cancelling at the supplier and refunding the customer are separate steps, so the answer is an `operation` handle to poll with POST /v1/hotel\_cancel\_status.


## OpenAPI

````yaml api-reference/public-api.yaml POST /v1/hotel_cancel_commit
openapi: 3.0.0
info:
  title: Jinko Public API
  version: 0.5.0
  description: >-
    Curated public REST surface for Jinko. Authenticated with jnk_ API keys. See
    https://docs.gojinko.com for guides.


    ### Per-end-user attribution


    On booking calls you may send an optional `X-End-User-Id` request header to
    attribute the booking to one of your own end users (for per-end-user
    attribution and rate-limiting). The value is an **opaque, tenant-scoped**
    identifier that you choose — not a Jinko account id. Omit it to book as the
    tenant. WorkOS-shaped values (prefixed `user_` or `org_`) are rejected.
servers: []
security: []
paths:
  /v1/hotel_cancel_commit:
    post:
      tags:
        - Post-booking
      summary: Cancel a hotel booking against a quote the customer accepted
      description: >-
        Start the cancellation the preview described. The body is the `quote`
        handle and the `acknowledged` figure PLUS the same auth mode the preview
        took — booking_ref + last_name, or provider_reference: ownership is
        re-checked here rather than carried by the handle, so the mode has to be
        sent again. `acknowledged` is the refund the customer was shown, copied
        from the preview as it arrived (`value` and `decimal_places` included):
        if the refund has moved since the preview, the call is refused with 409
        `quote_drift`, carrying a fresh `requote` handle and the current figure,
        and nothing is cancelled — so nobody is cancelled into a number they
        never saw. The other refusals are 409 `quote_expired` (run
        hotel_cancel_preview again), `active_operation_exists` (poll the
        `active_operation` it names instead of starting a second one),
        `not_cancellable` and `funds_unavailable`. Success does NOT mean the
        money has moved: cancelling at the supplier and refunding the customer
        are separate steps, so the answer is an `operation` handle to poll with
        POST /v1/hotel_cancel_status.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HotelCancelCommitRequest'
      responses:
        '200':
          description: The cancellation was accepted and is running
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HotelCancelCommitResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: BAD_REQUEST
                  message: Malformed JSON in request body.
                  doc_url: https://docs.gojinko.com/concepts/errors
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: AUTH_REQUIRED
                  message: Invalid or expired API key.
                  doc_url: https://docs.gojinko.com/api-reference/authentication
        '402':
          description: Payment required — organization balance exhausted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: PAYMENT_REQUIRED
                  message: >-
                    Insufficient balance — this call costs $0.0150 and your
                    organization has $0.0000 available. Top up at
                    https://dashboard.gojinko.com/developers/billing/topup
                  doc_url: https://docs.gojinko.com/concepts/errors
        '404':
          description: Unknown booking, or one this credential may not read.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: NOT_FOUND
                  message: booking not found
        '409':
          description: >-
            The commit cannot run as quoted; nothing was cancelled. `code` says
            which case, as a sibling of `error` — see `ServicingConflictCode`
            for the closed list and what each one asks of you. A 409 whose
            `code` is outside that list is NOT a servicing refusal: it arrives
            as the plain error envelope, with no `code` beside it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServicingConflictResponse'
              example:
                error:
                  code: CONFLICT
                  message: >-
                    The refund changed since the quote you acknowledged; nothing
                    was cancelled. Quote again with `requote` and commit against
                    the new figure.
                code: quote_drift
                requote: svq_01J8AB4N7GLY3Q0D
                current:
                  refund:
                    value: 20500
                    currency: USD
                    decimal_places: 2
        '422':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: BAD_REQUEST
                  message: >-
                    origins: origins is required; trip_type: trip_type is
                    required
                  doc_url: https://docs.gojinko.com/concepts/errors
        '429':
          description: Rate limit or quota exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: RATE_LIMITED
                  message: Rate limit or quota exceeded.
                  doc_url: https://docs.gojinko.com/concepts/errors
        '502':
          description: The travel provider rejected the request, or an upstream call failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: UPSTREAM_REJECTED
                  message: >-
                    sabre-rest BargainFinderMaxRQ failed with status 400: 27131
                    - Number of connection locations exceeds maximum allowed
                  doc_url: https://docs.gojinko.com/concepts/errors
        '503':
          description: No travel provider can serve the request right now
          headers:
            Retry-After:
              description: >-
                Seconds to wait before retrying, forwarded verbatim from the
                upstream service. Absent when the upstream named no interval.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: UPSTREAM_UNAVAILABLE
                  message: >-
                    All flight providers are temporarily unable to serve this
                    search. Please retry later. Provider reasons: sabre-rest:
                    provider temporarily closed; travelfusion: quota exhausted
                  doc_url: https://docs.gojinko.com/concepts/errors
        '504':
          description: The travel provider did not answer in time
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: UPSTREAM_TIMEOUT
                  message: sabre-rest BargainFinderMaxRQ timed out after 30s
                  doc_url: https://docs.gojinko.com/concepts/errors
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
components:
  schemas:
    HotelCancelCommitRequest:
      type: object
      properties:
        provider_reference:
          type: string
          description: >-
            The supplier's own booking reference. OWNER mode: beyond the API
            authentication every call needs, this one requires a credential that
            OWNS the booking — a tenant-bound key reaches its whole tenant, any
            other credential must belong to the booking's own user. Mutually
            exclusive with booking_ref + last_name; sending both is rejected
            with 422. An unknown reference, another tenant's booking and another
            user's booking are all answered identically, so this field cannot be
            used to discover which supplier references exist.
          example: NUITEE-88231
        booking_ref:
          type: string
          description: >-
            The Jinko reference. GUEST mode: pair it with last_name and the pair
            identifies the booking on its own — API authentication is still
            required, as on every call here, but the credential does not have to
            own the booking. Mutually exclusive with provider_reference; one
            without the other is rejected with 422.
          example: JNK-H1ZK90
        last_name:
          type: string
          description: >-
            The lead traveller's surname. Required with booking_ref, and only
            with it.
          example: Carrard
        intent:
          $ref: '#/components/schemas/IntentInput'
        quote:
          type: string
          description: >-
            The "svq_…" handle from a preceding POST /v1/hotel_cancel_preview.
            Binds this commit to the figures that quote reported.
          example: svq_01J7ZR3M8FKX2P9C
        acknowledged:
          type: object
          properties:
            value:
              type: integer
              description: >-
                Integer amount in MINOR units, always paired with decimal_places
                — divide by 10 ** decimal_places to display. Example: value
                15977 with decimal_places 2 is 159.77 USD. Rendering this field
                directly shows prices 100x too high for 2-decimal currencies.
              example: 25000
            currency:
              type: string
              minLength: 1
              description: ISO 4217 currency code.
              example: USD
            decimal_places:
              type: integer
              description: >-
                Scale of the integer value/amount: display = integer / 10 **
                decimal_places. Always sent alongside minor-unit amounts.
                Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP,
                0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices
                can carry a different scale — ALWAYS use the decimal_places sent
                with the amount, never a hardcoded 2. Only if the field is
                genuinely absent on a value-shaped object, fall back to the ISO
                digits for the currency.
              example: 2
          required:
            - value
            - currency
            - decimal_places
          description: >-
            The refund the customer was shown — copy the preview's `refund`
            object across. All three of `value` (minor units), `currency` and
            `decimal_places` are REQUIRED: the platform compares this against
            the refund as it stands, and a figure whose scale is unstated cannot
            be compared. `amount` is not accepted in its place. Anything else on
            the preview's `refund`, such as `basis`, is ignored. A mismatch
            against the current refund is answered 409 `quote_drift`, carrying a
            fresh `requote` handle and the current figure, and nothing is
            cancelled.
      required:
        - quote
        - acknowledged
      example:
        booking_ref: JNK-H1ZK90
        last_name: Carrard
        quote: svq_01J7ZR3M8FKX2P9C
        acknowledged:
          value: 25000
          currency: USD
          decimal_places: 2
    HotelCancelCommitResponse:
      type: object
      properties:
        operation:
          type: string
          description: >-
            The cancellation this call started ("svc_…"). Poll it with POST
            /v1/hotel_cancel_status; it is the only handle that reports how the
            supplier call and the refund ended.
          example: svc_01J7ZR5Q2KME8V4T
        state:
          $ref: '#/components/schemas/ServicingState'
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - AUTH_REQUIRED
                - PAYMENT_REQUIRED
                - RATE_LIMITED
                - BAD_REQUEST
                - FORBIDDEN
                - NOT_FOUND
                - CONFLICT
                - PRICE_CHANGED
                - QUOTE_EXPIRED
                - TRIP_EXPIRED
                - TRIP_STATE_CONFLICT
                - OFFER_EXPIRED
                - OFFER_UNAVAILABLE
                - MISSING_CUSTOMER_DETAILS
                - CURRENCY_UNSUPPORTED
                - UPSTREAM_REJECTED
                - UPSTREAM_UNAVAILABLE
                - UPSTREAM_TIMEOUT
                - UPSTREAM_ERROR
                - INTERNAL
              description: >-
                What went wrong, as a stable machine-readable code. This is a
                closed set — branch on it rather than on `message`, which is
                prose and may change. Codes are only ever ADDED, in a minor
                version, so treat an unknown one as its HTTP status.
              example: BAD_REQUEST
            message:
              type: string
            doc_url:
              type: string
          required:
            - code
            - message
      required:
        - error
    ServicingConflictResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: CONFLICT
            message:
              type: string
            doc_url:
              type: string
          required:
            - code
            - message
        code:
          $ref: '#/components/schemas/ServicingConflictCode'
        requote:
          type: string
          description: >-
            A fresh quote handle, on the refusals that give you one
            (`quote_drift`, `superseded`). Show the customer the figure it
            reports, then commit against it.
          example: svq_01J8AB4N7GLY3Q0D
        current:
          type: object
          properties:
            refund:
              type: object
              properties:
                amount:
                  type: number
                  description: >-
                    Alternative to `value` on some endpoints (the two never
                    appear together); its scale depends on decimal_places. When
                    this object carries decimal_places, amount is an INTEGER in
                    minor units — divide by 10 ** decimal_places (e.g.
                    select_ancillaries total_with_ancillaries). When there is no
                    decimal_places field, amount is a decimal in MAJOR units,
                    safe to display as-is (e.g. trip and checkout totals).
                value:
                  type: integer
                  description: >-
                    Integer amount in MINOR units, always paired with
                    decimal_places — divide by 10 ** decimal_places to display.
                    Example: value 15977 with decimal_places 2 is 159.77 USD.
                    Rendering this field directly shows prices 100x too high for
                    2-decimal currencies.
                  example: 41250
                currency:
                  type: string
                  description: ISO 4217 currency code.
                  example: USD
                decimal_places:
                  type: integer
                  description: >-
                    Scale of the integer value/amount: display = integer / 10 **
                    decimal_places. Always sent alongside minor-unit amounts.
                    Usually the ISO 4217 digits of the currency (2 for
                    USD/EUR/GBP, 0 for JPY/KRW, 3 for BHD/JOD/KWD) but
                    currency-converted prices can carry a different scale —
                    ALWAYS use the decimal_places sent with the amount, never a
                    hardcoded 2. Only if the field is genuinely absent on a
                    value-shaped object, fall back to the ISO digits for the
                    currency.
                  example: 2
          description: >-
            The customer refund as it stands now, on `quote_drift`. This is what
            the customer would get if they accepted the new quote.
        active_operation:
          type: string
          description: >-
            The cancellation already running, on `active_operation_exists`. Poll
            it with POST /v1/hotel_cancel_status.
          example: svc_01J7ZR5Q2KME8V4T
        shortfall:
          type: object
          properties:
            value:
              type: integer
              description: >-
                Integer amount in MINOR units, always paired with decimal_places
                — divide by 10 ** decimal_places to display. Example: value
                15977 with decimal_places 2 is 159.77 USD. Rendering this field
                directly shows prices 100x too high for 2-decimal currencies.
              example: 41250
            amount:
              type: number
              description: >-
                Alternative to `value` on some endpoints (the two never appear
                together); its scale depends on decimal_places. When this object
                carries decimal_places, amount is an INTEGER in minor units —
                divide by 10 ** decimal_places (e.g. select_ancillaries
                total_with_ancillaries). When there is no decimal_places field,
                amount is a decimal in MAJOR units, safe to display as-is (e.g.
                trip and checkout totals).
            currency:
              type: string
              description: ISO 4217 currency code.
              example: USD
            decimal_places:
              type: integer
              description: >-
                Scale of the integer value/amount: display = integer / 10 **
                decimal_places. Always sent alongside minor-unit amounts.
                Usually the ISO 4217 digits of the currency (2 for USD/EUR/GBP,
                0 for JPY/KRW, 3 for BHD/JOD/KWD) but currency-converted prices
                can carry a different scale — ALWAYS use the decimal_places sent
                with the amount, never a hardcoded 2. Only if the field is
                genuinely absent on a value-shaped object, fall back to the ISO
                digits for the currency.
              example: 2
          description: >-
            How much of the refund could not be reserved, on
            `funds_unavailable`. The refusal is about money still moving on the
            original payment, not about this booking, so this is the figure that
            says how much has to clear before a commit can succeed.
        blocking:
          type: array
          items:
            type: string
          description: >-
            What is holding that money, on `funds_unavailable`: the refunds
            already in flight against the same payment, which have to settle
            before this one can be reserved. Opaque handles — read them as
            identity, do not parse them. This is what to wait on; without it,
            "retry later" has nothing behind it.
          example:
            - re_3UBIxK2eZvKYlo2C
      required:
        - error
        - code
    IntentInput:
      type: object
      properties:
        user_intent:
          type: string
          nullable: true
          description: >-
            The user's natural-language intent: the Alpic PII-stripped
            paraphrase when available, else a best-effort fallback to the
            client-provided NL query.
          example: find a cheap flight to Tokyo
    ServicingState:
      type: string
      enum:
        - in_progress
        - attention_required
        - succeeded
        - failed
      description: >-
        Where the cancellation has got to. `in_progress` — running; keep
        polling. `attention_required` — stalled on something a person at Jinko
        has to resolve (`reason` names it); keep polling, and do not report it
        to the customer as a failure, because the booking may already be
        cancelled at the supplier. `succeeded` and `failed` are terminal: the
        supplier outcome and the money are both settled and nothing further will
        change.
    ServicingConflictCode:
      type: string
      enum:
        - quote_drift
        - quote_expired
        - active_operation_exists
        - not_cancellable
        - funds_unavailable
        - superseded
      description: >-
        Which refusal this is. `quote_drift` — the refund moved since the quote
        you acknowledged; `requote` is a fresh quote handle and `current.refund`
        the figure now. `quote_expired` — the quote stopped binding; take a
        fresh one. `active_operation_exists` — one cancellation is already
        running on this booking; poll `active_operation` instead of starting a
        second. `not_cancellable` — the booking cannot be cancelled as it
        stands. `funds_unavailable` — the original payment cannot cover the
        refund yet; `shortfall` is how much is missing and `blocking` names the
        refunds that have to settle first. `superseded` — the item this quote
        named is no longer the live one with the supplier (an exchange moved
        it); quote again with `requote`. In every case NOTHING was cancelled.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
    BearerAuth:
      type: http
      scheme: bearer

````