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 Bearer

Giriş

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/token hariç tüm istekler Authorization: Bearer <token> ister
  • Etkileşimli keşif: Swagger UI → api.woxxtravelglobal.com (üstten "Acente API v1" seçin)

Hızlı başlangıç

  1. Anahtar alın. Resvisor destek ekibinden ([email protected]) apiKey + secret talep edin. İki ortam vardır: sandbox (test) ve live (gerçek satış).
  2. Token üretin. POST /v1/auth/token ile anahtarınızı kısa ömürlü bir JWT'ye çevirin (1 saat geçerli).
  3. Çağrı yapın. Token'ı Authorization: Bearer baş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

POST/v1/auth/token

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"
}
Token ömrü 1 saattir. 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[].

POST/v1/hotels/search

İ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.
POST/v1/tours/search
{ "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).

POST/v1/cruises/search
{ "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

GET/v1/hotels/{id}?provider=...

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
}
GET/v1/destinations?q=...&countryCode=...

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

POST/v1/bookings

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.

GET/v1/bookings/{reference}

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"
}
POST/v1/bookings/{reference}/cancel

İ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

GET/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ı

KonuSandbox aktifLive yakında
AmaçEntegrasyon testiGerçek satış
AramaGerçek sonuç dönerGerçek sonuç döner
BookingMock — gerçek kayıt oluşmaz, SBX-... referans dönerGerçek rezervasyon (cari/kredi hesabı ile)
Günlük kota200 (varsayılan)5000 (varsayılan)
Sandbox booking efemerdir. Test rezervasyonu gerçek bir kayıt yaratmaz; gerçekçi bir SBX-... referans döner ama GET /v1/bookings/{ref} ile sorgulanamaz (sandbox_ephemeral döner). Böylece test ortamı gerçek veriyi kirletmez.
Live booking şu an kapalıdır. 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.
Her yeni rezervasyon için benzersiz bir key üretin (ör. kendi sipariş numaranız). Ağ hatası sonrası aynı key ile tekrar deneyin — çift çekim olmaz.

Rate limit & kota

  • Dakikalık burst: 60 istek/dk (varsayılan). Aşımda 429 rate_limited + Retry-After baş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.

Statik IP'niz varsa [email protected] adresine bildirin; anahtarınıza kaydedilir. IPv6 üzerinden bağlanıyorsanız tek adres yerine /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).
Webhook erişimi için [email protected] ile iletişime geçin.

Hata kodları

HTTPcodeAnlam
400invalid_request · unsupported_type · idempotency_key_requiredHatalı / eksik istek
401invalid_key · invalid_secret · key_revoked · key_suspended · invalid_token · no_agencyKimlik
403forbidden_scope · live_booking_unavailable · quota_exceeded · ip_not_allowedYetki / kota / IP
404not_found · snapshot_not_found · sandbox_ephemeralBulunamadı / kapsam dışı
409in_progress · cancellation_already_requestedIdempotency / eşzamanlılık
429rate_limited (+Retry-After)Dakikalık burst / token brute-force
502gateway_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_in dolmadan yenileyin.
  • Her zaman response.data okuyun (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-After başlığına saygı gösterip geri çekilin (exponential backoff).