Create 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.
Request Structure
Required Fields
| Field | Type | Description |
|---|---|---|
account_id | integer | Stock account ID (owner of the goods) |
location_id | integer | Dock location ID (must have available_in_booking_diary enabled) |
consignment_reference | string | Consignment reference number |
direction | string | One of: inbound, outbound |
hazardous | boolean | Whether the goods are hazardous |
start_time | datetime | Booking start time (ISO 8601) |
end_time | datetime | Booking end time (must be after start_time) |
Optional Fields
| Field | Type | Description |
|---|---|---|
account_reference | string | Account’s own reference |
booking_reference | string | Booking reference number |
container_number | string | Container number (for container intake) |
haulier | string | Haulier/carrier name |
intake_type | string | One of: container, part_load, full_load |
notes | string | Free text notes |
status | string | One of: pending, arrived, departed, fail_to_arrive, cancelled (default: pending) |
storage_unit_quantity | integer | Expected number of storage units (e.g., pallets) |
Slot Availability Validation
The system validates that the requested time slot is available before creating the booking.Opening Times Compliance
The location (or its parent warehouse) may have opening times configured. Opening hours vary by day of week, so the system checks the specific day’s hours for both start and end times. Error Response (code 295):No Overlapping Bookings
The system uses time range overlap detection to ensure no other booking exists at the same location during the requested period. Error Response (code 296):Finding Available Slots Workflow
To find an available time slot before creating a booking:- Query eligible locations - Find locations where
available_in_booking_diaryis true (via GraphQL) - Query existing bookings - Retrieve bookings for the target date range and location (via GraphQL)
- Calculate gaps - Identify time gaps that fall within the location’s opening hours for the target day of week
- Create booking - POST /bookings with the chosen slot
Authorizations
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 - 1001Payment confirmation supplied by the bank on booking creation (request input).
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).
Payment confirmation echoed back on the booking (response projection). The monetary
amounts (customer total, net due to Safariat, surcharge) are exposed via pricing.
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.
Initialed (e.g. 'A. B.') for PII protection in lists.
ID of the settlement this booking is included in (null if not yet invoiced).
Pre-signed URL to the PDF voucher (7-day TTL).