curl --request POST \
--url https://api.example.com/v1/hotel_cancel \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"provider_booking_id": "NUITEE-88231",
"provider": "nuitee",
"booking_ref": "JNK-H1ZK90",
"last_name": "Carrard",
"intent": {
"user_intent": "find a cheap flight to Tokyo"
}
}
'import requests
url = "https://api.example.com/v1/hotel_cancel"
payload = {
"provider_booking_id": "NUITEE-88231",
"provider": "nuitee",
"booking_ref": "JNK-H1ZK90",
"last_name": "Carrard",
"intent": { "user_intent": "find a cheap flight to Tokyo" }
}
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({
provider_booking_id: 'NUITEE-88231',
provider: 'nuitee',
booking_ref: 'JNK-H1ZK90',
last_name: 'Carrard',
intent: {user_intent: 'find a cheap flight to Tokyo'}
})
};
fetch('https://api.example.com/v1/hotel_cancel', 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.example.com/v1/hotel_cancel",
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([
'provider_booking_id' => 'NUITEE-88231',
'provider' => 'nuitee',
'booking_ref' => 'JNK-H1ZK90',
'last_name' => 'Carrard',
'intent' => [
'user_intent' => 'find a cheap flight to Tokyo'
]
]),
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.example.com/v1/hotel_cancel"
payload := strings.NewReader("{\n \"provider_booking_id\": \"NUITEE-88231\",\n \"provider\": \"nuitee\",\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Carrard\",\n \"intent\": {\n \"user_intent\": \"find a cheap flight to Tokyo\"\n }\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.example.com/v1/hotel_cancel")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"provider_booking_id\": \"NUITEE-88231\",\n \"provider\": \"nuitee\",\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Carrard\",\n \"intent\": {\n \"user_intent\": \"find a cheap flight to Tokyo\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/hotel_cancel")
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 \"provider_booking_id\": \"NUITEE-88231\",\n \"provider\": \"nuitee\",\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Carrard\",\n \"intent\": {\n \"user_intent\": \"find a cheap flight to Tokyo\"\n }\n}"
response = http.request(request)
puts response.read_body{
"provider_booking_id": "NUITEE-88231",
"provider": "nuitee",
"status": "cancelled",
"operation": "svc_01J7ZR5Q2KME8V4T",
"state": "in_progress",
"refund_amount": {
"value": 41250,
"amount": 123,
"currency": "USD",
"decimal_places": 2
},
"customer_refund_amount": {
"value": 41250,
"amount": 123,
"currency": "USD",
"decimal_places": 2
},
"penalty_amount": {
"value": 41250,
"amount": 123,
"currency": "USD",
"decimal_places": 2
},
"provider_figures": {
"refund_net": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2
},
"penalty_net": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2
},
"currency": "USD"
},
"connector_reference": "cxl_2f90a1c3",
"cancelled_at": "2026-06-01T12:34:56Z",
"idempotent": false
}{
"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/api-reference/authentication"
}
}{
"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": "CONFLICT",
"message": "an exchange is already in progress for this booking",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "origins: origins is required; trip_type: trip_type is required",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit or quota exceeded.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"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"
}
}{
"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"
}
}{
"error": {
"code": "UPSTREAM_TIMEOUT",
"message": "sabre-rest BargainFinderMaxRQ timed out after 30s",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}hotel-cancel
Cancel a hotel booking and get a refund when eligible. Preview and commit in one call, POST /v1/hotel_cancel_preview plus /v1/hotel_cancel_commit is the two-step form, which shows the customer the refund before anything is cancelled. refund_amount is what the CUSTOMER gets back; the supplier’s own net figures are under provider_figures. Returning the money outlives this call: poll the operation handle with POST /v1/hotel_cancel_status. Every call here needs API authentication (an API key or a Bearer token); this is about which mode identifies the booking. Use EXACTLY ONE: provider_booking_id, which additionally requires a credential that OWNS the booking, or booking_ref + last_name, which identifies it without one. Sending both is a 422.
curl --request POST \
--url https://api.example.com/v1/hotel_cancel \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"provider_booking_id": "NUITEE-88231",
"provider": "nuitee",
"booking_ref": "JNK-H1ZK90",
"last_name": "Carrard",
"intent": {
"user_intent": "find a cheap flight to Tokyo"
}
}
'import requests
url = "https://api.example.com/v1/hotel_cancel"
payload = {
"provider_booking_id": "NUITEE-88231",
"provider": "nuitee",
"booking_ref": "JNK-H1ZK90",
"last_name": "Carrard",
"intent": { "user_intent": "find a cheap flight to Tokyo" }
}
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({
provider_booking_id: 'NUITEE-88231',
provider: 'nuitee',
booking_ref: 'JNK-H1ZK90',
last_name: 'Carrard',
intent: {user_intent: 'find a cheap flight to Tokyo'}
})
};
fetch('https://api.example.com/v1/hotel_cancel', 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.example.com/v1/hotel_cancel",
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([
'provider_booking_id' => 'NUITEE-88231',
'provider' => 'nuitee',
'booking_ref' => 'JNK-H1ZK90',
'last_name' => 'Carrard',
'intent' => [
'user_intent' => 'find a cheap flight to Tokyo'
]
]),
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.example.com/v1/hotel_cancel"
payload := strings.NewReader("{\n \"provider_booking_id\": \"NUITEE-88231\",\n \"provider\": \"nuitee\",\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Carrard\",\n \"intent\": {\n \"user_intent\": \"find a cheap flight to Tokyo\"\n }\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.example.com/v1/hotel_cancel")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"provider_booking_id\": \"NUITEE-88231\",\n \"provider\": \"nuitee\",\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Carrard\",\n \"intent\": {\n \"user_intent\": \"find a cheap flight to Tokyo\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/hotel_cancel")
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 \"provider_booking_id\": \"NUITEE-88231\",\n \"provider\": \"nuitee\",\n \"booking_ref\": \"JNK-H1ZK90\",\n \"last_name\": \"Carrard\",\n \"intent\": {\n \"user_intent\": \"find a cheap flight to Tokyo\"\n }\n}"
response = http.request(request)
puts response.read_body{
"provider_booking_id": "NUITEE-88231",
"provider": "nuitee",
"status": "cancelled",
"operation": "svc_01J7ZR5Q2KME8V4T",
"state": "in_progress",
"refund_amount": {
"value": 41250,
"amount": 123,
"currency": "USD",
"decimal_places": 2
},
"customer_refund_amount": {
"value": 41250,
"amount": 123,
"currency": "USD",
"decimal_places": 2
},
"penalty_amount": {
"value": 41250,
"amount": 123,
"currency": "USD",
"decimal_places": 2
},
"provider_figures": {
"refund_net": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2
},
"penalty_net": {
"amount": 123,
"value": 41250,
"currency": "USD",
"decimal_places": 2
},
"currency": "USD"
},
"connector_reference": "cxl_2f90a1c3",
"cancelled_at": "2026-06-01T12:34:56Z",
"idempotent": false
}{
"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/api-reference/authentication"
}
}{
"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": "CONFLICT",
"message": "an exchange is already in progress for this booking",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "BAD_REQUEST",
"message": "origins: origins is required; trip_type: trip_type is required",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit or quota exceeded.",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}{
"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"
}
}{
"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"
}
}{
"error": {
"code": "UPSTREAM_TIMEOUT",
"message": "sabre-rest BargainFinderMaxRQ timed out after 30s",
"doc_url": "https://docs.gojinko.com/concepts/errors"
}
}- Guest:
booking_ref+last_nametogether. - Authenticated:
provider_booking_id(optionally withprovider).
Authorizations
Body
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.
"NUITEE-88231"
A hint only. The provider stored on the resolved booking wins, so a stale value here cannot send the cancellation to the wrong supplier.
"nuitee"
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_booking_id; one without the other is rejected with 422.
"JNK-H1ZK90"
The lead traveller's surname. Required with booking_ref, and only with it.
"Carrard"
Show child attributes
Show child attributes
Response
Cancel a hotel booking and get a refund when eligible. Preview and commit in one call — POST /v1/hotel_cancel_preview plus /v1/hotel_cancel_commit is the two-step form, which shows the customer the refund before anything is cancelled. refund_amount is what the CUSTOMER gets back; the supplier's own net figures are under provider_figures. Returning the money outlives this call: poll the operation handle with POST /v1/hotel_cancel_status. Every call here needs API authentication (an API key or a Bearer token); this is about which mode identifies the booking. Use EXACTLY ONE: provider_booking_id, which additionally requires a credential that OWNS the booking, or booking_ref + last_name, which identifies it without one. Sending both is a 422.
"NUITEE-88231"
"nuitee"
"cancelled"
The cancellation this call started ("svc_…"). Cancelling the booking at the supplier and returning the money are separate steps, so this call answering does not mean both finished — poll the handle with POST /v1/hotel_cancel_status until state is terminal.
"svc_01J7ZR5Q2KME8V4T"
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.
in_progress, attention_required, succeeded, failed THE CUSTOMER FIGURE: what goes back to the payment method that paid for this booking — what the customer paid for it, less penalty_amount. Up to contract version 0.3.0 this field carried the SUPPLIER's net refund instead, which on a booking sold at a margin is a smaller number; the supplier's figure now lives in provider_figures.refund_net. Absent when no refund was scheduled — absent is not zero.
Show child attributes
Show child attributes
Deprecated alias of refund_amount, carrying the same value. It existed because refund_amount used to be the supplier figure; now that refund_amount IS the customer figure the alias is redundant. Read refund_amount — this field is removed in the next contract version.
Show child attributes
Show child attributes
The cancellation penalty withheld from the customer, on the same basis as refund_amount: what they paid, less this, is what they get back. The penalty the SUPPLIER withheld from Jinko is provider_figures.penalty_net.
Show child attributes
Show child attributes
The supplier's own net figures, for reconciliation — never what the customer receives. refund_net is what the supplier returns to Jinko and penalty_net what it withheld from Jinko; on a booking sold at a margin both differ from the customer figures. Show the customer the top-level refund_amount / penalty_amount, never these. currency is the currency the supplier settles in, which can differ from the currency charged.
Show child attributes
Show child attributes
"cxl_2f90a1c3"
"2026-06-01T12:34:56Z"
false
