curl --request POST \
--url https://api.example.com/v1/flight_refund_commit \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"booking_ref": "JNK-A0AUR2",
"last_name": "Carrard",
"quote": "svq_01J7ZR3M8FKX2P9C",
"acknowledged": {
"value": 25000,
"currency": "USD",
"decimal_places": 2
}
}
'import requests
url = "https://api.example.com/v1/flight_refund_commit"
payload = {
"booking_ref": "JNK-A0AUR2",
"last_name": "Carrard",
"quote": "svq_01J7ZR3M8FKX2P9C",
"acknowledged": {
"value": 25000,
"currency": "USD",
"decimal_places": 2
}
}
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-A0AUR2',
last_name: 'Carrard',
quote: 'svq_01J7ZR3M8FKX2P9C',
acknowledged: {value: 25000, currency: 'USD', decimal_places: 2}
})
};
fetch('https://api.example.com/v1/flight_refund_commit', 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/flight_refund_commit",
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-A0AUR2',
'last_name' => 'Carrard',
'quote' => 'svq_01J7ZR3M8FKX2P9C',
'acknowledged' => [
'value' => 25000,
'currency' => 'USD',
'decimal_places' => 2
]
]),
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/flight_refund_commit"
payload := strings.NewReader("{\n \"booking_ref\": \"JNK-A0AUR2\",\n \"last_name\": \"Carrard\",\n \"quote\": \"svq_01J7ZR3M8FKX2P9C\",\n \"acknowledged\": {\n \"value\": 25000,\n \"currency\": \"USD\",\n \"decimal_places\": 2\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/flight_refund_commit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"booking_ref\": \"JNK-A0AUR2\",\n \"last_name\": \"Carrard\",\n \"quote\": \"svq_01J7ZR3M8FKX2P9C\",\n \"acknowledged\": {\n \"value\": 25000,\n \"currency\": \"USD\",\n \"decimal_places\": 2\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/flight_refund_commit")
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-A0AUR2\",\n \"last_name\": \"Carrard\",\n \"quote\": \"svq_01J7ZR3M8FKX2P9C\",\n \"acknowledged\": {\n \"value\": 25000,\n \"currency\": \"USD\",\n \"decimal_places\": 2\n }\n}"
response = http.request(request)
puts response.read_body{
"operation": "svc_01J7ZR5Q2KME8V4T",
"state": "in_progress",
"reason": "manual_required"
}{
"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": "NOT_FOUND",
"message": "booking not found"
}
}{
"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"
}{
"error": {
"code": "GONE",
"message": "The resource no longer exists.",
"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"
}
}flight-refund-commit
Refund or void a flight ticket against a quote the customer accepted
curl --request POST \
--url https://api.example.com/v1/flight_refund_commit \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"booking_ref": "JNK-A0AUR2",
"last_name": "Carrard",
"quote": "svq_01J7ZR3M8FKX2P9C",
"acknowledged": {
"value": 25000,
"currency": "USD",
"decimal_places": 2
}
}
'import requests
url = "https://api.example.com/v1/flight_refund_commit"
payload = {
"booking_ref": "JNK-A0AUR2",
"last_name": "Carrard",
"quote": "svq_01J7ZR3M8FKX2P9C",
"acknowledged": {
"value": 25000,
"currency": "USD",
"decimal_places": 2
}
}
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-A0AUR2',
last_name: 'Carrard',
quote: 'svq_01J7ZR3M8FKX2P9C',
acknowledged: {value: 25000, currency: 'USD', decimal_places: 2}
})
};
fetch('https://api.example.com/v1/flight_refund_commit', 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/flight_refund_commit",
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-A0AUR2',
'last_name' => 'Carrard',
'quote' => 'svq_01J7ZR3M8FKX2P9C',
'acknowledged' => [
'value' => 25000,
'currency' => 'USD',
'decimal_places' => 2
]
]),
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/flight_refund_commit"
payload := strings.NewReader("{\n \"booking_ref\": \"JNK-A0AUR2\",\n \"last_name\": \"Carrard\",\n \"quote\": \"svq_01J7ZR3M8FKX2P9C\",\n \"acknowledged\": {\n \"value\": 25000,\n \"currency\": \"USD\",\n \"decimal_places\": 2\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/flight_refund_commit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"booking_ref\": \"JNK-A0AUR2\",\n \"last_name\": \"Carrard\",\n \"quote\": \"svq_01J7ZR3M8FKX2P9C\",\n \"acknowledged\": {\n \"value\": 25000,\n \"currency\": \"USD\",\n \"decimal_places\": 2\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/flight_refund_commit")
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-A0AUR2\",\n \"last_name\": \"Carrard\",\n \"quote\": \"svq_01J7ZR3M8FKX2P9C\",\n \"acknowledged\": {\n \"value\": 25000,\n \"currency\": \"USD\",\n \"decimal_places\": 2\n }\n}"
response = http.request(request)
puts response.read_body{
"operation": "svc_01J7ZR5Q2KME8V4T",
"state": "in_progress",
"reason": "manual_required"
}{
"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": "NOT_FOUND",
"message": "booking not found"
}
}{
"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"
}{
"error": {
"code": "GONE",
"message": "The resource no longer exists.",
"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"
}
}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.Authorizations
Body
The "svq_…" handle from a preceding POST /v1/flight_refund_preview. Binds this commit to the figures that quote reported.
"svq_01J7ZR3M8FKX2P9C"
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.
"QQIUIQ"
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.
"JNK-H1ZK90"
The lead traveller's surname. Required with booking_ref, and only with it.
"Carrard"
Show child attributes
Show child attributes
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.
Show child attributes
Show child attributes
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.
false
Response
The refund or void was accepted and is running
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.
"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 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.
"manual_required"
