{
  "openapi": "3.1.0",
  "info": {
    "title": "VenturaCuida — Public Booking API",
    "summary": "Book massage and therapy services in Portugal via AI agents or applications.",
    "description": "VenturaCuida connects hotel and Airbnb guests with professional therapists (physiotherapists, masseurs) in Portugal. This public API allows AI agents, chatbots, and external applications to:\n\n1. **Discover available services** — pricing catalog from VenturaCuida (service + duration + price) and locations\n2. **Submit a session request** — collect client info and submit a session; our team will evaluate and contact the client to confirm\n3. **Submit a partnership request** — hotels/Airbnbs can request to become partners\n\n## How It Works\n\n1. Call `GET /api/public/v1/services` to see available services (pricing) and locations.\n2. Call `GET /api/public/v1/services/{serviceSlug}/booking?locationSlug=...&preferredDate=...&...` with all params in the query string to submit a session request.\n3. Call `GET /api/public/v1/partnership-request?name=...&email=...&...` with all params in the query string to submit a partnership request.\n\n**All endpoints use GET with query params** — AI agents that do not handle POST can send all data via GET.\n\n## Rate Limits\n\n- Read endpoints (GET): 15 requests per minute\n- Booking endpoint (GET): 3 requests per 10 minutes\n\nRate limit headers (`RateLimit-*`) are included in responses.",
    "version": "2.0.0",
    "contact": {
      "name": "VenturaCuida",
      "url": "https://www.venturacuida.com",
      "email": "venturacareportugal@gmail.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://ventura-cuida-backend.herokuapp.com",
      "description": "Production server"
    }
  ],
  "tags": [
    {
      "name": "Services",
      "description": "Discover available services (pricing from VenturaCuida) and locations."
    },
    {
      "name": "Booking",
      "description": "Submit session requests; evaluated by our team."
    },
    {
      "name": "Partnership",
      "description": "Submit partnership requests (hotels, Airbnbs)."
    }
  ],
  "paths": {
    "/api/public/v1/services": {
      "get": {
        "operationId": "listServices",
        "tags": ["Services"],
        "summary": "List all available massage and therapy services",
        "description": "Returns services as pricing records from VenturaCuida partnership (service + duration + price) and available locations. Each service item is a bookable product.\n\nNo authentication required.",
        "security": [],
        "responses": {
          "200": {
            "description": "List of services and locations",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicesResponse"
                },
                "examples": {
                  "success": {
                    "summary": "Successful response with services",
                    "value": {
                      "description": "VenturaCuida — On-demand massage and therapy services in Portugal.",
                      "website": "https://www.venturacuida.com",
                      "services": [
                        {
                          "id": "abc123",
                          "serviceSlug": "massage",
                          "serviceName": "Massage",
                          "duration": 60,
                          "totalPrice": 55,
                          "currency": "EUR",
                          "photo": null,
                          "therapistTypes": ["massage"]
                        }
                      ],
                      "locations": [
                        {
                          "name": "Example Hotel",
                          "slug": "example-hotel",
                          "workLocations": ["madeira-island"]
                        }
                      ],
                      "bookingInstructions": {
                        "summary": "GET with query params to submit a session request; our team will evaluate and contact you.",
                        "bookingEndpoint": "GET /api/public/v1/services/{serviceSlug}/booking",
                        "requiredFields": [
                          "locationSlug",
                          "preferredDate",
                          "preferredTime",
                          "duration",
                          "clientName",
                          "clientPhone"
                        ],
                        "optionalFields": [
                          "clientEmail",
                          "address",
                          "notes",
                          "language",
                          "nationality"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/v1/services/{serviceSlug}/booking": {
      "get": {
        "operationId": "createServiceBooking",
        "tags": ["Booking"],
        "summary": "Submit a session request for evaluation by our team",
        "description": "Submit a session request via GET with all parameters in the query string. The request will be evaluated by our team, who will contact the client via phone to confirm availability and finalize the appointment.\n\nAll params in URL: locationSlug, preferredDate, preferredTime, duration, clientName, clientPhone, plus optional clientEmail, address, notes, language, nationality. Session is created with status 'in_validation'.\n\n**Rate limit:** 3 requests per 10 minutes.\n\nNo authentication required.",
        "security": [],
        "parameters": [
          {
            "name": "serviceSlug",
            "in": "path",
            "required": true,
            "description": "The slug identifier of the service to book.",
            "schema": { "type": "string" },
            "example": "massage"
          },
          {
            "name": "locationSlug",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "preferredDate",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD format",
            "schema": { "type": "string", "format": "date" }
          },
          {
            "name": "preferredTime",
            "in": "query",
            "required": true,
            "description": "HH:mm 24-hour format",
            "schema": {
              "type": "string",
              "pattern": "^([01]\\d|2[0-3]):([0-5]\\d)$"
            }
          },
          {
            "name": "duration",
            "in": "query",
            "required": true,
            "schema": { "type": "integer", "enum": [30, 60, 90, 120] }
          },
          {
            "name": "clientName",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "clientPhone",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "clientEmail",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "format": "email" }
          },
          {
            "name": "address",
            "in": "query",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "notes",
            "in": "query",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "language",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "enum": ["en", "pt"] }
          },
          {
            "name": "nationality",
            "in": "query",
            "required": false,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "201": {
            "description": "Session request submitted successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionResponse"
                },
                "examples": {
                  "success": {
                    "summary": "Session submitted",
                    "value": {
                      "message": "Session request submitted successfully.",
                      "session": {
                        "id": "60f7b1a2c3d4e5f6a7b8c9d0",
                        "status": "in_validation",
                        "service": "Massage",
                        "location": "Example Hotel",
                        "dateTime": "2026-03-15T14:00",
                        "duration": 60,
                        "totalPrice": 55,
                        "currency": "EUR"
                      },
                      "nextSteps": "Your session request will be evaluated by our team. We will contact you via phone to confirm availability and finalize the appointment."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid fields",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Service, location, or pricing not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "/api/public/v1/partnership-request": {
    "get": {
      "operationId": "createPartnershipRequest",
      "tags": ["Partnership"],
      "summary": "Submit a partnership request (hotel/airbnb)",
      "description": "Submit a partnership request via GET with all parameters in the query string. For AI agents that do not handle POST. All params in URL: name, contactPersonName, email, phone, partnershipType, plus optional address, roomsOrHouses, workLocations (comma-separated: lisbon,madeira-island), language, notes.\n\nNo authentication required.",
      "security": [],
      "parameters": [
        { "name": "name", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Establishment name" },
        { "name": "contactPersonName", "in": "query", "required": true, "schema": { "type": "string" } },
        { "name": "email", "in": "query", "required": true, "schema": { "type": "string", "format": "email" } },
        { "name": "phone", "in": "query", "required": true, "schema": { "type": "string" } },
        { "name": "partnershipType", "in": "query", "required": true, "schema": { "type": "string", "enum": ["hotel", "boutique_hotel", "airbnb_manager"] } },
        { "name": "address", "in": "query", "required": false, "schema": { "type": "string" } },
        { "name": "roomsOrHouses", "in": "query", "required": false, "schema": { "type": "string" } },
        { "name": "workLocations", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Comma-separated: lisbon,madeira-island" },
        { "name": "language", "in": "query", "required": false, "schema": { "type": "string", "enum": ["en", "pt"] } },
        { "name": "notes", "in": "query", "required": false, "schema": { "type": "string" } }
      ],
      "responses": {
        "201": {
          "description": "Partnership request submitted successfully",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": { "type": "string" },
                  "nextSteps": { "type": "string" }
                }
              }
            }
          }
        },
        "400": { "description": "Missing or invalid fields" },
        "409": { "description": "Partnership with this email already exists" },
        "500": { "description": "Internal server error" }
      }
    }
  },
  "components": {
    "schemas": {
      "ServicesResponse": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "description": "Human-readable description of VenturaCuida services."
          },
          "website": {
            "type": "string",
            "format": "uri",
            "description": "VenturaCuida website URL."
          },
          "services": {
            "type": "array",
            "description": "List of available services (pricing from VenturaCuida). Each item = service + duration + price.",
            "items": {
              "$ref": "#/components/schemas/ServicePricingItem"
            }
          },
          "locations": {
            "type": "array",
            "description": "List of partner locations where services are available.",
            "items": {
              "$ref": "#/components/schemas/Location"
            }
          },
          "bookingInstructions": {
            "type": "object",
            "description": "Instructions for AI agents on how to create a booking.",
            "properties": {
              "summary": { "type": "string" },
              "endpoint": { "type": "string" },
              "requiredFields": {
                "type": "array",
                "items": { "type": "string" }
              },
              "optionalFields": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          }
        }
      },
      "PricingResponse": {
        "type": "object",
        "properties": {
          "services": {
            "type": "array",
            "description": "List of services with pricing information.",
            "items": {
              "$ref": "#/components/schemas/ServicePricing"
            }
          }
        }
      },
      "ServicePricingItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Pricing identifier."
          },
          "serviceSlug": {
            "type": "string",
            "description": "Service slug used in booking endpoint."
          },
          "serviceName": {
            "type": "string",
            "description": "Display name of the service."
          },
          "duration": {
            "type": "integer",
            "description": "Duration in minutes."
          },
          "totalPrice": {
            "type": "number",
            "description": "Total price in EUR."
          },
          "currency": {
            "type": "string",
            "const": "EUR"
          },
          "photo": {
            "type": ["string", "null"]
          },
          "therapistTypes": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "ServicePricing": {
        "type": "object",
        "properties": {
          "serviceSlug": {
            "type": "string",
            "description": "Service slug identifier."
          },
          "serviceName": {
            "type": "string",
            "description": "Display name of the service."
          },
          "pricing": {
            "type": "array",
            "description": "Available pricing options for this service.",
            "items": {
              "$ref": "#/components/schemas/PricingOption"
            }
          }
        }
      },
      "PricingOption": {
        "type": "object",
        "properties": {
          "duration": {
            "type": "integer",
            "description": "Duration in minutes."
          },
          "totalPrice": {
            "type": "number",
            "description": "Total price for the session."
          },
          "currency": {
            "type": "string",
            "description": "Currency code (always EUR).",
            "const": "EUR"
          },
          "numberOfSessions": {
            "type": "integer",
            "description": "Number of sessions included (1 for single bookings)."
          },
          "location": {
            "type": ["object", "null"],
            "description": "The location/partnership where this price applies.",
            "properties": {
              "name": { "type": "string" },
              "slug": { "type": "string" }
            }
          }
        }
      },
      "Location": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the partner location."
          },
          "slug": {
            "type": "string",
            "description": "URL-friendly identifier for this location."
          },
          "workLocations": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": ["lisbon", "madeira-island", "west-madeira-island"]
            },
            "description": "Geographic areas served by this partner."
          }
        }
      },
      "AvailabilityResponse": {
        "type": "object",
        "properties": {
          "location": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "slug": { "type": "string" }
            }
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "The requested date."
          },
          "timezone": {
            "type": "string",
            "description": "Timezone used for all times (always Europe/Lisbon).",
            "const": "Europe/Lisbon"
          },
          "slots": {
            "type": "array",
            "description": "Available time slots. Empty array means no therapist has declared availability yet, but a booking request can still be submitted.",
            "items": {
              "$ref": "#/components/schemas/TimeSlot"
            }
          },
          "note": {
            "type": "string",
            "description": "Additional context for the AI agent or user."
          }
        }
      },
      "TimeSlot": {
        "type": "object",
        "properties": {
          "dateTime": {
            "type": "string",
            "description": "Full date and time in YYYY-MM-DDTHH:mm format (Europe/Lisbon timezone)."
          },
          "time": {
            "type": "string",
            "description": "Time only in HH:mm format."
          },
          "available": {
            "type": "boolean",
            "description": "Whether this slot is available for booking."
          }
        }
      },
      "BookingRequest": {
        "type": "object",
        "required": [
          "locationSlug",
          "preferredDate",
          "preferredTime",
          "duration",
          "clientName",
          "clientPhone"
        ],
        "properties": {
          "locationSlug": {
            "type": "string",
            "description": "Location identifier (e.g. 'example-hotel'). Get from GET /api/public/v1/services."
          },
          "preferredDate": {
            "type": "string",
            "format": "date",
            "description": "Preferred date in YYYY-MM-DD format."
          },
          "preferredTime": {
            "type": "string",
            "description": "Preferred time in HH:mm 24-hour format (e.g. '14:00'). Timezone is Europe/Lisbon.",
            "pattern": "^([01]\\d|2[0-3]):([0-5]\\d)$"
          },
          "duration": {
            "type": "integer",
            "description": "Session duration in minutes.",
            "enum": [30, 60, 90, 120]
          },
          "clientName": {
            "type": "string",
            "description": "Full name of the person receiving the service."
          },
          "clientPhone": {
            "type": "string",
            "description": "Phone number with country code (e.g. '+351912345678'). Used for booking confirmation."
          },
          "clientEmail": {
            "type": "string",
            "format": "email",
            "description": "Optional email for booking confirmation."
          },
          "address": {
            "type": "string",
            "description": "Address where the therapist should go (hotel, apartment, etc.)."
          },
          "notes": {
            "type": "string",
            "description": "Special requests or additional information (e.g. 'deep tissue massage', 'allergies to certain oils')."
          },
          "language": {
            "type": "string",
            "enum": ["en", "pt"],
            "description": "Preferred communication language. Defaults to 'en'."
          },
          "nationality": {
            "type": "string",
            "description": "Client nationality (e.g. 'UK', 'Portugal')."
          }
        }
      },
      "SessionResponse": {
        "type": "object",
        "properties": {
          "message": { "type": "string" },
          "session": {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "status": {
                "type": "string",
                "enum": [
                  "in_validation",
                  "awaiting_therapist_availability",
                  "session_scheduled",
                  "completed",
                  "cancelled"
                ]
              },
              "service": { "type": "string" },
              "location": { "type": "string" },
              "dateTime": { "type": "string" },
              "duration": { "type": "integer" },
              "totalPrice": { "type": "number" },
              "currency": { "type": "string", "const": "EUR" }
            }
          },
          "nextSteps": { "type": "string" }
        }
      },
      "BookingResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Confirmation message."
          },
          "booking": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "Unique booking identifier."
              },
              "status": {
                "type": "string",
                "description": "Current booking status.",
                "enum": [
                  "awaiting_therapist_availability",
                  "availability_provided",
                  "confirmed",
                  "session_scheduled",
                  "completed",
                  "cancelled"
                ]
              },
              "service": {
                "type": "string",
                "description": "Name of the booked service."
              },
              "location": {
                "type": "string",
                "description": "Name of the location."
              },
              "dateTime": {
                "type": "string",
                "description": "Booked date and time (YYYY-MM-DDTHH:mm, Europe/Lisbon)."
              },
              "duration": {
                "type": "integer",
                "description": "Session duration in minutes."
              },
              "totalPrice": {
                "type": "number",
                "description": "Total price for the session."
              },
              "currency": {
                "type": "string",
                "const": "EUR"
              }
            }
          },
          "nextSteps": {
            "type": "string",
            "description": "What happens next after booking."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable error description."
          },
          "missingFields": {
            "type": "array",
            "items": { "type": "string" },
            "description": "List of missing required fields (for 400 errors)."
          },
          "error": {
            "type": "string",
            "description": "Machine-readable error code."
          },
          "example": {
            "type": "object",
            "description": "Example of a valid request (for 400 errors)."
          }
        }
      }
    }
  }
}
