Перейти к основному содержимому

API Contracts — OpenAPI 3.0 Specifications

Версия: 1.0
Дата: 19.04.2026
Статус: Черновик

Обзор

API Contracts определяет полную REST API спецификацию в формате OpenAPI 3.0 для vitrip.store согласно API Layer. Экспонирует все gRPC интерфейсы из Business Services как RESTful API для фронтенда из Clients Layer и Partner API для B2B интеграций. Использует схемы данных из Database Schema и реализует принцип "Веб-сайт и внешние партнёры используют одни и те же API" из Архитектура платформы.

API Architecture: См. диаграмму vitrip_api_structure.jpg — полная схема REST endpoints с HTTP методами, аутентификацией и маршрутизацией.

API Structure vitrip.store

Request/Response Flow: См. диаграмму vitrip_api_flows.jpg — детальные потоки данных для search, booking и tour endpoints с валидацией и error handling.

API Request/Response Flows vitrip.store

Authentication & Authorization: См. диаграмму vitrip_api_auth.jpg — схемы аутентификации для пользователей, агентов и Partner API с sandbox/production ключами.

API Authentication vitrip.store

OpenAPI 3.0 Specification

API Information

openapi: 3.0.3
info:
title: vitrip.store Travel Platform API
description: |
REST API для платформы агрегации отелей vitrip.store

