{
  "openapi": "3.1.0",
  "info": {
    "title": "AI Visibility Studio Booking API",
    "version": "2026-09-06",
    "description": "Check live availability and book a 30-minute Website & AI Visibility Consultation. The booking calendar uses Europe/Lisbon time, Monday-Friday, 10:00-18:00. Explicit authorization from the attendee is required before creating a booking."
  },
  "servers": [{ "url": "https://aivisibilitystudio.com" }],
  "paths": {
    "/api/booking/availability": {
      "get": {
        "operationId": "getBookingAvailability",
        "summary": "Get live bookable appointment slots",
        "parameters": [
          { "name": "from", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } },
          { "name": "to", "in": "query", "required": false, "schema": { "type": "string", "format": "date" } },
          { "name": "days", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 14, "default": 7 } }
        ],
        "responses": {
          "200": { "description": "Live availability", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AvailabilityResponse" } } } },
          "503": { "description": "Calendar connection not configured" }
        }
      }
    },
    "/api/booking/create": {
      "post": {
        "operationId": "createBooking",
        "summary": "Create a confirmed appointment",
        "description": "Call only after the attendee has explicitly authorized the booking. Re-check availability immediately before booking. Supply an Idempotency-Key header when possible.",
        "parameters": [{ "name": "Idempotency-Key", "in": "header", "required": false, "schema": { "type": "string", "maxLength": 200 } }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBookingRequest" } } }
        },
        "responses": {
          "201": { "description": "Booking confirmed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BookingResponse" } } } },
          "409": { "description": "Slot no longer available" },
          "429": { "description": "Rate limit reached" }
        }
      }
    },
    "/api/booking/reschedule": {
      "post": {
        "operationId": "rescheduleBooking",
        "summary": "Move an existing booking",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["booking_id", "management_token", "start"], "properties": { "booking_id": { "type": "string" }, "management_token": { "type": "string" }, "start": { "type": "string", "format": "date-time" } } } } } },
        "responses": { "200": { "description": "Booking rescheduled" }, "409": { "description": "Requested slot is unavailable" } }
      }
    },
    "/api/booking/cancel": {
      "post": {
        "operationId": "cancelBooking",
        "summary": "Cancel an existing booking",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["booking_id", "management_token"], "properties": { "booking_id": { "type": "string" }, "management_token": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Booking cancelled" } }
      }
    },
    "/api/booking/status": {
      "get": {
        "operationId": "getBookingStatus",
        "summary": "Check whether instant booking is connected",
        "responses": { "200": { "description": "Booking service status" } }
      }
    }
  },
  "components": {
    "schemas": {
      "Slot": {
        "type": "object",
        "required": ["start", "end", "localDate", "localTime"],
        "properties": {
          "start": { "type": "string", "format": "date-time" },
          "end": { "type": "string", "format": "date-time" },
          "localDate": { "type": "string", "format": "date" },
          "localTime": { "type": "string", "examples": ["14:30"] }
        }
      },
      "AvailabilityResponse": {
        "type": "object",
        "required": ["service", "service_id", "timezone", "duration_minutes", "working_hours", "slots"],
        "properties": {
          "service": { "type": "string" },
          "service_id": { "type": "string" },
          "timezone": { "const": "Europe/Lisbon" },
          "duration_minutes": { "const": 30 },
          "buffer_minutes": { "const": 15 },
          "min_notice_hours": { "const": 24 },
          "working_hours": { "type": "object" },
          "slots": { "type": "array", "items": { "$ref": "#/components/schemas/Slot" } }
        }
      },
      "CreateBookingRequest": {
        "type": "object",
        "required": ["start", "name", "email", "project_summary", "authorized_to_book"],
        "properties": {
          "start": { "type": "string", "format": "date-time", "description": "Use a start value returned by the availability endpoint." },
          "name": { "type": "string", "minLength": 2, "maxLength": 100 },
          "email": { "type": "string", "format": "email" },
          "project_summary": { "type": "string", "minLength": 10, "maxLength": 1800 },
          "authorized_to_book": { "type": "boolean", "const": true, "description": "True only when the attendee has explicitly authorized this appointment." },
          "source": { "type": "string", "maxLength": 80, "examples": ["ai-agent", "website"] },
          "idempotency_key": { "type": "string", "maxLength": 200 }
        }
      },
      "BookingResponse": {
        "type": "object",
        "required": ["status", "booking_id", "start", "end", "timezone"],
        "properties": {
          "status": { "const": "confirmed" },
          "booking_id": { "type": "string" },
          "start": { "type": "string", "format": "date-time" },
          "end": { "type": "string", "format": "date-time" },
          "timezone": { "const": "Europe/Lisbon" },
          "meeting_url": { "type": ["string", "null"], "format": "uri" },
          "manage_url": { "type": "string", "format": "uri" }
        }
      }
    }
  }
}
