This commit is contained in:
2026-03-17 18:32:44 +03:00
commit efcd4a8dfd
209 changed files with 33355 additions and 0 deletions
@@ -0,0 +1,658 @@
openapi: 3.1.0
info:
title: Travel Product Manager API
description: 'Synthetic API with multiple linear demo workflows.
Travel workflow:
1. `GET /users/recent`
2. `GET /hotels/top`
3. `POST /segments/hotel`
4. `POST /assignments/hotels`
5. `POST /emails/send-offers`
CRM workflow:
1. `GET /crm/leads/recent`
2. `POST /crm/leads/qualify`
3. `POST /crm/offers/prepare`
4. `POST /crm/offers/send`'
version: 1.0.0
servers:
- url: http://84.201.161.175
description: production
paths:
/users/recent:
get:
tags:
- travel-offer-workflow
summary: Get Recent Users
operationId: getRecentUsers
parameters:
- name: last_active_after
in: query
required: false
schema:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Last Active After
- name: limit
in: query
required: false
schema:
type: integer
maximum: 100
minimum: 1
default: 30
title: Limit
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/RecentUsersResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/hotels/top:
get:
tags:
- travel-offer-workflow
summary: Get Top Hotels
operationId: getTopHotels
parameters:
- name: limit
in: query
required: false
schema:
type: integer
maximum: 20
minimum: 1
default: 5
title: Limit
- name: city
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: City
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/TopHotelsResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/segments/hotel:
post:
tags:
- travel-offer-workflow
summary: Segment Users By Hotel Preferences
operationId: segmentUsersByHotelPreferences
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/HotelSegmentsRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/HotelSegmentsResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/assignments/hotels:
post:
tags:
- travel-offer-workflow
summary: Assign Users To Hotels
operationId: assignUsersToHotels
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/AssignmentsRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/AssignmentsResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/emails/send-offers:
post:
tags:
- travel-offer-workflow
summary: Send Hotel Offers By Email
operationId: sendHotelOffersByEmail
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/EmailOfferRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/EmailOfferResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/crm/leads/recent:
get:
tags:
- crm-linear-workflow
summary: Get Recent Leads
operationId: getRecentLeads
parameters:
- name: limit
in: query
required: false
schema:
type: integer
maximum: 50
minimum: 1
default: 20
title: Limit
- name: source
in: query
required: false
schema:
anyOf:
- type: string
- type: 'null'
title: Source
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/RecentLeadsResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/crm/leads/qualify:
post:
tags:
- crm-linear-workflow
summary: Qualify Leads For Offer
operationId: qualifyLeadsForOffer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/QualifyLeadsRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/QualifyLeadsResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/crm/offers/prepare:
post:
tags:
- crm-linear-workflow
summary: Prepare Offers For Leads
operationId: prepareOffersForLeads
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PrepareOffersRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/PrepareOffersResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/crm/offers/send:
post:
tags:
- crm-linear-workflow
summary: Send Prepared Offers
operationId: sendPreparedOffers
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SendOffersRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SendOffersResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/health:
get:
summary: Health
operationId: health_health_get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
additionalProperties:
type: string
type: object
title: Response Health Health Get
components:
schemas:
Assignment:
properties:
user_id:
type: string
title: User Id
hotel_id:
type: string
title: Hotel Id
type: object
required:
- user_id
- hotel_id
title: Assignment
AssignmentsRequest:
properties:
segments:
items:
$ref: '#/components/schemas/Segment'
type: array
title: Segments
type: object
title: AssignmentsRequest
AssignmentsResponse:
properties:
assignments:
items:
$ref: '#/components/schemas/Assignment'
type: array
title: Assignments
type: object
title: AssignmentsResponse
EmailOfferRequest:
properties:
template_id:
type: string
title: Template Id
assignments:
items:
$ref: '#/components/schemas/Assignment'
type: array
title: Assignments
type: object
required:
- template_id
title: EmailOfferRequest
EmailOfferResponse:
properties:
sent_count:
type: integer
title: Sent Count
failed_count:
type: integer
title: Failed Count
failed:
items:
$ref: '#/components/schemas/FailedDelivery'
type: array
title: Failed
type: object
required:
- sent_count
- failed_count
title: EmailOfferResponse
FailedDelivery:
properties:
user_id:
type: string
title: User Id
reason:
type: string
title: Reason
type: object
required:
- user_id
- reason
title: FailedDelivery
FailedLeadDelivery:
properties:
lead_id:
type: string
title: Lead Id
reason:
type: string
title: Reason
type: object
required:
- lead_id
- reason
title: FailedLeadDelivery
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
Hotel:
properties:
id:
type: string
title: Id
name:
type: string
title: Name
city:
type: string
title: City
type: object
required:
- id
- name
- city
title: Hotel
HotelSegmentsRequest:
properties:
users:
items:
$ref: '#/components/schemas/User'
type: array
title: Users
hotels:
items:
$ref: '#/components/schemas/Hotel'
type: array
title: Hotels
type: object
title: HotelSegmentsRequest
HotelSegmentsResponse:
properties:
segments:
items:
$ref: '#/components/schemas/Segment'
type: array
title: Segments
type: object
title: HotelSegmentsResponse
Lead:
properties:
lead_id:
type: string
title: Lead Id
email:
type: string
title: Email
source:
type: string
title: Source
type: object
required:
- lead_id
- email
- source
title: Lead
PrepareOffersRequest:
properties:
qualified_leads:
items:
$ref: '#/components/schemas/QualifiedLead'
type: array
title: Qualified Leads
type: object
title: PrepareOffersRequest
PrepareOffersResponse:
properties:
offers:
items:
$ref: '#/components/schemas/PreparedOffer'
type: array
title: Offers
type: object
title: PrepareOffersResponse
PreparedOffer:
properties:
offer_id:
type: string
title: Offer Id
lead_id:
type: string
title: Lead Id
channel:
type: string
title: Channel
message:
type: string
title: Message
type: object
required:
- offer_id
- lead_id
- channel
- message
title: PreparedOffer
QualifiedLead:
properties:
lead_id:
type: string
title: Lead Id
email:
type: string
title: Email
score:
type: integer
title: Score
tier:
type: string
title: Tier
type: object
required:
- lead_id
- email
- score
- tier
title: QualifiedLead
QualifyLeadsRequest:
properties:
leads:
items:
$ref: '#/components/schemas/Lead'
type: array
title: Leads
type: object
title: QualifyLeadsRequest
QualifyLeadsResponse:
properties:
qualified_leads:
items:
$ref: '#/components/schemas/QualifiedLead'
type: array
title: Qualified Leads
type: object
title: QualifyLeadsResponse
RecentLeadsResponse:
properties:
leads:
items:
$ref: '#/components/schemas/Lead'
type: array
title: Leads
type: object
title: RecentLeadsResponse
RecentUsersResponse:
properties:
users:
items:
$ref: '#/components/schemas/User'
type: array
title: Users
type: object
title: RecentUsersResponse
Segment:
properties:
segment_id:
type: string
title: Segment Id
hotel_id:
type: string
title: Hotel Id
user_ids:
items:
type: string
type: array
title: User Ids
type: object
required:
- segment_id
- hotel_id
title: Segment
SendOffersRequest:
properties:
offers:
items:
$ref: '#/components/schemas/PreparedOffer'
type: array
title: Offers
type: object
title: SendOffersRequest
SendOffersResponse:
properties:
sent_count:
type: integer
title: Sent Count
failed_count:
type: integer
title: Failed Count
failed:
items:
$ref: '#/components/schemas/FailedLeadDelivery'
type: array
title: Failed
type: object
required:
- sent_count
- failed_count
title: SendOffersResponse
TopHotelsResponse:
properties:
hotels:
items:
$ref: '#/components/schemas/Hotel'
type: array
title: Hotels
type: object
title: TopHotelsResponse
User:
properties:
id:
type: string
title: Id
email:
type: string
title: Email
last_active:
type: string
format: date-time
title: Last Active
type: object
required:
- id
- email
- last_active
title: User
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
type: object
required:
- loc
- msg
- type
title: ValidationError
servers:
- url: http://demo-api:8010
- url: http://localhost:8010
@@ -0,0 +1,214 @@
openapi: 3.0.3
info:
title: CRM Linear Demo API
version: 1.0.0
description: |
Demo OpenAPI for a strict linear CRM scenario:
1) get recent leads,
2) qualify leads,
3) prepare offers,
4) send offers.
servers:
- url: http://demo-api:8010
- url: http://localhost:8010
paths:
/crm/leads/recent:
get:
operationId: getRecentLeads
tags: [crm-linear-workflow]
summary: Get recent leads
parameters:
- in: query
name: limit
required: false
schema:
type: integer
minimum: 1
maximum: 50
default: 20
- in: query
name: source
required: false
schema:
type: string
responses:
"200":
description: Leads list
content:
application/json:
schema:
$ref: "#/components/schemas/RecentLeadsResponse"
/crm/leads/qualify:
post:
operationId: qualifyLeadsForOffer
tags: [crm-linear-workflow]
summary: Qualify leads
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/QualifyLeadsRequest"
responses:
"200":
description: Qualified leads
content:
application/json:
schema:
$ref: "#/components/schemas/QualifyLeadsResponse"
/crm/offers/prepare:
post:
operationId: prepareOffersForLeads
tags: [crm-linear-workflow]
summary: Prepare offers from qualified leads
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/PrepareOffersRequest"
responses:
"200":
description: Prepared offers
content:
application/json:
schema:
$ref: "#/components/schemas/PrepareOffersResponse"
/crm/offers/send:
post:
operationId: sendPreparedOffers
tags: [crm-linear-workflow]
summary: Send prepared offers
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SendOffersRequest"
responses:
"200":
description: Send summary
content:
application/json:
schema:
$ref: "#/components/schemas/SendOffersResponse"
components:
schemas:
Lead:
type: object
required: [lead_id, email, source]
properties:
lead_id:
type: string
email:
type: string
format: email
source:
type: string
QualifiedLead:
type: object
required: [lead_id, email, score, tier]
properties:
lead_id:
type: string
email:
type: string
format: email
score:
type: integer
tier:
type: string
PreparedOffer:
type: object
required: [offer_id, lead_id, channel, message]
properties:
offer_id:
type: string
lead_id:
type: string
channel:
type: string
message:
type: string
RecentLeadsResponse:
type: object
required: [leads]
properties:
leads:
type: array
items:
$ref: "#/components/schemas/Lead"
QualifyLeadsRequest:
type: object
required: [leads]
properties:
leads:
type: array
items:
$ref: "#/components/schemas/Lead"
QualifyLeadsResponse:
type: object
required: [qualified_leads]
properties:
qualified_leads:
type: array
items:
$ref: "#/components/schemas/QualifiedLead"
PrepareOffersRequest:
type: object
required: [qualified_leads]
properties:
qualified_leads:
type: array
items:
$ref: "#/components/schemas/QualifiedLead"
PrepareOffersResponse:
type: object
required: [offers]
properties:
offers:
type: array
items:
$ref: "#/components/schemas/PreparedOffer"
SendOffersRequest:
type: object
required: [offers]
properties:
offers:
type: array
items:
$ref: "#/components/schemas/PreparedOffer"
FailedLeadDelivery:
type: object
required: [lead_id, reason]
properties:
lead_id:
type: string
reason:
type: string
SendOffersResponse:
type: object
required: [sent_count, failed_count, failed]
properties:
sent_count:
type: integer
failed_count:
type: integer
failed:
type: array
items:
$ref: "#/components/schemas/FailedLeadDelivery"
+144
View File
@@ -0,0 +1,144 @@
openapi: 3.1.0
info:
title: Travel & CRM Pipeline API
description: |
Это API предназначено для автоматизации маркетинговых и операционных процессов в сфере туризма.
Оно поддерживает два основных сценария автоматизации (пайплайна):
### 1. Сценарий: Рассылка спецпредложений по отелям
Используется для реактивации пользователей, которые были активны недавно.
**Цепочка:** Получение юзеров → Подбор топ-отелей → Сегментация (матчинг) → Назначение конкретных пар Юзер-Отель → Отправка Email.
### 2. Сценарий: Обработка лидов в CRM
Предназначен для отдела продаж.
**Цепочка:** Сбор новых лидов → Квалификация (оценка качества) → Подготовка оффера → Финальная отправка.
version: 1.1.0
servers:
- url: http://84.201.161.175
paths:
/users/recent:
get:
tags:
- travel-offer-workflow
summary: Получить список недавно активных пользователей
description: |
Возвращает список клиентов, которые заходили в приложение за последнее время.
Используйте этот метод как входную точку для начала маркетинговой кампании.
operationId: getRecentUsers
parameters:
- name: last_active_after
in: query
description: Фильтр по дате и времени. Будут возвращены только те, кто был активен ПОСЛЕ указанного момента.
required: false
schema:
anyOf:
- type: string
format: date-time
- type: "null"
- name: limit
in: query
description: Ограничение выборки. По умолчанию возвращается 30 пользователей для оптимальной нагрузки на почтовый сервер.
required: false
schema:
type: integer
maximum: 100
minimum: 1
default: 30
responses:
"200":
description: Список пользователей успешно сформирован.
/hotels/top:
get:
tags:
- travel-offer-workflow
summary: Получить список популярных отелей
description: |
Выгружает наиболее востребованные отели. Можно фильтровать по конкретному городу, чтобы сделать предложение более точным.
operationId: getTopHotels
parameters:
- name: limit
in: query
description: Максимальное количество отелей в выдаче (не более 20).
- name: city
in: query
description: Название города (например, 'Moscow', 'Dubai'). Если не указано, вернутся топ-отели по всем направлениям.
responses:
"200":
description: Список отелей получен.
/segments/hotel:
post:
tags:
- travel-offer-workflow
summary: Сгруппировать пользователей по интересам к отелям
description: |
Принимает списки пользователей и отелей, анализирует их и создает группы (сегменты).
Это "умный" этап, который определяет, кому какой тип отдыха подходит больше.
operationId: segmentUsersByHotelPreferences
requestBody:
description: Данные для анализа (массивы объектов User и Hotel).
content:
application/json:
schema:
$ref: "#/components/schemas/HotelSegmentsRequest"
responses:
"200":
description: Сегментация успешно завершена.
/assignments/hotels:
post:
tags:
- travel-offer-workflow
summary: Назначить конкретные отели пользователям
description: |
Финальное закрепление. На основе сегментов метод создает пары "ID пользователя — ID отеля".
Результат этого метода передается напрямую в сервис рассылки.
operationId: assignUsersToHotels
responses:
"200":
description: Пары для рассылки сформированы.
/emails/send-offers:
post:
tags:
- travel-offer-workflow
summary: Разослать персонализированные предложения
description: |
Запускает процесс отправки писем. Требует ID шаблона письма и список назначений, сформированный на предыдущем шаге.
operationId: sendHotelOffersByEmail
requestBody:
description: Шаблон письма и список получателей с назначенными им отелями.
responses:
"200":
description: Рассылка запущена. В ответе придет статистика (сколько отправлено, сколько сбоев).
/crm/leads/qualify:
post:
tags:
- crm-linear-workflow
summary: Оценить качество лидов (Lead Scoring)
description: |
Метод проверяет входящие заявки и присваивает им рейтинг (score) и уровень (tier).
Это позволяет продакту сфокусироваться на самых "горячих" клиентах.
operationId: qualifyLeadsForOffer
responses:
"200":
description: Лиды успешно квалифицированы.
components:
schemas:
User:
type: object
description: Информация о клиенте сервиса.
properties:
id:
type: string
description: Уникальный идентификатор пользователя (UUID).
email:
type: string
description: Адрес электронной почты для связи.
last_active:
type: string
format: date-time
description: Таймстамп последнего действия в системе
+556
View File
@@ -0,0 +1,556 @@
openapi: 3.0.3
info:
title: Travel Product Manager API
version: 1.0.0
description: |
Synthetic API for a single travel offer workflow.
Intended order of operations:
1. get recent users,
2. get top hotels,
3. build hotel preference segments from users and hotels,
4. build user-to-hotel assignments from segments,
5. send hotel offers by email from assignments.
Each endpoint has one specific responsibility.
The workflow should be interpreted as a strict data pipeline where the output
array of one step becomes the input field of the next step.
servers:
- url: http://demo-api:8010
- url: http://localhost:8010
- url: https://api.travel.example.com
paths:
/users/recent:
get:
operationId: getRecentUsers
tags:
- travel-offer-workflow
summary: Get recent users for travel campaigns
description: |
Returns a list of recent users active in the last 7 days.
By default this endpoint returns up to 30 users because the limit parameter
defaults to 30.
Output of this endpoint is the users array that should be passed as the users
field to /segments/hotel.
This endpoint does not retrieve hotels, create segments, create assignments,
or send emails.
parameters:
- in: query
name: last_active_after
schema:
type: string
format: date-time
required: false
description: |
Optional lower bound for user activity time.
Only users active after this timestamp should be returned.
If omitted, the endpoint behaves like "last 7 days".
- in: query
name: limit
schema:
type: integer
minimum: 1
maximum: 100
default: 30
required: false
description: |
Maximum number of users to return.
If omitted, the endpoint returns up to 30 users.
responses:
"200":
description: |
Successful response containing the users array for the first workflow step.
This users array should be passed forward to /segments/hotel.
content:
application/json:
schema:
$ref: "#/components/schemas/RecentUsersResponse"
examples:
sample:
value:
users:
- id: usr_001
email: user001@example.com
last_active: "2026-03-13T10:00:00Z"
- id: usr_002
email: user002@example.com
last_active: "2026-03-13T09:55:00Z"
- id: usr_003
email: user003@example.com
last_active: "2026-03-13T09:50:00Z"
- id: usr_004
email: user004@example.com
last_active: "2026-03-13T09:45:00Z"
- id: usr_005
email: user005@example.com
last_active: "2026-03-13T09:40:00Z"
- id: usr_006
email: user006@example.com
last_active: "2026-03-13T09:35:00Z"
- id: usr_007
email: user007@example.com
last_active: "2026-03-13T09:30:00Z"
- id: usr_008
email: user008@example.com
last_active: "2026-03-13T09:25:00Z"
- id: usr_009
email: user009@example.com
last_active: "2026-03-13T09:20:00Z"
- id: usr_010
email: user010@example.com
last_active: "2026-03-13T09:15:00Z"
- id: usr_011
email: user011@example.com
last_active: "2026-03-13T09:10:00Z"
- id: usr_012
email: user012@example.com
last_active: "2026-03-13T09:05:00Z"
- id: usr_013
email: user013@example.com
last_active: "2026-03-13T09:00:00Z"
- id: usr_014
email: user014@example.com
last_active: "2026-03-13T08:55:00Z"
- id: usr_015
email: user015@example.com
last_active: "2026-03-13T08:50:00Z"
- id: usr_016
email: user016@example.com
last_active: "2026-03-13T08:45:00Z"
- id: usr_017
email: user017@example.com
last_active: "2026-03-13T08:40:00Z"
- id: usr_018
email: user018@example.com
last_active: "2026-03-13T08:35:00Z"
- id: usr_019
email: user019@example.com
last_active: "2026-03-13T08:30:00Z"
- id: usr_020
email: user020@example.com
last_active: "2026-03-13T08:25:00Z"
- id: usr_021
email: user021@example.com
last_active: "2026-03-13T08:20:00Z"
- id: usr_022
email: user022@example.com
last_active: "2026-03-13T08:15:00Z"
- id: usr_023
email: user023@example.com
last_active: "2026-03-13T08:10:00Z"
- id: usr_024
email: user024@example.com
last_active: "2026-03-13T08:05:00Z"
- id: usr_025
email: user025@example.com
last_active: "2026-03-13T08:00:00Z"
- id: usr_026
email: user026@example.com
last_active: "2026-03-13T07:55:00Z"
- id: usr_027
email: user027@example.com
last_active: "2026-03-13T07:50:00Z"
- id: usr_028
email: user028@example.com
last_active: "2026-03-13T07:45:00Z"
- id: usr_029
email: user029@example.com
last_active: "2026-03-13T07:40:00Z"
- id: usr_030
email: user030@example.com
last_active: "2026-03-13T07:35:00Z"
/hotels/top:
get:
operationId: getTopHotels
tags:
- travel-offer-workflow
summary: Get top hotels for offers
description: |
Returns a list of candidate hotels for the offer workflow.
By default this endpoint returns up to 5 hotels because the limit parameter
defaults to 5.
Output of this endpoint is the hotels array that should be passed as the hotels
field to /segments/hotel.
This endpoint does not retrieve users, create segments, create assignments,
or send emails.
parameters:
- in: query
name: limit
schema:
type: integer
minimum: 1
maximum: 20
default: 5
required: false
description: |
Maximum number of hotels to return.
If omitted, the endpoint returns up to 5 hotels.
- in: query
name: city
schema:
type: string
required: false
description: |
Optional city filter.
If provided, only hotels from this city should be returned.
responses:
"200":
description: |
Successful response containing the hotels array for the second workflow step.
This hotels array should be passed forward to /segments/hotel.
content:
application/json:
schema:
$ref: "#/components/schemas/TopHotelsResponse"
examples:
sample:
value:
hotels:
- id: hotel_001
name: Hotel Aurora
city: Berlin
- id: hotel_002
name: Sea Breeze Resort
city: Lisbon
- id: hotel_003
name: Mountain Vista
city: Zurich
- id: hotel_004
name: City Loft
city: Amsterdam
- id: hotel_005
name: River Palace
city: Prague
/segments/hotel:
post:
operationId: segmentUsersByHotelPreferences
tags:
- travel-offer-workflow
summary: Segment recent users by hotel preferences
description: |
Creates hotel-based user segments from two required inputs in one request:
users and hotels.
The users field must contain the users array returned by /users/recent.
The hotels field must contain the hotels array returned by /hotels/top.
A common workflow is: get up to 30 recent users, get top hotels, then send
both arrays to this endpoint to distribute users across hotels by preference.
Output of this endpoint is the segments array used as the segments field in
/assignments/hotels.
This endpoint does not send emails.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/HotelSegmentsRequest"
examples:
sample:
value:
users:
- id: usr_001
email: user001@example.com
last_active: "2026-03-13T10:00:00Z"
hotels:
- id: hotel_001
name: Hotel Aurora
city: Berlin
responses:
"200":
description: |
Successful response containing the segments array.
This segments array should be passed forward to /assignments/hotels.
content:
application/json:
schema:
$ref: "#/components/schemas/HotelSegmentsResponse"
examples:
sample:
value:
segments:
- segment_id: seg_berlin
hotel_id: hotel_001
user_ids: ["usr_001", "usr_002"]
/assignments/hotels:
post:
operationId: assignUsersToHotels
tags:
- travel-offer-workflow
summary: Assign users to hotels based on segments
description: |
Builds final user-to-hotel assignments from segments.
The segments field must contain the segments array returned by /segments/hotel.
Output of this endpoint is the assignments array used as the assignments field
in /emails/send-offers.
This endpoint does not send emails and does not fetch users or hotels.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AssignmentsRequest"
examples:
sample:
value:
segments:
- segment_id: seg_berlin
hotel_id: hotel_001
user_ids: ["usr_001", "usr_002"]
responses:
"200":
description: |
Successful response containing the assignments array.
This assignments array should be passed forward to /emails/send-offers.
content:
application/json:
schema:
$ref: "#/components/schemas/AssignmentsResponse"
examples:
sample:
value:
assignments:
- user_id: usr_001
hotel_id: hotel_001
- user_id: usr_002
hotel_id: hotel_001
/emails/send-offers:
post:
operationId: sendHotelOffersByEmail
tags:
- travel-offer-workflow
summary: Send hotel offers by email
description: |
Sends hotel offer emails to users based on final assignments.
The assignments field must contain the assignments array returned by
/assignments/hotels.
This endpoint is the final delivery step of the workflow.
It does not build new segments or assignments.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/EmailOfferRequest"
examples:
sample:
value:
template_id: offer_template_2026
assignments:
- user_id: usr_001
hotel_id: hotel_001
- user_id: usr_002
hotel_id: hotel_001
responses:
"200":
description: |
Successful response containing the result of the email delivery step.
This is the final output of the workflow.
content:
application/json:
schema:
$ref: "#/components/schemas/EmailOfferResponse"
examples:
sample:
value:
sent_count: 2
failed_count: 0
failed: []
components:
schemas:
User:
description: |
A recent user eligible to receive a hotel offer email.
User objects are produced by /users/recent and then reused in
/segments/hotel.
type: object
required: [id, email, last_active]
properties:
id:
type: string
description: Stable unique user identifier.
email:
type: string
format: email
description: Email address used in the final offer delivery step.
last_active:
type: string
format: date-time
description: Most recent activity timestamp used to identify recent users.
Hotel:
description: |
A hotel candidate that may be recommended to users.
Hotel objects are produced by /hotels/top and then reused in
/segments/hotel.
type: object
required: [id, name, city]
properties:
id:
type: string
description: Stable unique hotel identifier.
name:
type: string
description: Human-readable hotel name shown in offers.
city:
type: string
description: City where the hotel is located.
Segment:
description: |
A hotel preference segment that groups users for one hotel.
Segment objects are produced by /segments/hotel and then reused in
/assignments/hotels.
type: object
required: [segment_id, hotel_id, user_ids]
properties:
segment_id:
type: string
description: Stable unique segment identifier.
hotel_id:
type: string
description: Hotel identifier associated with this segment.
user_ids:
type: array
description: User identifiers that belong to this hotel preference segment.
items:
type: string
Assignment:
description: |
A final mapping between one user and one hotel offer.
Assignment objects are produced by /assignments/hotels and then reused in
/emails/send-offers.
type: object
required: [user_id, hotel_id]
properties:
user_id:
type: string
description: Identifier of the user who should receive the offer.
hotel_id:
type: string
description: Identifier of the hotel assigned to the user.
RecentUsersResponse:
description: Response containing the users array produced by /users/recent.
type: object
required: [users]
properties:
users:
type: array
description: |
Recent users that should be copied into the users field of
/segments/hotel. With the default limit this array usually contains
up to 30 users.
items:
$ref: "#/components/schemas/User"
TopHotelsResponse:
description: Response containing the hotels array produced by /hotels/top.
type: object
required: [hotels]
properties:
hotels:
type: array
description: |
Candidate hotels that should be copied into the hotels field of
/segments/hotel. With the default limit this array usually contains
up to 5 hotels.
items:
$ref: "#/components/schemas/Hotel"
HotelSegmentsRequest:
description: |
Request body for building segments from users and hotels.
This request combines the users array from /users/recent and the hotels array
from /hotels/top.
type: object
required: [users, hotels]
properties:
users:
type: array
description: |
Users from /users/recent. This is typically the same array of up to 30
recent users returned by the first step. These users are being
distributed across candidate hotels by preference.
items:
$ref: "#/components/schemas/User"
hotels:
type: array
description: |
Hotels from /hotels/top that should be used as candidate destinations
for user distribution.
items:
$ref: "#/components/schemas/Hotel"
HotelSegmentsResponse:
description: Response containing the segments array produced by /segments/hotel.
type: object
required: [segments]
properties:
segments:
type: array
description: |
Segments that should be copied into the segments field of
/assignments/hotels.
items:
$ref: "#/components/schemas/Segment"
AssignmentsRequest:
description: |
Request body for building assignments from the segments array returned by
/segments/hotel.
type: object
required: [segments]
properties:
segments:
type: array
description: |
Segments from /segments/hotel that should be converted into final
user-to-hotel assignments.
items:
$ref: "#/components/schemas/Segment"
AssignmentsResponse:
description: Response containing the assignments array produced by /assignments/hotels.
type: object
required: [assignments]
properties:
assignments:
type: array
description: |
Assignments that should be copied into the assignments field of
/emails/send-offers.
items:
$ref: "#/components/schemas/Assignment"
EmailOfferRequest:
description: |
Request body for sending offer emails from the assignments array returned
by /assignments/hotels.
type: object
required: [template_id, assignments]
properties:
template_id:
type: string
default: offer_template_2026
description: Identifier of the email template to use for every assignment in this request.
assignments:
type: array
description: |
Assignments from /assignments/hotels that should be emailed in the
final step.
items:
$ref: "#/components/schemas/Assignment"
EmailOfferResponse:
description: |
Result of the final email delivery step.
This response does not contain new users, hotels, segments, or assignments.
type: object
required: [sent_count, failed_count, failed]
properties:
sent_count:
type: integer
description: Number of assignments for which an email was sent successfully.
failed_count:
type: integer
description: Number of assignments for which email delivery failed.
failed:
type: array
description: Failed deliveries with reasons for each affected user.
items:
type: object
required: [user_id, reason]
properties:
user_id:
type: string
description: Identifier of the user whose email delivery failed.
reason:
type: string
description: Human-readable explanation of why the email could not be sent.