Resvisor Acente API — Geliştirici Kılavuzu
Acentenizin kendi sisteminden otel, tur ve cruise araması + rezervasyonu yapabilmesi için REST API. Bu kılavuz, entegrasyonu adım adım anlatır: kimlik, istek/yanıt biçimleri, gerçek örnekler ve hata yönetimi.
Base URL · api.woxxtravelglobal.com JSON · camelCase JWT BearerGiriş
Acente API, web B2B portalında elle yaptığınız işi (arama + rezervasyon) programatik olarak yapmanızı sağlar. Her acentenin kendi API anahtarı vardır; anahtar acente kimliğinizi taşır, böylece markup, kaynak-pazar ve muhasebe otomatik uygulanır.
- Base URL:
https://api.woxxtravelglobal.com - İçerik tipi:
application/json· tüm alanlar camelCase - Kimlik:
/v1/auth/tokenhariç tüm isteklerAuthorization: Bearer <token>ister - Etkileşimli keşif: Swagger UI → api.woxxtravelglobal.com (üstten "Acente API v1" seçin)
Hızlı başlangıç
- Anahtar alın. Resvisor destek ekibinden ([email protected])
apiKey+secrettalep edin. İki ortam vardır: sandbox (test) ve live (gerçek satış). - Token üretin.
POST /v1/auth/tokenile anahtarınızı kısa ömürlü bir JWT'ye çevirin (1 saat geçerli). - Çağrı yapın. Token'ı
Authorization: Bearerbaşlığında gönderip arama/booking yapın.
# 1) Token al
curl -s -X POST https://api.woxxtravelglobal.com/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"apiKey":"pk_sandbox_...","secret":"sk_sandbox_..."}'
# → { "token":"eyJhbGci...", "expires_in":3599, "environment":"sandbox", ... }
# 2) Token ile otel ara
curl -s -X POST https://api.woxxtravelglobal.com/v1/hotels/search \
-H "Authorization: Bearer eyJhbGci..." \
-H "Content-Type: application/json" \
-d '{"checkIn":"2026-08-08","checkOut":"2026-08-12","adults":2,"destinationId":1176,"currency":"EUR"}'
Kimlik doğrulama
API anahtarı + secret → kısa ömürlü JWT. Bu endpoint token gerektirmez (anonim).
İstek
{ "apiKey": "pk_sandbox_...", "secret": "sk_sandbox_..." }
Yanıt 200
{
"token": "eyJhbGciOiJ...<jwt>...",
"token_type": "Bearer",
"expires_at": "2026-06-24T07:02:47Z",
"expires_in": 3599,
"environment": "sandbox",
"scopes": "hotel,tour,cruise"
}
expires_in (saniye) veya expires_at (ISO) değerine göre, süre dolmadan yenileyin. Token'ı güvenli saklayın; sızarsa kısa ömrü riski sınırlar.Swagger üzerinden de deneyebilirsiniz — endpoint'i açıp "Try it out" diyin:
İstek temelleri
Yetkilendirme başlığı
Token dışındaki tüm endpoint'ler şu başlığı ister:
Authorization: Bearer <token>
Content-Type: application/json
Yanıt zarfı
Okuma (READ) endpoint'lerinin başarılı yanıtı tek biçimdedir — her zaman response.data okuyun:
{ "data": <içerik>, "meta": { "requestId": "...", "environment": "sandbox" } }
| Zarflı | /v1/me, /v1/destinations, /v1/hotels/{id}, hotels|tours|cruises/search |
|---|---|
| Zarfsız | /v1/auth/token (token doğrudan), /v1/bookings* (işlem sonucu), hata yanıtları |
Hata biçimi
Tüm hatalar tek biçim döner:
{ "error": { "code": "invalid_secret", "message": "Geçersiz secret." } }
Arama — otel / tur / cruise
Üçü de yanıt zarfında döner: { "data": <birleşik sonuç>, "meta": {...} }. data içinde ilgili dizi dolu gelir: hotelData[], tourPackageData[] + groupedTours[], cruiseData[].
İstek
{
"checkIn": "2026-08-08", "checkOut": "2026-08-12",
"adults": 2, "children": 0,
"destinationId": 1176, "currency": "EUR",
"sourceCountryCode": "TR"
}
destinationId kaynağı: GET /v1/destinations. Alternatifler: destinationCode, cityId, destinationZoneId, hotelIds[]. sourceCountryCode = sattığınız pazar (kaynak-pazar elemesi).
Yanıt 200 (data içeriği — alanlar kısaltıldı)
{
"count": 1,
"hotelData": [{
"hotelId": 2007482, "name": "Rixos Beldibi", "stars": 5, "rating": 4.6,
"cityName": "Antalya", "countryCode": "TR", "thumbnail": "https://...",
"rooms": [{
"name": "CLASSIC ROOM MOUNTAIN VIEW", "roomId": 866,
"rates": [{
"rateKey": "2007482|866|BED_AND_BREAKFAST|ALL INCLUSIVE|1786147200|1786406400",
"net": "24036.77", "sellingRate": "24036.77", "currencyCode": "TRY",
"boardName": "ALL INCLUSIVE", "allotment": 10,
"adults": 2, "children": 0,
"cancellationPolicies": [{ "amount": 453.4, "from": "2026-08-05T00:00:00Z" }]
}]
}]
}],
"tourPackageData": [], "cruiseData": []
}
rateKey rezervasyonun çapasıdır — seçilen oda/pansiyon/tarih kombinasyonunu taşır ve POST /v1/bookings snapshot'ında kullanılır. Markup liste fiyatında görünmez (web ile birebir); acente marjı otel-detay ve booking'de uygulanır.{ "startDate":"2026-08-08", "endDate":"2026-08-20", "adults":2, "destinationId":1176, "currency":"EUR" }
Yanıt: tourPackageData[] (ham paketler) + groupedTours[] (kalkış/fiyata göre gruplanmış — listeleme için bunu kullanın).
Tarih aralığı: startDate–endDate en fazla 62 gün; daha geniş aralık 400 range_too_wide döner (daha uzun dönem için ardışık aramalar yapın).
{ "departureFrom":"2026-08-01", "departureTo":"2026-12-01", "adults":2, "currency":"EUR" }
Yanıt: cruiseData[] — her sefer için gemi, rota, tarih, kabin tipleri, fiyat, dahil/hariç servisler.
İçerik & referans
Otelin statik içeriği (fiyat değil — fiyat için hotels/search içinde hotelIds=[id]).
{
"hotelId": 2007482, "name": "Rixos Beldibi",
"address": "...", "city": "Antalya", "countryCode": "TR",
"latitude": 36.4, "longitude": 30.5, "distanceToSeaM": 120,
"shortDescription": "...", "photos": ["..."], "amenities": ["..."],
"checkInTime": "14:00", "checkOutTime": "12:00", "rating": 4.6
}
Arama parametresi için destinasyon lookup. q yoksa popüler destinasyonlar döner. Yanıt zarflı (data = destinasyon dizisi).
{
"data": [
{ "destinationId": 830, "code": "ABS", "name": "Abu Simbel", "countryCode": "EG" },
{ "destinationId": 3091, "code": "ID9", "name": "Bangka - Belitung", "countryCode": "ID" }
],
"meta": { "requestId": "...", "environment": "sandbox" }
}
Rezervasyon
Otel / tur / cruise rezervasyonu. Idempotency-Key başlığı ZORUNLU (çift booking önleme — aynı key → aynı yanıt). Acente kimliği token'dan alınır (sunucu-otoriter).
İstek başlıkları
Authorization: Bearer <token>
Idempotency-Key: <benzersiz-değer-siz-üretirsiniz>
Content-Type: application/json
Gövde — otel / tur
{
"type": "hotel",
"data": { /* sağlayıcı snapshot'ı — rateKey vb. (search'ten) */ },
"amount": 24036.77,
"currency": "TRY",
"clientReference": "PARTNER-REF-123",
"lang": "tr"
}
Gövde — cruise (sunucu fiyatlı; amount gerekmez)
{ "type":"cruise", "data": { "sailingId":12, "cabinId":5, "adults":2, "childAges":[], "guestName":"Ahmet Yılmaz", "email":"[email protected]", "phone":"+90..." } }
Yanıt 200: başarılı booking sonucu. GET /v1/bookings/{reference} için referans bu yanıttan alınır.
Rezervasyon durumu. Yalnız kendi booking'inizi görürsünüz (başkasınınki → 404).
{
"reference": "...", "clientReference": "PARTNER-REF-123", "bookingStatus": "...",
"reservationId": 12345, "guestName": "Ahmet Yılmaz",
"checkinDate": "2026-08-08", "checkoutDate": "2026-08-12",
"totalPrice": 24036.77, "currency": "TRY"
}
İptal talebi oluşturur (anında iptal değil — sağlayıcı-iptali + iade Resvisor onayında işlenir).
// istek
{ "reason": "müşteri talebi", "description": "..." }
// yanıt 200
{ "reference": "...", "bookingReference": "...", "status": "PENDING" }
Kimlik bilgisi — /v1/me
Token'ın kime ait olduğunu döner (entegrasyon doğrulama için pratik bir "whoami").
{
"data": { "agencyId": "5", "environment": "sandbox", "apiKeyId": "1", "role": "ACENTE_CALISANI" },
"meta": { "requestId": "0HN...:00000001", "environment": "sandbox" }
}
Sandbox & Live ortamları
| Konu | Sandbox aktif | Live yakında |
|---|---|---|
| Amaç | Entegrasyon testi | Gerçek satış |
| Arama | Gerçek sonuç döner | Gerçek sonuç döner |
| Booking | Mock — gerçek kayıt oluşmaz, SBX-... referans döner | Gerçek rezervasyon (cari/kredi hesabı ile) |
| Günlük kota | 200 (varsayılan) | 5000 (varsayılan) |
SBX-... referans döner ama GET /v1/bookings/{ref} ile sorgulanamaz (sandbox_ephemeral döner). Böylece test ortamı gerçek veriyi kirletmez.env=live anahtarla booking denemesi şimdilik 403 live_booking_unavailable döner. Canlı satış, pilot onayı sonrası açılacaktır; o zaman ödeme kart değil acente cari/kredi hesabı üzerinden işler (limit dahilinde satış → hesaba borç). Arama her iki ortamda da çalışır.Idempotency
Booking'de Idempotency-Key başlığı zorunludur. Davranış:
- Aynı key tekrar gönderilir + ilk istek tamamlandıysa → ilk yanıt aynen döner (yeni booking oluşmaz).
- İlk istek hâlâ işleniyorsa (<15 dk) →
409 in_progress. - İlk deneme başarısız olduysa → aynı key ile güvenle tekrar deneyebilirsiniz.
Rate limit & kota
- Dakikalık burst: 60 istek/dk (varsayılan). Aşımda
429 rate_limited+Retry-Afterbaşlığı. - Günlük kota: sandbox 200 / live 5000 (varsayılan). Aşımda
403 quota_exceeded. - Token üretimi: anahtar başına 20 deneme/dk (brute-force koruması).
Limitler anahtarınıza özel ayarlanabilir — ihtiyaç halinde [email protected] adresine bildirin.
IP allow-list (opsiyonel sertleştirme)
Güvenlik için anahtarınız belirli IP'lerle sınırlandırılabilir. Liste tanımlıysa, dışındaki bir IP'den gelen istek 403 ip_not_allowed alır. Liste boşsa kısıt yoktur.
/64 bloğu önerilir (IPv6 adres bitleri değişebilir).Webhook'lar
Rezervasyon durum değişimlerini (özellikle asenkron iptal/iade) yoklama yapmadan, imzalı bir push bildirimiyle öğrenebilirsiniz. Bir endpoint URL'i kaydedersiniz; olay olduğunda Resvisor oraya imzalı bir POST gönderir.
- İmza: her teslimat
X-Travapi-Signature(HMAC-SHA256) taşır — gövdeyi paylaşılan secret ile doğrulayın. - Yeniden deneme: başarısız teslimatlar otomatik tekrar edilir (idempotent — aynı olay birden çok kez gelebilir, olay id'siyle tekilleştirin).
Hata kodları
| HTTP | code | Anlam |
|---|---|---|
| 400 | invalid_request · unsupported_type · idempotency_key_required | Hatalı / eksik istek |
| 401 | invalid_key · invalid_secret · key_revoked · key_suspended · invalid_token · no_agency | Kimlik |
| 403 | forbidden_scope · live_booking_unavailable · quota_exceeded · ip_not_allowed | Yetki / kota / IP |
| 404 | not_found · snapshot_not_found · sandbox_ephemeral | Bulunamadı / kapsam dışı |
| 409 | in_progress · cancellation_already_requested | Idempotency / eşzamanlılık |
| 429 | rate_limited (+Retry-After) | Dakikalık burst / token brute-force |
| 502 | gateway_error · booking_error · content_error | Üst sistem erişilemedi |
İpuçları & iyi pratikler
- Token'ı yeniden kullanın (her istek için yeni token almayın);
expires_indolmadan yenileyin. - Her zaman
response.dataokuyun (READ endpoint'leri) — iç şema değişse de dış zarf sabittir. - Kapsamınız (scopes) dışında ürün çağırmayın →
403 forbidden_scope. - Önce sandbox'ta uçtan uca test edin; live anahtar yalnız onaylı entegrasyon için verilir.
- rateKey'i değiştirmeden taşıyın — booking snapshot'ı bu çapaya dayanır.
- 429 alırsanız
Retry-Afterbaşlığına saygı gösterip geri çekilin (exponential backoff).