{
  "openapi": "3.1.1",
  "info": {
    "title": "Threadsmith API",
    "version": "1.0.0",
    "summary": "Deterministic tools for people and AI agents.",
    "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://threadsmith.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/tools": {
      "get": {
        "operationId": "listTools",
        "summary": "Catalog of all tools",
        "responses": {
          "200": {
            "description": "Tool list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/{id}": {
      "get": {
        "operationId": "getTool",
        "summary": "Definition of one tool",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "threadsmith-digitize",
                "threadsmith-stitch-file",
                "threadsmith-design-stats"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/threadsmith-digitize/calculate": {
      "post": {
        "operationId": "calculateThreadsmithDigitize",
        "summary": "Turn shapes, SVG paths, text or a whole project into embroidery stitches with stats and preview.",
        "description": "Digitizes vector artwork into machine embroidery: running, bean, satin (with pull compensation, auto split and short stitches), tatami fill with stagger and graded density, contour fill and motif runs, plus underlay. Returns stitch counts, trims, colour changes, thread and bobbin estimates, sewing time, hoop fit, a density check and a thread-look SVG preview — the same engine as the Threadsmith app.\n\nMethodology:\n- Shapes are flattened to polygons (Bézier curves within 0.04 mm); closed shapes are regions (even-odd, holes allowed), open paths are lines.\n- Tatami: scanlines at the fill angle every row-spacing; intervals are split into blocks that can be sewn back and forth, ordered nearest-first, and penetrations sit on a staggered grid (1/3 offset by default) so no visible columns form.\n- Satin: zig-zag between the edges every density step (rows perpendicular to the shape’s principal axis); columns along lines measure spacing on the outer edge and shorten every other inner stitch on tight curves; stitches longer than the max width are split with interleaved penetrations.\n- Contour fill and edge-walk underlay use an exact Euclidean distance transform of the rasterised shape and marching-squares iso-lines, so insets stay valid for concave shapes and holes.\n- Sequencing: objects grouped by colour (first appearance) and visited nearest-first; gaps up to 1.2 mm are sewn, gaps below the trim distance are jumped, longer gaps get tie-off, trim, jump and tie-in.\n- Thread estimate = sewn length + 0.8 mm take-up per penetration + 5 % waste; bobbin ≈ ⅓ of the sewn length; time = stitches ÷ speed + 4 s per trim + 20 s per colour change.\n\nEstimates assume average fabric and settings; always test-stitch on the real fabric and stabiliser. Thread names are generic colours, not brand shade numbers.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "threadsmith_digitize",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadsmithDigitizeInput"
              },
              "examples": {
                "example1": {
                  "summary": "40 × 30 mm oval, automatic tatami with underlay",
                  "value": {
                    "source": "shape",
                    "shape": "ellipse",
                    "widthMm": 40,
                    "heightMm": 30,
                    "color": "ruby"
                  }
                },
                "example2": {
                  "summary": "Satin column along a curve",
                  "value": {
                    "source": "path",
                    "pathD": "M-30 10 C-15 -15 15 25 30 0",
                    "stitchType": "satin",
                    "satinWidth": 3.5,
                    "spacing": 0.4,
                    "color": "emerald"
                  }
                },
                "example3": {
                  "summary": "Name in single-line satin lettering",
                  "value": {
                    "source": "text",
                    "text": "Ada",
                    "textSize": 14,
                    "stitchType": "satin",
                    "satinWidth": 2,
                    "color": "navy",
                    "includeStitches": true
                  }
                },
                "example4": {
                  "summary": "SVG badge (circle + square)",
                  "value": {
                    "source": "svg",
                    "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"60mm\" height=\"60mm\" viewBox=\"0 0 60 60\"><circle cx=\"30\" cy=\"30\" r=\"26\" fill=\"#0f4c81\"/><rect x=\"20\" y=\"20\" width=\"20\" height=\"20\" fill=\"#e4c13b\" stroke=\"#ffffff\" stroke-width=\"1.5\"/></svg>"
                  }
                },
                "example5": {
                  "summary": "Template project: botanical leaf",
                  "value": {
                    "source": "project",
                    "project": "{\"format\":\"threadsmith\",\"version\":1,\"name\":\"Botanical leaf\",\"hoop\":\"100x100\",\"palette\":[{\"id\":\"emerald\",\"name\":\"Emerald\",\"hex\":\"#0b7a4b\"},{\"id\":\"malachite\",\"name\":\"Malachite\",\"hex\":\"#1f5c3f\"},{\"id\":\"walnut\",\"name\":\"Walnut\",\"hex\":\"#6d4a33\"}],\"objects\":[{\"id\":\"leaf\",\"name\":\"Leaf\",\"kind\":\"path\",\"color\":\"emerald\",\"d\":\"M0 -38C24 -24 26 14 0 36C-26 14 -24 -24 0 -38Z\",\"stitch\":{\"type\":\"tatami\",\"angle\":60,\"spacing\":0.4,\"length\":4,\"stagger\":\"1/3\",\"pullComp\":0.2},\"underlay\":{\"edge\":true,\"tatami\":true,\"inset\":0.5}},{\"id\":\"vein\",\"name\":\"Centre vein\",\"kind\":\"path\",\"color\":\"malachite\",\"d\":\"M0 -32C2 -10 2 12 0 33\",\"stitch\":{\"type\":\"running\",\"length\":2.2,\"bean\":true}},{\"id\":\"veins\",\"name\":\"Side veins\",\"kind\":\"path\",\"color\":\"malachite\",\"d\":\"M1 -18L12 -26M1 -6L15 -13M1 6L15 0M1 18L12 13M-1 -18L-12 -26M-1 -6L-15 -13M-1 6L-15 0M-1 18L-12 13\",\"stitch\":{\"type\":\"running\",\"length\":2,\"bean\":true}},{\"id\":\"stem\",\"name\":\"Stem\",\"kind\":\"path\",\"color\":\"walnut\",\"d\":\"M0 35C1 40 3 43 7 46\",\"stitch\":{\"type\":\"satin\",\"width\":2.2,\"density\":0.35},\"underlay\":{\"center\":true}}]}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadsmithDigitizeResult"
                }
              }
            }
          },
          "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/tools/threadsmith-stitch-file/calculate": {
      "post": {
        "operationId": "calculateThreadsmithStitchFile",
        "summary": "Write Tajima DST or Melco EXP machine files from a stitch list or a Threadsmith project.",
        "description": "Encodes stitches as a Tajima DST (512-byte header, balanced-ternary 3-byte records, jumps split to ≤ 12.1 mm, colour changes, trims as zero-sum jumps, END) or Melco EXP file. The file is decoded again to verify every stitch position before you download it.\n\nMethodology:\n- Coordinates are rounded to 0.1 mm on absolute positions, so rounding never accumulates; each move becomes the difference to the previous rounded position.\n- DST: each axis is written in balanced ternary with digits for 1, 3, 9, 27 and 81 (±121 max). Byte 3 = 0x03 | flags: 0x80 jump, 0xC0 colour change; END = 00 00 F3. Longer moves are split into equal records. Y is inverted (DST is y-up).\n- Trims are written as three jumps (+2,+2) (−4,−4) (+2,+2) that cancel out — the convention machines use to trigger a trim after a run of jumps.\n- Header: LA label, ST record count, CO colour changes, ±X/±Y extents in 0.1 mm, AX/AY end offset, MX/MY 0, PD ******, 0x1A, padded with spaces to 512 bytes.\n- EXP: 2 signed bytes per stitch (±127), 0x80 0x04 dx dy jump, 0x80 0x01 colour change; trims as three zero-sum jumps.\n\nDST and EXP carry no thread colours: set the needle/colour order on the machine using the colour list from Threadsmith. Test on scrap fabric first.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "threadsmith_stitch_file",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadsmithStitchFileInput"
              },
              "examples": {
                "example1": {
                  "summary": "Tiny square outline with a jump and a colour change → DST",
                  "value": {
                    "format": "dst",
                    "label": "SQUARE",
                    "stitches": "[[0,0],[10,0],[10,10],[0,10],[0,0],[30,0,\"jump\"],[30,0,\"color\"],[30,0],[40,5],[30,10]]"
                  }
                },
                "example2": {
                  "summary": "Template project (name tag) → Melco EXP",
                  "value": {
                    "format": "exp",
                    "project": "{\"format\":\"threadsmith\",\"version\":1,\"name\":\"Name tag\",\"hoop\":\"130x180\",\"palette\":[{\"id\":\"sapphire\",\"name\":\"Sapphire\",\"hex\":\"#0f4c81\"},{\"id\":\"coral\",\"name\":\"Coral\",\"hex\":\"#e8705a\"},{\"id\":\"ivory\",\"name\":\"Ivory\",\"hex\":\"#fffbf0\"}],\"objects\":[{\"id\":\"frame\",\"name\":\"Frame\",\"kind\":\"rect\",\"color\":\"sapphire\",\"x\":-45,\"y\":-22,\"w\":90,\"h\":44,\"r\":8,\"stitch\":{\"type\":\"satin\",\"mode\":\"border\",\"width\":3,\"density\":0.4},\"underlay\":{\"center\":true}},{\"id\":\"hello\",\"name\":\"Hello\",\"kind\":\"text\",\"color\":\"coral\",\"text\":\"Hello, my name is\",\"x\":0,\"y\":-7,\"size\":6,\"font\":\"single\",\"align\":\"center\",\"stitch\":{\"type\":\"running\",\"length\":1.8,\"bean\":true}},{\"id\":\"name\",\"name\":\"Name\",\"kind\":\"text\",\"color\":\"sapphire\",\"text\":\"Ada\",\"x\":0,\"y\":14,\"size\":16,\"font\":\"single\",\"align\":\"center\",\"spacing\":1,\"stitch\":{\"type\":\"satin\",\"width\":2.4,\"density\":0.36},\"underlay\":{\"center\":true}}]}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadsmithStitchFileResult"
                }
              }
            }
          },
          "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/tools/threadsmith-design-stats/calculate": {
      "post": {
        "operationId": "calculateThreadsmithDesignStats",
        "summary": "Stitch count, colours, trims, thread, bobbin, sewing time and density of a DST/EXP file.",
        "description": "Reads a Tajima DST or Melco EXP file (or a stitch list) and reports stitches, jumps, trims, colour changes, size and hoop fit, thread length per colour with a bobbin estimate, sewing time at your machine speed and a thread-density check with the densest spots, plus a thread-look preview.\n\nMethodology:\n- DST records are decoded from balanced ternary (bits for ±1, ±3, ±9, ±27, ±81 per axis); runs of three or more jumps count as one trim, as on most machines. EXP bytes are two’s-complement 0.1 mm steps with 0x80 control codes.\n- Sewn length = sum of stitch lengths per colour; top thread = sewn length + 0.8 mm take-up per penetration + 5 % waste; bobbin ≈ 33 % of sewn length.\n- Sewing time = stitches ÷ speed + trims × trim time + colour changes × change time + 0.15 s per jump.\n- Density: each stitch deposits length × 0.4 mm thread width into the 1 mm cells it crosses; values are thread layers (1 ≈ fully covered once).\n\nThread and time figures are planning estimates; real consumption depends on tension, fabric, stabiliser and machine.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "threadsmith_design_stats",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadsmithDesignStatsInput"
              },
              "examples": {
                "example1": {
                  "summary": "Leaf template as DST, 850 stitches/min",
                  "value": {
                    "file": {
                      "filename": "leaf.dst",
                      "contentBase64": "TEE6TEVBRiAgICAgICAgICAgIA1TVDogICAyMjc5DUNPOiAgMg0rWDogIDE4OQ0tWDogIDE4OA0rWTogIDM4Mg0tWTogIDQ2OA1BWDorICAgNzgNQVk6LSAgNDUwDU1YOisgICAgMA1NWTorICAgIDANUEQ6KioqKioqDRogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAUQIcWQIcAAAOgQANQgAMCYQMCYQOCYAMCYAMAYgMCYgMIYQNKYQNJYANKYAOZogMYogNVqQNUqQMWKQOVaAOUaANVagOligOkmgOlmgNmmAMlGANlGAOkWQMqUgMqUgOJkgOIkAOJkAMKkQMJkQNCkgMBkgNCkANAkANCkQMCkQMBkQNEkgMFkgOFkgMEkANlUAOGkQMkUQNlUQOoVgOoVgNpFgMqFAOoFAOoFANZJAMYJAOYJANZpgNYpgMYpgOVoQOVoQNGYQMFYAMEYAMFYgOEYgOGYgOBYQOJEgNJkgNIkgNJkgOJEgNJkgNJkgNJkgOJEgNJkgNJkgNUCAMgGgMWSgNUaQOmQQNRJQMQJQNQJQMpBgOgJgNVCANVCAOiGgMgGgOgGgMWCQMUCQMVaQNphAMRJQNQJQMQJQNRJQMQJQMpBgNoZgMhmAMgGgOgGgMiGgOgGgMgGgOiGgNKQQOYYgNQZgMRJQNQJQMQJQNRJQMQJQNQJQMpBgMpBgOGIANVCANVCAOiGgMgGgOgGgMiGgOgGgMgGgOiGgNZggNZIgOQZANRJQMQJQNQJQMRJQNQJQMQJQNRJQNohAOoBANGIgNUCAOUSAOgGgMiGgOgGgMgGgOiGgMgGgOgGgMiGgNaggNYIAOQZAMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQOqBAOqBAMCYQMUCQMWCQOgGgMgGgOiGgMgGgOgGgMiGgOgGgMgGgOiGgNZggMYIAMRZgNRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQNQJQOQZANAYANWSgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgOgGgMiGgNKQQOaIQMphANRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQNQJQMRJQNqBgORogNKQQMiGgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgOgGgMWCQMUCQNYoQMmQQMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQMQJQNRJQNSJQNZoQOWSAOiGgMgGgOgGgMgGgOiGgMgGgOgGgMiGgOgGgMgGgMWSgMSogMpBgNoBgMQJQNQJQMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQMkQAOYIQNimAMiGgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgOgGgOYgAORogMpBgNQJQMQJQNRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQMpBgOZIANVCAMWCQMiGgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgNWSgNAYAOQZAMQJQNRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQNphANaIANVCANVCAOiGgMgGgOgGgMgGgOiGgMgGgOgGgMiGgMUCQMWCQOAYQMpBgOqBAMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQNphANYIgNWCQMWCQMiGgOgGgMgGgOgGgMiGgOgGgMgGgNUCANVCAMGIgMoBAOqBAMQJQNQJQMRJQNQJQMQJQNRJQMQJQMoBgMaYgNgmAOiGgMgGgOgGgMgGgOiGgMgGgMWCQMWCQMGoQMSZANQJQMRJQNQJQMQJQNRJQMQJQOFggNWaQNWCQOiGgMgGgOgGgMgGgOiGgNWCQNqJQMpBgMpBgMQJQNQJQMRJQNRZgOkiAMWCQNVCAOgGgNUCAOVSAOCRAMARAMARAOCRAMARAMARAOCRAMVQgOaFQOaFQOVQgOEkAMYogNlKgOlagNlKgMoAQNYYgNlEAMmEQNalQOaFQOaFQNKlgNmkgOGKQOlagNlKgNlKgOlagNEKQMoAAOlkgNalQOaFQNalQOaFQOaFQMJFgNQQQMGKQOlagNlKgNlKgOlagNlKgOlagOpQAOkUQOaFQNalQOaFQOaFQNalQOaFQMFkQOSQQOYogOlagNlKgNlKgOlagNlKgOlagNYogNZogMpQAMKlgOaFQOaFQNalQOaFQOaFQNalQOaFQMlEANRAAMZYAOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNiAgOJgAOIFgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQNUAgMRAANYogNZogNlKgOlagNlKgNlKgOlagNlKgNlKgOlagOoAQOAQgMkUQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQMlEAMlEAORAAMEqANlKgOlagNlKgNlKgOlagNlKgOlagNlKgMmaAMAQgMWAgNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQMKlgOQAAOZIgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNZogOCQgOZlAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNVQgNQgAOiQgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagMpAQOCQgOkUQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQMlEAMlEANQgANFqANlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOEqAOAQgORAQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOKlgNSgAOYYANlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOZIgMCQgMIVAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMWAgNSgAOaIAMZIgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNYogMYogMAQgOlEAOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQOKVAMSgAOaogOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNGqQMAQgOmUQNkEQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMmkAMSgANiAgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgMYYAOAQgMIFgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMSgAMGKQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgMZIgOaIAMCQgNWQAOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQNmEQMSgANogQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOFaQMAQgMlEAOlEAOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNmEQNmEQORggMFqAOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNogQMAQgNkUQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNlUQORggOpgANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNkaAMAQgOWAgOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOmUQOkUQMRggNkaANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOZIgMAQgOKVAOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQOKlgORggMYYANlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNZogNYogMBQgNmkAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOkUQMkUQORggNkaANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOEaQMAQgMlEAMmEQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQMKlgORggMYYANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgMqgQNBQgMkUQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQOkUQOkUQORggNmaANlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNkaAMAQgOWAgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQOFkQNBQgNZYAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNaIAMAQgNJFgOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOkUQOmUQORggOEqANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagOZIgOaIANAQgMVQgOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQNlUQNBQgMqgQNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNKYgNAQgMZlANalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNmEQNmEQNBQgNFqAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOiQgNAQgOlEAOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNkEQOQggNpgQOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagMGKQNBQgMlEAMmEQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMlEAMlEANBQgNEKQNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNqgQNBQgMkUQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOlEANAQgOiQgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgMFqANBQgOmUQOkUQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQOYlANBQgNKYgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagMZYAOQggOKlgOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNWQANAQgOZIgOZIgNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagMmaANBQgORAQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOKVAMAQgMaIANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOaYAORggNIlgNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMWAgNAQgOmaAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagORggOWAgOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMKlgMAQgNZYAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagMaIAOSgAOIFgOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOmUQNmEQNAQgNGqQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgORggMWAgNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQNmEQNAQgMpAQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNaIAOSgAOKFgNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMalQNAQgMKYgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgMlagOSgANRgQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNVQgMBQgMZIgOZIgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagOYYAMSgAOFkQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQNJFgMAQgNaIANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNkaAMSgAOmUQNkEQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQNRgQNAQgNkaAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgMqgQMRggOkUQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMkUQMAQgNogQOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgMGKQMSgAOlEAMlEAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMlEAOmEQMAQgMFaQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNpAQMSgAMkkAOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQMmkAMAQgNZogOZIgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOaogNQgANJFgOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNJFgMAQgMaIAOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgMaIAMaIANSgAOWAgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMkUQMmUQMBQgOEqANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNZYAOSAAOkUQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQMkUQMAQgOoAQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagMEKQOQAAOYlAOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMalQMAQgNKYgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOlagOZIgOZIgOQAAMWAgNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMVQgMAQgMZIgMaIANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNZYAMQAANmEQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOIFgOAQgNYYANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNKYgMQAANKFANalQOaFQOaFQNalQOaFQOaFQNalQOaFQNmEQNmEQMAQgOGKQOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNmaAOSQQOlEAMlEANalQOaFQOaFQNalQOaFQOaFQNalQOaFQOlEAOAQgNYogNYogNlKgOlagNlKgNlKgOlagNlKgNlKgOZIgOZIgOSQQMkUQOmUQOaFQNalQOaFQOaFQNalQOaFQNalQMKlgNoAAMZIgOaIAOlagNlKgNlKgOlagNlKgNlKgOlagNpAQNQQQOWAgOaFQOaFQNalQOaFQOaFQNalQOaFQOmkANpggMFaQNlKgOlagNlKgNlKgOlagNlKgNpAQMmkgMmUQOkUQOaFQNalQOaFQOaFQNalQNaYgMYogNYogNlKgNlKgOlagOaIAMZIgOlUgMIVAOaFQOaFQNalQNWAgNGKANYogNZogOlagMZYAMQgQMgQgNCgYNCQoNCgYMAAMNSVqMAAAMEQgMIgQOYBANkCAOYBAMZBAMmCAMZBAOYBANkCAOYBAOYBANkCAOYBAMZBAMmCAMZBAOYBANkCAOYBAMKBYMAAANoBAOUCANoBAMpBAMWCAMpBANoBAOUCANoBANoBAOUCANoBAMpBAMWCAMpBANoBAOUCANoBAMIQQMEggNCgYNCQoNCgYORSqsAAAMEggMIQQNoBAOUCANoBAOqRANViAOqRANoBAOUCANoBAOqRANViAOqRANoBAOUCANoBAOqRANViAOqRANoBAOUCANoBAOqRANViAOqRAMIQQMEggNCgYNCQoNCgYNgkKNgkKMAAAMIQQMEggMVCAMqBAMVCAOUCANoBAOUCAMVCAMqBAMVCAMUCAMoBAMUCAMVCAMqBAMVCAOUCANoBAOUCAMVCAMqBAMVCAMUCAMoBAMUCAMFCoMAAAMlCAMaBAMlCAMkCAMYBAMkCANlCAOaBANlCAMkCAMYBAMkCAMlCAMaBAMlCAMkCAMYBAMkCANlCAOaBANlCAMkCAMYBAMkCAMEQgMIgQNCgYNCQoNCgYOQYJOQYJMAAAMEQgMIgQNYhAOkSANYhAOaBANlCAOaBANYhAOkSANYhAOaBANlCAOaBANYhAOkSANYhAOaBANlCAOaBANYhAOkSANYhAOaBANlCAOaBAMIgQMEQgNCgYNCQoNCgYMgoKMgoKMAAAOIgQNEQgNmiQOZRgNmiQMkiQMYRgMkiQNmiQOZRgNmiQMmiQMZRgMmiQNmiQOZRgNmiQMkiQMYRgMkiQNmiQOZRgNmiQNEQgOIgQNCgYNCQoNCgYNWWYdWWYcAAAOEggNIQQNphgOWSQNphgMohgMUSQMohgNphgOWSQNphgMphgMWSQMphgNphgOWSQNphgMohgMUSQMohgNphgOWSQNphgNIQQOEggNCgYNCQoNCgYMoWqsAAANRgAOiQANiUQORogNiUQMhUAMSoAMhUANiUQORogNiUQMiUQMRogMiUQNhUAOSoANhUANhUAOSoANhUAMiUQMRogMiUQNhUAOSoANhUANgUAOQoANgUAMhUAMSoAMhUANhUAOSoANhUANgUAOQoANgUANhUAOSoANhUAMgUAMQoAMgUANgUAOQoANgUANgUAOQoANgUAMgUAMQoAMgUANiUAORoANiUANgUAOQoANgUAMiUAMRoAMiUANgUAOQoANgUANiUAORoANiUAMiUAMRoAMiUANiUAORoANiUANhUgOSoQNhUgMiUAMRoAMiUANhUgOSoQNhUgNiUAORoANiUAMhUgMSoQMhUgNhUgOSoQNhUgOhQANSgANCgYNCQoNCgYMAAMOgUIMAAANSgQOhQgOFEgNFkANqVgOpVAOqFQNpQQOWggNCgYNCQoNCgYNBIqsAAAOFAgNKAQNAhANSCQNChANQCQMChAORSQOChAMQSQNiRANRSQMiRAOmmgNgBgOkmgMhBgNkmgMgBgNlmgMghgNmGAMihgMkGANZZQMlWAMZZQOlWAOYZQMkWQOYZQNkWQNaJQOKkgMZJAOKkgMZJAOZgQNmQgNCgYNCQoNCgYMAAPM="
                    },
                    "spm": 850,
                    "colors": [
                      "#0b7a4b",
                      "#1f5c3f",
                      "#6d4a33"
                    ],
                    "hoop": "100x100"
                  }
                },
                "example2": {
                  "summary": "Hand-written stitch list with a jump",
                  "value": {
                    "stitches": "[[0,0],[5,0],[10,0],[10,5],[10,10],[40,10,\"jump\"],[40,10],[45,12],[50,10]]",
                    "spm": 600
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadsmithDesignStatsResult"
                }
              }
            }
          },
          "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/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "summary": "Multi-tool workflows",
        "responses": {
          "200": {
            "description": "Workflow list.",
            "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"
                }
              }
            }
          }
        }
      },
      "ThreadsmithDigitizeInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "source": {
            "title": "What to digitize",
            "type": "string",
            "enum": [
              "shape",
              "path",
              "svg",
              "text",
              "project"
            ],
            "default": "shape"
          },
          "shape": {
            "title": "Shape",
            "type": "string",
            "enum": [
              "ellipse",
              "rect",
              "star",
              "hexagon",
              "heart"
            ],
            "default": "ellipse"
          },
          "widthMm": {
            "title": "Shape width",
            "type": "number",
            "minimum": 2,
            "maximum": 400,
            "default": 40,
            "x-unit": "mm"
          },
          "heightMm": {
            "title": "Shape height",
            "type": "number",
            "minimum": 2,
            "maximum": 400,
            "default": 30,
            "x-unit": "mm"
          },
          "pathD": {
            "title": "Path data",
            "description": "SVG path syntax in millimetres (y down, 0,0 = hoop centre). Closed paths (Z) become fills, open paths become lines.",
            "type": "string",
            "maxLength": 200000
          },
          "svg": {
            "title": "SVG markup",
            "description": "paths, rect, circle, ellipse, line, polyline, polygon and transforms are imported; text, images and gradients are skipped with a warning.",
            "type": "string",
            "maxLength": 1000000
          },
          "fitWidth": {
            "title": "Scale SVG to width",
            "description": "Optional. Default: the SVG’s own physical size (px at 96 dpi).",
            "type": "number",
            "minimum": 5,
            "maximum": 400,
            "x-unit": "mm"
          },
          "text": {
            "title": "Text",
            "description": "A–Z, a–z, 0–9 and . , ! ? - _ ' \" : ; ( ) / & + # * =",
            "type": "string",
            "maxLength": 200
          },
          "textSize": {
            "title": "Letter height (capitals)",
            "type": "number",
            "minimum": 3,
            "maximum": 200,
            "default": 12,
            "x-unit": "mm"
          },
          "font": {
            "title": "Font style",
            "type": "string",
            "enum": [
              "single",
              "outline"
            ],
            "default": "single"
          },
          "project": {
            "title": "Project JSON",
            "description": "A .threadsmith.json project (see the app’s project schema).",
            "type": "string",
            "maxLength": 2000000
          },
          "stitchType": {
            "title": "Stitch type",
            "type": "string",
            "enum": [
              "auto",
              "running",
              "satin",
              "tatami",
              "contour",
              "motif"
            ],
            "default": "auto"
          },
          "angle": {
            "title": "Fill angle",
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "default": 45,
            "x-unit": "°"
          },
          "spacing": {
            "title": "Row spacing / satin density",
            "type": "number",
            "minimum": 0.2,
            "maximum": 3,
            "default": 0.4,
            "x-unit": "mm"
          },
          "stitchLength": {
            "title": "Stitch length",
            "type": "number",
            "minimum": 1,
            "maximum": 7,
            "default": 3.5,
            "x-unit": "mm"
          },
          "satinWidth": {
            "title": "Satin column width (lines)",
            "type": "number",
            "minimum": 0.8,
            "maximum": 10,
            "default": 3,
            "x-unit": "mm"
          },
          "pullComp": {
            "title": "Pull compensation",
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.2,
            "x-unit": "mm"
          },
          "underlay": {
            "title": "Underlay",
            "type": "string",
            "enum": [
              "auto",
              "none",
              "center",
              "edge",
              "center-edge",
              "edge-tatami",
              "zigzag",
              "full"
            ],
            "default": "auto"
          },
          "color": {
            "title": "Thread",
            "description": "#rrggbb or a built-in thread name such as ruby, emerald, gold, navy.",
            "type": "string",
            "maxLength": 30,
            "default": "sapphire"
          },
          "hoop": {
            "title": "Hoop",
            "type": "string",
            "enum": [
              "50x50",
              "100x100",
              "130x180",
              "150x150",
              "160x260",
              "200x200",
              "200x300",
              "240x150",
              "360x200"
            ],
            "default": "130x180"
          },
          "spm": {
            "title": "Machine speed",
            "type": "integer",
            "minimum": 100,
            "maximum": 1500,
            "default": 700,
            "x-unit": "stitches/min"
          },
          "includeStitches": {
            "title": "Include the stitch list in the result",
            "description": "Adds result.stitchList ([x, y, type] in mm) for chaining into threadsmith-stitch-file.",
            "type": "boolean",
            "default": false
          }
        },
        "required": []
      },
      "ThreadsmithDigitizeResult": {
        "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",
            "required": [
              "objects",
              "order",
              "stitches",
              "jumps",
              "trims",
              "colorChanges",
              "widthMm",
              "heightMm",
              "sewingMinutes",
              "sewingTime",
              "topThreadM",
              "bobbinThreadM",
              "longestStitchMm",
              "hoop",
              "colors",
              "density",
              "warnings",
              "project",
              "openInApp"
            ],
            "properties": {
              "objects": {
                "type": "integer"
              },
              "order": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "stitches": {
                "type": "integer"
              },
              "jumps": {
                "type": "integer"
              },
              "trims": {
                "type": "integer"
              },
              "colorChanges": {
                "type": "integer"
              },
              "widthMm": {
                "type": "number"
              },
              "heightMm": {
                "type": "number"
              },
              "sewingMinutes": {
                "type": "number"
              },
              "sewingTime": {
                "type": "string"
              },
              "topThreadM": {
                "type": "number"
              },
              "bobbinThreadM": {
                "type": "number"
              },
              "longestStitchMm": {
                "type": "number"
              },
              "hoop": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "fits": {
                    "type": "boolean"
                  },
                  "overflowMm": {
                    "type": "number"
                  }
                },
                "required": [
                  "id",
                  "fits",
                  "overflowMm"
                ]
              },
              "colors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "hex": {
                      "type": "string"
                    },
                    "stitches": {
                      "type": "integer"
                    },
                    "topThreadM": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "name",
                    "hex",
                    "stitches",
                    "topThreadM"
                  ]
                }
              },
              "density": {
                "type": "object",
                "properties": {
                  "maxLayers": {
                    "type": "number"
                  },
                  "areaOverMm2": {
                    "type": "number"
                  },
                  "threshold": {
                    "type": "number"
                  }
                },
                "required": [
                  "maxLayers",
                  "areaOverMm2",
                  "threshold"
                ]
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "project": {
                "type": "object",
                "description": "Normalised Threadsmith project (open it in the app or pass it to threadsmith-stitch-file)."
              },
              "openInApp": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Threadsmith app link with the project in the #project= fragment (null when the project is too large for a URL)."
              },
              "stitchList": {
                "type": "array",
                "items": {
                  "type": "array"
                },
                "description": "[x, y, type] in mm, y down; type = stitch | jump | trim | color | end."
              }
            }
          },
          "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"
              }
            }
          }
        }
      },
      "ThreadsmithStitchFileInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "format": {
            "title": "File format",
            "type": "string",
            "enum": [
              "dst",
              "exp"
            ],
            "default": "dst"
          },
          "stitches": {
            "title": "Stitch list (JSON)",
            "description": "[[x, y], [x, y, \"jump\"], [x, y, \"trim\"], [x, y, \"color\"], …] in mm (y down), or {\"cmds\": […], \"colors\": [{\"name\", \"hex\"}]} from threadsmith-digitize.",
            "type": "string",
            "maxLength": 8000000
          },
          "project": {
            "title": "Or: Threadsmith project JSON",
            "description": "If given, the project is digitized first.",
            "type": "string",
            "maxLength": 2000000
          },
          "label": {
            "title": "Design label (DST header, 8 characters)",
            "type": "string",
            "maxLength": 16,
            "default": "DESIGN"
          }
        },
        "required": []
      },
      "ThreadsmithStitchFileResult": {
        "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",
            "required": [
              "format",
              "filename",
              "bytes",
              "records",
              "stitchRecords",
              "jumpRecords",
              "colorChanges",
              "splitStitches",
              "verified",
              "maxErrorMm",
              "sha256",
              "artifactUrl",
              "base64",
              "firstRecords"
            ],
            "properties": {
              "format": {
                "type": "string",
                "enum": [
                  "dst",
                  "exp"
                ]
              },
              "filename": {
                "type": "string"
              },
              "bytes": {
                "type": "integer"
              },
              "records": {
                "type": "integer"
              },
              "stitchRecords": {
                "type": "integer"
              },
              "jumpRecords": {
                "type": "integer"
              },
              "colorChanges": {
                "type": "integer"
              },
              "splitStitches": {
                "type": "integer",
                "description": "Stitches longer than the format limit that were split into several records."
              },
              "verified": {
                "type": "boolean",
                "description": "The written file was decoded again and every stitch landed within 0.08 mm of the input."
              },
              "maxErrorMm": {
                "type": "number"
              },
              "sha256": {
                "type": "string"
              },
              "artifactUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "base64": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "File content (only when no artifact store is available or the file is ≤ 64 KiB)."
              },
              "header": {
                "type": "object"
              },
              "firstRecords": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          },
          "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"
              }
            }
          }
        }
      },
      "ThreadsmithDesignStatsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "file": {
            "title": "Embroidery file",
            "description": "Tajima .dst or Melco .exp. Upload as {filename, contentType?, contentBase64}. Max 6144 KiB. Accepts .dst,.exp.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "stitches": {
            "title": "Or: stitch list (JSON)",
            "description": "Same format as threadsmith-stitch-file.",
            "type": "string",
            "maxLength": 8000000
          },
          "colors": {
            "title": "Thread colours in sewing order",
            "description": "Optional #rrggbb per colour block (DST/EXP files store no colours).",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 60
          },
          "spm": {
            "title": "Machine speed",
            "type": "integer",
            "minimum": 100,
            "maximum": 1500,
            "default": 700,
            "x-unit": "stitches/min"
          },
          "trimSeconds": {
            "title": "Time per trim",
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 4,
            "x-unit": "s"
          },
          "colorSeconds": {
            "title": "Time per colour change",
            "type": "number",
            "minimum": 0,
            "maximum": 600,
            "default": 20,
            "x-unit": "s"
          },
          "hoop": {
            "title": "Hoop",
            "type": "string",
            "enum": [
              "50x50",
              "100x100",
              "130x180",
              "150x150",
              "160x260",
              "200x200",
              "200x300",
              "240x150",
              "360x200"
            ],
            "default": "130x180"
          },
          "densityThreshold": {
            "title": "Too-dense limit",
            "description": "Thread coverage per 1 mm cell above which the design is flagged (2.6 ≈ underlay + top + some overlap).",
            "type": "number",
            "minimum": 1,
            "maximum": 6,
            "default": 2.6,
            "x-unit": "thread layers"
          }
        },
        "required": []
      },
      "ThreadsmithDesignStatsResult": {
        "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",
            "required": [
              "format",
              "stitches",
              "jumps",
              "trims",
              "colorChanges",
              "widthMm",
              "heightMm",
              "sewingMinutes",
              "sewingTime",
              "topThreadM",
              "bobbinThreadM",
              "longestStitchMm",
              "hoop",
              "colors",
              "density",
              "hotspots",
              "warnings"
            ],
            "properties": {
              "format": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "header": {
                "type": "object"
              },
              "stitches": {
                "type": "integer"
              },
              "jumps": {
                "type": "integer"
              },
              "trims": {
                "type": "integer"
              },
              "colorChanges": {
                "type": "integer"
              },
              "widthMm": {
                "type": "number"
              },
              "heightMm": {
                "type": "number"
              },
              "sewingMinutes": {
                "type": "number"
              },
              "sewingTime": {
                "type": "string"
              },
              "topThreadM": {
                "type": "number"
              },
              "bobbinThreadM": {
                "type": "number"
              },
              "longestStitchMm": {
                "type": "number"
              },
              "hoop": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "fits": {
                    "type": "boolean"
                  },
                  "overflowMm": {
                    "type": "number"
                  }
                },
                "required": [
                  "id",
                  "fits",
                  "overflowMm"
                ]
              },
              "colors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "hex": {
                      "type": "string"
                    },
                    "stitches": {
                      "type": "integer"
                    },
                    "topThreadM": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "name",
                    "hex",
                    "stitches",
                    "topThreadM"
                  ]
                }
              },
              "density": {
                "type": "object",
                "properties": {
                  "maxLayers": {
                    "type": "number"
                  },
                  "areaOverMm2": {
                    "type": "number"
                  },
                  "threshold": {
                    "type": "number"
                  }
                },
                "required": [
                  "maxLayers",
                  "areaOverMm2",
                  "threshold"
                ]
              },
              "hotspots": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    },
                    "layers": {
                      "type": "number"
                    }
                  }
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "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"
              }
            }
          }
        }
      }
    }
  }
}