{
  "openapi": "3.1.0",
  "info": {
    "title": "QR Studio API",
    "version": "1.0.0",
    "description": "QR code generation, dynamic redirect management, and digital menu APIs for QR Studio (freeqrstudio.online).",
    "contact": {
      "url": "https://freeqrstudio.online"
    }
  },
  "servers": [
    { "url": "https://freeqrstudio.online", "description": "Production" }
  ],
  "paths": {
    "/api/qr/generate": {
      "post": {
        "operationId": "generateQrCode",
        "summary": "Generate a QR code SVG",
        "description": "Generates a QR code as an SVG string for any text, URL, or data content. No authentication required.",
        "tags": ["QR Codes"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/QrGenerateRequest" },
              "example": {
                "content": "https://example.com",
                "size": 256,
                "error_correction": "M"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "QR code generated successfully",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/QrGenerateResponse" }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ValidationError" }
              }
            }
          }
        }
      }
    },
    "/api/qr/save": {
      "post": {
        "operationId": "saveQrCode",
        "summary": "Save a QR code to your account",
        "description": "Persists a QR code linked to the authenticated user. Requires a Pro or Business subscription for most QR types.",
        "tags": ["QR Codes"],
        "security": [{ "sessionAuth": [] }],
        "x-payment-info": {
          "intent": "session",
          "method": "stripe",
          "amount": 19.00,
          "currency": "USD",
          "description": "Pro plan subscription required to save and manage QR codes ($19/month)."
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/QrSaveRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "QR code saved",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/QrSaveResponse" }
              }
            }
          },
          "401": { "description": "Unauthenticated" },
          "422": {
            "description": "Validation error or QR code limit reached",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ValidationError" }
              }
            }
          }
        }
      }
    },
    "/r/{shortCode}": {
      "get": {
        "operationId": "redirectDynamicQr",
        "summary": "Resolve a dynamic QR code redirect",
        "description": "Follows the redirect stored for a dynamic QR code short code. Tracks the scan if analytics are enabled.",
        "tags": ["QR Codes"],
        "parameters": [
          {
            "name": "shortCode",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "302": { "description": "Redirect to the target URL" },
          "404": { "description": "Short code not found" }
        }
      }
    },
    "/menu/api/{slug}": {
      "get": {
        "operationId": "getMenuPreview",
        "summary": "Get restaurant menu (preview)",
        "description": "Returns the full menu for a restaurant without table context. Useful for previews and agents browsing menus.",
        "tags": ["Digital Menu"],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "description": "Restaurant slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Menu data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MenuResponse" }
              }
            }
          },
          "404": { "description": "Restaurant not found" }
        }
      }
    },
    "/menu/api/{slug}/{tableSlug}": {
      "get": {
        "operationId": "getMenu",
        "summary": "Get restaurant menu for a table",
        "description": "Returns menu data scoped to a specific table, including availability and ordering context.",
        "tags": ["Digital Menu"],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "tableSlug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Menu data",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MenuResponse" }
              }
            }
          },
          "404": { "description": "Restaurant or table not found" }
        }
      }
    },
    "/menu/{slug}/{tableSlug}/order": {
      "post": {
        "operationId": "placeOrder",
        "summary": "Place a food order",
        "description": "Places an order for the given items at a restaurant table.",
        "tags": ["Digital Menu"],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "tableSlug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/OrderRequest" }
            }
          }
        },
        "responses": {
          "200": { "description": "Order placed successfully" },
          "422": { "description": "Validation error" }
        }
      }
    },
    "/menu/{slug}/{tableSlug}/waiter-call": {
      "post": {
        "operationId": "callWaiter",
        "summary": "Call a waiter to the table",
        "description": "Sends a waiter call notification for the specified table.",
        "tags": ["Digital Menu"],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "tableSlug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": { "description": "Waiter call sent" }
        }
      }
    },
    "/menu/{slug}/{tableSlug}/feedback": {
      "post": {
        "operationId": "submitFeedback",
        "summary": "Submit dining feedback",
        "description": "Submits a feedback entry for a dining session at a table.",
        "tags": ["Digital Menu"],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "tableSlug",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/FeedbackRequest" }
            }
          }
        },
        "responses": {
          "200": { "description": "Feedback submitted" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sessionAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "laravel_session",
        "description": "Laravel session cookie. Obtain by logging in at /login."
      }
    },
    "schemas": {
      "QrGenerateRequest": {
        "type": "object",
        "required": ["content"],
        "properties": {
          "content": {
            "type": "string",
            "maxLength": 2048,
            "description": "Text or URL to encode into the QR code."
          },
          "size": {
            "type": "integer",
            "enum": [128, 256, 512, 1024],
            "default": 256,
            "description": "Output size in pixels."
          },
          "error_correction": {
            "type": "string",
            "enum": ["L", "M", "Q", "H"],
            "default": "M",
            "description": "Reed-Solomon error correction level."
          }
        }
      },
      "QrGenerateResponse": {
        "type": "object",
        "properties": {
          "svg": { "type": "string", "description": "SVG markup of the generated QR code." }
        }
      },
      "QrSaveRequest": {
        "type": "object",
        "required": ["type_slug", "content_data"],
        "properties": {
          "type_slug": {
            "type": "string",
            "description": "QR code type. One of: url, text, email, phone, sms, wifi, vcard, location, event, social, dynamic, pdf, image, menu, gallery, landing_page."
          },
          "content_data": {
            "type": "object",
            "description": "Type-specific content payload."
          },
          "options": {
            "type": "object",
            "description": "Optional styling/customisation options."
          }
        }
      },
      "QrSaveResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "short_code": { "type": "string", "nullable": true },
          "landing_slug": { "type": "string", "nullable": true }
        }
      },
      "MenuResponse": {
        "type": "object",
        "properties": {
          "restaurant": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "logo_url": { "type": "string", "nullable": true },
              "slug": { "type": "string" }
            }
          },
          "menus": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "integer" },
                "name": { "type": "string" },
                "categories": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": { "type": "integer" },
                      "name": { "type": "string" },
                      "items": {
                        "type": "array",
                        "items": { "$ref": "#/components/schemas/MenuItem" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "MenuItem": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "description": { "type": "string", "nullable": true },
          "price": { "type": "number", "format": "float" },
          "available": { "type": "boolean" },
          "image_url": { "type": "string", "nullable": true }
        }
      },
      "OrderRequest": {
        "type": "object",
        "required": ["items"],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["item_id", "quantity"],
              "properties": {
                "item_id": { "type": "integer" },
                "quantity": { "type": "integer", "minimum": 1 }
              }
            }
          },
          "notes": { "type": "string", "description": "Special instructions for the order." }
        }
      },
      "FeedbackRequest": {
        "type": "object",
        "properties": {
          "rating": { "type": "integer", "minimum": 1, "maximum": 5 },
          "comment": { "type": "string" }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "message": { "type": "string" },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": { "type": "string" }
            }
          }
        }
      }
    }
  }
}
