{
  "openapi": "3.1.0",
  "info": {
    "title": "QR Platby API",
    "version": "1.0.0",
    "description": "Generate PAY by square, SPAYD, and EPC (EU SEPA) QR codes for bank payments. Free, no authentication required.",
    "contact": {
      "name": "QR Platby",
      "url": "https://qr-platby.com"
    },
    "license": {
      "name": "Free to use",
      "url": "https://qr-platby.com/podmienky"
    }
  },
  "servers": [
    {
      "url": "https://qr-platby.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/v1/qr": {
      "get": {
        "operationId": "getApiDocs",
        "summary": "API documentation",
        "description": "Returns machine-readable JSON documentation for the QR generation API.",
        "responses": {
          "200": {
            "description": "API documentation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "generateQr",
        "summary": "Generate a PAY by square, SPAYD, or EPC QR code",
        "description": "Generates a QR code in BySquare, SPAYD, or EPC format for bank payments. Returns base64 PNG data URI or SVG markup.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QrRequest"
              },
              "examples": {
                "basicPayment": {
                  "summary": "Basic payment with amount and variable symbol",
                  "value": {
                    "iban": "SK3112000000198742637541",
                    "amount": 25.5,
                    "currency": "EUR",
                    "variableSymbol": "2024001"
                  }
                },
                "ibanOnly": {
                  "summary": "Minimal request with IBAN only",
                  "value": {
                    "iban": "SK3112000000198742637541"
                  }
                },
                "czechSpayd": {
                  "summary": "Czech SPAYD QR code",
                  "value": {
                    "iban": "CZ6508000000192000145399",
                    "amount": 480.5,
                    "currency": "CZK",
                    "paymentFormat": "spayd",
                    "variableSymbol": "1234567890"
                  }
                },
                "epcPayment": {
                  "summary": "EU SEPA EPC QR code",
                  "value": {
                    "iban": "DE89370400440532013000",
                    "amount": 100,
                    "paymentFormat": "epc",
                    "recipientName": "Max Mustermann"
                  }
                },
                "czechSvg": {
                  "summary": "Czech CZK payment as SVG",
                  "value": {
                    "iban": "CZ6508000000192000145399",
                    "amount": 1500,
                    "currency": "CZK",
                    "recipientName": "Jan Novák",
                    "format": "svg"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "QR code generated successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QrSuccessResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "schema": { "type": "integer" },
                "description": "Maximum requests per window"
              },
              "X-RateLimit-Remaining": {
                "schema": { "type": "integer" },
                "description": "Remaining requests in current window"
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QrErrorResponse"
                },
                "example": {
                  "success": false,
                  "error": {
                    "code": "VALIDATION_ERROR",
                    "message": "Validation failed. Check the issues array for details.",
                    "issues": [
                      {
                        "path": "iban",
                        "message": "Invalid IBAN"
                      }
                    ],
                    "hint": "Required: iban (string). Optional: amount (number), currency (EUR|CZK), variableSymbol, specificSymbol, constantSymbol, recipientName, paymentNote, paymentFormat (bysquare|spayd|epc), format (png|svg), size (100-1000).",
                    "example": {
                      "iban": "SK3112000000198742637541",
                      "amount": 25.5,
                      "variableSymbol": "2024001"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QrErrorResponse"
                },
                "example": {
                  "success": false,
                  "error": {
                    "code": "RATE_LIMIT",
                    "message": "Rate limit exceeded. Limit is 20 requests/minute and 100 requests/day. Retry after 45 seconds.",
                    "hint": "The GET endpoint (API docs) and OPTIONS (CORS preflight) are not rate-limited."
                  }
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "schema": { "type": "integer" },
                "description": "Maximum requests per window"
              },
              "X-RateLimit-Remaining": {
                "schema": { "type": "integer", "example": 0 },
                "description": "Always 0 when rate limited"
              },
              "Retry-After": {
                "schema": { "type": "integer" },
                "description": "Seconds until rate limit resets"
              }
            }
          }
        }
      },
      "options": {
        "operationId": "corsPreflightQr",
        "summary": "CORS preflight",
        "responses": {
          "204": {
            "description": "CORS headers returned"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "QrRequest": {
        "type": "object",
        "required": ["iban"],
        "properties": {
          "iban": {
            "type": "string",
            "description": "Recipient IBAN (e.g. SK3112000000198742637541)",
            "example": "SK3112000000198742637541"
          },
          "amount": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 999999999.99,
            "description": "Payment amount"
          },
          "currency": {
            "type": "string",
            "enum": ["EUR", "CZK"],
            "default": "EUR"
          },
          "variableSymbol": {
            "type": "string",
            "maxLength": 10,
            "pattern": "^\\d*$",
            "description": "Variable symbol (up to 10 digits)"
          },
          "specificSymbol": {
            "type": "string",
            "maxLength": 10,
            "pattern": "^\\d*$",
            "description": "Specific symbol (up to 10 digits)"
          },
          "constantSymbol": {
            "type": "string",
            "maxLength": 4,
            "pattern": "^\\d*$",
            "description": "Constant symbol (up to 4 digits)"
          },
          "recipientName": {
            "type": "string",
            "maxLength": 70,
            "description": "Recipient name"
          },
          "paymentNote": {
            "type": "string",
            "maxLength": 140,
            "description": "Payment note / message for recipient"
          },
          "paymentFormat": {
            "type": "string",
            "enum": ["bysquare", "spayd", "epc"],
            "default": "bysquare",
            "description": "Payment QR standard. Use 'bysquare' for Slovak banks (PAY by square), 'spayd' for Czech banks (QR Platba/SPD), or 'epc' for EU SEPA payments (EPC QR). Note: EPC does not support variableSymbol, specificSymbol, or constantSymbol."
          },
          "format": {
            "type": "string",
            "enum": ["png", "svg"],
            "default": "png",
            "description": "Output format — png returns base64 data URI, svg returns SVG markup"
          },
          "size": {
            "type": "integer",
            "minimum": 100,
            "maximum": 1000,
            "default": 300,
            "description": "QR code size in pixels. Applies to png width/height and svg intrinsic width/height."
          },
          "darkColor": {
            "type": "string",
            "pattern": "^#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$",
            "description": "Foreground color as hex (#RRGGBB or #RRGGBBAA). Defaults to #000000.",
            "example": "#0070f3"
          },
          "lightColor": {
            "type": "string",
            "pattern": "^#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$",
            "description": "Background color as hex (#RRGGBB or #RRGGBBAA). Defaults to #ffffff. Use #FFFFFF00 for a transparent background.",
            "example": "#ffffff"
          },
          "margin": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "default": 2,
            "description": "Quiet zone width in QR modules. Reducing below 2 may hurt scanner reliability."
          },
          "errorCorrectionLevel": {
            "type": "string",
            "enum": ["L", "M", "Q", "H"],
            "description": "Override the auto-derived error correction level (L=7%, M=15%, Q=25%, H=30% recovery). Payload may not fit at lower levels."
          }
        }
      },
      "QrSuccessResponse": {
        "type": "object",
        "required": ["success", "data", "format", "iban", "currency"],
        "properties": {
          "success": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "type": "string",
            "description": "Base64 data URI (png) or SVG markup string (svg)"
          },
          "format": {
            "type": "string",
            "enum": ["png", "svg"]
          },
          "iban": {
            "type": "string",
            "description": "Normalized IBAN"
          },
          "amount": {
            "type": "number",
            "description": "Echoed back if provided in request"
          },
          "currency": {
            "type": "string",
            "enum": ["EUR", "CZK"]
          }
        }
      },
      "QrErrorResponse": {
        "type": "object",
        "required": ["success", "error"],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": ["VALIDATION_ERROR", "RATE_LIMIT", "INTERNAL_ERROR"]
              },
              "message": {
                "type": "string"
              },
              "issues": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": ["path", "message"],
                  "properties": {
                    "path": { "type": "string" },
                    "message": { "type": "string" }
                  }
                },
                "description": "Validation issue details (only for VALIDATION_ERROR)"
              },
              "field": {
                "type": "string",
                "description": "Name of the field that caused the error (when applicable)"
              },
              "hint": {
                "type": "string",
                "description": "Actionable suggestion to fix the error"
              },
              "docs": {
                "type": "string",
                "description": "Link to API documentation",
                "example": "https://qr-platby.com/en/docs"
              },
              "example": {
                "type": "object",
                "description": "Example of a correct request body"
              }
            }
          }
        }
      }
    }
  }
}
