curl --request POST \
--url https://api.gojinko.com/v1/hotel_cancel_preview \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"booking_ref": "JNK-H1ZK90",
"last_name": "Doe"
}
'import requests
url = "https://api.gojinko.com/v1/hotel_cancel_preview"
payload = {
"booking_ref": "JNK-H1ZK90",
"last_name": "Doe"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({booking_ref: 'JNK-H1ZK90', last_name: 'Doe'})
};
fetch('https://api.gojinko.com/v1/hotel_cancel_preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gojinko.com/v1/hotel_cancel_preview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'booking_ref' => 'JNK-H1ZK90',
'last_name' => 'Doe'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.gojinko.com/v1/hotel_cancel_preview"
payload := strings.NewReader("{\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Doe\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gojinko.com/v1/hotel_cancel_preview")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Doe\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gojinko.com/v1/hotel_cancel_preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Doe\"\n}"
response = http.request(request)
puts response.read_body{
"quote": "svq_01J7ZR3M8FKX2P9C",
"state": "completed",
"commitable": true,
"support_level": "AUTO",
"manual_reason": "the supplier's cancellation policy names no fee in force for this booking, so the fee could not be established",
"expires_at": "2026-09-03T12:15:00Z",
"item": "itm_7f2c9a4e8b1d",
"refund": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50",
"amount_money": {
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50"
},
"basis": "sell_minus_penalty"
},
"penalty": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50",
"amount_money": {
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50"
},
"fee_known": true
},
"policy": {
"free_cancel_until": "2026-09-01T00:00:00Z",
"is_refundable_now": true,
"tiers": [
{
"from": "2026-09-01T00:00:00Z",
"amount": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50"
},
"fraction": 0.5
}
]
},
"not_commitable_reason": "penalty_exceeds_sell"
}{
"error": {
"code": "BAD_REQUEST",
"message": "Malformed JSON in request body.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "AUTH_REQUIRED",
"message": "Invalid or expired API key.",
"doc_url": "https://docs.gojinko.com/authentication/api-keys"
}
}{
"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"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "booking not found"
}
}{
"error": {
"code": "CONFLICT",
"message": "This booking cannot be cancelled online. Contact Jinko support."
},
"code": "not_cancellable"
}{
"error": {
"code": "GONE",
"message": "The resource no longer exists.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "A required field is missing or invalid.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after the Retry-After delay.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "UPSTREAM_REJECTED",
"message": "The request was declined for this offer. Try another offer.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "UPSTREAM_UNAVAILABLE",
"message": "The service is temporarily unavailable. Retry after the Retry-After delay.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "UPSTREAM_TIMEOUT",
"message": "The request timed out before a result was available. Retry the request.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}hotel-cancel-preview
Preview what cancelling a hotel booking would cost
curl --request POST \
--url https://api.gojinko.com/v1/hotel_cancel_preview \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"booking_ref": "JNK-H1ZK90",
"last_name": "Doe"
}
'import requests
url = "https://api.gojinko.com/v1/hotel_cancel_preview"
payload = {
"booking_ref": "JNK-H1ZK90",
"last_name": "Doe"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({booking_ref: 'JNK-H1ZK90', last_name: 'Doe'})
};
fetch('https://api.gojinko.com/v1/hotel_cancel_preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.gojinko.com/v1/hotel_cancel_preview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'booking_ref' => 'JNK-H1ZK90',
'last_name' => 'Doe'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.gojinko.com/v1/hotel_cancel_preview"
payload := strings.NewReader("{\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Doe\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.gojinko.com/v1/hotel_cancel_preview")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Doe\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gojinko.com/v1/hotel_cancel_preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Doe\"\n}"
response = http.request(request)
puts response.read_body{
"quote": "svq_01J7ZR3M8FKX2P9C",
"state": "completed",
"commitable": true,
"support_level": "AUTO",
"manual_reason": "the supplier's cancellation policy names no fee in force for this booking, so the fee could not be established",
"expires_at": "2026-09-03T12:15:00Z",
"item": "itm_7f2c9a4e8b1d",
"refund": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50",
"amount_money": {
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50"
},
"basis": "sell_minus_penalty"
},
"penalty": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50",
"amount_money": {
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50"
},
"fee_known": true
},
"policy": {
"free_cancel_until": "2026-09-01T00:00:00Z",
"is_refundable_now": true,
"tiers": [
{
"from": "2026-09-01T00:00:00Z",
"amount": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2,
"display": "USD 412.50"
},
"fraction": 0.5
}
]
},
"not_commitable_reason": "penalty_exceeds_sell"
}{
"error": {
"code": "BAD_REQUEST",
"message": "Malformed JSON in request body.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "AUTH_REQUIRED",
"message": "Invalid or expired API key.",
"doc_url": "https://docs.gojinko.com/authentication/api-keys"
}
}{
"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"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "booking not found"
}
}{
"error": {
"code": "CONFLICT",
"message": "This booking cannot be cancelled online. Contact Jinko support."
},
"code": "not_cancellable"
}{
"error": {
"code": "GONE",
"message": "The resource no longer exists.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "A required field is missing or invalid.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after the Retry-After delay.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "UPSTREAM_REJECTED",
"message": "The request was declined for this offer. Try another offer.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "UPSTREAM_UNAVAILABLE",
"message": "The service is temporarily unavailable. Retry after the Retry-After delay.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "UPSTREAM_TIMEOUT",
"message": "The request timed out before a result was available. Retry the request.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}refund is the customer figure, what they paid for the item less the penalty. All amounts are customer-facing only. commitable: false means a commit would be refused and not_commitable_reason says why. The handle this returns is named quote: it is what POST /v1/hotel_cancel_commit consumes, and it stops binding at expires_at. Every call here needs API authentication; this is about which mode identifies the booking. Call get_booking first, then send booking_ref and the selected item_id. An owning credential identifies the booking without last_name; guests must supply it. provider, order_id, provider_reference and provider_booking_id are not accepted: a request carrying one is refused with 422 naming the field.Authorizations
Body
Jinko booking reference. Call get_booking first, then select its item_id for servicing.
1"JNK-A0AUR2"
Stable booked item ID from get_booking items[].item_id. Preserved across exchanges. Omit only when the booking has exactly one item of the requested domain; otherwise a 409 returns items to choose from.
0 < x <= 900719925474099142
Required for guest lookup. A credential that owns the booking may omit it; ownership is checked by the platform.
1"Doe"
Show child attributes
Show child attributes
Response
What cancelling would cost
This quote ("svq_…"). Pass it to POST /v1/hotel_cancel_commit — it binds the commit to the figures below. Opaque; the format may evolve.
"svq_01J7ZR3M8FKX2P9C"
Lifecycle of the QUOTE, not of a cancellation — nothing has been cancelled by this call. completed means the figures are final until expires_at.
"completed"
Whether a commit against this quote would be accepted right now. When false, not_commitable_reason says why and committing is pointless.
true
How this cancellation would be carried out. AUTO — the platform completes it end to end. MANUAL_REQUIRED — it is possible, but a Jinko agent has to act; expect a delay rather than an immediate result, and do not treat it as a failure; manual_reason says why. There is no "unsupported" level: a supplier that cannot be cancelled through the API arrives as commitable: false with not_commitable_reason: provider_unsupported, and a booking the supplier will not cancel as it stands arrives as a 409 not_cancellable — from the preview as well as the commit — with error.message “This booking cannot be cancelled online. Contact Jinko support.”
AUTO, MANUAL_REQUIRED Why a person has to act, in words the customer can be shown. Present with support_level: MANUAL_REQUIRED. Where the reason would be the supplier’s own words, it reads “This request needs manual handling by Jinko support.” not_commitable_reason is the code to branch on; this is the sentence to show.
"the supplier's cancellation policy names no fee in force for this booking, so the fee could not be established"
When this quote stops binding. Committing after it answers 409 quote_expired; take a fresh quote and show the customer the new figure before committing again.
"2026-09-03T12:15:00Z"
The booked item this quote would cancel ("itm_…"). A booking holding several items is quoted and cancelled one item at a time.
"itm_7f2c9a4e8b1d"
THE CUSTOMER FIGURE: what would go back to the payment method, on the basis named in basis. Show this one. Absent is not zero.
Show child attributes
Show child attributes
What the customer would forfeit. When unknown, the amount is omitted; a fee_known: false marker may remain. Absent is not zero. Read fee_known before showing it.
- Option 1
- Option 2
Show child attributes
Show child attributes
The supplier's cancellation policy as it stands, for explaining the figures to the customer.
Show child attributes
Show child attributes
Why commitable is false. Today: penalty_exceeds_sell (the penalty is at least what the customer paid), multi_currency_basis (the supplier prices the fee in a currency the customer was not charged in, so no single refund figure exists). A fee the supplier’s policy does not establish (penalty.fee_known: false) is support_level: MANUAL_REQUIRED with no reason code — read manual_reason. Two refusals are never values here: a booking the supplier will not cancel as it stands is 409 not_cancellable from this preview itself, and a supplier with no cancellation route is 422 provider_unsupported. The contract reserves further values (provider_unsupported, funds_unavailable, insufficient_time_to_converge) and new reasons may be added, so treat an unrecognised value as "not right now".
"penalty_exceeds_sell"
