{
  "openapi": "3.1.1",
  "info": {
    "title": "TrueType & Variable Font Builder API",
    "version": "1.0.0",
    "summary": "Turn glyph outlines or a grid font into a verified TTF, including variable weight fonts.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://luminafontbuilder.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Turn glyph outlines or a grid font into a verified TTF, including variable weight fonts.",
        "description": "Paste a Lumina font project (glyph outlines with Light and Bold masters, kerning, ligatures) or a Grid Font Builder project and get an installable TrueType font. Variable fonts get fvar, gvar, avar, STAT and HVAR tables. The font is read back and every outline, character mapping, checksum and master is verified.\n\nMethodology:\n- Glyph outlines use the UFO point model: line, cubic curve (two off-curve points), quadratic qcurve and off-curve points, closed contours, y up from the baseline. Cubic segments are split into the fewest quadratic pieces (up to 16) that stay within the curve tolerance in every master, measured at 31 points per piece; coordinates are then rounded to whole font units.\n- The writer produces sfnt 1.0 TrueType with head, hhea, maxp 1.0, OS/2 version 4, name, cmap formats 4 and 12, post 3.0, hmtx, glyf and loca, GDEF glyph classes, GPOS pair kerning (lookup type 2, format 1) and GSUB ligatures (lookup type 4). Table checksums and head.checkSumAdjustment follow the OpenType specification. A .notdef box is added when the project has none.\n- With two or more masters the font becomes variable: fvar axes and named instances, gvar deltas for every point plus the four phantom points (advance changes), HVAR advance deltas, GDEF item variations for kerning, STAT axis values and avar when an axis map is given. Every non-default master must sit at the minimum or maximum of one axis and must be point-compatible with the default master.\n- Grid Font Builder projects are traced: each brick contributes its edges, shared edges cancel, and the remaining edges are linked into merged contours (outer clockwise, counters counter-clockwise), with an 8 × 8 grid filling the em square above the baseline.\n- Verification parses the written file with Lumina's own reader: table and whole-font checksums, glyph count, every character mapping, every outline point, advances and side bearings, kerning pairs, ligatures, and for variable fonts every master and named instance through gvar, HVAR and kerning variations. Automated tests additionally load the fonts with FreeType (fontconfig) and Chrome.\n\nFonts you build belong to you; add your own license text. The builder writes no hinting instructions, so small sizes on Windows can look softer than hinted fonts. WOFF2, CFF outlines, composite glyphs, mark positioning and contextual features are not written.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_font_builder",
        "x-owner-app": "lumina",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaFontBuilderInput"
              },
              "examples": {
                "example1": {
                  "summary": "Variable starter font with Light and Bold masters",
                  "value": {
                    "source": "{\"format\":\"lumina-font\",\"version\":1,\"familyName\":\"Lumina Starter\",\"designer\":\"\",\"copyright\":\"\",\"license\":\"\",\"licenseUrl\":\"\",\"fontVersion\":1,\"unitsPerEm\":1000,\"ascender\":800,\"descender\":-200,\"lineGap\":0,\"xHeight\":500,\"capHeight\":700,\"axes\":[{\"tag\":\"wght\",\"name\":\"Weight\",\"min\":300,\"default\":300,\"max\":700}],\"masters\":[{\"name\":\"Light\",\"location\":{\"wght\":300},\"weightClass\":300},{\"name\":\"Bold\",\"location\":{\"wght\":700},\"weightClass\":700}],\"glyphs\":[{\"name\":\"space\",\"unicodes\":[32],\"layers\":[{\"advance\":260,\"contours\":[]},{\"advance\":280,\"contours\":[]}]},{\"name\":\"H\",\"unicodes\":[72],\"layers\":[{\"advance\":632,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":374,\"type\":\"line\"},{\"x\":516,\"y\":374,\"type\":\"line\"},{\"x\":516,\"y\":700,\"type\":\"line\"},{\"x\":572,\"y\":700,\"type\":\"line\"},{\"x\":572,\"y\":0,\"type\":\"line\"},{\"x\":516,\"y\":0,\"type\":\"line\"},{\"x\":516,\"y\":326,\"type\":\"line\"},{\"x\":116,\"y\":326,\"type\":\"line\"},{\"x\":116,\"y\":0,\"type\":\"line\"}]]},{\"advance\":820,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":414,\"type\":\"line\"},{\"x\":610,\"y\":414,\"type\":\"line\"},{\"x\":610,\"y\":700,\"type\":\"line\"},{\"x\":760,\"y\":700,\"type\":\"line\"},{\"x\":760,\"y\":0,\"type\":\"line\"},{\"x\":610,\"y\":0,\"type\":\"line\"},{\"x\":610,\"y\":286,\"type\":\"line\"},{\"x\":210,\"y\":286,\"type\":\"line\"},{\"x\":210,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"I\",\"unicodes\":[73],\"layers\":[{\"advance\":176,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":0,\"type\":\"line\"}]]},{\"advance\":270,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"L\",\"unicodes\":[76],\"layers\":[{\"advance\":506,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":0,\"type\":\"line\"}]]},{\"advance\":600,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":150,\"type\":\"line\"},{\"x\":540,\"y\":150,\"type\":\"line\"},{\"x\":540,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"O\",\"unicodes\":[79],\"layers\":[{\"advance\":696,\"contours\":[[{\"x\":518,\"y\":710,\"type\":\"offcurve\"},{\"x\":656,\"y\":549,\"type\":\"offcurve\"},{\"x\":656,\"y\":350,\"type\":\"curve\"},{\"x\":656,\"y\":151,\"type\":\"offcurve\"},{\"x\":518,\"y\":-10,\"type\":\"offcurve\"},{\"x\":348,\"y\":-10,\"type\":\"curve\"},{\"x\":178,\"y\":-10,\"type\":\"offcurve\"},{\"x\":40,\"y\":151,\"type\":\"offcurve\"},{\"x\":40,\"y\":350,\"type\":\"curve\"},{\"x\":40,\"y\":549,\"type\":\"offcurve\"},{\"x\":178,\"y\":710,\"type\":\"offcurve\"},{\"x\":348,\"y\":710,\"type\":\"curve\"}],[{\"x\":209,\"y\":660,\"type\":\"offcurve\"},{\"x\":96,\"y\":521,\"type\":\"offcurve\"},{\"x\":96,\"y\":350,\"type\":\"curve\"},{\"x\":96,\"y\":179,\"type\":\"offcurve\"},{\"x\":209,\"y\":40,\"type\":\"offcurve\"},{\"x\":348,\"y\":40,\"type\":\"curve\"},{\"x\":487,\"y\":40,\"type\":\"offcurve\"},{\"x\":600,\"y\":179,\"type\":\"offcurve\"},{\"x\":600,\"y\":350,\"type\":\"curve\"},{\"x\":600,\"y\":521,\"type\":\"offcurve\"},{\"x\":487,\"y\":660,\"type\":\"offcurve\"},{\"x\":348,\"y\":660,\"type\":\"curve\"}]]},{\"advance\":790,\"contours\":[[{\"x\":591,\"y\":710,\"type\":\"offcurve\"},{\"x\":750,\"y\":549,\"type\":\"offcurve\"},{\"x\":750,\"y\":350,\"type\":\"curve\"},{\"x\":750,\"y\":151,\"type\":\"offcurve\"},{\"x\":591,\"y\":-10,\"type\":\"offcurve\"},{\"x\":395,\"y\":-10,\"type\":\"curve\"},{\"x\":199,\"y\":-10,\"type\":\"offcurve\"},{\"x\":40,\"y\":151,\"type\":\"offcurve\"},{\"x\":40,\"y\":350,\"type\":\"curve\"},{\"x\":40,\"y\":549,\"type\":\"offcurve\"},{\"x\":199,\"y\":710,\"type\":\"offcurve\"},{\"x\":395,\"y\":710,\"type\":\"curve\"}],[{\"x\":282,\"y\":575,\"type\":\"offcurve\"},{\"x\":190,\"y\":474,\"type\":\"offcurve\"},{\"x\":190,\"y\":350,\"type\":\"curve\"},{\"x\":190,\"y\":226,\"type\":\"offcurve\"},{\"x\":282,\"y\":125,\"type\":\"offcurve\"},{\"x\":395,\"y\":125,\"type\":\"curve\"},{\"x\":508,\"y\":125,\"type\":\"offcurve\"},{\"x\":600,\"y\":226,\"type\":\"offcurve\"},{\"x\":600,\"y\":350,\"type\":\"curve\"},{\"x\":600,\"y\":474,\"type\":\"offcurve\"},{\"x\":508,\"y\":575,\"type\":\"offcurve\"},{\"x\":395,\"y\":575,\"type\":\"curve\"}]]}]},{\"name\":\"T\",\"unicodes\":[84],\"layers\":[{\"advance\":672,\"contours\":[[{\"x\":308,\"y\":0,\"type\":\"line\"},{\"x\":308,\"y\":644,\"type\":\"line\"},{\"x\":30,\"y\":644,\"type\":\"line\"},{\"x\":30,\"y\":700,\"type\":\"line\"},{\"x\":642,\"y\":700,\"type\":\"line\"},{\"x\":642,\"y\":644,\"type\":\"line\"},{\"x\":364,\"y\":644,\"type\":\"line\"},{\"x\":364,\"y\":0,\"type\":\"line\"}]]},{\"advance\":860,\"contours\":[[{\"x\":355,\"y\":0,\"type\":\"line\"},{\"x\":355,\"y\":550,\"type\":\"line\"},{\"x\":30,\"y\":550,\"type\":\"line\"},{\"x\":30,\"y\":700,\"type\":\"line\"},{\"x\":830,\"y\":700,\"type\":\"line\"},{\"x\":830,\"y\":550,\"type\":\"line\"},{\"x\":505,\"y\":550,\"type\":\"line\"},{\"x\":505,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"V\",\"unicodes\":[86],\"layers\":[{\"advance\":676,\"contours\":[[{\"x\":10,\"y\":700,\"type\":\"line\"},{\"x\":72,\"y\":700,\"type\":\"line\"},{\"x\":338,\"y\":90,\"type\":\"line\"},{\"x\":604,\"y\":700,\"type\":\"line\"},{\"x\":666,\"y\":700,\"type\":\"line\"},{\"x\":369,\"y\":0,\"type\":\"line\"},{\"x\":307,\"y\":0,\"type\":\"line\"}]]},{\"advance\":770,\"contours\":[[{\"x\":10,\"y\":700,\"type\":\"line\"},{\"x\":175,\"y\":700,\"type\":\"line\"},{\"x\":385,\"y\":240,\"type\":\"line\"},{\"x\":595,\"y\":700,\"type\":\"line\"},{\"x\":760,\"y\":700,\"type\":\"line\"},{\"x\":468,\"y\":0,\"type\":\"line\"},{\"x\":303,\"y\":0,\"type\":\"line\"}]]}]}],\"kerning\":[{\"left\":\"T\",\"right\":\"O\",\"values\":[-40,-50]},{\"left\":\"O\",\"right\":\"T\",\"values\":[-40,-50]},{\"left\":\"V\",\"right\":\"O\",\"values\":[-30,-40]},{\"left\":\"O\",\"right\":\"V\",\"values\":[-30,-40]},{\"left\":\"L\",\"right\":\"T\",\"values\":[-90,-100]},{\"left\":\"L\",\"right\":\"V\",\"values\":[-80,-90]},{\"left\":\"T\",\"right\":\"V\",\"values\":[0,10]}],\"ligatures\":[],\"tolerance\":1,\"sample\":\"HOT VOLT TOIL\"}",
                    "output": "variable",
                    "master": 1,
                    "sample": "HOT VOLT",
                    "date": ""
                  }
                },
                "example2": {
                  "summary": "Static font with a ligature and an open license",
                  "value": {
                    "source": "{\"format\":\"lumina-font\",\"version\":1,\"familyName\":\"Lumina Starter Static\",\"designer\":\"\",\"copyright\":\"\",\"license\":\"This Font Software is licensed under the SIL Open Font License, Version 1.1.\",\"licenseUrl\":\"https://openfontlicense.org\",\"fontVersion\":1,\"unitsPerEm\":1000,\"ascender\":800,\"descender\":-200,\"lineGap\":0,\"xHeight\":500,\"capHeight\":700,\"axes\":[],\"masters\":[{\"name\":\"Regular\",\"location\":{},\"weightClass\":400}],\"glyphs\":[{\"name\":\"space\",\"unicodes\":[32],\"layers\":[{\"advance\":260,\"contours\":[]}]},{\"name\":\"H\",\"unicodes\":[72],\"layers\":[{\"advance\":632,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":374,\"type\":\"line\"},{\"x\":516,\"y\":374,\"type\":\"line\"},{\"x\":516,\"y\":700,\"type\":\"line\"},{\"x\":572,\"y\":700,\"type\":\"line\"},{\"x\":572,\"y\":0,\"type\":\"line\"},{\"x\":516,\"y\":0,\"type\":\"line\"},{\"x\":516,\"y\":326,\"type\":\"line\"},{\"x\":116,\"y\":326,\"type\":\"line\"},{\"x\":116,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"I\",\"unicodes\":[73],\"layers\":[{\"advance\":176,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"L\",\"unicodes\":[76],\"layers\":[{\"advance\":506,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"O\",\"unicodes\":[79],\"layers\":[{\"advance\":696,\"contours\":[[{\"x\":518,\"y\":710,\"type\":\"offcurve\"},{\"x\":656,\"y\":549,\"type\":\"offcurve\"},{\"x\":656,\"y\":350,\"type\":\"curve\"},{\"x\":656,\"y\":151,\"type\":\"offcurve\"},{\"x\":518,\"y\":-10,\"type\":\"offcurve\"},{\"x\":348,\"y\":-10,\"type\":\"curve\"},{\"x\":178,\"y\":-10,\"type\":\"offcurve\"},{\"x\":40,\"y\":151,\"type\":\"offcurve\"},{\"x\":40,\"y\":350,\"type\":\"curve\"},{\"x\":40,\"y\":549,\"type\":\"offcurve\"},{\"x\":178,\"y\":710,\"type\":\"offcurve\"},{\"x\":348,\"y\":710,\"type\":\"curve\"}],[{\"x\":209,\"y\":660,\"type\":\"offcurve\"},{\"x\":96,\"y\":521,\"type\":\"offcurve\"},{\"x\":96,\"y\":350,\"type\":\"curve\"},{\"x\":96,\"y\":179,\"type\":\"offcurve\"},{\"x\":209,\"y\":40,\"type\":\"offcurve\"},{\"x\":348,\"y\":40,\"type\":\"curve\"},{\"x\":487,\"y\":40,\"type\":\"offcurve\"},{\"x\":600,\"y\":179,\"type\":\"offcurve\"},{\"x\":600,\"y\":350,\"type\":\"curve\"},{\"x\":600,\"y\":521,\"type\":\"offcurve\"},{\"x\":487,\"y\":660,\"type\":\"offcurve\"},{\"x\":348,\"y\":660,\"type\":\"curve\"}]]}]},{\"name\":\"T\",\"unicodes\":[84],\"layers\":[{\"advance\":672,\"contours\":[[{\"x\":308,\"y\":0,\"type\":\"line\"},{\"x\":308,\"y\":644,\"type\":\"line\"},{\"x\":30,\"y\":644,\"type\":\"line\"},{\"x\":30,\"y\":700,\"type\":\"line\"},{\"x\":642,\"y\":700,\"type\":\"line\"},{\"x\":642,\"y\":644,\"type\":\"line\"},{\"x\":364,\"y\":644,\"type\":\"line\"},{\"x\":364,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"V\",\"unicodes\":[86],\"layers\":[{\"advance\":676,\"contours\":[[{\"x\":10,\"y\":700,\"type\":\"line\"},{\"x\":72,\"y\":700,\"type\":\"line\"},{\"x\":338,\"y\":90,\"type\":\"line\"},{\"x\":604,\"y\":700,\"type\":\"line\"},{\"x\":666,\"y\":700,\"type\":\"line\"},{\"x\":369,\"y\":0,\"type\":\"line\"},{\"x\":307,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"L_T\",\"unicodes\":[],\"layers\":[{\"advance\":1058,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":0,\"type\":\"line\"}],[{\"x\":694,\"y\":0,\"type\":\"line\"},{\"x\":694,\"y\":644,\"type\":\"line\"},{\"x\":416,\"y\":644,\"type\":\"line\"},{\"x\":416,\"y\":700,\"type\":\"line\"},{\"x\":1028,\"y\":700,\"type\":\"line\"},{\"x\":1028,\"y\":644,\"type\":\"line\"},{\"x\":750,\"y\":644,\"type\":\"line\"},{\"x\":750,\"y\":0,\"type\":\"line\"}]]}]}],\"kerning\":[{\"left\":\"T\",\"right\":\"O\",\"values\":[-40]},{\"left\":\"O\",\"right\":\"T\",\"values\":[-40]},{\"left\":\"V\",\"right\":\"O\",\"values\":[-30]},{\"left\":\"O\",\"right\":\"V\",\"values\":[-30]},{\"left\":\"L\",\"right\":\"T\",\"values\":[-90]},{\"left\":\"L\",\"right\":\"V\",\"values\":[-80]},{\"left\":\"T\",\"right\":\"V\",\"values\":[0]}],\"ligatures\":[{\"glyph\":\"L_T\",\"components\":[\"L\",\"T\"]}],\"tolerance\":1,\"sample\":\"HOLT VOLT\"}",
                    "output": "static",
                    "master": 1,
                    "sample": "HOLT VOLT",
                    "date": "2026-11-02"
                  }
                },
                "example3": {
                  "summary": "Grid font traced into outlines",
                  "value": {
                    "source": "{\"format\":\"lumina-grid-font\",\"version\":1,\"name\":\"Brick Sans\",\"glyphs\":[{\"character\":\"H\",\"cells\":[1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0]},{\"character\":\"I\",\"cells\":[0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0],\"advance\":8},{\"character\":\"O\",\"cells\":[0,1,1,1,1,1,1,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0]},{\"character\":\" \",\"cells\":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],\"advance\":4}]}",
                    "output": "static",
                    "master": 1,
                    "sample": "HI OH",
                    "date": ""
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaFontBuilderResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "LuminaFontBuilderInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "source": {
            "title": "Font project JSON (lumina-font or lumina-grid-font)",
            "type": "string",
            "maxLength": 2000000,
            "default": "{\"format\":\"lumina-font\",\"version\":1,\"familyName\":\"Lumina Starter\",\"designer\":\"\",\"copyright\":\"\",\"license\":\"\",\"licenseUrl\":\"\",\"fontVersion\":1,\"unitsPerEm\":1000,\"ascender\":800,\"descender\":-200,\"lineGap\":0,\"xHeight\":500,\"capHeight\":700,\"axes\":[{\"tag\":\"wght\",\"name\":\"Weight\",\"min\":300,\"default\":300,\"max\":700}],\"masters\":[{\"name\":\"Light\",\"location\":{\"wght\":300},\"weightClass\":300},{\"name\":\"Bold\",\"location\":{\"wght\":700},\"weightClass\":700}],\"glyphs\":[{\"name\":\"space\",\"unicodes\":[32],\"layers\":[{\"advance\":260,\"contours\":[]},{\"advance\":280,\"contours\":[]}]},{\"name\":\"H\",\"unicodes\":[72],\"layers\":[{\"advance\":632,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":374,\"type\":\"line\"},{\"x\":516,\"y\":374,\"type\":\"line\"},{\"x\":516,\"y\":700,\"type\":\"line\"},{\"x\":572,\"y\":700,\"type\":\"line\"},{\"x\":572,\"y\":0,\"type\":\"line\"},{\"x\":516,\"y\":0,\"type\":\"line\"},{\"x\":516,\"y\":326,\"type\":\"line\"},{\"x\":116,\"y\":326,\"type\":\"line\"},{\"x\":116,\"y\":0,\"type\":\"line\"}]]},{\"advance\":820,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":414,\"type\":\"line\"},{\"x\":610,\"y\":414,\"type\":\"line\"},{\"x\":610,\"y\":700,\"type\":\"line\"},{\"x\":760,\"y\":700,\"type\":\"line\"},{\"x\":760,\"y\":0,\"type\":\"line\"},{\"x\":610,\"y\":0,\"type\":\"line\"},{\"x\":610,\"y\":286,\"type\":\"line\"},{\"x\":210,\"y\":286,\"type\":\"line\"},{\"x\":210,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"I\",\"unicodes\":[73],\"layers\":[{\"advance\":176,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":0,\"type\":\"line\"}]]},{\"advance\":270,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"L\",\"unicodes\":[76],\"layers\":[{\"advance\":506,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":700,\"type\":\"line\"},{\"x\":116,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":56,\"type\":\"line\"},{\"x\":446,\"y\":0,\"type\":\"line\"}]]},{\"advance\":600,\"contours\":[[{\"x\":60,\"y\":0,\"type\":\"line\"},{\"x\":60,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":700,\"type\":\"line\"},{\"x\":210,\"y\":150,\"type\":\"line\"},{\"x\":540,\"y\":150,\"type\":\"line\"},{\"x\":540,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"O\",\"unicodes\":[79],\"layers\":[{\"advance\":696,\"contours\":[[{\"x\":518,\"y\":710,\"type\":\"offcurve\"},{\"x\":656,\"y\":549,\"type\":\"offcurve\"},{\"x\":656,\"y\":350,\"type\":\"curve\"},{\"x\":656,\"y\":151,\"type\":\"offcurve\"},{\"x\":518,\"y\":-10,\"type\":\"offcurve\"},{\"x\":348,\"y\":-10,\"type\":\"curve\"},{\"x\":178,\"y\":-10,\"type\":\"offcurve\"},{\"x\":40,\"y\":151,\"type\":\"offcurve\"},{\"x\":40,\"y\":350,\"type\":\"curve\"},{\"x\":40,\"y\":549,\"type\":\"offcurve\"},{\"x\":178,\"y\":710,\"type\":\"offcurve\"},{\"x\":348,\"y\":710,\"type\":\"curve\"}],[{\"x\":209,\"y\":660,\"type\":\"offcurve\"},{\"x\":96,\"y\":521,\"type\":\"offcurve\"},{\"x\":96,\"y\":350,\"type\":\"curve\"},{\"x\":96,\"y\":179,\"type\":\"offcurve\"},{\"x\":209,\"y\":40,\"type\":\"offcurve\"},{\"x\":348,\"y\":40,\"type\":\"curve\"},{\"x\":487,\"y\":40,\"type\":\"offcurve\"},{\"x\":600,\"y\":179,\"type\":\"offcurve\"},{\"x\":600,\"y\":350,\"type\":\"curve\"},{\"x\":600,\"y\":521,\"type\":\"offcurve\"},{\"x\":487,\"y\":660,\"type\":\"offcurve\"},{\"x\":348,\"y\":660,\"type\":\"curve\"}]]},{\"advance\":790,\"contours\":[[{\"x\":591,\"y\":710,\"type\":\"offcurve\"},{\"x\":750,\"y\":549,\"type\":\"offcurve\"},{\"x\":750,\"y\":350,\"type\":\"curve\"},{\"x\":750,\"y\":151,\"type\":\"offcurve\"},{\"x\":591,\"y\":-10,\"type\":\"offcurve\"},{\"x\":395,\"y\":-10,\"type\":\"curve\"},{\"x\":199,\"y\":-10,\"type\":\"offcurve\"},{\"x\":40,\"y\":151,\"type\":\"offcurve\"},{\"x\":40,\"y\":350,\"type\":\"curve\"},{\"x\":40,\"y\":549,\"type\":\"offcurve\"},{\"x\":199,\"y\":710,\"type\":\"offcurve\"},{\"x\":395,\"y\":710,\"type\":\"curve\"}],[{\"x\":282,\"y\":575,\"type\":\"offcurve\"},{\"x\":190,\"y\":474,\"type\":\"offcurve\"},{\"x\":190,\"y\":350,\"type\":\"curve\"},{\"x\":190,\"y\":226,\"type\":\"offcurve\"},{\"x\":282,\"y\":125,\"type\":\"offcurve\"},{\"x\":395,\"y\":125,\"type\":\"curve\"},{\"x\":508,\"y\":125,\"type\":\"offcurve\"},{\"x\":600,\"y\":226,\"type\":\"offcurve\"},{\"x\":600,\"y\":350,\"type\":\"curve\"},{\"x\":600,\"y\":474,\"type\":\"offcurve\"},{\"x\":508,\"y\":575,\"type\":\"offcurve\"},{\"x\":395,\"y\":575,\"type\":\"curve\"}]]}]},{\"name\":\"T\",\"unicodes\":[84],\"layers\":[{\"advance\":672,\"contours\":[[{\"x\":308,\"y\":0,\"type\":\"line\"},{\"x\":308,\"y\":644,\"type\":\"line\"},{\"x\":30,\"y\":644,\"type\":\"line\"},{\"x\":30,\"y\":700,\"type\":\"line\"},{\"x\":642,\"y\":700,\"type\":\"line\"},{\"x\":642,\"y\":644,\"type\":\"line\"},{\"x\":364,\"y\":644,\"type\":\"line\"},{\"x\":364,\"y\":0,\"type\":\"line\"}]]},{\"advance\":860,\"contours\":[[{\"x\":355,\"y\":0,\"type\":\"line\"},{\"x\":355,\"y\":550,\"type\":\"line\"},{\"x\":30,\"y\":550,\"type\":\"line\"},{\"x\":30,\"y\":700,\"type\":\"line\"},{\"x\":830,\"y\":700,\"type\":\"line\"},{\"x\":830,\"y\":550,\"type\":\"line\"},{\"x\":505,\"y\":550,\"type\":\"line\"},{\"x\":505,\"y\":0,\"type\":\"line\"}]]}]},{\"name\":\"V\",\"unicodes\":[86],\"layers\":[{\"advance\":676,\"contours\":[[{\"x\":10,\"y\":700,\"type\":\"line\"},{\"x\":72,\"y\":700,\"type\":\"line\"},{\"x\":338,\"y\":90,\"type\":\"line\"},{\"x\":604,\"y\":700,\"type\":\"line\"},{\"x\":666,\"y\":700,\"type\":\"line\"},{\"x\":369,\"y\":0,\"type\":\"line\"},{\"x\":307,\"y\":0,\"type\":\"line\"}]]},{\"advance\":770,\"contours\":[[{\"x\":10,\"y\":700,\"type\":\"line\"},{\"x\":175,\"y\":700,\"type\":\"line\"},{\"x\":385,\"y\":240,\"type\":\"line\"},{\"x\":595,\"y\":700,\"type\":\"line\"},{\"x\":760,\"y\":700,\"type\":\"line\"},{\"x\":468,\"y\":0,\"type\":\"line\"},{\"x\":303,\"y\":0,\"type\":\"line\"}]]}]}],\"kerning\":[{\"left\":\"T\",\"right\":\"O\",\"values\":[-40,-50]},{\"left\":\"O\",\"right\":\"T\",\"values\":[-40,-50]},{\"left\":\"V\",\"right\":\"O\",\"values\":[-30,-40]},{\"left\":\"O\",\"right\":\"V\",\"values\":[-30,-40]},{\"left\":\"L\",\"right\":\"T\",\"values\":[-90,-100]},{\"left\":\"L\",\"right\":\"V\",\"values\":[-80,-90]},{\"left\":\"T\",\"right\":\"V\",\"values\":[0,10]}],\"ligatures\":[],\"tolerance\":1,\"sample\":\"HOT VOLT TOIL\"}"
          },
          "output": {
            "title": "Font type",
            "type": "string",
            "enum": [
              "variable",
              "static"
            ],
            "default": "variable"
          },
          "master": {
            "title": "Master for a static font (1 = first)",
            "type": "integer",
            "minimum": 1,
            "maximum": 9,
            "default": 1
          },
          "sample": {
            "title": "Sample text for the preview",
            "type": "string",
            "maxLength": 120,
            "default": ""
          },
          "date": {
            "title": "Creation date written into the font (optional, YYYY-MM-DD)",
            "type": "string",
            "maxLength": 10,
            "default": ""
          }
        },
        "required": [
          "source"
        ]
      },
      "LuminaFontBuilderResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "mode",
              "familyName",
              "styleName",
              "postScriptName",
              "bytes",
              "sha256",
              "glyphs",
              "characters",
              "contours",
              "points",
              "kerningPairs",
              "ligatures",
              "axes",
              "instances",
              "tables",
              "conversion",
              "checks",
              "verified",
              "warnings",
              "compatibility",
              "sampleMissing",
              "artifactUrl",
              "fontBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "mode": {
                "enum": [
                  "variable",
                  "static"
                ]
              },
              "familyName": {
                "type": "string"
              },
              "styleName": {
                "type": "string"
              },
              "postScriptName": {
                "type": "string"
              },
              "bytes": {
                "type": "integer"
              },
              "sha256": {
                "type": "string"
              },
              "glyphs": {
                "type": "integer"
              },
              "characters": {
                "type": "integer"
              },
              "contours": {
                "type": "integer"
              },
              "points": {
                "type": "integer"
              },
              "kerningPairs": {
                "type": "integer"
              },
              "ligatures": {
                "type": "integer"
              },
              "axes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "tag",
                    "min",
                    "default",
                    "max"
                  ],
                  "properties": {
                    "tag": {
                      "type": "string"
                    },
                    "min": {
                      "type": "number"
                    },
                    "default": {
                      "type": "number"
                    },
                    "max": {
                      "type": "number"
                    }
                  }
                }
              },
              "instances": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "location"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "location": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "number"
                      }
                    }
                  }
                }
              },
              "tables": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "tag",
                    "length",
                    "checksum"
                  ],
                  "properties": {
                    "tag": {
                      "type": "string"
                    },
                    "length": {
                      "type": "integer"
                    },
                    "checksum": {
                      "type": "string"
                    }
                  }
                }
              },
              "conversion": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "cubicSegments",
                  "quadraticPieces",
                  "maxError"
                ],
                "properties": {
                  "cubicSegments": {
                    "type": "integer"
                  },
                  "quadraticPieces": {
                    "type": "integer"
                  },
                  "maxError": {
                    "type": "number"
                  }
                }
              },
              "checks": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "passed",
                    "detail"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "passed": {
                      "type": "boolean"
                    },
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              },
              "verified": {
                "type": "boolean"
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "compatibility": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "glyph",
                    "issues"
                  ],
                  "properties": {
                    "glyph": {
                      "type": "string"
                    },
                    "issues": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "sampleMissing": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "artifactUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "fontBase64": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}