> ## 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.

# flight-refund-commit

> Refund or void a flight ticket against a quote the customer accepted

Start the operation the preview described, a refund, or a void of the original charge, whichever the preview reported as `operation_kind`. The body is the `quote` handle and the `acknowledged` figure PLUS the same auth mode the preview took: 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 call is refused with 409 `quote_drift`, carrying a fresh `requote` handle and the current figure, and nothing is sent to the airline, so nobody is refunded a number they never saw. A MANUAL\_REQUIRED quote needs `manual_ok: true`: the operation is then raised for a Jinko agent and answers `attention_required` with `reason: manual_required`, again without the airline being called. Committing one without the flag is refused 409 `manual_required`. That submission is also the one case where `acknowledged` may be left out: a MANUAL\_REQUIRED quote whose penalty is unknown carries no `refund` to acknowledge, and there is nothing to compare against. Whenever the preview DID name a refund, send it. The other refusals are 409 `quote_expired` (preview again), `active_operation_exists` (poll the `active_operation` it names instead of starting a second one), `not_cancellable`, `funds_unavailable`, `superseded`, and, when the preview already said `commitable: false` and you committed anyway, that quote’s own reason answered back as the code: `penalty_exceeds_sell` or `multi_currency_basis`. Success does NOT mean the money has moved: giving the ticket back at the airline and returning the money to the customer are separate steps, so the answer is an `operation` handle to poll with POST /v1/flight\_refund\_status.


## OpenAPI

