{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/rendry-io/json-schema/rendry-io-preset-schema.json",
  "title": "Preset",
  "description": "A stored template as GET /presets/{id} returns it: the DSL POST /presets accepted plus the ids the server assigned. `id` is this VERSION; `preset_group_id` is the template, the value every render, patch, share and delete-group door takes as the preset id.",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/rendry-io-openapi.json#/components/schemas/Preset",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "This version's id; changes on every saved version"
    },
    "user_id": {
      "type": "string"
    },
    "preset_group_id": {
      "type": "string",
      "description": "The template's id, stable across versions — what /render/*, PATCH, /share and DELETE /presets/{id}/group take"
    },
    "parent_id": {
      "type": "string",
      "description": "The version this one was saved over or forked from"
    },
    "name": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "preview_key": {
      "type": "string",
      "description": "Storage key of the server-made preview picture; fetch it as /files/{preview_key}"
    },
    "version_note": {
      "type": "string",
      "description": "Label of an explicitly marked version; routine saves carry none"
    },
    "canvas": {
      "$ref": "#/$defs/Canvas"
    },
    "layers": {
      "type": "array",
      "items": {
        "type": "object"
      },
      "description": "Ordinary DSL layers; text layers may opt into document continuation with text_flow (see TextFlow). The authoritative native text_flows receipt is returned before charge in dry runs. Text may use text_overflow:\"clip_lines\" with positive integer max_lines and explicit width. It keeps complete native lines without an ellipsis after full-source auto_fit/fit_group; height is optional and never a second line cap. Plates size retained content; text_flow cannot truncate. Every layer, including nested children, accepts an optional literal printable single-line Unicode name label. Absent, null or empty is unnamed; duplicate labels are allowed. C0 controls (U+0000–U+001F), DEL (U+007F), and line/paragraph separators (U+2028/U+2029) are refused. Emoji, RTL and combining marks are preserved. Names never render, substitute variables, or define output IDs or metadata. Image layers may declare generate (ImageGenerate): style_id, prompt and optional aspect. Rendering reads prepared art; a nonempty dynamic_key URL wins."
    },
    "modifications": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Sample values of the {variables}: the defaults of every render that omits a key"
    },
    "variable_modes": {
      "type": "object",
      "additionalProperties": {
        "type": "string",
        "enum": [
          "text",
          "template",
          "formula"
        ]
      },
      "description": "How each modifications source is read; a missing name is text"
    },
    "sample_rows": {
      "type": "object",
      "additionalProperties": {
        "type": "array",
        "items": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      },
      "description": "Sample rows of repeat groups and tables; a call's rows override them"
    },
    "derived": {
      "type": "array",
      "items": {
        "type": "object"
      },
      "description": "Colours derived from an image layer, as in PresetInput"
    },
    "palettes": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": {
          "type": "string"
        }
      },
      "description": "Colours a field takes from data, as in PresetInput"
    },
    "variable_formats": {
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": {
        "$ref": "#/$defs/DisplayFormat"
      },
      "description": "Sparse name -> display format beside modifications and variable_modes. A name needs a source in modifications and cannot be page, pages or row.*. Applied after source evaluation and also to caller overrides; formulas consume raw exact values while templates insert formatted text. Invalid schema refuses save; invalid data refuses dry run and render before any charge, without echoing values. update_preset patches: absent or {} keeps; name:null removes; top-level null clears. Whole preset and document saves replace the map."
    }
  },
  "$defs": {
    "Canvas": {
      "type": "object",
      "properties": {
        "width": {
          "type": "integer"
        },
        "height": {
          "type": "integer"
        },
        "background_color": {
          "type": "string"
        },
        "output_format": {
          "type": "string"
        },
        "quality": {
          "type": "integer"
        },
        "max_width": {
          "type": "integer"
        },
        "finish_profile": {
          "type": "string",
          "description": "Optional whole-canvas finish applied once after composition using the profile-specific paint policy. Use an exact id from the finish_profiles field returned by GET /capabilities; that runtime catalog also declares compatible formats and minimum dimensions."
        }
      },
      "additionalProperties": true
    },
    "DisplayFormat": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "number",
            "currency",
            "percent",
            "date",
            "upper",
            "lower"
          ]
        },
        "decimals": {
          "type": "integer",
          "minimum": 0,
          "maximum": 34,
          "default": 0
        },
        "grouping": {
          "type": "boolean",
          "default": false
        },
        "decimal_mark": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1,
          "default": "."
        },
        "group_mark": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1,
          "default": ","
        },
        "symbol": {
          "type": "string",
          "maxLength": 32
        },
        "symbol_position": {
          "type": "string",
          "enum": [
            "prefix",
            "suffix"
          ],
          "default": "prefix"
        },
        "prefix": {
          "type": "string",
          "maxLength": 32
        },
        "suffix": {
          "type": "string",
          "maxLength": 32
        },
        "pattern": {
          "type": "string",
          "minLength": 1,
          "maxLength": 128,
          "default": "YYYY-MM-DD"
        }
      },
      "description": "One deterministic display vocabulary. Numbers use exact arithmetic and half away from zero rounding. Currency needs symbol (a symbol or ISO code), with explicit spacing in that text. Percent multiplies a raw ratio by 100. Dates accept strict YYYY-MM-DD only and patterns D, DD, M, MM, MMM, MMMM, YY, YYYY with English month names. upper/lower use Unicode simple case mapping; no locale or multi-character expansions. No format leaves existing text untouched. Fields unrelated to the selected type are refused."
    }
  }
}