{
  "openapi": "3.1.0",
  "info": {
    "title": "Holdspace API",
    "description": "Public API for discovering and booking wellness, yoga, retreat, and workshop spaces in the Netherlands and Belgium. Designed for AI agents, search engines, and third-party integrations.",
    "version": "1.1.0",
    "contact": {
      "name": "Holdspace",
      "url": "https://holdspace.guru",
      "email": "hello@holdspace.guru"
    }
  },
  "servers": [
    {
      "url": "https://vjsoijnuubbcyecwbnyc.supabase.co/functions/v1",
      "description": "Production"
    }
  ],
  "paths": {
    "/api-discover": {
      "get": {
        "operationId": "discoverSpaces",
        "summary": "Search and discover available spaces",
        "description": "Search for wellness, yoga, retreat, and workshop spaces. Returns publicly listed spaces with location details, facilities, and booking links. No authentication required.",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "description": "Filter by city (case-insensitive substring match, e.g. 'Amsterdam', 'Haarlem')",
            "schema": { "type": "string" },
            "example": "Amsterdam"
          },
          {
            "name": "activity",
            "in": "query",
            "description": "Filter by activity type (case-insensitive). Common values: Yoga, Pilates, Meditatie, Breathwork, Sound Healing, Workshops, Coaching, Retreats, Dans",
            "schema": { "type": "string" },
            "example": "yoga"
          },
          {
            "name": "capacity",
            "in": "query",
            "description": "Minimum required capacity (number of people)",
            "schema": { "type": "integer", "minimum": 1 },
            "example": 10
          },
          {
            "name": "overnight",
            "in": "query",
            "description": "Set to 'true' to only show spaces where overnight stays are possible",
            "schema": { "type": "string", "enum": ["true", "false"] },
            "example": "true"
          },
          {
            "name": "location_id",
            "in": "query",
            "description": "Filter by location UUID to get all spaces within a specific location",
            "schema": { "type": "string", "format": "uuid" },
            "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Response language",
            "schema": { "type": "string", "enum": ["nl", "en", "de", "fr", "es"], "default": "nl" },
            "example": "en"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of results (1-50)",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 },
            "example": 10
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Pagination offset",
            "schema": { "type": "integer", "minimum": 0, "default": 0 },
            "example": 0
          }
        ],
        "responses": {
          "200": {
            "description": "List of matching spaces",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": { "type": "integer", "example": 3 },
                        "limit": { "type": "integer", "example": 20 },
                        "offset": { "type": "integer", "example": 0 },
                        "lang": { "type": "string", "example": "en" },
                        "api_version": { "type": "string", "example": "1.1.0" }
                      }
                    },
                    "spaces": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string", "format": "uuid" },
                          "name": { "type": "string", "example": "Zen Yoga Loft" },
                          "description": { "type": "string" },
                          "city": { "type": "string", "example": "Amsterdam" },
                          "region": { "type": "string", "example": "Noord-Holland" },
                          "country": { "type": "string", "example": "NL" },
                          "capacity": { "type": "integer", "example": 25 },
                          "square_meters": { "type": "integer", "example": 80 },
                          "activities": { "type": "array", "items": { "type": "string" }, "example": ["Yoga", "Meditatie", "Breathwork"] },
                          "atmosphere": { "type": "string" },
                          "has_privacy_options": { "type": "boolean" },
                          "is_featured": { "type": "boolean" },
                          "photos": { "type": "array", "items": { "type": "string", "format": "uri" } },
                          "location": {
                            "type": "object",
                            "properties": {
                              "id": { "type": "string", "format": "uuid" },
                              "name": { "type": "string" },
                              "address": { "type": "string" },
                              "city": { "type": "string" },
                              "overnight_possible": { "type": "boolean" },
                              "lunch_available": { "type": "boolean" },
                              "coordinates": {
                                "type": "object",
                                "properties": {
                                  "lat": { "type": "number" },
                                  "lng": { "type": "number" }
                                }
                              }
                            }
                          },
                          "links": {
                            "type": "object",
                            "properties": {
                              "detail": { "type": "string", "format": "uri" },
                              "book": { "type": "string", "format": "uri" }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api-discover-locations": {
      "get": {
        "operationId": "discoverLocations",
        "summary": "Search and discover locations",
        "description": "Search for physical retreat and workshop locations. A location can contain multiple spaces. Filter by city, region, country, and overnight availability. No authentication required.",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "description": "Filter by city (case-insensitive substring match)",
            "schema": { "type": "string" },
            "example": "Amsterdam"
          },
          {
            "name": "region",
            "in": "query",
            "description": "Filter by region or province (e.g. 'Noord-Holland')",
            "schema": { "type": "string" },
            "example": "Noord-Holland"
          },
          {
            "name": "country",
            "in": "query",
            "description": "Filter by country code (e.g. 'NL', 'BE')",
            "schema": { "type": "string" },
            "example": "NL"
          },
          {
            "name": "overnight",
            "in": "query",
            "description": "Set to 'true' to only show locations with overnight accommodation",
            "schema": { "type": "string", "enum": ["true", "false"] },
            "example": "true"
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Response language",
            "schema": { "type": "string", "enum": ["nl", "en", "de", "fr", "es"], "default": "nl" },
            "example": "en"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": { "type": "integer", "minimum": 0, "default": 0 }
          }
        ],
        "responses": {
          "200": {
            "description": "List of matching locations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "meta": {
                      "type": "object",
                      "properties": {
                        "total": { "type": "integer" },
                        "limit": { "type": "integer" },
                        "offset": { "type": "integer" },
                        "lang": { "type": "string" },
                        "api_version": { "type": "string" }
                      }
                    },
                    "locations": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string", "format": "uuid" },
                          "name": { "type": "string" },
                          "description": { "type": "string" },
                          "city": { "type": "string" },
                          "region": { "type": "string" },
                          "country": { "type": "string" },
                          "address": { "type": "string" },
                          "coordinates": {
                            "type": "object",
                            "properties": {
                              "lat": { "type": "number" },
                              "lng": { "type": "number" }
                            }
                          },
                          "overnight": {
                            "type": "object",
                            "properties": {
                              "possible": { "type": "boolean" },
                              "description": { "type": "string" }
                            }
                          },
                          "dining": {
                            "type": "object",
                            "properties": {
                              "lunch_available": { "type": "boolean" },
                              "self_catering": { "type": "boolean" }
                            }
                          },
                          "accessibility": {
                            "type": "object",
                            "properties": {
                              "car": { "type": "string" },
                              "public_transport": { "type": "string" },
                              "train": { "type": "string" }
                            }
                          },
                          "photos": { "type": "array", "items": { "type": "string", "format": "uri" } },
                          "links": {
                            "type": "object",
                            "properties": {
                              "detail": { "type": "string", "format": "uri" },
                              "spaces": { "type": "string", "format": "uri", "description": "Frontend URL for spaces at this location" },
                              "api_spaces": { "type": "string", "format": "uri", "description": "Direct API URL to get spaces at this location" }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/check-availability": {
      "post": {
        "operationId": "checkAvailability",
        "summary": "Check space availability",
        "description": "Check whether a specific space is available for a given date and time range. Always call this before creating a booking. No authentication required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["space_id", "start_datetime", "end_datetime"],
                "properties": {
                  "space_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the space to check (from discover endpoint)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "start_datetime": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Start time in ISO 8601 format",
                    "example": "2025-06-15T09:00:00+02:00"
                  },
                  "end_datetime": {
                    "type": "string",
                    "format": "date-time",
                    "description": "End time in ISO 8601 format",
                    "example": "2025-06-15T17:00:00+02:00"
                  },
                  "room_index": {
                    "type": "integer",
                    "description": "Room index for multi-room spaces (default 0)",
                    "default": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Availability result",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "description": "Space is available",
                      "properties": {
                        "available": { "type": "boolean", "example": true },
                        "space_id": { "type": "string", "format": "uuid" },
                        "start_datetime": { "type": "string", "format": "date-time" },
                        "end_datetime": { "type": "string", "format": "date-time" }
                      }
                    },
                    {
                      "type": "object",
                      "description": "Space is not available",
                      "properties": {
                        "available": { "type": "boolean", "example": false },
                        "reason": { "type": "string", "example": "Dit tijdslot is al geboekt" },
                        "conflicts": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "type": { "type": "string" },
                              "start": { "type": "string", "format": "date-time" },
                              "end": { "type": "string", "format": "date-time" },
                              "reason": { "type": "string" }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/natural-language-search": {
      "post": {
        "operationId": "naturalLanguageSearch",
        "summary": "Search spaces with natural language",
        "description": "Parse a natural language query into structured search filters using AI. The returned filters can be used directly with the /api-discover endpoint. Understands Dutch and English queries. No authentication required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": {
                  "query": {
                    "type": "string",
                    "description": "Natural language search query in Dutch or English",
                    "example": "rustige yoga ruimte in Utrecht voor 20 personen met overnachting"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted search filters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "filters": {
                      "type": "object",
                      "description": "Structured filters ready for use with /api-discover",
                      "properties": {
                        "activities": { "type": "array", "items": { "type": "string" }, "example": ["Yoga"] },
                        "city": { "type": "string", "example": "Utrecht" },
                        "region": { "type": "string" },
                        "minCapacity": { "type": "integer", "example": 20 },
                        "maxCapacity": { "type": "integer" },
                        "overnightPossible": { "type": "boolean", "example": true },
                        "atmosphere": { "type": "string", "example": "rustig" },
                        "keywords": { "type": "array", "items": { "type": "string" } }
                      }
                    },
                    "interpretation": {
                      "type": "string",
                      "description": "Human-readable Dutch summary of what was understood",
                      "example": "Je zoekt naar een rustige yoga ruimte in Utrecht voor minimaal 20 personen met overnachting"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api-book": {
      "post": {
        "operationId": "createBooking",
        "summary": "Create a new booking request",
        "description": "Submit a booking request for a specific space and time slot. The booking is created with 'pending' status. Always check availability first with /check-availability. No authentication required.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["space_id", "start_datetime", "end_datetime", "customer_name", "customer_email"],
                "properties": {
                  "space_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "ID of the space to book (from discover endpoint)",
                    "example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                  },
                  "start_datetime": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Booking start in ISO 8601 format (e.g. 2025-06-15T09:00:00+02:00)",
                    "example": "2025-06-15T09:00:00+02:00"
                  },
                  "end_datetime": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Booking end in ISO 8601 format",
                    "example": "2025-06-15T17:00:00+02:00"
                  },
                  "customer_name": {
                    "type": "string",
                    "description": "Full name of the person booking",
                    "example": "Jan de Vries"
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email address for booking confirmation",
                    "example": "jan@voorbeeld.nl"
                  },
                  "customer_phone": {
                    "type": "string",
                    "description": "Phone number (optional)",
                    "example": "+31612345678"
                  },
                  "notes": {
                    "type": "string",
                    "description": "Additional notes or requirements",
                    "example": "Yogaretreat voor gevorderden, graag extra yogamatten"
                  },
                  "participant_count": {
                    "type": "integer",
                    "description": "Expected number of participants (optional)",
                    "example": 15
                  },
                  "room_index": {
                    "type": "integer",
                    "description": "Room index for multi-room spaces (default 0)",
                    "default": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Booking successfully created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": true },
                    "booking": {
                      "type": "object",
                      "properties": {
                        "id": { "type": "string", "format": "uuid" },
                        "status": { "type": "string", "example": "pending" },
                        "space_name": { "type": "string" },
                        "location_name": { "type": "string" },
                        "start_datetime": { "type": "string", "format": "date-time" },
                        "end_datetime": { "type": "string", "format": "date-time" },
                        "price": {
                          "type": "object",
                          "properties": {
                            "total_cents": { "type": "integer", "example": 35000 },
                            "currency": { "type": "string", "example": "EUR" },
                            "formatted": { "type": "string", "example": "€350.00" },
                            "includes_vat": { "type": "boolean" }
                          }
                        }
                      }
                    },
                    "next_steps": {
                      "type": "object",
                      "properties": {
                        "message": { "type": "string" },
                        "payment_url": {
                          "type": "string",
                          "format": "uri",
                          "description": "MUST be shared with the customer to complete Stripe payment (PCI compliance)"
                        },
                        "detail_url": { "type": "string", "format": "uri" }
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Time slot not available",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": false },
                    "available": { "type": "boolean", "example": false },
                    "reason": { "type": "string" },
                    "suggestion": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid request parameters" },
          "404": { "description": "Space not found" },
          "429": { "description": "Rate limit exceeded" }
        }
      }
    }
  }
}
