{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://watermark.noxxxx.com/api/v1/recipe.schema.json",
  "title": "Local Watermark Recipe v1",
  "description": "Portable settings only; no image bytes. Also call validateRecipe: unique layer IDs and crop sums require semantic validation. Logo assets and custom fonts must be supplied separately.",
  "type": "object",
  "required": [
    "version",
    "layers",
    "image",
    "date",
    "sequence",
    "frame",
    "output"
  ],
  "properties": {
    "version": {
      "const": 1
    },
    "layers": {
      "type": "array",
      "maxItems": 30,
      "items": {
        "type": "object",
        "required": [
          "id",
          "type",
          "text",
          "assetId",
          "fontFamily",
          "weight",
          "size",
          "color",
          "opacity",
          "angle",
          "position",
          "x",
          "y",
          "margin",
          "tiled",
          "gap",
          "stroke",
          "shadow",
          "autoContrast"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.-]{1,100}$"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "logo"
            ]
          },
          "text": {
            "type": "string",
            "maxLength": 2000
          },
          "assetId": {
            "type": "string",
            "maxLength": 100
          },
          "fontFamily": {
            "type": "string",
            "maxLength": 100,
            "pattern": "^[^;{}]*$"
          },
          "weight": {
            "type": "string",
            "enum": [
              "400",
              "500",
              "600",
              "700"
            ]
          },
          "size": {
            "type": "number",
            "minimum": 0.2,
            "maximum": 100
          },
          "color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$"
          },
          "opacity": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "angle": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "position": {
            "type": "string",
            "enum": [
              "top-left",
              "top-center",
              "top-right",
              "center-left",
              "center",
              "center-right",
              "bottom-left",
              "bottom-center",
              "bottom-right",
              "footer",
              "custom"
            ]
          },
          "x": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "y": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "margin": {
            "type": "number",
            "minimum": 0,
            "maximum": 45
          },
          "tiled": {
            "type": "boolean"
          },
          "gap": {
            "type": "number",
            "minimum": 1,
            "maximum": 100
          },
          "stroke": {
            "type": "boolean"
          },
          "shadow": {
            "type": "boolean"
          },
          "autoContrast": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      }
    },
    "image": {
      "type": "object",
      "required": [
        "crop",
        "rotate",
        "flipX",
        "flipY",
        "border",
        "borderColor",
        "footer"
      ],
      "properties": {
        "crop": {
          "type": "object",
          "required": [
            "x",
            "y",
            "width",
            "height"
          ],
          "properties": {
            "x": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "y": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "width": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "exclusiveMinimum": 0
            },
            "height": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "exclusiveMinimum": 0
            },
            "aspectRatio": {
              "type": "number",
              "minimum": 0.01,
              "maximum": 100,
              "description": "Optional width/height ratio after rotation, centered within the normalized crop per image. Borders and footers are added afterwards."
            }
          },
          "additionalProperties": false
        },
        "rotate": {
          "type": "integer",
          "enum": [
            0,
            90,
            180,
            270
          ]
        },
        "flipX": {
          "type": "boolean"
        },
        "flipY": {
          "type": "boolean"
        },
        "border": {
          "type": "number",
          "minimum": 0,
          "maximum": 50
        },
        "borderColor": {
          "type": "string",
          "pattern": "^#[0-9a-fA-F]{6}$"
        },
        "footer": {
          "type": "number",
          "minimum": 0,
          "maximum": 50
        }
      },
      "additionalProperties": false
    },
    "date": {
      "type": "object",
      "required": [
        "source",
        "custom",
        "format",
        "missing",
        "offsetMinutes"
      ],
      "properties": {
        "source": {
          "type": "string",
          "enum": [
            "exif",
            "custom"
          ]
        },
        "custom": {
          "type": "string",
          "maxLength": 40
        },
        "format": {
          "type": "string",
          "enum": [
            "YYYY-MM-DD",
            "DD/MM/YYYY",
            "MM/DD/YYYY",
            "YYYY-MM-DD HH:mm",
            "YY.MM.DD"
          ]
        },
        "missing": {
          "type": "string",
          "enum": [
            "skip",
            "custom",
            "error"
          ]
        },
        "offsetMinutes": {
          "type": "number",
          "minimum": -1440,
          "maximum": 1440
        }
      },
      "additionalProperties": false
    },
    "sequence": {
      "type": "object",
      "required": [
        "start",
        "padding"
      ],
      "properties": {
        "start": {
          "type": "integer",
          "minimum": 0
        },
        "padding": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10
        }
      },
      "additionalProperties": false
    },
    "frame": {
      "type": "object",
      "required": [
        "enabled"
      ],
      "properties": {
        "enabled": {
          "type": "boolean"
        }
      },
      "additionalProperties": false
    },
    "output": {
      "type": "object",
      "required": [
        "format",
        "quality",
        "longEdge",
        "prefix",
        "suffix",
        "metadata",
        "variants"
      ],
      "properties": {
        "format": {
          "type": "string",
          "enum": [
            "image/jpeg",
            "image/png",
            "image/webp"
          ]
        },
        "quality": {
          "type": "number",
          "minimum": 0.1,
          "maximum": 1
        },
        "longEdge": {
          "type": "number",
          "minimum": 0,
          "maximum": 32768
        },
        "prefix": {
          "type": "string",
          "maxLength": 100
        },
        "suffix": {
          "type": "string",
          "maxLength": 100
        },
        "metadata": {
          "type": "string",
          "enum": [
            "strip",
            "safe",
            "all"
          ]
        },
        "variants": {
          "type": "array",
          "maxItems": 6,
          "items": {
            "type": "integer",
            "minimum": 32,
            "maximum": 32768
          }
        }
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}
