557 lines
21 KiB
YAML
557 lines
21 KiB
YAML
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.
|