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

# Best Price Guarantee

> Ask for a Jinko hotel offer at a lower price a traveller found on another site.

> [!NOTE]
>
> Best Price Guarantee works with every production and sandbox key. On sandbox
> (`https://api.sandbox.gojinko.com`, `https://mcp.builders.sandbox.gojinko.com/mcp`, CLI
> `--env sandbox`) Jinko does not open the reference page: an offer comes back with
> `offer.reference.verified` `false`, and the declines that come from checking the link and the page
> (`hotel_mismatch`, `stay_mismatch`, `conditions_mismatch`, `reference_mismatch`) are not
> returned there. Test those on production.

A traveller compares a Jinko hotel offer with the same stay on another site and finds it cheaper
there. Best Price Guarantee takes that price, with the link and a screenshot of the page, and
answers with a **new offer** at that price, or with a decline. The new offer is added to a trip
and paid like any other hotel offer.

## What it covers

* Hotels only, and offers for a single room.
* Jinko matches its price to a credible reference: the same hotel, room, board and cancellation
  terms, for the same dates and guests, on a site accepted as a reference.
* Jinko opens the link and reads the page: the hotel, the dates and guests, the room, board and
  cancellation terms, and the total. A page for another hotel, stay or terms, or with a higher
  total, is declined. Send the link to this hotel's page on that site, for the same stay as the
  Jinko offer. `offer.reference.verified` is `true` when the page confirmed the reference.
* The reference is the total payable at booking on the other site, in the currency of the Jinko
  offer. Taxes payable at the property are not part of it, and nothing is converted.
* A request can be declined. Do not promise the traveller a match before the answer says `offered`.

## Submit the lower price

Start from an offer returned by `POST /v1/hotel_search`. An offer can be submitted for
75 minutes after the search that returned it.

```javascript theme={null}
const api = async (method, path, body) => {
  const response = await fetch(`https://api.gojinko.com/v1/${path}`, {
    method,
    headers: {
      'X-API-Key': process.env.JINKO_API_KEY,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
  })
  if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`)
  return response.json()
}

const request = await api('POST', 'best_price_guarantee', {
  product: 'hotel',
  trip_item_token: 'htl_8f3c1d5e9a7b2c40',
  reference: {
    url: 'https://www.booking.com/hotel/fr/example.html?checkin=2026-11-10',
    screenshot_url: 'https://files.example.com/screenshots/8f3.png',
    total: '412.50',
    currency: 'USD',
  },
  wait_seconds: 15,
})
```

The review runs in the background. `wait_seconds` (0 to 20) is how long the call waits for it;
most requests finish within that time. The answer carries an `id` and a `status`:

| `status` | Meaning |
| - | - |
| `pending` | The review is still running. Read the request again; this is not a decline. Reading the reference page can take a few minutes. |
| `offered` | `offer` holds the new offer. |
| `declined` | No offer. `decline.reason` says why, and `decline.detail` what the page showed when the decline comes from reading it. |
| `expired` | An offer was made and was not used before `offer.expires_at`. |

A decline is answered with HTTP 200: it is an answer, not a failure of the call.

## Read the result

```javascript theme={null}
const result = await api(
  'GET',
  `best_price_guarantee/${request.id}?wait_seconds=15`
)
```

An offered result looks like this:

```json theme={null}
{
  "id": "ngo_mfrggzdfmztwq2lknnwg23tpob",
  "status": "offered",
  "offer": {
    "trip_item_token": "htl_neg_7k2m9q4x8w1e5r3t",
    "price_money": { "value": 41250, "currency": "USD", "decimal_places": 2, "display": "USD 412.50" },
    "original": {
      "kind": "offer",
      "trip_item_token": "htl_8f3c1d5e9a7b2c40",
      "total_money": { "value": 44500, "currency": "USD", "decimal_places": 2, "display": "USD 445.00" }
    },
    "reference": {
      "total_money": { "value": 41250, "currency": "USD", "decimal_places": 2, "display": "USD 412.50" },
      "verified": true
    },
    "saving_money": { "value": 3250, "currency": "USD", "decimal_places": 2, "display": "USD 32.50" },
    "expires_at": "2026-11-01T10:30:00Z"
  }
}
```

`offer.original.total_money` is the current standard price of that room, board and cancellation
terms. It can be lower than the price the earlier search showed.

### When Jinko's current price is already at or below the reference

There is nothing to match, and the answer is one of two:

* `offered`, with the new offer priced at that current price and `saving_money` zero. This is
  the usual answer. Book the new offer as described below.
* `declined` with `reference_not_lower`. No new offer is made for this rate. Search the hotel
  alone (`hotel_ids`) and book the rate at its current price.

## Book the new offer

Add `offer.trip_item_token` to a trip with `POST /v1/trip` (`add_item.trip_item_token`), then add the
travellers and call `POST /v1/checkout`, exactly as for any hotel offer. See
[Hotel booking](/guides/hotel-booking).

* **A trip that holds the new offer holds nothing else.** Start a new trip for it. Adding it
  beside another item is refused with HTTP 409.
* The new offer is valid for 30 minutes (`offer.expires_at`).
* The total at checkout can move with the hotel's price. When the price can no longer be offered,
  checkout answers `OFFER_EXPIRED`; search and submit again.
* Payment, confirmation, cancellation and refund work as for any hotel booking, on the price paid.

## When a request is declined

| `decline.reason` | What to do |
| - | - |
| `feature_disabled` | Best Price Guarantee is not enabled for this key. |
| `product_not_found` | The offer is unknown or older than 75 minutes. Search again. |
| `product_not_supported` | Not a single-room hotel offer from a search. |
| `rate_unavailable` | The same room, board and cancellation terms were not found again. Search again and resubmit. |
| `reference_source_not_allowed` | The link is not on a site accepted as a reference. |
| `reference_invalid` | The link, the screenshot address or the total cannot be used as submitted. |
| `currency_mismatch` | The reference is not in the currency of the offer. |
| `price_cannot_be_matched` | This price cannot be offered for this rate. |
| `reference_not_lower` | Jinko's current price is already at or below the reference and no new offer is made. See above. |
| `objective_not_supported` | The requested `objective` is not available. Omit it, or send `price_parity`. |
| `temporarily_unavailable` | The review did not complete. Submitting again may succeed. |
| `timeout` | The review did not complete in time. Submitting again may succeed. |
| `hotel_mismatch` | The reference page is for another hotel. |
| `stay_mismatch` | The link or the page is for other dates or another number of guests, so the prices cannot be compared. Ask for a link for the same stay. |
| `conditions_mismatch` | The page sells this stay only with another room, board or cancellation terms. |
| `reference_mismatch` | The page shows a higher total for this stay than the reference submitted. |

The last four come from reading the reference page, and `decline.detail` says what the page
showed, for example `the page shows prices for 1 adult(s); the Jinko offer is for 2`. Pass it on to
the traveller as it is.

The list grows. Treat a reason that is not listed as "no offer". After `price_cannot_be_matched`,
search that hotel alone (`hotel_ids`) as well: the price of the same rate there can be lower than
the one an area search showed.

## With the MCP server

The MCP server serves the same flow as two tools.
[`claim_best_price_guarantee`](/tools/claim-best-price-guarantee) makes the request, with the same
fields as `POST /v1/best_price_guarantee`.
[`get_best_price_guarantee`](/tools/get-best-price-guarantee) reads its result by the `id` the claim returned;
it never makes a request. Describe the result to the traveller as the price Jinko can offer after
its own review.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.