{
  "openapi": "3.0.3",
  "info": {
    "title": "Online booking lookup API",
    "version": "1.0.0",
    "description": "Find treatments, employees and available appointments on the booking site's own origin. These POST operations only retrieve information. Give the customer a prefilled link to the actual booking page to select a time and complete the booking. A lookup does not reserve an appointment. Use current IDs from API responses; examples are illustrative. See the linked guide for the different API item and booking-link formats, local date formats, and the double-encoded availability response."
  },
  "servers": [
    { "url": "/", "description": "The origin serving this description and the booking page" }
  ],
  "externalDocs": {
    "description": "Workflow, request examples, response decoding and prefilled booking links",
    "url": "agent-guide.html"
  },
  "security": [],
  "paths": {
    "/module/booksystem/calendaritems/gettreatments/": {
      "post": {
        "operationId": "listBookingTreatments",
        "summary": "List available services and packages",
        "description": "No parameters or request body. Mode 1 is a service and Mode 2 is a package. Construct availability Items as Mode_Id (1_22 or 2_22); booking links use t=22 or t=p22 instead. Send Accept: application/json.",
        "responses": {
          "200": {
            "description": "A JSON array of treatments and packages; not a JSON-encoded string.",
            "content": {
              "application/json": {
                "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Treatment" } },
                "example": [
                  { "Id": 22, "Mode": 1, "Category": "Facial treatments", "NameHomepage": "Example facial treatment", "PriceGross": 95.0, "AttributeItems": "", "DescriptionHomepage": "Example description", "CategoryDescriptionHomepage": "" }
                ]
              }
            }
          },
          "default": { "description": "Lookup failed. Do not interpret errors as an empty treatment list." }
        }
      }
    },
    "/module/booksystem/calendaritems/getcontacts/": {
      "post": {
        "operationId": "listBookingEmployees",
        "summary": "List employees for online booking",
        "description": "No parameters or request body. Match the requested employee by Name or Firstname and use their current Id. Ask the customer if a name is ambiguous. Example employee 216 is not asserted to be Sarah. A listed employee may not have availability for a particular treatment. Send Accept: application/json.",
        "responses": {
          "200": {
            "description": "A JSON array of employees; not a JSON-encoded string.",
            "content": {
              "application/json": {
                "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Employee" } },
                "example": [
                  { "Id": 216, "Name": "Example employee", "Firstname": "Example", "Lastname": "Employee", "Gender": 0 }
                ]
              }
            }
          },
          "default": { "description": "Lookup failed. Do not interpret errors as an empty employee list." }
        }
      }
    },
    "/module/booksystem/calendaritems/gettimes/": {
      "post": {
        "operationId": "findBookingAppointments",
        "summary": "Find available appointments for treatments and an optional employee",
        "description": "Use a nonempty Items array and an explicit earliest search date. Keep PersonId when the customer requested a specific employee. A successful response is a JSON string containing a JSON array: parse JSON twice. An error message can also be returned inside a JSON string with HTTP 200; parsing errors must be treated as lookup failures. Returned dates and times are local business times without a UTC offset, and do not include employee IDs. Retain the request's employee filter. Availability may change before booking. Send Accept: application/json.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AvailabilityRequest" },
              "examples": {
                "serviceWithEmployee": {
                  "summary": "Service with a specific employee (illustrative IDs)",
                  "value": { "Items": ["1_22"], "DateTime": "09.12.2026", "PersonId": 216 }
                },
                "packageAnyEmployee": {
                  "summary": "Package with any employee (illustrative ID)",
                  "value": { "Items": ["2_22"], "DateTime": "09.12.2026", "PersonId": null }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON string containing serialized Appointment objects, or an error message. The actual HTTP JSON value is a string, not an array. An empty successful result is the string '[]'.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string",
                  "description": "Parse this string as JSON and validate that the result is an array of Appointment objects. If parsing or validation fails, the lookup failed.",
                  "x-decoded-schema": { "type": "array", "items": { "$ref": "#/components/schemas/Appointment" } }
                },
                "examples": {
                  "available": {
                    "summary": "JSON-encoded array (example only)",
                    "value": "[{\"DateString\":\"09.12.2026\",\"DayString\":\"Mi\",\"TimeString\":\"10:00\"}]"
                  },
                  "noneAvailable": { "summary": "Successful lookup with no appointments", "value": "[]" }
                }
              }
            }
          },
          "default": { "description": "Lookup failed. Do not interpret errors as no available appointments." }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Treatment": {
        "type": "object",
        "properties": {
          "Id": { "type": "integer", "description": "Service or package ID. Interpret together with Mode." },
          "Mode": { "type": "integer", "enum": [1, 2], "description": "1 = service; 2 = package." },
          "Category": { "type": "string", "nullable": true },
          "NameHomepage": { "type": "string", "nullable": true },
          "PriceGross": { "type": "number", "description": "Displayed gross price; final pricing can change at the business." },
          "AttributeItems": { "type": "string", "nullable": true },
          "DescriptionHomepage": { "type": "string", "nullable": true, "description": "May contain HTML." },
          "CategoryDescriptionHomepage": { "type": "string", "nullable": true, "description": "May contain HTML." }
        }
      },
      "Employee": {
        "type": "object",
        "properties": {
          "Id": { "type": "integer", "description": "Use as PersonId for availability and employee for the booking link." },
          "Name": { "type": "string", "nullable": true, "description": "Display name, potentially with a surname or initial to disambiguate." },
          "Firstname": { "type": "string", "nullable": true },
          "Lastname": { "type": "string", "nullable": true },
          "Gender": { "type": "integer" }
        }
      },
      "AvailabilityRequest": {
        "type": "object",
        "required": ["Items", "DateTime"],
        "properties": {
          "Items": {
            "type": "array",
            "minItems": 1,
            "description": "Current service/package IDs prefixed with Mode and an underscore. Multiple items are supported for availability, but prefilled booking links accept only one service or package.",
            "items": { "type": "string", "pattern": "^[12]_[1-9][0-9]*$" },
            "example": ["1_22"]
          },
          "DateTime": {
            "type": "string",
            "description": "Earliest search date. Use dd.MM.yyyy for the German installation; booking-link date instead uses YYYY-MM-DD. Supply a real calendar date.",
            "pattern": "^[0-9]{2}\\.[0-9]{2}\\.[0-9]{4}$",
            "example": "09.12.2026"
          },
          "PersonId": {
            "type": "integer",
            "nullable": true,
            "minimum": 1,
            "description": "Current employee Id. Omit or use null for any employee. Do not silently replace a requested employee with any employee.",
            "example": 216
          }
        }
      },
      "Appointment": {
        "type": "object",
        "required": ["DateString", "DayString", "TimeString"],
        "properties": {
          "DateString": { "type": "string", "description": "Localized business date; dd.MM.yyyy on the German installation.", "example": "09.12.2026" },
          "DayString": { "type": "string", "description": "Localized abbreviated weekday.", "example": "Mi" },
          "TimeString": { "type": "string", "description": "Localized business time, without UTC offset.", "example": "10:00" }
        }
      }
    }
  }
}
