We have a proper API contract for BookingRecommendation as a Hydra resource:
- Contract:
booking-recommendations.yaml
Currently the AI service streams recommendations directly to the frontend via SSE (POST /recommendations), returning flat objects like:
{"type": "restaurant", "identifier": "rt_f93b0058", "name": "Le Plaza", "thumbnail": null, "reason": "...", "price": null, "time": "19:00"}
{"type": "activity", "identifier": "snorkeling", "name": "Snorkeling Adventure", "thumbnail": null, "reason": "...", "price": null, "time": "10:00"}
{"type": "service", "identifier": "srv_0215f573", "name": "Airport Transfer", "thumbnail": null, "reason": "...", "price": 36000, "time": null}-
Frontend should not call the AI service directly — the frontend should fetch recommendations from the main API like any other resource (
GET /booking-recommendations), not from the AI service. -
Fabricated identifiers — some identifiers like
"snorkeling"and"sunset_cruise"are not real entities in the system. The AI must only recommend entities that actually exist. -
Flat structure doesn't match the API contract — the contract defines a proper Hydra resource (
BookingRecommendation) with@id,@type,identifier,entityIdentifier,booking,reasoning, etc.
┌──────────┐ POST /booking-recommendations ┌──────────┐
│ AI │ ─────────────────────────────────────────────► │ API │
│ Service │ { booking, items: [{ type, title, │ │
│ │ description, reasoning, entityIdentifier }]│ │
└──────────┘ └────┬─────┘
│
│ persists
▼
┌──────────┐ GET /booking-recommendations?iri[booking]= ┌──────────┐
│ Frontend │ ◄───────────────────────────────────────────── │ DB │
│ │ standard Hydra collection response │ │
└──────────┘ └──────────┘
- AI service generates recommendations and POSTs them to the API (
POST /booking-recommendations) per the contract - API persists them as
BookingRecommendationentities - Frontend fetches via standard
GET /booking-recommendations?iri[booking]=/bookings/bk_xxx— no AI interaction
- Stop streaming recommendations directly to the frontend
- Instead,
POST /booking-recommendationsto the main API with the payload defined in the contract:
{
"booking": "/bookings/bk_xyz789",
"items": [
{
"type": "restaurant",
"title": "Le Plaza",
"description": "Fine French dining with Mediterranean flavors",
"reasoning": "You enjoyed a dinner at Le Plaza during a previous stay...",
"entityIdentifier": "rt_f93b0058"
},
{
"type": "activity",
"title": "Snorkeling Adventure",
"description": "Guided snorkeling exploring the coral reef",
"reasoning": "Given your active history of booking activities...",
"entityIdentifier": "act_a1b2c3d4"
}
]
}- Only recommend entities that exist in the system — use real
entityIdentifiervalues (e.g.,rt_xxx,act_xxx,svc_xxx). Never fabricate identifiers like"snorkeling"or"sunset_cruise".
- Remove direct AI service call (
NEXT_PUBLIC_AI_API_URL/recommendations) - Fetch from the main API:
GET /booking-recommendations?iri[booking]=/bookings/bk_xxx - Consume as a standard Hydra collection (same pattern as every other entity)
- Implement the
BookingRecommendationresource as defined in the contract POSTreplaces all existing recommendations for the booking+guest pair (C-001)GETcollection supportsiri[booking]filter andX-Hotelheader for admin access
The full contract is already defined: booking-recommendations.yaml
Key schemas:
BookingRecommendation.info:@id,@type,identifier,type,title,description,entityIdentifier,bookingBookingRecommendation.read: info +reasoning,guest,createdAtBookingRecommendationInput:booking(IRI) +items[]withtype,title,description,reasoning,entityIdentifier