{
  "openapi": "3.1.1",
  "info": {
    "title": "Threadsmith Digitizer API",
    "version": "1.0.0",
    "summary": "Turn shapes, SVG paths, text or a whole project into embroidery stitches with stats and preview.",
    "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://threadsmithdigitize.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "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/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"
                }
              }
            }
          }
        }
      },
      "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"
              }
            }
          }
        }
      }
    }
  }
}