curl --request POST \
--url https://api.example.com/v1/car_exchange_commit \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"booking_ref": "JNK-8PT9VS",
"last_name": "Carrard",
"exchange_id": "exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c",
"exchange_offer_token": "cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f"
}
'import requests
url = "https://api.example.com/v1/car_exchange_commit"
payload = {
"booking_ref": "JNK-8PT9VS",
"last_name": "Carrard",
"exchange_id": "exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c",
"exchange_offer_token": "cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f"
}
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-8PT9VS',
last_name: 'Carrard',
exchange_id: 'exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c',
exchange_offer_token: 'cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f'
})
};
fetch('https://api.example.com/v1/car_exchange_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/car_exchange_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-8PT9VS',
'last_name' => 'Carrard',
'exchange_id' => 'exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c',
'exchange_offer_token' => 'cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f'
]),
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/car_exchange_commit"
payload := strings.NewReader("{\n \"booking_ref\": \"JNK-8PT9VS\",\n \"last_name\": \"Carrard\",\n \"exchange_id\": \"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c\",\n \"exchange_offer_token\": \"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f\"\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/car_exchange_commit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"booking_ref\": \"JNK-8PT9VS\",\n \"last_name\": \"Carrard\",\n \"exchange_id\": \"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c\",\n \"exchange_offer_token\": \"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/car_exchange_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-8PT9VS\",\n \"last_name\": \"Carrard\",\n \"exchange_id\": \"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c\",\n \"exchange_offer_token\": \"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f\"\n}"
response = http.request(request)
puts response.read_body{
"exchange_id": "exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c",
"state": "confirmed",
"delta": {
"direction": "add_collect",
"amount": {
"value": 41250,
"currency": "EUR",
"decimal_places": 2
}
},
"new_total": {
"value": 41250,
"currency": "EUR",
"decimal_places": 2
},
"paid": {
"value": 41250,
"currency": "EUR",
"decimal_places": 2
},
"refund_pending_review": true,
"stripe_refund_id": "<string>",
"rejected_reason": "<string>",
"completed_at": "2026-09-02T15:18:00Z",
"awaiting_payment": true,
"checkout_url": "https://book.gojinko.com/checkout?sid=1048576",
"stripe_checkout_url": "<string>"
}{
"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": "the exchange preview is out of date — run a new preview and choose again"
}
}{
"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"
}
}car-exchange-commit
Exchange a car rental booking for a previewed alternative
curl --request POST \
--url https://api.example.com/v1/car_exchange_commit \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"booking_ref": "JNK-8PT9VS",
"last_name": "Carrard",
"exchange_id": "exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c",
"exchange_offer_token": "cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f"
}
'import requests
url = "https://api.example.com/v1/car_exchange_commit"
payload = {
"booking_ref": "JNK-8PT9VS",
"last_name": "Carrard",
"exchange_id": "exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c",
"exchange_offer_token": "cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f"
}
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-8PT9VS',
last_name: 'Carrard',
exchange_id: 'exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c',
exchange_offer_token: 'cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f'
})
};
fetch('https://api.example.com/v1/car_exchange_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/car_exchange_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-8PT9VS',
'last_name' => 'Carrard',
'exchange_id' => 'exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c',
'exchange_offer_token' => 'cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f'
]),
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/car_exchange_commit"
payload := strings.NewReader("{\n \"booking_ref\": \"JNK-8PT9VS\",\n \"last_name\": \"Carrard\",\n \"exchange_id\": \"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c\",\n \"exchange_offer_token\": \"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f\"\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/car_exchange_commit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"booking_ref\": \"JNK-8PT9VS\",\n \"last_name\": \"Carrard\",\n \"exchange_id\": \"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c\",\n \"exchange_offer_token\": \"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/car_exchange_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-8PT9VS\",\n \"last_name\": \"Carrard\",\n \"exchange_id\": \"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c\",\n \"exchange_offer_token\": \"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f\"\n}"
response = http.request(request)
puts response.read_body{
"exchange_id": "exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c",
"state": "confirmed",
"delta": {
"direction": "add_collect",
"amount": {
"value": 41250,
"currency": "EUR",
"decimal_places": 2
}
},
"new_total": {
"value": 41250,
"currency": "EUR",
"decimal_places": 2
},
"paid": {
"value": 41250,
"currency": "EUR",
"decimal_places": 2
},
"refund_pending_review": true,
"stripe_refund_id": "<string>",
"rejected_reason": "<string>",
"completed_at": "2026-09-02T15:18:00Z",
"awaiting_payment": true,
"checkout_url": "https://book.gojinko.com/checkout?sid=1048576",
"stripe_checkout_url": "<string>"
}{
"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": "the exchange preview is out of date — run a new preview and choose again"
}
}{
"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"
}
}refund_pending_review. When it costs more, the response is awaiting_payment: true with a checkout_url: send the customer there; the booking is rewritten only once the difference is paid, poll car_exchange_status. A provider rejection is a 200 with state: rejected and the booking stands as it was. Safe to call twice, a committed exchange is reported as it stands rather than repeated. 409 means the exchange cannot run as previewed: another exchange is in flight, or the preview / offer went stale, run a new preview.Authorizations
Body
Jinko booking reference from the confirmation email.
1"JNK-8PT9VS"
Lead traveler's last name, exactly as on the booking.
1"Carrard"
The "exc_…" reference from a preceding car_exchange_preview. Binds the commit to the prices the customer saw.
1"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c"
The "cxo_…" token of the chosen offer from that same preview.
1"cxo_3f9a1c7e5b2d4a8c6e1f3b5d7a9c2e4f"
Show child attributes
Show child attributes
Response
Exchange result
"exc_7c1e4b9a2d8f4e6b9a3c5d7e1f2a4b6c"
One of preview (previewed, nothing committed), awaiting_payment, processing, confirmed, rejected, failed.
"confirmed"
The price difference between a candidate and what was already paid, and which way the money moves. Compared against paid on the preview, never against the candidate total.
Show child attributes
Show child attributes
The replacement rental's pay-now total.
Show child attributes
Show child attributes
What the customer had paid before the exchange.
Show child attributes
Show child attributes
true means the booking IS exchanged but the refund of the difference needs manual review before it is issued.
Why the provider declined the exchange. The booking stands as it was.
RFC 3339, set once the attempt is terminal.
"2026-09-02T15:18:00Z"
true means the replacement costs more and the difference must be paid first: send the customer to checkout_url. The booking is rewritten only after the payment is authorized — poll car_exchange_status.
Hosted payment page for the price difference. Present only while awaiting_payment.
"https://book.gojinko.com/checkout?sid=1048576"
The Stripe Checkout session behind checkout_url, for callers that redirect to Stripe directly.