Экспонирует gRPC интерфейсы как RESTful API согласно business_services.md:
- SearchService → /hotels/* endpoints
- PricingService → /prices/* endpoints
- BookingService → /bookings/* endpoints
- TourBuilderService → /tours/* endpoints

**Базовые URL:**
- Production: https://api.vitrip.store/v1
- Sandbox: https://api-sandbox.vitrip.store/v1

**Аутентификация:**
- Пользователи: Bearer JWT токен
- Partner API: X-API-Key header (sb_test_... или pk_live_...)

version: '1.0'
contact:
name: vitrip.store API Support
url: https://docs.vitrip.store
email: api-support@vitrip.store
license:
name: Proprietary

servers:
- url: https://api.vitrip.store/v1
description: Production server
- url: https://api-sandbox.vitrip.store/v1
description: Sandbox server for testing

security:
- BearerAuth: []
- ApiKeyAuth: []

Security Schemes

components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT токен для аутентификации пользователей и агентов.
Получить через POST /auth/login или /auth/refresh

ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: |
API ключ для Partner интеграций согласно overview/layers.md:
- Sandbox: sb_test_1234567890abcdef123456789012
- Production: pk_live_1234567890abcdef123456789012

Rate limits:
- Sandbox: 100 requests/hour
- Production: 1000 requests/hour

Базируется на SearchService gRPC из Business Services.

paths:
/hotels/search:
post:
tags: [Search]
summary: Search hotels by location and criteria
description: |
Экспонирует SearchService.SearchHotels() как REST API.
Использует PostGIS geo-поиск из reference/database-schema.md.
Кеширует результаты согласно reference/storage.md TTL стратегии.

security:
- BearerAuth: []
- ApiKeyAuth: []

requestBody:
required: true
content:
application/json:
schema:
type: object
required: [location, dates, guests]
properties:
location:
$ref: '#/components/schemas/GeoLocation'
dates:
$ref: '#/components/schemas/DateRange'
guests:
type: integer
minimum: 1
maximum: 20
description: Количество гостей
priceRange:
$ref: '#/components/schemas/PriceRange'
amenities:
type: array
items:
type: string
description: |
Фильтр по удобствам из hotels.amenities таблицы:
['wifi', 'parking', 'pool', 'fitness', 'restaurant']
filters:
$ref: '#/components/schemas/SearchFilters'
sortBy:
type: string
enum: [price_asc, price_desc, rating_desc, distance_asc, popularity_desc]
default: rating_desc
page:
type: integer
minimum: 1
default: 1
pageSize:
type: integer
minimum: 1
maximum: 100
default: 20

example:
location:
latitude: 50.0755
longitude: 14.4378
radius: 5.0
dates:
checkIn: "2026-07-15"
checkOut: "2026-07-20"
guests: 2
amenities: ["wifi", "parking"]
sortBy: "rating_desc"
page: 1
pageSize: 20

responses:
'200':
description: Hotel search results
content:
application/json:
schema:
$ref: '#/components/schemas/HotelSearchResponse'
example:
hotels:
- id: "550e8400-e29b-41d4-a716-446655440000"
name: "Hotel Golden Well"
starRating: 5.0
address:
street: "U Zlaté studně 166/4"
city: "Prague"
postalCode: "118 00"
country: "Czech Republic"
coordinates:
latitude: 50.0755
longitude: 14.4378
distance: 1.2
basePrice: 180.50
currency: "EUR"
availability: true
totalCount: 1247
pagination:
page: 1
pageSize: 20
totalPages: 63
filters:
priceRange:
min: 45.00
max: 890.00
starRatings: [3, 4, 5]
amenities:
- id: "wifi"
name: "Free WiFi"
count: 1180

'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/RateLimited'

Hotel Details API

/hotels/{hotelId}:
get:
tags: [Search]
summary: Get detailed hotel information
description: |
Экспонирует SearchService.GetHotelDetails() как REST API.
Возвращает полную информацию из hotels.properties таблицы.
Включает переводы из content.translations.

parameters:
- name: hotelId
in: path
required: true
schema:
type: string
format: uuid
description: UUID отеля из hotels.properties(id)
example: "550e8400-e29b-41d4-a716-446655440000"

- name: language
in: query
schema:
type: string
enum: [ru, en, uk, cs]
default: ru
description: Язык для переводов из content.translations

responses:
'200':
description: Detailed hotel information
content:
application/json:
schema:
$ref: '#/components/schemas/HotelDetails'
example:
id: "550e8400-e29b-41d4-a716-446655440000"
name: "Hotel Golden Well"
description: "Роскошный отель в историческом центре Праги"
starRating: 5.0
chain:
id: 1
name: "Relais & Châteaux"
address:
street: "U Zlaté studně 166/4"
city: "Prague"
region: "Prague"
postalCode: "118 00"
country: "Czech Republic"
coordinates:
latitude: 50.0755
longitude: 14.4378
amenities:
- id: "wifi"
name: "Free WiFi"
category: "Internet"
- id: "spa"
name: "Spa Services"
category: "Wellness"
images:
- url: "https://cdn.vitrip.store/hotels/550e8400/exterior.jpg"
alt: "Hotel exterior view"
isPrimary: true
width: 1920
height: 1080
roomTypes:
- id: 1
name: "Deluxe Room"
maxOccupancy: 2
sizeSqm: 35
bedConfiguration:
king: 1
amenities: ["wifi", "minibar", "safe"]
basePrice: 180.50
currency: "EUR"

'404':
$ref: '#/components/responses/NotFound'

Room Availability API

/hotels/{hotelId}/rooms/available:
get:
tags: [Search]
summary: Get available rooms for specific dates
description: |
Экспонирует SearchService.GetAvailableRooms() как REST API.
Проверяет availability в Redis cache из reference/storage.md.
Возвращает актуальные цены от PricingService.

parameters:
- name: hotelId
in: path
required: true
schema:
type: string
format: uuid

- name: checkIn
in: query
required: true
schema:
type: string
format: date
example: "2026-07-15"

- name: checkOut
in: query
required: true
schema:
type: string
format: date
example: "2026-07-20"

- name: guests
in: query
required: true
schema:
type: integer
minimum: 1
maximum: 10
example: 2

- name: currency
in: query
schema:
type: string
enum: [EUR, USD, CZK, UAH]
default: EUR
description: Валюта для цен из public.currencies

responses:
'200':
description: Available room types with pricing
content:
application/json:
schema:
type: object
properties:
hotelId:
type: string
format: uuid
dates:
$ref: '#/components/schemas/DateRange'
currency:
type: string
availableRooms:
type: array
items:
$ref: '#/components/schemas/AvailableRoom'

example:
hotelId: "550e8400-e29b-41d4-a716-446655440000"
dates:
checkIn: "2026-07-15"
checkOut: "2026-07-20"
currency: "EUR"
availableRooms:
- roomTypeId: 1
name: "Deluxe Room"
maxOccupancy: 2
availableCount: 3
pricing:
basePrice: 180.50
taxesAndFees: 25.30
totalPrice: 205.80
pricePerNight: 205.80
totalStay: 1029.00
cancellationPolicy:
freeCancellationUntil: "2026-07-13T18:00:00Z"
penaltyPercent: 100

Pricing API

Базируется на PricingService gRPC из Business Services.

/hotels/{hotelId}/prices:
get:
tags: [Pricing]
summary: Get hotel pricing for specific dates
description: |
Экспонирует PricingService.GetHotelPrices() как REST API.
Использует Redis cache ключи hotel:prices:uuid:date.
Применяет markup согласно users.agencies настройкам.

parameters:
- name: hotelId
in: path
required: true
schema:
type: string
format: uuid

- name: checkIn
in: query
required: true
schema:
type: string
format: date

- name: checkOut
in: query
required: true
schema:
type: string
format: date

- name: roomTypes
in: query
schema:
type: array
items:
type: string
description: Фильтр по типам номеров (опционально)

- name: currency
in: query
schema:
type: string
enum: [EUR, USD, CZK, UAH]
default: EUR

- name: agentId
in: query
schema:
type: string
format: uuid
description: ID агента для расчёта комиссии

responses:
'200':
description: Hotel pricing information
content:
application/json:
schema:
$ref: '#/components/schemas/HotelPricingResponse'

example:
hotelId: "550e8400-e29b-41d4-a716-446655440000"
currency: "EUR"
dates:
checkIn: "2026-07-15"
checkOut: "2026-07-20"
roomPrices:
- roomTypeId: 1
roomTypeName: "Deluxe Room"
dailyPrices:
- date: "2026-07-15"
basePrice: 180.50
markup: 18.05
totalPrice: 198.55
- date: "2026-07-16"
basePrice: 185.00
markup: 18.50
totalPrice: 203.50
totalStay:
basePrice: 902.50
markup: 90.25
totalPrice: 992.75
averageRate: 198.55
priceBreakdown:
basePrice: 902.50
agentMarkup: 90.25
taxesAndFees: 125.30
totalPrice: 1118.05

/prices/currency/convert:
post:
tags: [Pricing]
summary: Convert currency for pricing
description: |
Экспонирует PricingService.ConvertCurrency() как REST API.
Использует актуальные курсы из public.exchange_rates таблицы.

requestBody:
required: true
content:
application/json:
schema:
type: object
required: [amount, fromCurrency, toCurrency]
properties:
amount:
type: number
format: decimal
example: 100.50
fromCurrency:
type: string
enum: [EUR, USD, CZK, UAH]
example: "EUR"
toCurrency:
type: string
enum: [EUR, USD, CZK, UAH]
example: "CZK"
date:
type: string
format: date
description: Дата для курса (по умолчанию текущая)

responses:
'200':
description: Currency conversion result
content:
application/json:
schema:
type: object
properties:
originalAmount:
type: number
format: decimal
convertedAmount:
type: number
format: decimal
fromCurrency:
type: string
toCurrency:
type: string
exchangeRate:
type: number
format: decimal
rateDate:
type: string
format: date
rateSource:
type: string

example:
originalAmount: 100.50
convertedAmount: 2463.25
fromCurrency: "EUR"
toCurrency: "CZK"
exchangeRate: 24.5
rateDate: "2026-04-19"
rateSource: "ecb"

Booking API

Базируется на BookingService gRPC из Business Services.

/bookings:
post:
tags: [Bookings]
summary: Create a new hotel booking
description: |
Экспонирует BookingService.CreateBooking() как REST API.
Создаёт запись в bookings.reservations таблице.
Интегрируется с suppliers для резервации.
Автогенерирует confirmation_code согласно reference/database-schema.md.

requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateBookingRequest'

example:
hotelId: "550e8400-e29b-41d4-a716-446655440000"
roomTypeId: 1
dates:
checkIn: "2026-07-15"
checkOut: "2026-07-20"
guests:
adults: 2
children: 0
guestDetails:
primaryGuest:
firstName: "Иван"
lastName: "Петров"
email: "ivan.petrov@example.com"
phone: "+420123456789"
additionalGuests:
- firstName: "Мария"
lastName: "Петрова"
age: 28
pricing:
basePrice: 902.50
markupAmount: 90.25
totalAmount: 992.75
currency: "EUR"
agencyId: 1
specialRequests: "Late check-in после 22:00"

responses:
'201':
description: Booking created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/BookingResponse'

example:
id: "660e8400-e29b-41d4-a716-446655440001"
confirmationCode: "VT789234"
status: "confirmed"
hotelId: "550e8400-e29b-41d4-a716-446655440000"
hotelName: "Hotel Golden Well"
dates:
checkIn: "2026-07-15"
checkOut: "2026-07-20"
nights: 5
guests:
adults: 2
children: 0
guestDetails:
primaryGuest:
firstName: "Иван"
lastName: "Петров"
email: "ivan.petrov@example.com"
phone: "+420123456789"
pricing:
totalAmount: 992.75
currency: "EUR"
supplierBookingId: "STB123456789"
createdAt: "2026-04-19T10:30:00Z"
confirmedAt: "2026-04-19T10:30:15Z"

'400':
$ref: '#/components/responses/BadRequest'
'402':
description: Payment required
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentRequiredError'
'409':
description: Room no longer available
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "room_not_available"
message:
type: string
example: "Selected room is no longer available for these dates"

get:
tags: [Bookings]
summary: Get user's bookings list
description: |
Возвращает список бронирований пользователя.
Фильтрация по статусу и датам.
Pagination support.

parameters:
- name: status
in: query
schema:
type: array
items:
type: string
enum: [pending, confirmed, cancelled, completed]
description: Фильтр по статусу бронирований

- name: dateFrom
in: query
schema:
type: string
format: date
description: Бронирования от даты

- name: dateTo
in: query
schema:
type: string
format: date
description: Бронирования до даты

- name: page
in: query
schema:
type: integer
minimum: 1
default: 1

- name: pageSize
in: query
schema:
type: integer
minimum: 1
maximum: 50
default: 20

responses:
'200':
description: User's bookings list
content:
application/json:
schema:
type: object
properties:
bookings:
type: array
items:
$ref: '#/components/schemas/BookingSummary'
pagination:
$ref: '#/components/schemas/PaginationInfo'

/bookings/{bookingId}:
get:
tags: [Bookings]
summary: Get specific booking details
description: |
Возвращает детальную информацию о бронировании.
Доступно владельцу бронирования или агенту агентства.

parameters:
- name: bookingId
in: path
required: true
schema:
type: string
format: uuid

responses:
'200':
description: Booking details
content:
application/json:
schema:
$ref: '#/components/schemas/BookingDetails'

'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'

patch:
tags: [Bookings]
summary: Update booking details
description: |
Обновляет информацию о бронировании.
Ограниченное обновление после подтверждения.

parameters:
- name: bookingId
in: path
required: true
schema:
type: string
format: uuid

requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
guestDetails:
$ref: '#/components/schemas/GuestDetails'
specialRequests:
type: string
maxLength: 500
internalNotes:
type: string
maxLength: 1000
description: Заметки агента (только для агентов)

responses:
'200':
description: Booking updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/BookingResponse'

delete:
tags: [Bookings]
summary: Cancel booking
description: |
Отменяет бронирование согласно cancellation policy.
Интегрируется с поставщиком для отмены резерва.
Обрабатывает возвраты средств через payments API.

parameters:
- name: bookingId
in: path
required: true
schema:
type: string
format: uuid

requestBody:
required: true
content:
application/json:
schema:
type: object
required: [reason]
properties:
reason:
type: string
maxLength: 500
description: Причина отмены
refundRequested:
type: boolean
default: true
description: Запрос на возврат средств

responses:
'200':
description: Booking cancelled successfully
content:
application/json:
schema:
type: object
properties:
bookingId:
type: string
format: uuid
status:
type: string
enum: [cancelled]
cancelledAt:
type: string
format: date-time
refundStatus:
type: string
enum: [processing, approved, rejected]
refundAmount:
type: number
format: decimal
cancellationFee:
type: number
format: decimal

Tours API

Базируется на TourBuilderService gRPC из Business Services.

/tours:
post:
tags: [Tours]
summary: Create a new tour program
description: |
Экспонирует TourBuilderService.CreateTourProgram() как REST API.
Создаёт запись в bookings.tours и bookings.tour_days таблицах.
Интегрируется с SearchService для подбора отелей.

requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTourRequest'

example:
name: "Прага — Вена — Будапешт"
description: "Классический тур по столицам Центральной Европы"
dates:
startDate: "2026-07-15"
endDate: "2026-07-22"
maxParticipants: 20
basePricePerPerson: 890.00
currency: "EUR"
itinerary:
- day: 1
date: "2026-07-15"
city: "Prague"
title: "Прибытие в Прагу"
description: "Трансфер, размещение, обзорная экскурсия"
hotelRequirements:
starRating: 4
location:
latitude: 50.0755
longitude: 14.4378
radius: 2.0
activities:
- time: "14:00"
title: "Обзорная экскурсия по Старому городу"
duration: 180
included: true
- day: 2
date: "2026-07-16"
city: "Prague"
title: "Прага — культурная программа"
activities:
- time: "09:00"
title: "Пражский град"
duration: 240
price: 25.00
included: true

responses:
'201':
description: Tour created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/TourResponse'

get:
tags: [Tours]
summary: Get user's tours list
description: |
Возвращает список туров созданных пользователем.
Поддерживает фильтрацию по статусу и датам.

parameters:
- name: status
in: query
schema:
type: array
items:
type: string
enum: [draft, published, active, completed, cancelled]

- name: page
in: query
schema:
type: integer
minimum: 1
default: 1

- name: pageSize
in: query
schema:
type: integer
minimum: 1
maximum: 50
default: 20

responses:
'200':
description: Tours list
content:
application/json:
schema:
type: object
properties:
tours:
type: array
items:
$ref: '#/components/schemas/TourSummary'
pagination:
$ref: '#/components/schemas/PaginationInfo'

/tours/{tourId}:
get:
tags: [Tours]
summary: Get detailed tour information
description: |
Экспонирует TourBuilderService.GetTourDetails() как REST API.
Возвращает полную программу тура с отелями и активностями.

parameters:
- name: tourId
in: path
required: true
schema:
type: string
format: uuid

responses:
'200':
description: Detailed tour information
content:
application/json:
schema:
$ref: '#/components/schemas/TourDetails'

/tours/{tourId}/pdf:
get:
tags: [Tours]
summary: Generate tour program PDF
description: |
Экспонирует TourBuilderService.GenerateTourPDF() как REST API.
Генерирует PDF программу тура для клиентов.
Поддерживает мультиязычность из content.translations.

parameters:
- name: tourId
in: path
required: true
schema:
type: string
format: uuid

- name: language
in: query
schema:
type: string
enum: [ru, en, uk, cs]
default: ru
description: Язык генерируемого PDF

- name: template
in: query
schema:
type: string
enum: [standard, detailed, client]
default: standard
description: Шаблон PDF (стандартный, детальный, клиентский)

responses:
'200':
description: Generated PDF file
content:
application/pdf:
schema:
type: string
format: binary
headers:
Content-Disposition:
schema:
type: string
example: 'attachment; filename="tour_program_praga_vena_budapesht.pdf"'
Content-Length:
schema:
type: integer

'404':
$ref: '#/components/responses/NotFound'

/tours/{tourId}/hotels:
post:
tags: [Tours]
summary: Add hotel to tour day
description: |
Экспонирует TourBuilderService.AddHotelToTour() как REST API.
Добавляет отель к конкретному дню тура.
Обновляет bookings.tour_days таблицу.

parameters:
- name: tourId
in: path
required: true
schema:
type: string
format: uuid

requestBody:
required: true
content:
application/json:
schema:
type: object
required: [day, hotelId]
properties:
day:
type: integer
minimum: 1
description: День тура для добавления отеля
hotelId:
type: string
format: uuid
description: UUID отеля из hotels.properties
roomTypeId:
type: integer
description: Тип номера из hotels.room_types
checkInDate:
type: string
format: date
checkOutDate:
type: string
format: date
specialRequests:
type: string
maxLength: 500

responses:
'200':
description: Hotel added to tour successfully
content:
application/json:
schema:
$ref: '#/components/schemas/TourResponse'

Authentication API

/auth/login:
post:
tags: [Authentication]
summary: User authentication
description: |
Аутентификация пользователей и агентов.
Возвращает JWT токен для последующих запросов.
Проверяет users.profiles таблицу.

requestBody:
required: true
content:
application/json:
schema:
type: object
required: [email, password]
properties:
email:
type: string
format: email
example: "agent@example.com"
password:
type: string
format: password
minLength: 8
example: "securepassword123"
rememberMe:
type: boolean
default: false
description: Продлённый срок действия токена

responses:
'200':
description: Authentication successful
content:
application/json:
schema:
type: object
properties:
accessToken:
type: string
description: JWT access token (1 час)
refreshToken:
type: string
description: JWT refresh token (30 дней)
tokenType:
type: string
enum: [Bearer]
expiresIn:
type: integer
description: Время действия access token в секундах
user:
$ref: '#/components/schemas/UserProfile'

example:
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
tokenType: "Bearer"
expiresIn: 3600
user:
id: "770e8400-e29b-41d4-a716-446655440003"
email: "agent@example.com"
firstName: "Иван"
lastName: "Петров"
agency:
id: 1
name: "Prague Travel Agency"
roles: ["agent"]
permissions: ["search.hotels", "booking.create"]

'401':
description: Authentication failed
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "invalid_credentials"
message:
type: string
example: "Invalid email or password"

/auth/refresh:
post:
tags: [Authentication]
summary: Refresh JWT token
description: |
Обновление JWT access token с помощью refresh token.
Продлевает сессию пользователя.

requestBody:
required: true
content:
application/json:
schema:
type: object
required: [refreshToken]
properties:
refreshToken:
type: string
description: Valid refresh token

responses:
'200':
description: Token refreshed successfully
content:
application/json:
schema:
type: object
properties:
accessToken:
type: string
refreshToken:
type: string
tokenType:
type: string
enum: [Bearer]
expiresIn:
type: integer

'401':
description: Invalid refresh token

/auth/logout:
post:
tags: [Authentication]
summary: User logout
description: |
Logout пользователя и аннулирование токенов.
Добавление токенов в blacklist.

security:
- BearerAuth: []

responses:
'200':
description: Logout successful
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "Logged out successfully"

Data Schemas

Core Data Types

components:
schemas:
GeoLocation:
type: object
required: [latitude, longitude]
properties:
latitude:
type: number
format: double
minimum: -90
maximum: 90
example: 50.0755
longitude:
type: number
format: double
minimum: -180
maximum: 180
example: 14.4378
radius:
type: number
format: float
minimum: 0.1
maximum: 100
description: Радиус поиска в километрах
example: 5.0

DateRange:
type: object
required: [checkIn, checkOut]
properties:
checkIn:
type: string
format: date
example: "2026-07-15"
checkOut:
type: string
format: date
example: "2026-07-20"
description: |
Период пребывания. checkOut должен быть больше checkIn.
Минимум 1 ночь, максимум 30 ночей.

PriceRange:
type: object
properties:
min:
type: number
format: decimal
minimum: 0
example: 50.00
max:
type: number
format: decimal
minimum: 0
example: 500.00
currency:
type: string
enum: [EUR, USD, CZK, UAH]
default: EUR
description: Валюта из public.currencies таблицы

Address:
type: object
required: [city, country]
properties:
street:
type: string
example: "U Zlaté studně 166/4"
city:
type: string
example: "Prague"
region:
type: string
example: "Prague"
postalCode:
type: string
example: "118 00"
country:
type: string
example: "Czech Republic"
description: |
Адрес согласно JSONB структуре из hotels.properties.address

Amenity:
type: object
required: [id, name]
properties:
id:
type: string
example: "wifi"
name:
type: string
example: "Free WiFi"
category:
type: string
example: "Internet"
icon:
type: string
example: "wifi"
description: CSS class или emoji иконки
important:
type: boolean
example: true
description: Важное удобство для фильтров

Image:
type: object
required: [url]
properties:
url:
type: string
format: uri
example: "https://cdn.vitrip.store/hotels/550e8400/exterior.jpg"
alt:
type: string
example: "Hotel exterior view"
caption:
type: string
example: "Main building exterior"
isPrimary:
type: boolean
default: false
width:
type: integer
example: 1920
height:
type: integer
example: 1080

SearchFilters:
type: object
properties:
starRatings:
type: array
items:
type: number
format: float
minimum: 1
maximum: 5
example: [4, 5]
description: Фильтр по звёздности отеля
propertyTypes:
type: array
items:
type: string
enum: [hotel, apartment, villa, resort]
example: ["hotel", "resort"]
chains:
type: array
items:
type: integer
example: [1, 2, 3]
description: ID цепочек отелей из hotels.chains
paymentOptions:
type: array
items:
type: string
enum: [free_cancellation, pay_at_hotel, pay_now]
description: Варианты оплаты и отмены

HotelSearchResponse:
type: object
required: [hotels, totalCount, pagination]
properties:
hotels:
type: array
items:
$ref: '#/components/schemas/HotelSearchResult'
totalCount:
type: integer
example: 1247
description: Общее количество найденных отелей
pagination:
$ref: '#/components/schemas/PaginationInfo'
filters:
$ref: '#/components/schemas/AvailableFilters'
searchId:
type: string
format: uuid
description: ID поиска для tracking и analytics
executionTime:
type: number
format: float
example: 0.245
description: Время выполнения запроса в секундах

HotelSearchResult:
type: object
required: [id, name, starRating, address, coordinates]
properties:
id:
type: string
format: uuid
example: "550e8400-e29b-41d4-a716-446655440000"
description: UUID из hotels.properties(id)
name:
type: string
example: "Hotel Golden Well"
starRating:
type: number
format: float
minimum: 1
maximum: 5
example: 5.0
address:
$ref: '#/components/schemas/Address'
coordinates:
$ref: '#/components/schemas/GeoLocation'
distance:
type: number
format: float
example: 1.2
description: Расстояние от центра поиска в км
basePrice:
type: number
format: decimal
example: 180.50
description: Базовая цена за ночь
currency:
type: string
enum: [EUR, USD, CZK, UAH]
example: "EUR"
availability:
type: boolean
example: true
description: Доступность на выбранные даты
images:
type: array
items:
$ref: '#/components/schemas/Image'
maxItems: 5
description: Основные изображения отеля
amenities:
type: array
items:
$ref: '#/components/schemas/Amenity'
maxItems: 10
description: Топ удобства для preview
rating:
type: object
properties:
overall:
type: number
format: float
minimum: 1
maximum: 10
example: 9.2
reviewCount:
type: integer
example: 1847
chain:
type: object
properties:
id:
type: integer
name:
type: string
example: "Relais & Châteaux"

PaginationInfo:
type: object
required: [page, pageSize, totalPages, totalCount]
properties:
page:
type: integer
minimum: 1
example: 1
pageSize:
type: integer
minimum: 1
maximum: 100
example: 20
totalPages:
type: integer
example: 63
totalCount:
type: integer
example: 1247
hasNext:
type: boolean
example: true
hasPrevious:
type: boolean
example: false

Booking Schemas

CreateBookingRequest:
type: object
required: [hotelId, dates, guests, guestDetails, pricing]
properties:
hotelId:
type: string
format: uuid
description: UUID отеля из hotels.properties(id)
roomTypeId:
type: integer
description: ID типа номера из hotels.room_types(id)
dates:
$ref: '#/components/schemas/DateRange'
guests:
$ref: '#/components/schemas/GuestCounts'
guestDetails:
$ref: '#/components/schemas/GuestDetails'
pricing:
$ref: '#/components/schemas/BookingPricing'
agencyId:
type: integer
description: ID агентства из users.agencies(id)
specialRequests:
type: string
maxLength: 1000
example: "Late check-in after 22:00, non-smoking room"
paymentMethod:
type: string
enum: [card, bank_transfer, cash, agent_credit]
default: card
marketingConsent:
type: boolean
default: false
description: Согласие на маркетинговые коммуникации

GuestCounts:
type: object
required: [adults]
properties:
adults:
type: integer
minimum: 1
maximum: 10
example: 2
children:
type: integer
minimum: 0
maximum: 5
example: 0

GuestDetails:
type: object
required: [primaryGuest]
properties:
primaryGuest:
$ref: '#/components/schemas/Guest'
additionalGuests:
type: array
items:
$ref: '#/components/schemas/Guest'
maxItems: 9
description: Дополнительные гости (исключая primary)

Guest:
type: object
required: [firstName, lastName]
properties:
firstName:
type: string
maxLength: 100
example: "Иван"
lastName:
type: string
maxLength: 100
example: "Петров"
email:
type: string
format: email
example: "ivan.petrov@example.com"
description: Обязательно для primaryGuest
phone:
type: string
maxLength: 20
example: "+420123456789"
description: Обязательно для primaryGuest
age:
type: integer
minimum: 0
maximum: 120
example: 35
description: Возраст (для детей обязательно)
documentType:
type: string
enum: [passport, id_card, driving_license]
documentNumber:
type: string
maxLength: 50
description: Номер документа (для международных поездок)

BookingPricing:
type: object
required: [basePrice, totalAmount, currency]
properties:
basePrice:
type: number
format: decimal
example: 902.50
description: Базовая стоимость от поставщика
markupAmount:
type: number
format: decimal
example: 90.25
description: Наценка агента
taxesAndFees:
type: number
format: decimal
example: 125.30
description: Налоги и сборы
totalAmount:
type: number
format: decimal
example: 1118.05
description: Итоговая сумма к оплате
currency:
type: string
enum: [EUR, USD, CZK, UAH]
example: "EUR"
breakdown:
type: array
items:
type: object
properties:
name:
type: string
example: "Room rate (5 nights)"
amount:
type: number
format: decimal
example: 902.50
type:
type: string
enum: [base_price, markup, tax, fee, discount]

BookingResponse:
type: object
required: [id, confirmationCode, status, dates, pricing]
properties:
id:
type: string
format: uuid
description: UUID бронирования из bookings.reservations(id)
confirmationCode:
type: string
example: "VT789234"
description: Код подтверждения для клиента
status:
type: string
enum: [pending, confirmed, cancelled, completed, failed]
example: "confirmed"
hotelId:
type: string
format: uuid
hotelName:
type: string
example: "Hotel Golden Well"
roomType:
type: object
properties:
id:
type: integer
name:
type: string
example: "Deluxe Room"
maxOccupancy:
type: integer
example: 2
dates:
allOf:
- $ref: '#/components/schemas/DateRange'
- type: object
properties:
nights:
type: integer
example: 5
guests:
$ref: '#/components/schemas/GuestCounts'
guestDetails:
$ref: '#/components/schemas/GuestDetails'
pricing:
$ref: '#/components/schemas/BookingPricing'
supplierBookingId:
type: string
example: "STB123456789"
description: ID бронирования у поставщика
cancellationPolicy:
type: object
properties:
freeCancellationUntil:
type: string
format: date-time
penaltyPercent:
type: number
format: float
minimum: 0
maximum: 100
createdAt:
type: string
format: date-time
confirmedAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time

Error Responses

responses:
BadRequest:
description: Bad request - validation errors
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error: "validation_failed"
message: "Request validation failed"
details:
- field: "dates.checkOut"
message: "Check-out date must be after check-in date"
- field: "guests"
message: "At least 1 adult guest is required"

Unauthorized:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error: "authentication_required"
message: "Valid authentication token is required"

Forbidden:
description: Insufficient permissions
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error: "insufficient_permissions"
message: "You don't have permission to access this resource"

NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error: "resource_not_found"
message: "The requested resource was not found"

RateLimited:
description: Rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error: "rate_limit_exceeded"
message: "API rate limit exceeded. Try again later."
headers:
Retry-After:
schema:
type: integer
description: Seconds to wait before retrying
X-RateLimit-Limit:
schema:
type: integer
description: Request limit per time window
X-RateLimit-Remaining:
schema:
type: integer
description: Remaining requests in current window
X-RateLimit-Reset:
schema:
type: integer
description: Timestamp when rate limit resets

ErrorResponse:
type: object
required: [error, message]
properties:
error:
type: string
example: "validation_failed"
description: Уникальный код ошибки для программной обработки
message:
type: string
example: "Request validation failed"
description: Человекочитаемое описание ошибки
details:
type: array
items:
type: object
properties:
field:
type: string
example: "dates.checkIn"
message:
type: string
example: "Invalid date format"
code:
type: string
example: "invalid_format"
description: Детали валидационных ошибок
requestId:
type: string
format: uuid
description: ID запроса для отслеживания в логах
timestamp:
type: string
format: date-time
description: Время возникновения ошибки

API Guidelines

Rate Limiting

# Rate limits по типам ключей согласно overview/layers.md
Rate Limits:
Sandbox API Keys (sb_test_...):
- 100 requests/hour
- 10 requests/minute (burst)
- Search endpoints: 50 requests/hour
- Booking endpoints: 20 requests/hour

Production API Keys (pk_live_...):
- 1000 requests/hour
- 100 requests/minute (burst)
- Search endpoints: 500 requests/hour
- Booking endpoints: 200 requests/hour

JWT User Tokens:
- 500 requests/hour per user
- 50 requests/minute (burst)
- No limits on read operations

Headers:
X-RateLimit-Limit: Request limit per time window
X-RateLimit-Remaining: Remaining requests in current window
X-RateLimit-Reset: Unix timestamp when limit resets
Retry-After: Seconds to wait before retrying (on 429 response)

Pagination

# Стандартная пагинация для всех list endpoints
Pagination Parameters:
page:
type: integer
minimum: 1
default: 1
description: Номер страницы (1-based)

pageSize:
type: integer
minimum: 1
maximum: 100
default: 20
description: Количество элементов на странице

Response Format:
pagination:
page: 1
pageSize: 20
totalPages: 63
totalCount: 1247
hasNext: true
hasPrevious: false

# Cursor-based pagination для больших datasets
Cursor Parameters:
cursor:
type: string
description: Токен для следующей страницы

limit:
type: integer
minimum: 1
maximum: 100
default: 20

Localization

# Мультиязычность согласно content.translations из reference/database-schema.md
Language Support:
Supported Languages:
- ru (Russian) - default
- en (English)
- uk (Ukrainian)
- cs (Czech)

Language Selection:
Header: Accept-Language: ru,en;q=0.8
Query Parameter: ?language=ru

Localized Fields:
- Hotel names and descriptions
- Amenity names
- Error messages
- Tour program content

Fallback Strategy:
ru → en → original (if translation missing)

Связанная документация

  • Архитектура API: разделы "### 5. API layer (Слой API)" и "Partner API" из API Layer
  • gRPC интерфейсы: все gRPC сервисы из Business Services экспонированы как REST
  • Схемы данных: JSON структуры соответствуют PostgreSQL схемам из Database Schema
  • Фронтенд интеграция: VitripApiClient endpoints из Clients Layer покрыты спецификацией
  • Partner API: SDK интеграции и webhook события из Clients Layer
  • Аутентификация: JWT и API keys согласно users.profiles и users.roles из Database Schema
  • Кеширование: Redis TTL стратегии и naming convention из Storage Layer