{
  "openapi": "3.1.0",
  "info": {
    "title": "Melatone Skin Clinic Content API",
    "version": "1.3.0",
    "summary": "Public, read-only content and catalogue API for AI agents and developers.",
    "description": "A small, public, read-only API for Melatone Skin Clinic, a clinician-led medical aesthetics clinic in Battersea, London. It exposes the treatment catalogue and clinic identity as JSON. No authentication is required and no personal data is accepted.\n\n**No booking or payment API.** Appointments are booked via the Treatwell widget; human contact is via WhatsApp.\n\n**Versioning & deprecation.** Endpoints under `/api/v1/` are versioned in the URL path and every response carries `X-API-Version`. Breaking changes ship under a new prefix (`/api/v2/`). A retiring version is announced with the RFC 8594 `Deprecation` and `Sunset` response headers at least six months before removal. Full policy: https://www.melatoneskinclinic.co.uk/api-versioning-policy\n\n**Errors.** Every operation returns RFC 9457 `application/problem+json` errors with a machine-readable `code` (see the `Problem` schema and the `default` response on each operation). **Rate limits** are advertised with the standard RFC RateLimit headers on every response; a `429` carries `Retry-After`.",
    "contact": {
      "name": "Melatone Skin Clinic",
      "url": "https://www.melatoneskinclinic.co.uk/developers",
      "email": "hello@melatoneskinclinic.co.uk"
    },
    "license": {
      "name": "Content usage policy",
      "url": "https://www.melatoneskinclinic.co.uk/llms.txt"
    }
  },
  "servers": [
    {
      "url": "https://www.melatoneskinclinic.co.uk",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "catalogue",
      "description": "Treatment catalogue and clinic identity."
    },
    {
      "name": "ops",
      "description": "Operational endpoints."
    }
  ],
  "paths": {
    "/api": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "ops"
        ],
        "summary": "API index",
        "description": "Machine-readable directory of the available API endpoints.",
        "responses": {
          "200": {
            "description": "The API directory.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-API-Version": {
                "$ref": "#/components/headers/XApiVersion"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "5XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/treatments": {
      "get": {
        "operationId": "listTreatments",
        "tags": [
          "catalogue"
        ],
        "summary": "List treatments",
        "description": "Returns the treatment catalogue as a JSON list. Filter with `category`. Fetch a single treatment at `/api/v1/treatments/{slug}`. Cite-safe descriptions only; prices are not included.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Filter the list to one category, e.g. `laser`, `injectable`, `facial`.",
            "schema": {
              "type": "string"
            },
            "example": "laser"
          }
        ],
        "responses": {
          "200": {
            "description": "The treatment list.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-API-Version": {
                "$ref": "#/components/headers/XApiVersion"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TreatmentList"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "5XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/treatments/{slug}": {
      "get": {
        "operationId": "getTreatment",
        "tags": [
          "catalogue"
        ],
        "summary": "Get one treatment",
        "description": "Returns a single treatment by its slug (e.g. `laser-hair-removal`). An unknown slug returns an RFC 9457 problem+json 404.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "The treatment slug from listTreatments.",
            "schema": {
              "type": "string"
            },
            "example": "microneedling"
          }
        ],
        "responses": {
          "200": {
            "description": "The treatment.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-API-Version": {
                "$ref": "#/components/headers/XApiVersion"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Treatment"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "5XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/clinic": {
      "get": {
        "operationId": "getClinic",
        "tags": [
          "catalogue"
        ],
        "summary": "Clinic identity",
        "description": "Returns the clinic's identity: name, address, opening hours, contact channels and booking link. The machine-readable twin of the homepage Organization JSON-LD.",
        "responses": {
          "200": {
            "description": "Clinic identity.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-API-Version": {
                "$ref": "#/components/headers/XApiVersion"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Clinic"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "5XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "ops"
        ],
        "summary": "Service health",
        "description": "Liveness check for the content API.",
        "responses": {
          "200": {
            "description": "The service is up.",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-API-Version": {
                "$ref": "#/components/headers/XApiVersion"
              },
              "Deprecation": {
                "$ref": "#/components/headers/Deprecation"
              },
              "Sunset": {
                "$ref": "#/components/headers/Sunset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "5XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "mcpRpc",
        "tags": [
          "ops"
        ],
        "summary": "MCP server (Streamable HTTP, JSON-RPC 2.0)",
        "description": "Model Context Protocol endpoint. Send JSON-RPC 2.0 requests (`initialize`, `tools/list`, `tools/call`). Responds with `application/json`, or `text/event-stream` when the client's Accept header allows it. See /developers and /.well-known/mcp.json.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC 2.0 response (JSON or SSE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted (the request was a JSON-RPC notification)."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          },
          "5XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApiIndex": {
        "type": "object",
        "required": [
          "object",
          "endpoints"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "api_index"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "openapi": {
            "type": "string",
            "format": "uri"
          },
          "versioningPolicy": {
            "type": "string",
            "format": "uri"
          },
          "mcp": {
            "type": "string",
            "format": "uri"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "method": {
                  "type": "string"
                },
                "path": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "errors": {
            "type": "string"
          },
          "rateLimit": {
            "type": "string"
          }
        }
      },
      "Treatment": {
        "type": "object",
        "description": "One aesthetic treatment offered by the clinic.",
        "required": [
          "slug",
          "name",
          "category",
          "summary",
          "deliveredBy",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "treatment"
          },
          "version": {
            "type": "string",
            "description": "Dated API contract version."
          },
          "slug": {
            "type": "string",
            "example": "microneedling"
          },
          "name": {
            "type": "string",
            "example": "Microneedling (collagen induction)"
          },
          "category": {
            "type": "string",
            "example": "skin-resurfacing"
          },
          "summary": {
            "type": "string",
            "description": "Cite-safe description (no prices)."
          },
          "deliveredBy": {
            "type": "string",
            "example": "Arman Zaki"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "TreatmentList": {
        "type": "object",
        "required": [
          "object",
          "count",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "version": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "booking": {
            "type": "string",
            "format": "uri"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Treatment"
            }
          }
        }
      },
      "Clinic": {
        "type": "object",
        "required": [
          "object",
          "name",
          "url",
          "address",
          "contact"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "clinic"
          },
          "name": {
            "type": "string"
          },
          "legalName": {
            "type": "string"
          },
          "companyNumber": {
            "type": "string"
          },
          "tagline": {
            "type": "string"
          },
          "slogan": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "developers": {
            "type": "string",
            "format": "uri"
          },
          "booking": {
            "type": "string",
            "format": "uri"
          },
          "address": {
            "type": "object",
            "properties": {
              "streetAddress": {
                "type": "string"
              },
              "addressLocality": {
                "type": "string"
              },
              "addressRegion": {
                "type": "string"
              },
              "postalCode": {
                "type": "string"
              },
              "addressCountry": {
                "type": "string"
              }
            }
          },
          "geo": {
            "type": "object",
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              }
            }
          },
          "contact": {
            "type": "object",
            "properties": {
              "landline": {
                "type": "string"
              },
              "whatsapp": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "contactPage": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "founder": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "role": {
                "type": "string"
              },
              "registration": {
                "type": "string"
              }
            }
          },
          "openingHours": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "days": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "opens": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "closes": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "version": {
            "type": "string"
          }
        }
      },
      "Health": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          },
          "service": {
            "type": "string"
          },
          "time": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem detail with a machine-readable code.",
        "required": [
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "URI identifying the problem type."
          },
          "title": {
            "type": "string",
            "example": "Not Found"
          },
          "status": {
            "type": "integer",
            "example": 404
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code.",
            "enum": [
              "treatment_not_found",
              "category_not_found",
              "endpoint_not_found",
              "method_not_allowed",
              "rate_limited",
              "bad_request"
            ]
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation and resolution hint."
          },
          "instance": {
            "type": "string",
            "description": "The request path that produced the error."
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "method": {
            "type": "string",
            "example": "initialize"
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Requests allowed per window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitPolicy": {
        "description": "Rate-limit policy, e.g. `1200;w=60`.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying (on 429).",
        "schema": {
          "type": "integer"
        }
      },
      "XApiVersion": {
        "description": "Dated API contract version.",
        "schema": {
          "type": "string"
        }
      },
      "Deprecation": {
        "description": "RFC 8594. Present only when the version is deprecated; carries the deprecation date.",
        "schema": {
          "type": "string"
        }
      },
      "Sunset": {
        "description": "RFC 8594. Date after which a deprecated version stops responding.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "The requested resource does not exist.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "BadRequest": {
        "description": "The request could not be parsed.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The endpoint is read-only; only GET/OPTIONS are supported.",
        "headers": {
          "Allow": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Error": {
        "description": "Unexpected error. All errors use RFC 9457 problem+json with a machine-readable code.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    }
  }
}