````yaml api-reference/public-api.yaml POST /v1/flight_refund_commit
openapi: 3.0.0
info:
  title: Jinko Public API
  version: 0.8.1
  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/flight_refund_commit:
    post:
      tags:
        - Post-booking
      summary: Refund or void a flight ticket against a quote the customer accepted
      description: >-
        Start the operation the preview described — a refund, or a void of the
        original charge, whichever the preview reported as `operation_kind`. The
        body is the `quote` handle and the `acknowledged` figure PLUS the same
        auth mode the preview took: 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 call is refused with 409 `quote_drift`, carrying a fresh
        `requote` handle and the current figure, and nothing is sent to the
        airline — so nobody is refunded a number they never saw. A
        MANUAL_REQUIRED quote needs `manual_ok: true`: the operation is then
        raised for a Jinko agent and answers `attention_required` with `reason:
        manual_required`, again without the airline being called. Committing one
        without the flag is refused 409 `manual_required`. That submission is
        also the one case where `acknowledged` may be left out: a
        MANUAL_REQUIRED quote whose penalty is unknown carries no `refund` to
        acknowledge, and there is nothing to compare against. Whenever the
        preview DID name a refund, send it. The other refusals are 409
        `quote_expired` (preview again), `active_operation_exists` (poll the
        `active_operation` it names instead of starting a second one),
        `not_cancellable`, `funds_unavailable`, `superseded`, and — when the
        preview already said `commitable: false` and you committed anyway — that
        quote’s own reason answered back as the code: `penalty_exceeds_sell` or
        `multi_currency_basis`. Success does NOT mean the money has moved:
        giving the ticket back at the airline and returning the money to the
        customer are separate steps, so the answer is an `operation` handle to
        poll with POST /v1/flight_refund_status.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlightRefundCommitRequest'
      responses:
        '200':
          description: The refund or void was accepted and is running
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlightRefundCommitResponse'
        '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 sent to the airline.
            `code` says which case, as a sibling of `error` — see
            `FlightServicingConflictCode` 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/FlightServicingConflictResponse'
              example:
                error:
                  code: CONFLICT
                  message: >-
                    This refund cannot be carried out automatically and nothing
                    was sent to the airline. The quote said `support_level:
                    MANUAL_REQUIRED`; commit again with `manual_ok: true` to
                    hand it to a Jinko agent, then poll the operation.
                code: manual_required
        '410':
          description: The resource this request names no longer exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: GONE
                  message: The resource no longer exists.
                  doc_url: https://docs.gojinko.com/concepts/errors
        '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:
    FlightRefundCommitRequest:
      type: object
      properties:
        provider_reference:
          type: string
          description: >-
            The CONNECTOR's own order id for this flight booking — the Sabre PNR
            for a Sabre booking, the TravelFusion booking reference for a
            TravelFusion one. NOT the airline record locator, which names the
            carrier's own copy of the reservation and is answered 404 here,
            identically to an unknown booking. 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.
          example: QQIUIQ
        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/flight_refund_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. REQUIRED whenever the preview carried a `refund`,
            which is every quote that names a figure. It may be omitted in
            exactly one case: a MANUAL_REQUIRED quote that carries no `refund`
            at all because the penalty is unknown (`penalty.fee_known: false`),
            submitted with `manual_ok: true` — there is no figure to
            acknowledge, and inventing one would have the customer agree to a
            number nobody computed. Omitting it anywhere else is refused by the
            platform, not here. When present, 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 sent to the airline.
        manual_ok:
          type: boolean
          description: >-
            Send `true` to submit a quote whose `support_level` is
            MANUAL_REQUIRED. The operation is then raised for a Jinko agent and
            answers `state: attention_required` with `reason: manual_required` —
            NOTHING is sent to the airline by this call, and the agent settles
            or fails it. Without the flag such a commit is refused 409
            `manual_required`, so a customer is never put in an agent queue
            unasked. On an AUTO or AUTO_VOID quote the flag changes nothing.
            This is also the one submission that may carry no `acknowledged`
            figure — when the quote itself named none, because the penalty is
            unknown.
          example: false
      required:
        - quote
      example:
        booking_ref: JNK-A0AUR2
        last_name: Carrard
        quote: svq_01J7ZR3M8FKX2P9C
        acknowledged:
          value: 25000
          currency: USD
          decimal_places: 2
    FlightRefundCommitResponse:
      type: object
      properties:
        operation:
          type: string
          description: >-
            The operation this call started ("svc_…"). Poll it with POST
            /v1/flight_refund_status; it is the only handle that reports how the
            airline call and the refund ended.
          example: svc_01J7ZR5Q2KME8V4T
        state:
          $ref: '#/components/schemas/ServicingState'
        reason:
          type: string
          description: >-
            Why the operation is parked, when `state` is `attention_required`.
            On a commit that is `manual_required`: you sent `manual_ok: true`, a
            Jinko agent now has it, and nothing has been sent to the airline.
            Not a failure — poll the operation.
          example: manual_required
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - AUTH_REQUIRED
                - PAYMENT_REQUIRED
                - RATE_LIMITED
                - BAD_REQUEST
                - FORBIDDEN
                - NOT_FOUND
                - CONFLICT
                - GONE
                - 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. New codes arrive in a minor version, so
                treat an unknown one as its HTTP status. A code can also stop
                being emitted: it leaves this set in a minor version, named in
                the changelog, and a branch you wrote for it goes unreached
                rather than wrong.
              example: BAD_REQUEST
            message:
              type: string
            doc_url:
              type: string
          required:
            - code
            - message
      required:
        - error
    FlightServicingConflictResponse:
      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/FlightServicingConflictCode'
        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 operation already running on this booking, on
            `active_operation_exists`. Poll it with the status route for its
            domain — POST /v1/hotel_cancel_status or POST
            /v1/flight_refund_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.
    FlightServicingConflictCode:
      type: string
      enum:
        - quote_drift
        - quote_expired
        - active_operation_exists
        - not_cancellable
        - funds_unavailable
        - superseded
        - manual_required
        - penalty_exceeds_sell
        - multi_currency_basis
      description: >-
        Which refusal this is. `manual_required` — the quote needs a Jinko agent
        and you did not send `manual_ok: true`; resend the same commit with it
        to hand the operation over. `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` — an operation is
        already running on this booking; poll `active_operation` instead of
        starting a second. `not_cancellable` — this ticket cannot be given back
        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 airline (an exchange
        moved it); quote again with `requote`. The last two arrive only on a
        commit against a quote that already answered `commitable: false`, and
        repeat that quote’s own `not_commitable_reason`: `penalty_exceeds_sell`
        — the penalty is at least what the customer paid, so there is nothing to
        refund; `multi_currency_basis` — the airline prices the refund in a
        currency the customer was not charged in, so no single figure exists and
        a Jinko agent has to settle it. In every case NOTHING was sent to the
        airline.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
    BearerAuth:
      type: http
      scheme: bearer

````