{
  "openapi": "3.1.0",
  "info": {
    "title": "Marka API",
    "version": "1.0.0",
    "description": "Public and Scoped API for Marka (https://mymarka.app) — Salon, Spa & Appointment Management Platform tailored for independent beauty and wellness professionals in Lagos and Algarve, Portugal.",
    "contact": {
      "name": "Marka Team",
      "email": "info@mymarka.app",
      "url": "https://mymarka.app"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://mymarka.app/terms"
    },
    "x-api-version": "1.0.0",
    "x-deprecation-policy": "Major API versions are maintained with at least 12 months advance notice via Sunset (RFC 8594) and Deprecation headers.",
    "x-free-tier": {
      "available": true,
      "trial_days": 30,
      "requires_credit_card": false,
      "signup_url": "https://app.mymarka.app/signup"
    },
    "x-sandbox": {
      "available": true,
      "url": "https://demo.mymarka.app"
    }
  },

  "servers": [
    {
      "url": "https://mymarka.app/api/v1",
      "description": "Production API v1 Server"
    },
    {
      "url": "https://mymarka.app/v1",
      "description": "Production v1 Server"
    },
    {
      "url": "https://mymarka.app",
      "description": "Base Production Server"
    }
  ],
  "components": {
    "parameters": {
      "ApiVersionHeader": {
        "name": "X-API-Version",
        "in": "header",
        "description": "API version identifier (e.g. v1 or 1.0.0)",
        "required": false,
        "schema": {
          "type": "string",
          "default": "v1",
          "enum": ["v1", "1.0.0"]
        }
      }
    },
    "securitySchemes": {

      "OAuth2": {
        "type": "oauth2",
        "description": "OAuth 2.0 authorization with scoped machine permissions (RFC 6749 & RFC 9728).",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://mymarka.app/api/auth/authorize",
            "tokenUrl": "https://mymarka.app/api/auth/token",
            "scopes": {
              "read:salons": "Read salon profile, working hours, and public configuration",
              "read:services": "Read catalog of salon services, categories, and prices",
              "read:staff": "Read staff members and their working schedules",
              "read:availability": "Check available appointment slots for booking",
              "write:bookings": "Create, modify, or cancel salon appointments and bookings",
              "read:bookings": "Read existing bookings and appointment status",
              "read:clients": "Search and view client CRM records (salon admin only)",
              "write:clients": "Create or update client CRM records"
            }
          },
          "clientCredentials": {
            "tokenUrl": "https://mymarka.app/api/auth/token",
            "scopes": {
              "read:salons": "Read salon profile, working hours, and public configuration",
              "read:services": "Read catalog of salon services, categories, and prices",
              "read:staff": "Read staff members and their working schedules",
              "read:availability": "Check available appointment slots for booking",
              "write:bookings": "Create, modify, or cancel salon appointments and bookings",
              "read:bookings": "Read existing bookings and appointment status",
              "read:clients": "Search and view client CRM records (salon admin only)",
              "write:clients": "Create or update client CRM records"
            }
          }
        }
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JSON Web Token (JWT) issued by Supabase Auth with scoped role permissions."
      }
    },
    "schemas": {
      "ProblemDetails": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "resolution", "status"],
            "properties": {
              "code": {
                "type": "string",
                "example": "VALIDATION_FAILED",
                "description": "Machine-readable error identifier."
              },
              "message": {
                "type": "string",
                "example": "The requested appointment date is already booked.",
                "description": "Human-readable explanation of the error."
              },
              "resolution": {
                "type": "string",
                "example": "Query /api/booking to find an alternate available slot.",
                "description": "Actionable guidance enabling agents to self-correct and recover."
              },
              "status": {
                "type": "integer",
                "example": 400,
                "description": "HTTP status code."
              },
              "documentation_url": {
                "type": "string",
                "format": "uri",
                "example": "https://mymarka.app/openapi.json",
                "description": "URL to machine-readable API documentation."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Optional contextual debug details or field-level validation errors."
              }
            }
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "required": ["name", "version", "description", "openapi", "endpoints"],
        "properties": {
          "name": { "type": "string", "example": "Marka API" },
          "version": { "type": "string", "example": "1.0.0" },
          "description": { "type": "string" },
          "openapi": { "type": "string", "format": "uri" },
          "protected_resource_metadata": { "type": "string", "format": "uri" },
          "endpoints": {
            "type": "object",
            "additionalProperties": { "type": "string", "format": "uri" }
          }
        }
      },
      "SalonProfile": {
        "type": "object",
        "required": ["id", "name", "subdomain"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "example": "Camelia Nails" },
          "subdomain": { "type": "string", "example": "camelia-nails" },
          "address": { "type": "string", "nullable": true },
          "phone": { "type": "string", "nullable": true },
          "currency": { "type": "string", "example": "EUR" },
          "is_active": { "type": "boolean" }
        }
      },
      "Service": {
        "type": "object",
        "required": ["id", "name", "price", "duration"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "example": "Manicure Completa" },
          "price": { "type": "number", "example": 25.0 },
          "duration": { "type": "integer", "description": "Duration in minutes", "example": 45 },
          "category": { "type": "string", "nullable": true }
        }
      },
      "StaffMember": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "example": "Maya" },
          "role": { "type": "string", "nullable": true },
          "avatar_url": { "type": "string", "nullable": true }
        }
      },
      "BookingRequest": {
        "type": "object",
        "required": ["business_id", "service_id", "client_name", "client_phone", "start_time"],
        "properties": {
          "business_id": { "type": "string", "format": "uuid" },
          "service_id": { "type": "string", "format": "uuid" },
          "staff_id": { "type": "string", "format": "uuid", "nullable": true },
          "client_name": { "type": "string", "example": "Maria Silva" },
          "client_phone": { "type": "string", "example": "+351912345678" },
          "client_email": { "type": "string", "format": "email", "nullable": true },
          "start_time": { "type": "string", "format": "date-time", "example": "2026-10-15T10:00:00Z" },
          "notes": { "type": "string", "nullable": true }
        }
      },
      "BookingResponse": {
        "type": "object",
        "required": ["success", "appointment_id"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "appointment_id": { "type": "string", "format": "uuid" },
          "cancellation_token": { "type": "string" },
          "message": { "type": "string", "example": "Appointment booked successfully." }
        }
      },
      "CancellationPreview": {
        "type": "object",
        "required": ["appointment_id", "service_name", "start_time", "can_cancel"],
        "properties": {
          "appointment_id": { "type": "string", "format": "uuid" },
          "business_name": { "type": "string", "example": "Royal Barber" },
          "service_name": { "type": "string", "example": "Haircut & Beard" },
          "start_time": { "type": "string", "format": "date-time" },
          "can_cancel": { "type": "boolean", "example": true }
        }
      },
      "CancellationResult": {
        "type": "object",
        "required": ["success", "message"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "message": { "type": "string", "example": "Appointment cancelled successfully." }
        }
      },
      "ClientRecord": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "example": "João Santos" },
          "phone": { "type": "string", "example": "+351912345678" },
          "email": { "type": "string", "format": "email", "nullable": true },
          "notes": { "type": "string", "nullable": true },
          "last_visit": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "ClientSearchResponse": {
        "type": "object",
        "required": ["clients"],
        "properties": {
          "clients": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ClientRecord" }
          }
        }
      },
      "SubdomainValidationResponse": {
        "type": "object",
        "required": ["available", "subdomain"],
        "properties": {
          "available": { "type": "boolean", "example": true },
          "subdomain": { "type": "string", "example": "novo-salao" },
          "reason": { "type": "string", "nullable": true }
        }
      }
    }
  },

  "paths": {
    "/api": {
      "get": {
        "summary": "API Service Index & Discovery",
        "description": "Returns machine-readable service directory, metadata, and link to OpenAPI specification.",
        "operationId": "getApiIndex",
        "responses": {
          "200": {
            "description": "API metadata and endpoints directory",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiIndex" }
              }
            }
          }
        }
      }
    },
    "/api/booking": {
      "get": {
        "summary": "Retrieve Salon Booking Data",
        "description": "Fetches public salon profile, service catalog, staff directory, and blocked dates by businessId or subdomain.",
        "operationId": "getBookingData",
        "security": [
          {},
          { "OAuth2": ["read:salons", "read:services", "read:staff"] }
        ],
        "parameters": [
          {
            "name": "businessId",
            "in": "query",
            "required": false,
            "schema": { "type": "string", "format": "uuid" },
            "description": "Unique business UUID"
          },
          {
            "name": "subdomain",
            "in": "query",
            "required": false,
            "schema": { "type": "string" },
            "description": "Salon subdomain slug (e.g. camelia-nails)"
          }
        ],
        "responses": {
          "200": {
            "description": "Salon booking information retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["business", "services", "staff", "blockedDates"],
                  "properties": {
                    "business": { "$ref": "#/components/schemas/SalonProfile" },
                    "services": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Service" }
                    },
                    "staff": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/StaffMember" }
                    },
                    "blockedDates": {
                      "type": "array",
                      "items": { "type": "object" }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Salon not found",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create Salon Appointment",
        "description": "Creates a client booking reservation and triggers email/WhatsApp confirmations.",
        "operationId": "createBooking",
        "security": [
          {},
          { "OAuth2": ["write:bookings"] }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BookingRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Appointment successfully booked",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BookingResponse" }
              }
            }
          },
          "400": {
            "description": "Validation error or time slot unavailable",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" }
              }
            }
          }
        }
      }
    },
    "/api/cancel/{token}": {
      "get": {
        "summary": "Inspect Cancellation Token",
        "description": "Verifies and retrieves appointment details for an authorized cancellation token.",
        "operationId": "inspectCancellation",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Cryptographic cancellation token"
          }
        ],
        "responses": {
          "200": {
            "description": "Appointment cancellation preview",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CancellationPreview" }
              }
            }
          },
          "404": {
            "description": "Invalid or expired token",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Confirm Appointment Cancellation",
        "description": "Cancels the appointment associated with the secure token.",
        "operationId": "confirmCancellation",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Appointment successfully cancelled",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CancellationResult" }
              }
            }
          },
          "400": {
            "description": "Cancellation failed or expired",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" }
              }
            }
          }
        }
      }
    },
    "/api/clients/search": {
      "get": {
        "summary": "Search Clients CRM",
        "description": "Scoped endpoint allowing authenticated salon managers to search client records by name or phone.",
        "operationId": "searchClients",
        "security": [
          { "OAuth2": ["read:clients"] },
          { "BearerAuth": [] }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "schema": { "type": "string" },
            "description": "Search term"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching client records",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ClientSearchResponse" }
              }
            }
          },
          "401": {
            "description": "Unauthorized — valid bearer token required",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" }
              }
            }
          }
        }
      }
    },
    "/api/subdomain/validate": {
      "get": {
        "summary": "Validate Subdomain Availability",
        "description": "Checks if a desired salon subdomain is valid and available for registration.",
        "operationId": "validateSubdomain",
        "parameters": [
          {
            "name": "subdomain",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Availability status",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/SubdomainValidationResponse" }
              }
            }
          }
        }
      }
    }
  }
}

