curl --request POST \
--url https://api.safarapi.com/api/partner/v1/bookings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-Signature: <x-signature>' \
--header 'X-Timestamp: <x-timestamp>' \
--data '
{
"quote_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"partner_reference": "<string>",
"traveler": {
"first_name": "<string>",
"last_name": "<string>",
"email": "jsmith@example.com",
"phone": "<string>",
"customer_reference": "<string>"
},
"rooms_allocation": [
{
"room_index": 1,
"occupants": [
{
"age": 8
}
]
}
],
"payment_confirmation": {
"bank_payment_ref": "<string>",
"paid_amount_total": 1116,
"paid_amount_net": 949.9,
"paid_at": "2023-11-07T05:31:56Z"
},
"special_requests": "<string>"
}
'import requests
url = "https://api.safarapi.com/api/partner/v1/bookings"
payload = {
"quote_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"partner_reference": "<string>",
"traveler": {
"first_name": "<string>",
"last_name": "<string>",
"email": "jsmith@example.com",
"phone": "<string>",
"customer_reference": "<string>"
},
"rooms_allocation": [
{
"room_index": 1,
"occupants": [{ "age": 8 }]
}
],
"payment_confirmation": {
"bank_payment_ref": "<string>",
"paid_amount_total": 1116,
"paid_amount_net": 949.9,
"paid_at": "2023-11-07T05:31:56Z"
},
"special_requests": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"X-Timestamp": "<x-timestamp>",
"X-Signature": "<x-signature>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-Timestamp': '<x-timestamp>',
'X-Signature': '<x-signature>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
quote_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
partner_reference: '<string>',
traveler: {
first_name: '<string>',
last_name: '<string>',
email: 'jsmith@example.com',
phone: '<string>',
customer_reference: '<string>'
},
rooms_allocation: [{room_index: 1, occupants: [{age: 8}]}],
payment_confirmation: {
bank_payment_ref: '<string>',
paid_amount_total: 1116,
paid_amount_net: 949.9,
paid_at: '2023-11-07T05:31:56Z'
},
special_requests: '<string>'
})
};
fetch('https://api.safarapi.com/api/partner/v1/bookings', 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.safarapi.com/api/partner/v1/bookings",
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([
'quote_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'partner_reference' => '<string>',
'traveler' => [
'first_name' => '<string>',
'last_name' => '<string>',
'email' => 'jsmith@example.com',
'phone' => '<string>',
'customer_reference' => '<string>'
],
'rooms_allocation' => [
[
'room_index' => 1,
'occupants' => [
[
'age' => 8
]
]
]
],
'payment_confirmation' => [
'bank_payment_ref' => '<string>',
'paid_amount_total' => 1116,
'paid_amount_net' => 949.9,
'paid_at' => '2023-11-07T05:31:56Z'
],
'special_requests' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-Signature: <x-signature>",
"X-Timestamp: <x-timestamp>"
],
]);
$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.safarapi.com/api/partner/v1/bookings"
payload := strings.NewReader("{\n \"quote_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"partner_reference\": \"<string>\",\n \"traveler\": {\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"phone\": \"<string>\",\n \"customer_reference\": \"<string>\"\n },\n \"rooms_allocation\": [\n {\n \"room_index\": 1,\n \"occupants\": [\n {\n \"age\": 8\n }\n ]\n }\n ],\n \"payment_confirmation\": {\n \"bank_payment_ref\": \"<string>\",\n \"paid_amount_total\": 1116,\n \"paid_amount_net\": 949.9,\n \"paid_at\": \"2023-11-07T05:31:56Z\"\n },\n \"special_requests\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-Timestamp", "<x-timestamp>")
req.Header.Add("X-Signature", "<x-signature>")
req.Header.Add("Authorization", "Bearer <token>")
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.safarapi.com/api/partner/v1/bookings")
.header("Idempotency-Key", "<idempotency-key>")
.header("X-Timestamp", "<x-timestamp>")
.header("X-Signature", "<x-signature>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"quote_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"partner_reference\": \"<string>\",\n \"traveler\": {\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"phone\": \"<string>\",\n \"customer_reference\": \"<string>\"\n },\n \"rooms_allocation\": [\n {\n \"room_index\": 1,\n \"occupants\": [\n {\n \"age\": 8\n }\n ]\n }\n ],\n \"payment_confirmation\": {\n \"bank_payment_ref\": \"<string>\",\n \"paid_amount_total\": 1116,\n \"paid_amount_net\": 949.9,\n \"paid_at\": \"2023-11-07T05:31:56Z\"\n },\n \"special_requests\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.safarapi.com/api/partner/v1/bookings")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-Timestamp"] = '<x-timestamp>'
request["X-Signature"] = '<x-signature>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"quote_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"partner_reference\": \"<string>\",\n \"traveler\": {\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"phone\": \"<string>\",\n \"customer_reference\": \"<string>\"\n },\n \"rooms_allocation\": [\n {\n \"room_index\": 1,\n \"occupants\": [\n {\n \"age\": 8\n }\n ]\n }\n ],\n \"payment_confirmation\": {\n \"bank_payment_ref\": \"<string>\",\n \"paid_amount_total\": 1116,\n \"paid_amount_net\": 949.9,\n \"paid_at\": \"2023-11-07T05:31:56Z\"\n },\n \"special_requests\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyCreate a booking
Creates a booking in CONFIRMED status from a valid quote. The booking is
confirmed immediately (no PENDING/24h window) — payment is recorded via the
payment_confirmation fields.
The traveler (traveler field) is resolved / created as a Keycloak shadow user
on the Safariat side (see ADR-018). The PDF voucher and confirmation email are sent
directly to the traveler at the provided email address.
Idempotency
The Idempotency-Key header is required. Same key + same body → the cached
response is returned. Same key + different body → 409 Conflict. Retained for 24 h.
Payment validation
payment_confirmation.paid_amount_net must equal the
quote.pricing.amount_due_to_safariat.amount returned by POST /quotes (anti-fraud).
Otherwise → 422 payment.amount.mismatch.
curl --request POST \
--url https://api.safarapi.com/api/partner/v1/bookings \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-Signature: <x-signature>' \
--header 'X-Timestamp: <x-timestamp>' \
--data '
{
"quote_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"partner_reference": "<string>",
"traveler": {
"first_name": "<string>",
"last_name": "<string>",
"email": "jsmith@example.com",
"phone": "<string>",
"customer_reference": "<string>"
},
"rooms_allocation": [
{
"room_index": 1,
"occupants": [
{
"age": 8
}
]
}
],
"payment_confirmation": {
"bank_payment_ref": "<string>",
"paid_amount_total": 1116,
"paid_amount_net": 949.9,
"paid_at": "2023-11-07T05:31:56Z"
},
"special_requests": "<string>"
}
'import requests
url = "https://api.safarapi.com/api/partner/v1/bookings"
payload = {
"quote_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"partner_reference": "<string>",
"traveler": {
"first_name": "<string>",
"last_name": "<string>",
"email": "jsmith@example.com",
"phone": "<string>",
"customer_reference": "<string>"
},
"rooms_allocation": [
{
"room_index": 1,
"occupants": [{ "age": 8 }]
}
],
"payment_confirmation": {
"bank_payment_ref": "<string>",
"paid_amount_total": 1116,
"paid_amount_net": 949.9,
"paid_at": "2023-11-07T05:31:56Z"
},
"special_requests": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"X-Timestamp": "<x-timestamp>",
"X-Signature": "<x-signature>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-Timestamp': '<x-timestamp>',
'X-Signature': '<x-signature>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
quote_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
partner_reference: '<string>',
traveler: {
first_name: '<string>',
last_name: '<string>',
email: 'jsmith@example.com',
phone: '<string>',
customer_reference: '<string>'
},
rooms_allocation: [{room_index: 1, occupants: [{age: 8}]}],
payment_confirmation: {
bank_payment_ref: '<string>',
paid_amount_total: 1116,
paid_amount_net: 949.9,
paid_at: '2023-11-07T05:31:56Z'
},
special_requests: '<string>'
})
};
fetch('https://api.safarapi.com/api/partner/v1/bookings', 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.safarapi.com/api/partner/v1/bookings",
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([
'quote_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'partner_reference' => '<string>',
'traveler' => [
'first_name' => '<string>',
'last_name' => '<string>',
'email' => 'jsmith@example.com',
'phone' => '<string>',
'customer_reference' => '<string>'
],
'rooms_allocation' => [
[
'room_index' => 1,
'occupants' => [
[
'age' => 8
]
]
]
],
'payment_confirmation' => [
'bank_payment_ref' => '<string>',
'paid_amount_total' => 1116,
'paid_amount_net' => 949.9,
'paid_at' => '2023-11-07T05:31:56Z'
],
'special_requests' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-Signature: <x-signature>",
"X-Timestamp: <x-timestamp>"
],
]);
$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.safarapi.com/api/partner/v1/bookings"
payload := strings.NewReader("{\n \"quote_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"partner_reference\": \"<string>\",\n \"traveler\": {\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"phone\": \"<string>\",\n \"customer_reference\": \"<string>\"\n },\n \"rooms_allocation\": [\n {\n \"room_index\": 1,\n \"occupants\": [\n {\n \"age\": 8\n }\n ]\n }\n ],\n \"payment_confirmation\": {\n \"bank_payment_ref\": \"<string>\",\n \"paid_amount_total\": 1116,\n \"paid_amount_net\": 949.9,\n \"paid_at\": \"2023-11-07T05:31:56Z\"\n },\n \"special_requests\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-Timestamp", "<x-timestamp>")
req.Header.Add("X-Signature", "<x-signature>")
req.Header.Add("Authorization", "Bearer <token>")
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.safarapi.com/api/partner/v1/bookings")
.header("Idempotency-Key", "<idempotency-key>")
.header("X-Timestamp", "<x-timestamp>")
.header("X-Signature", "<x-signature>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"quote_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"partner_reference\": \"<string>\",\n \"traveler\": {\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"phone\": \"<string>\",\n \"customer_reference\": \"<string>\"\n },\n \"rooms_allocation\": [\n {\n \"room_index\": 1,\n \"occupants\": [\n {\n \"age\": 8\n }\n ]\n }\n ],\n \"payment_confirmation\": {\n \"bank_payment_ref\": \"<string>\",\n \"paid_amount_total\": 1116,\n \"paid_amount_net\": 949.9,\n \"paid_at\": \"2023-11-07T05:31:56Z\"\n },\n \"special_requests\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.safarapi.com/api/partner/v1/bookings")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-Timestamp"] = '<x-timestamp>'
request["X-Signature"] = '<x-signature>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"quote_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"partner_reference\": \"<string>\",\n \"traveler\": {\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"email\": \"jsmith@example.com\",\n \"phone\": \"<string>\",\n \"customer_reference\": \"<string>\"\n },\n \"rooms_allocation\": [\n {\n \"room_index\": 1,\n \"occupants\": [\n {\n \"age\": 8\n }\n ]\n }\n ],\n \"payment_confirmation\": {\n \"bank_payment_ref\": \"<string>\",\n \"paid_amount_total\": 1116,\n \"paid_amount_net\": 949.9,\n \"paid_at\": \"2023-11-07T05:31:56Z\"\n },\n \"special_requests\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyAuthorizations
API key authentication. Issued by the Safariat admin or via the partner portal. The secret is shown only once at generation.
Production sk_live_* keys must additionally sign every write request
(POST/PUT/DELETE) with the X-Timestamp and X-Signature headers.
Sandbox sk_test_* keys are exempt from request signing: X-Signature and
X-Timestamp are not required for writes in the sandbox (so the developer-portal
"Try it" playground works end to end). The Idempotency-Key header remains
required on writes in both environments.
Headers
Opaque, client-generated string unique per logical operation (a UUID v4 is
recommended but any non-blank value up to 255 chars is accepted). Same key +
same body = the cached response is returned with the Idempotent-Replayed: true
header. Same key + different body = 409 Conflict. Retained for 24 h.
255Unix timestamp in seconds at the time the request is issued. Validity window
±5 minutes — beyond that the request is rejected. Required for production
sk_live_* keys only; not required for sandbox sk_test_* keys.
hex(HMAC_SHA256(secret, "{X-Timestamp}\n{METHOD}\n{path}\n{body}")).
The path includes the query string. The body is the exact JSON representation
sent — any reformatting invalidates the signature. Required for production
sk_live_* keys only; not required for sandbox sk_test_* keys.
^[a-f0-9]{64}$Body
The bank's internal reference (booking ID on the bank side). Unique per partner. Lets the bank locate the Safariat booking from its own primary key.
1 - 100Show child attributes
Show child attributes
1Show child attributes
Show child attributes
Payment confirmation supplied by the bank on booking creation (request input).
Show child attributes
Show child attributes
Special requests (dietary requirements, accessibility, etc.).
1000Response
Booking created and confirmed
"MV-AB7X92"
The partner channel skips the PENDING status (no payment-wait window).
Additional internal Safariat statuses (PAYMENT_PROCESSING, REFUND_PENDING)
are never exposed on this API.
CONFIRMED, COMPLETED, CANCELLED Revenue breakdown with the real surcharge recorded at booking (indicative = false).
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Payment confirmation echoed back on the booking (response projection). The monetary
amounts (customer total, net due to Safariat, surcharge) are exposed via pricing.
Show child attributes
Show child attributes
ID of the adventure snapshot captured at creation (see ADR-013). Guarantees the traveler receives the experience as it was at the time of booking.
Show child attributes
Show child attributes
Initialed (e.g. 'A. B.') for PII protection in lists.
ID of the settlement this booking is included in (null if not yet invoiced).
true when the booking was issued with a sandbox key. A sandbox booking never reaches
an operator, never produces a settlement, and its notifications are limited to the
traveller address supplied in the request, with a [TEST] subject prefix.
Pre-signed URL to the PDF voucher (7-day TTL).