Loading…
Loading…
Loading…
Programmatic access to Savunda inventory, availability, quotes, bookings, and lifecycle events. Built for OTA platforms, large agencies and corporate procurement systems that need real-time integration.
The Savunda Partner API is a JSON-over-HTTPS REST API. Every request requires a valid partner Bearer token (obtained from your portal). All times are UTC; we surface local-time equivalents where helpful. Prices are in USD by default with multi-currency support via the currency param.
All API calls authenticate via a partner Bearer token in the Authorization header. Production keys begin with savunda_live_; sandbox keys begin with savunda_test_. Generate, rotate, and revoke keys from the partner portal.
curl -X GET https://api.savunda.com/partner/v1/packages \ -H "Authorization: Bearer savunda_live_5a8b...c4f1" \ -H "Accept: application/json"
Standard HTTP semantics. Errors return a JSON body with error.code, error.message, and error.request_id — the last is useful when reaching out to support.
120 requests / minute per partner key in production; 30 / minute in sandbox. Burst allowance of 30 in any 5-second window. Response headers X-RateLimit-Remaining and X-RateLimit-Reset show your current state. Higher limits available for high-volume partners on request.
All endpoints are versioned via path prefix (/partner/v1/...). We commit to 12 months notice before sunsetting a version. Breaking changes go into the next version; non-breaking additions land in the current version and are announced in the changelog and via webhook event packages.updated.
Returns all bookable packages with your partner net rate alongside the public retail rate. Use this once at integration time, then cache for ~24h — we publish material changes via the packages.updated webhook event.
| Query param | Type | Description |
|---|---|---|
| currency | string | ISO 4217. Default USD. Supported: USD, EUR, GBP, AUD, ZAR. |
| tier | enum | Filter accommodation tier: mid, premium, ultra. Omit for all. |
| circuit | enum | north, south, zanzibar, kilimanjaro. Omit for all. |
{
"data": [
{
"id": "5-day-northern-circuit",
"name": "5-Day Northern Circuit Safari",
"duration_days": 5,
"parks": ["tarangire", "ngorongoro", "serengeti"],
"pricing": {
"retail_pp": 2580, // USD, shoulder season
"net_pp": 2012, // your contracted net
"commission_rate": 0.22,
"currency": "USD"
},
"capacity": { "min_pax": 2, "max_pax": 6 },
"updated_at": "2026-05-14T08:14:00Z"
}
],
"meta": { "total": 12, "version": "v1" }
}Check real-time availability and dynamic pricing for a package on a specific date and pax count. Returns available: true only when we can confirm vehicle, guide, and lodge inventory.
| Query param | Type | Description |
|---|---|---|
| package* | string | Package id from /packages |
| start_date* | YYYY-MM-DD | Departure date (UTC). Must be ≥7 days out. |
| pax* | integer | 1–8. Larger groups: contact partnerships. |
| tier | enum | mid (default), premium, ultra |
| currency | string | Default USD |
{
"available": true,
"package": "7-day-classic-tanzania",
"start_date": "2026-09-14",
"pax": 4,
"pricing": {
"retail_pp": 4490, // high season uplift applied
"net_pp": 3502,
"group_discount": 0.03, // 4+ pax
"total_net": 13588,
"currency": "USD"
},
"capacity_remaining": 2,
"hold_eligible": true
}Generates a versioned quote with a 14-day price hold. Pass customer.email if you want us to attach the quote to a future booking flow; pass null for anonymous price-discovery calls. Returns a quote_idthat's used to create the booking.
POST /partner/v1/quote
Content-Type: application/json
Authorization: Bearer savunda_live_•••
{
"package": "7-day-classic-tanzania",
"start_date": "2026-09-14",
"pax": 4,
"tier": "mid",
"addons": ["balloon_morning"],
"currency": "EUR",
"customer": {
"email": "bauer@example.de",
"locale": "de-DE"
},
"external_reference": "ATLAS-Q-0098"
}{
"quote_id": "qt_2W4xY8aB1cD9fE3g",
"package": "7-day-classic-tanzania",
"total_net": 11200,
"total_retail": 14358,
"currency": "EUR",
"valid_until": "2026-05-31T23:59:59Z",
"dossier_preview_url": "https://api.savunda.com/quote/qt_2W4xY8aB1cD9fE3g/preview"
}POST /quote — current pricing applies. Inventory is not reserved by quote creation; create the booking to reserve.Converts a valid quote_id to a confirmed booking. Reserves vehicle, guide, and lodge inventory atomically; returns a booking_ref and a deposit-due timestamp. Idempotent via Idempotency-Key header.
POST /partner/v1/booking
Idempotency-Key: atlas_booking_20260518_0086
{
"quote_id": "qt_2W4xY8aB1cD9fE3g",
"lead_passenger": {
"first_name": "Klaus",
"last_name": "Bauer",
"email": "bauer@example.de",
"phone": "+49 30 5555 1234",
"nationality": "DE"
},
"passengers": [ /* …4 pax with name/DOB/passport */ ],
"external_reference": "ATLAS-2026-B-0086"
}{
"booking_ref": "AS-2026-001847",
"status": "pending_deposit",
"deposit_due_at": "2026-05-25T23:59:59Z",
"deposit_amount": 2800,
"balance_due_at": "2026-07-15T23:59:59Z",
"dossier_url": "https://api.savunda.com/booking/AS-2026-001847"
}Full lifecycle state for a booking — payments received, dossier links, trip status, change-log, cancellation policy applicable today. Cache freely for short windows; subscribe to webhooks for state changes.
Initiates cancellation. Cancellation policy applied automatically:
Returns the refund schedule and pushes a booking.cancelled webhook event. Refund flows through original payment channel.
Savunda posts JSON to your registered endpoint on every state change. Configure URL and subscribed events in the partner portal. Events delivered with HMAC-SHA256 signature for verification.
Every webhook request includes an Savunda-Signature header containing the HMAC-SHA256 of the request body using your webhook signing secret. Verify before processing.
const crypto = require('crypto');
function verifySavundaSignature(rawBody, headerSig, secret) {
const hmac = crypto.createHmac('sha256', secret);
const expected = hmac.update(rawBody, 'utf8').digest('hex');
return crypto.timingSafeEqual(
Buffer.from(headerSig),
Buffer.from(expected)
);
}
// In your Express handler:
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['savunda-signature'];
if (!verifySavundaSignature(req.body, sig, WEBHOOK_SECRET)) {
return res.status(400).send('Invalid signature');
}
// process JSON.parse(req.body) here
res.status(200).end();
});Savunda expects a 2xx response within 10 seconds. Any other response triggers exponential-backoff retries: 30s → 2m → 10m → 1h → 6h → 24h. After 24h of failures we mark the event as permanently_failed and surface it in your portal. Resend manually from there.
Event subscriptions: booking.confirmed, booking.cancelled, booking.modified, payment.received, quote.created, quote.expired, trip.dossier_ready, trip.in_progress.daily_update, trip.completed, commission.settled, packages.updated.
Every partner gets a sandbox key out-of-the-box. Sandbox replicates production exactly — same endpoints, same response shapes, same webhook events — but bookings are non-billable and don't reserve real inventory. Use it to integration-test before going live.
// Sandbox base URL https://sandbox-api.savunda.com/partner/v1 // Test card numbers success: 4242 4242 4242 4242 decline: 4000 0000 0000 0002 insufficient_funds: 4000 0000 0000 9995
X-Savunda-Test-Fast-Forward: 7d to any sandbox request to simulate the trip being 7 days closer to departure — useful for testing deposit, balance, and cancellation flows.Maintained by Savunda engineering. Each one wraps auth, retries, webhook verification, and typing. MIT licensed.