{
  "openapi": "3.1.0",
  "info": {
    "title": "NestSolver API",
    "version": "1.0.0",
    "description": "Plan cuts and loads from scripts: linear and sheet jobs solve at once; true-shape nests and loads run in the background. Every call but this document needs a key, made on the portal's API keys page with any account, free included, and sent as \"Authorization: Bearer <key>\", and acts in the key's workspace under its plan's limits, the same as on the web and over MCP. Linear and sheet solves are unlimited on every plan and never touch the pool. Every nest and load search on a paid plan draws on one monthly full-speed pool shared by the web, the API, and MCP; once it's spent, searches keep running in a slower lane, never failing and never billed extra; on the Works plan a nest or load past the pool may ask for the deep lane instead, which keeps its full search time and starts once the workspace has nothing else waiting or running. Free plans 5 loads a month at full speed; the sixth is refused with a 403 naming the Bench plan. Solves and job starts count toward 30 a minute per workspace (6 on Free); checks, status, plans, exports, usage, and the lists of saved parts and spaces are free, up to 300 calls a minute (60 on Free). Each address may make 600 calls a minute in all. A retry with the same Idempotency-Key never counts twice against the workspace's limits."
  },
  "servers": [
    {
      "url": "https://api.nestsolver.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/linear/validate": {
      "post": {
        "summary": "Check a linear cut list",
        "description": "Check without solving: free.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Send the same key and body to get the first answer again, for a day; nothing new starts.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinearRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Whether the cut list is valid, and its problems.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Validation"
                }
              }
            },
            "headers": {
              "idempotent-replayed": {
                "description": "\"true\" when this is the first answer to an earlier request with the same Idempotency-Key and body, given again.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An Idempotency-Key that isn't 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The plan doesn't include this; `upgrade` names the plan that does.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "413": {
            "description": "A body over 4 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "The request breaks a rule, each problem naming its field, or its Idempotency-Key was used for another request, or it holds more rows or longer text than the API takes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/linear/solves": {
      "post": {
        "summary": "Solve a linear cut list",
        "description": "Solve at once in one of the workspace's solve slots.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Send the same key and body to get the first answer again, for a day; nothing new starts.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinearRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinearReport"
                }
              }
            },
            "headers": {
              "idempotent-replayed": {
                "description": "\"true\" when this is the first answer to an earlier request with the same Idempotency-Key and body, given again.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An Idempotency-Key that isn't 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The plan doesn't include this; `upgrade` names the plan that does.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "413": {
            "description": "A body over 4 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "The request breaks a rule, each problem naming its field, or its Idempotency-Key was used for another request, or it holds more rows or longer text than the API takes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 30 solves and job starts this minute (6 on Free), every one of the workspace's solve slots is busy, or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sheet/validate": {
      "post": {
        "summary": "Check a sheet cut list",
        "description": "Check without solving: free.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Send the same key and body to get the first answer again, for a day; nothing new starts.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SheetRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Whether the cut list is valid, and its problems.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Validation"
                }
              }
            },
            "headers": {
              "idempotent-replayed": {
                "description": "\"true\" when this is the first answer to an earlier request with the same Idempotency-Key and body, given again.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An Idempotency-Key that isn't 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The plan doesn't include this; `upgrade` names the plan that does.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "413": {
            "description": "A body over 4 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "The request breaks a rule, each problem naming its field, or its Idempotency-Key was used for another request, or it holds more rows or longer text than the API takes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sheet/solves": {
      "post": {
        "summary": "Solve a sheet cut list",
        "description": "Solve at once in one of the workspace's solve slots.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Send the same key and body to get the first answer again, for a day; nothing new starts.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SheetRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SheetReport"
                }
              }
            },
            "headers": {
              "idempotent-replayed": {
                "description": "\"true\" when this is the first answer to an earlier request with the same Idempotency-Key and body, given again.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An Idempotency-Key that isn't 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The plan doesn't include this; `upgrade` names the plan that does.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "413": {
            "description": "A body over 4 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "The request breaks a rule, each problem naming its field, or its Idempotency-Key was used for another request, or it holds more rows or longer text than the API takes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 30 solves and job starts this minute (6 on Free), every one of the workspace's solve slots is busy, or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/nests": {
      "post": {
        "summary": "Start a nest",
        "description": "Queue a true-shape nest; it runs in the background. Needs the Bench plan; remnants and sheet defects need the Shop plan, and the deep lane the Works plan, each refused with a 403 on a plan without it.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Send the same key and body to get the first answer again, for a day; nothing new starts.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NestRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued; watch its status link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Started"
                }
              }
            },
            "headers": {
              "idempotent-replayed": {
                "description": "\"true\" when this is the first answer to an earlier request with the same Idempotency-Key and body, given again.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An Idempotency-Key that isn't 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The plan doesn't include this; `upgrade` names the plan that does.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "413": {
            "description": "A body over 4 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "The request breaks a rule, each problem naming its field, or its Idempotency-Key was used for another request, or it holds more rows or longer text than the API takes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 30 solves and job starts this minute (6 on Free), the workspace already has 5 nests waiting, or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/loads": {
      "post": {
        "summary": "Start a load",
        "description": "Queue a load plan; it runs in the background. Every plan plans loads: Free 5 a month (failed loads don't count), paid plans within their monthly pool. Three stages, the lowest-cost target and the deep lane need the Works plan, each refused with a 403 on a plan without it.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Send the same key and body to get the first answer again, for a day; nothing new starts.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoadRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued; watch its status link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Started"
                }
              }
            },
            "headers": {
              "idempotent-replayed": {
                "description": "\"true\" when this is the first answer to an earlier request with the same Idempotency-Key and body, given again.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "An Idempotency-Key that isn't 1 to 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The plan doesn't include this, or a Free workspace started its 5 loads this month; `upgrade` names the plan that takes it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "413": {
            "description": "A body over 4 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "The request breaks a rule, each problem naming its field, or its Idempotency-Key was used for another request, or it holds more rows or longer text than the API takes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 30 solves and job starts this minute (6 on Free), the workspace already has 5 loads waiting, or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "summary": "Check a nest or load",
        "description": "Where it stands: free.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Its status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobStatus"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such nest or load in the key's workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plans/{id}": {
      "get": {
        "summary": "Read a finished plan",
        "description": "A nest's or load's plan and its exports: free.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such finished plan in the key's workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plans/{id}/exports/{format}": {
      "get": {
        "summary": "Export a finished plan",
        "description": "A nest's DXF, one sheet's SVG (pick it with ?sheet, from 0), part labels, or the print packet; a load's print packet, CSV or Excel workbook (each item with its loading step, each space's centre of gravity, and what didn't fit), or its pallet labels, which need the Works plan: free. A load has no DXF or SVG.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "dxf",
                "svg",
                "labels",
                "packet",
                "csv",
                "excel"
              ]
            }
          },
          {
            "name": "sheet",
            "in": "query",
            "required": false,
            "description": "Which sheet an SVG draws, from 0.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/dxf": {
                "schema": {
                  "type": "string"
                }
              },
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/vnd.ms-excel": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "A load's pallet labels need the Works plan; `upgrade` names it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "404": {
            "description": "No such plan in the key's workspace, or no such export of it: a load has no DXF or SVG, and an SVG no such sheet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/usage": {
      "get": {
        "summary": "Read this month's search time",
        "description": "The workspace's full-speed pool, used and left, the next lane, and on Free the loads started and left this month: free.",
        "responses": {
          "200": {
            "description": "The usage.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/parts": {
      "get": {
        "summary": "List saved parts",
        "description": "The workspace's nest part library, newest first: each part's id, label, size and holes, to name in a nest's `library`: free.",
        "parameters": [
          {
            "name": "unit",
            "in": "query",
            "required": false,
            "description": "The unit sizes are written in. Without it, the unit the key's owner picked on the web.",
            "schema": {
              "type": "string",
              "enum": [
                "mm",
                "cm",
                "m",
                "in"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The saved parts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Parts"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "Nesting needs the Bench plan; `upgrade` names it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "422": {
            "description": "A unit outside its choices, named at its field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/spaces": {
      "get": {
        "summary": "List saved spaces",
        "description": "The workspace's saved cartons, pallets and vehicles, each as a space row a load takes, with its id: free.",
        "parameters": [
          {
            "name": "unit",
            "in": "query",
            "required": false,
            "description": "The unit sizes are written in. Without it, the unit the key's owner picked on the web.",
            "schema": {
              "type": "string",
              "enum": [
                "mm",
                "cm",
                "m",
                "in"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The saved spaces.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Spaces"
                }
              }
            }
          },
          "401": {
            "description": "No live API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            },
            "headers": {
              "www-authenticate": {
                "description": "How to authenticate: a bearer token.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "A unit outside its choices, named at its field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "429": {
            "description": "More than 300 free calls this minute (60 on Free), or more than 600 calls from this address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "summary": "This document",
        "security": [],
        "responses": {
          "200": {
            "description": "The OpenAPI document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "description": "Too many calls from this address this minute; wait a moment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          },
          "500": {
            "description": "The document couldn't be read; try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Errors"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "LinearZone": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "description": "Where the zone starts along the stock.",
            "maxLength": 40
          },
          "end": {
            "type": "string",
            "description": "Where it ends.",
            "maxLength": 40
          },
          "keep": {
            "type": "boolean",
            "description": "Whether the span is kept as a drop."
          }
        },
        "required": [
          "start",
          "end",
          "keep"
        ],
        "description": "Fields it doesn't name are ignored."
      },
      "LinearStock": {
        "type": "object",
        "properties": {
          "length": {
            "type": "string",
            "description": "The stock length.",
            "maxLength": 40
          },
          "qty": {
            "type": "string",
            "description": "How many; blank for as many as needed.",
            "maxLength": 40
          },
          "minimum_drop": {
            "type": "string",
            "description": "The shortest drop worth keeping.",
            "maxLength": 40
          },
          "price": {
            "type": "string",
            "description": "One length's price in dollars.",
            "maxLength": 40,
            "default": ""
          },
          "material_key": {
            "type": "string",
            "description": "A material name; parts with one use only its stock. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "maxLength": 80,
            "default": ""
          },
          "zones": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearZone"
            },
            "description": "Blocked zones: defects, bad sections, or edge trims. At most 100 a stock.",
            "maxItems": 100
          }
        },
        "required": [
          "length",
          "qty",
          "minimum_drop",
          "zones"
        ],
        "description": "Fields it doesn't name are ignored."
      },
      "LinearPart": {
        "type": "object",
        "properties": {
          "length": {
            "type": "string",
            "description": "The part's length.",
            "maxLength": 40
          },
          "quantity": {
            "type": "string",
            "description": "How many: at most 20,000.",
            "maxLength": 40
          },
          "label": {
            "type": "string",
            "description": "A name for the part.",
            "maxLength": 20
          },
          "priority": {
            "type": "boolean",
            "description": "A rush part, placed first. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "default": false
          },
          "material_key": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "maxLength": 80,
            "default": ""
          },
          "finish_left": {
            "type": "boolean",
            "description": "Finish the left end. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "default": false
          },
          "finish_right": {
            "type": "boolean",
            "description": "Finish the right end. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "default": false
          },
          "finish_label": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "maxLength": 40,
            "default": ""
          },
          "miter_left": {
            "type": "string",
            "description": "The left end's angle in degrees, 1 to 179; 90 is square. Counts only with miter_enabled.",
            "maxLength": 40,
            "default": "90"
          },
          "miter_right": {
            "type": "string",
            "description": "The right end's angle in degrees, 1 to 179; 90 is square. Counts only with miter_enabled.",
            "maxLength": 40,
            "default": "90"
          }
        },
        "required": [
          "length",
          "quantity",
          "label"
        ],
        "description": "Fields it doesn't name are ignored."
      },
      "LinearRequest": {
        "type": "object",
        "description": "A linear cut list, field for field as the web optimizer saves it. Fields it doesn't name are ignored.",
        "properties": {
          "v": {
            "type": "integer",
            "description": "The form's version: 1.",
            "default": 1
          },
          "unit": {
            "type": "string",
            "description": "The unit lengths are typed in.",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ]
          },
          "kerf": {
            "type": "string",
            "description": "The saw's kerf.",
            "maxLength": 40
          },
          "stock_mode": {
            "type": "string",
            "description": "Lengths of stock, or a roll cut to length. \"roll\" needs the Bench plan; asked for on another plan, the request is refused with a 403. Anything but \"roll\" reads as \"stock\".",
            "enum": [
              "stock",
              "roll"
            ],
            "default": "stock"
          },
          "miter_enabled": {
            "type": "boolean",
            "description": "Whether parts' miter angles count. On every plan.",
            "default": false
          },
          "material_width": {
            "type": "string",
            "description": "The stock's width, for miters.",
            "maxLength": 40,
            "default": ""
          },
          "target": {
            "type": "string",
            "description": "What the plan aims for. \"cost\" needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "enum": [
              "bars",
              "waste",
              "drop",
              "cost"
            ]
          },
          "strict": {
            "type": "boolean",
            "description": "Fail when a part is left out."
          },
          "stocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearStock"
            },
            "maxItems": 100,
            "description": "The stock lengths to cut from. At most 100 rows."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearPart"
            },
            "maxItems": 1000,
            "description": "The parts to cut. At most 1000 rows, and 20,000 pieces in all."
          }
        },
        "required": [
          "unit",
          "kerf",
          "target",
          "strict",
          "stocks",
          "parts"
        ]
      },
      "SheetDefect": {
        "type": "object",
        "description": "A defect region: its bottom-left corner and size. Fields it doesn't name are ignored.",
        "properties": {
          "x": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "y": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          }
        }
      },
      "SheetStock": {
        "type": "object",
        "properties": {
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "qty": {
            "type": "string",
            "description": "How many; blank for as many as needed.",
            "maxLength": 40
          },
          "price": {
            "type": "string",
            "description": "One sheet's price in dollars; blank for none.",
            "maxLength": 40
          },
          "material_key": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "maxLength": 80
          },
          "trim_left": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "trim_right": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "trim_bottom": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "trim_top": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "defects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetDefect"
            },
            "maxItems": 50,
            "description": "Defect regions on the sheet. At most 50 a sheet."
          },
          "grain": {
            "type": "string",
            "description": "Which way the sheet's grain runs: along its width, along its height, or none. A part whose grain matters lines up with it; on a sheet with no grain any part may turn.",
            "enum": [
              "width",
              "height",
              "none"
            ],
            "default": "width"
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "SheetPart": {
        "type": "object",
        "properties": {
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "quantity": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 40
          },
          "label": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none.",
            "maxLength": 20
          },
          "grain": {
            "type": "boolean",
            "description": "The part's grain matters and runs along its width, so it lines up with each sheet's grain. Kept from turning on a sheet grained along its width.",
            "default": false
          },
          "priority": {
            "type": "boolean",
            "description": "A rush part. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "default": false
          },
          "material_key": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none. Needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "maxLength": 80
          },
          "grain_along": {
            "type": "string",
            "description": "Which way the part's grain runs: along its width or its height. Either one means its grain matters, as grain does; blank leaves it to grain.",
            "enum": [
              "",
              "width",
              "height"
            ],
            "default": ""
          },
          "band": {
            "type": "string",
            "description": "The name of one of band_types to band the part's edges with; blank for none. Needed when band_long or band_short is above 0.",
            "maxLength": 40,
            "default": ""
          },
          "band_long": {
            "type": "integer",
            "description": "How many of the part's long edges are banded. The long edges run along its longer typed side (its width when square).",
            "enum": [
              0,
              1,
              2
            ],
            "default": 0
          },
          "band_short": {
            "type": "integer",
            "description": "How many of the part's short edges are banded.",
            "enum": [
              0,
              1,
              2
            ],
            "default": 0
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "SheetRequest": {
        "type": "object",
        "description": "A sheet cut list, field for field as the web's sheet form saves it. Fields it doesn't name are ignored.",
        "properties": {
          "v": {
            "type": "integer",
            "description": "The form's version: 1.",
            "default": 1
          },
          "unit": {
            "type": "string",
            "description": "The unit lengths are typed in.",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ]
          },
          "kerf": {
            "type": "string",
            "description": "The saw's kerf; blank for none.",
            "maxLength": 40,
            "default": ""
          },
          "mode": {
            "type": "string",
            "description": "A panel saw's edge-to-edge cuts, or a CNC router's free placement. Anything but \"free\" reads as \"guillotine\".",
            "enum": [
              "guillotine",
              "free"
            ],
            "default": "guillotine"
          },
          "stages": {
            "type": "string",
            "description": "A panel saw's cut stages; anything else reads as 3.",
            "enum": [
              "2",
              "3"
            ],
            "default": "3"
          },
          "minimum_leftover": {
            "type": "string",
            "description": "The smallest leftover worth keeping.",
            "maxLength": 40,
            "default": ""
          },
          "target": {
            "type": "string",
            "description": "What the plan aims for. \"cost\" needs the Bench plan; asked for on another plan, the request is refused with a 403.",
            "enum": [
              "sheets",
              "waste",
              "leftover",
              "cost"
            ],
            "default": "sheets"
          },
          "strict": {
            "type": "boolean",
            "description": "Fail when a part is left out.",
            "default": false
          },
          "sheets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetStock"
            },
            "maxItems": 100,
            "description": "The sheets to cut from. At most 100 rows."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetPart"
            },
            "maxItems": 1000,
            "description": "The parts to cut. At most 1000 rows, and 20,000 pieces in all."
          },
          "band_types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetBandType"
            },
            "maxItems": 20,
            "description": "The edge bands parts may take. At most 20.",
            "default": []
          },
          "part_sizes": {
            "type": "string",
            "description": "Whether part sizes are finished sizes, edge band included, so the band's thickness comes off each banded edge to give the size cut; or the sizes cut from the sheet, with the band added on after.",
            "enum": [
              "finished",
              "cut"
            ],
            "default": "finished"
          },
          "band_waste": {
            "type": "string",
            "description": "The percent of edge band to order on top of what the edges measure; blank for none.",
            "maxLength": 40,
            "default": ""
          }
        },
        "required": [
          "unit",
          "sheets",
          "parts"
        ]
      },
      "NestSheet": {
        "type": "object",
        "properties": {
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "quantity": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "price": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "material": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "defects": {
            "type": "string",
            "description": "Defects as \"x y width height\" groups split by semicolons. Needs the Shop plan; asked for on another plan, the job is refused with a 403."
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "NestLibraryRow": {
        "type": "object",
        "properties": {
          "quantity": {
            "type": "string",
            "description": "How many to nest; 0 or blank leaves the part out."
          },
          "rotation": {
            "type": "string",
            "description": "How the part may turn. Anything else reads as \"quarter\".",
            "enum": [
              "fixed",
              "quarter",
              "eighth",
              "any"
            ],
            "default": "quarter"
          },
          "mirror": {
            "type": "boolean",
            "default": false,
            "description": "The part may be flipped over."
          },
          "rush": {
            "type": "boolean",
            "default": false,
            "description": "A rush part, placed first."
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "NestTypedRow": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "rectangle",
              "circle"
            ],
            "description": "A rectangle by width and height, or a circle by diameter. Anything but \"circle\" reads as \"rectangle\".",
            "default": "rectangle"
          },
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "diameter": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "quantity": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "label": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "rotation": {
            "type": "string",
            "enum": [
              "fixed",
              "quarter",
              "eighth",
              "any"
            ],
            "description": "How the part may turn. Anything else reads as \"quarter\".",
            "default": "quarter"
          },
          "mirror": {
            "type": "boolean",
            "default": false,
            "description": "The part may be flipped over."
          },
          "rush": {
            "type": "boolean",
            "default": false,
            "description": "A rush part, placed first."
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "NestRequest": {
        "type": "object",
        "description": "A true-shape nest, field for field as the web's nest form sends it. Any nest needs the Bench plan; on another plan the job is refused with a 403. Fields it doesn't name are ignored.",
        "properties": {
          "unit": {
            "type": "string",
            "description": "The unit lengths are typed in.",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ]
          },
          "part_spacing": {
            "type": "string",
            "description": "The gap kept between parts, in unit; blank is 0. It holds inside holes too: smaller parts nest in bigger parts' holes and keep this gap from the hole's edge."
          },
          "edge_spacing": {
            "type": "string",
            "description": "The strip kept clear along all four edges of every sheet, in unit, for clamps or a rough edge; blank is 0."
          },
          "target": {
            "type": "string",
            "enum": [
              "fewest",
              "waste",
              "cost"
            ],
            "description": "What the nest aims for: the fewest sheets, the least waste, or the lowest cost. Anything else reads as \"fewest\".",
            "default": "fewest"
          },
          "strict": {
            "type": "boolean",
            "description": "Fail when a part is left out.",
            "default": false
          },
          "deep": {
            "type": "boolean",
            "description": "Once the month's full-speed pool is spent, search in the deep lane rather than the slower one: the plan's whole search time, starting once the workspace has nothing else waiting or running. Within the pool it changes nothing. Needs the Works plan; asked for on another plan, the job is refused with a 403.",
            "default": false
          },
          "sheets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NestSheet"
            },
            "maxItems": 200,
            "description": "Sheet rows. A row with no width and no height is blank and is dropped. At most 200 rows."
          },
          "library": {
            "type": "object",
            "description": "Saved parts by id: the workspace's part library, which GET /v1/parts lists with each part's id. A part whose quantity is blank or 0 is left out. At most 200 parts.",
            "additionalProperties": {
              "$ref": "#/components/schemas/NestLibraryRow"
            },
            "maxProperties": 200
          },
          "typed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NestTypedRow"
            },
            "description": "Rectangles and circles typed by size. A row with no size (no width and no height, or no diameter for a circle) is blank and is dropped. At most 200 rows.",
            "maxItems": 200
          },
          "remnants": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ids of the workspace's nest remnants, sheets saved from an earlier nest, to nest on after the typed sheets. An id sent twice is used once. Needs the Shop plan; asked for on another plan, the job is refused with a 403. At most 200 ids.",
            "maxItems": 200
          }
        },
        "required": [
          "unit"
        ]
      },
      "LoadAxles": {
        "type": "object",
        "properties": {
          "on": {
            "type": "boolean",
            "description": "Check this semi-trailer's axle loads.",
            "default": false
          },
          "tractor_weight": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "empty_trailer_weight": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "front_axle_to_middle_axle": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "front_axle_to_tractor_centre": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "front_axle_to_harness": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "harness_to_rear_axle": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "trailer_centre_to_rear_axle": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "trailer_front_to_harness": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "middle_axle_limit": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "rear_axle_limit": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "LoadSpace": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "carton",
              "pallet",
              "vehicle"
            ],
            "description": "What the space is. Anything else reads as \"pallet\".",
            "default": "pallet"
          },
          "label": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "length": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "own_weight": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "weight_limit": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "quantity": {
            "type": "string",
            "description": "Blank for as many as needed."
          },
          "price": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "loading": {
            "type": "string",
            "description": "Where its doors are. Anything but \"side\" reads as \"back\".",
            "enum": [
              "back",
              "side"
            ],
            "default": "back"
          },
          "density": {
            "type": "string",
            "description": "The floor load limit: kg a square metre, or lb a square foot for inches."
          },
          "wall": {
            "type": "string",
            "description": "A carton's board thickness."
          },
          "deck": {
            "type": "string",
            "description": "A pallet's deck height."
          },
          "carries": {
            "type": "boolean",
            "description": "Others may stack on it once filled.",
            "default": false
          },
          "most_in_stack": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "most_above": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "axles": {
            "$ref": "#/components/schemas/LoadAxles"
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "LoadCargo": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "length": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "width": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "height": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "weight": {
            "type": "string",
            "description": "Kilograms, or pounds for inches."
          },
          "quantity": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "turning": {
            "type": "string",
            "enum": [
              "upright",
              "any"
            ],
            "description": "Whether it must stay upright or may turn any way. Anything but \"any\" reads as \"upright\".",
            "default": "upright"
          },
          "carries": {
            "type": "boolean",
            "default": true,
            "description": "Others may stack on it."
          },
          "most_in_stack": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "most_above": {
            "type": "string",
            "description": "Typed exactly as on the web form; blank for none."
          },
          "nesting": {
            "type": "string",
            "description": "How far each item sinks into the one below."
          },
          "stop": {
            "type": "string",
            "description": "The delivery it comes off at, 1 first."
          },
          "keep_apart": {
            "type": "string",
            "description": "A name it shares stacks only with."
          }
        },
        "description": "Fields it doesn't name are ignored."
      },
      "LoadRequest": {
        "type": "object",
        "description": "A load, field for field as the web's load form sends it. Every plan takes a load; on Free, a sixth load in a month is refused with a 403. Fields it doesn't name are ignored.",
        "properties": {
          "unit": {
            "type": "string",
            "description": "The unit lengths are typed in.",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ]
          },
          "stages": {
            "type": "string",
            "enum": [
              "one",
              "three"
            ],
            "description": "One stage of spaces, or cartons into pallets into vehicles. \"three\" needs the Works plan; asked for on another plan, the job is refused with a 403. Anything but \"three\" reads as \"one\".",
            "default": "one"
          },
          "mode": {
            "type": "string",
            "enum": [
              "stacked",
              "free"
            ],
            "description": "A one-stage load's placing: stacked in layers, or free. Anything but \"free\" reads as \"stacked\".",
            "default": "stacked"
          },
          "target": {
            "type": "string",
            "enum": [
              "fewest",
              "cost"
            ],
            "description": "What the plan aims for: the fewest spaces, or the lowest cost. \"cost\" needs the Works plan; asked for on another plan, the job is refused with a 403. Anything but \"cost\" reads as \"fewest\".",
            "default": "fewest"
          },
          "strict": {
            "type": "boolean",
            "description": "Fail when an item is left out.",
            "default": false
          },
          "deep": {
            "type": "boolean",
            "description": "Once the month's full-speed pool is spent, search in the deep lane rather than the slower one: the plan's whole search time, starting once the workspace has nothing else waiting or running. Within the pool it changes nothing. Needs the Works plan; asked for on another plan, the job is refused with a 403.",
            "default": false
          },
          "spaces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadSpace"
            },
            "maxItems": 30,
            "description": "A one-stage load's spaces. At most 30 rows."
          },
          "cartons": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadSpace"
            },
            "maxItems": 30,
            "description": "Three stages: the cartons items go into. At most 30 rows."
          },
          "pallets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadSpace"
            },
            "maxItems": 30,
            "description": "Three stages: the pallets cartons go onto. At most 30 rows."
          },
          "vehicles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadSpace"
            },
            "maxItems": 30,
            "description": "Three stages: the vehicles pallets go into. At most 30 rows."
          },
          "cargo": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadCargo"
            },
            "maxItems": 200,
            "description": "The items to load. At most 200 rows."
          }
        },
        "required": [
          "unit"
        ]
      },
      "IdArgument": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The job's or plan's id."
          }
        },
        "required": [
          "id"
        ],
        "additionalProperties": false
      },
      "NoArguments": {
        "type": "object",
        "properties": {},
        "additionalProperties": false
      },
      "UnitArgument": {
        "type": "object",
        "properties": {
          "unit": {
            "type": "string",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ],
            "description": "The unit sizes are written in. Without it, the unit the key's owner picked on the web."
          }
        },
        "additionalProperties": false
      },
      "Problem": {
        "type": "object",
        "properties": {
          "field": {
            "anyOf": [
              {
                "type": "string",
                "description": "Where in the request, like \"parts[2].length\"."
              },
              {
                "type": "null"
              }
            ]
          },
          "message": {
            "type": "string",
            "description": "The web's words for it."
          },
          "upgrade": {
            "anyOf": [
              {
                "type": "string",
                "description": "The lowest plan that takes the job, past a cap.",
                "enum": [
                  "free",
                  "bench",
                  "shop",
                  "works"
                ]
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "field",
          "message",
          "upgrade"
        ],
        "additionalProperties": false
      },
      "Errors": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Problem"
            },
            "description": "Each problem, naming its field when there is one."
          }
        },
        "required": [
          "errors"
        ],
        "additionalProperties": false
      },
      "Validation": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "Whether the cut list has no problems."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Problem"
            },
            "description": "Each problem, naming its field."
          }
        },
        "required": [
          "valid",
          "errors"
        ],
        "additionalProperties": false
      },
      "Started": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The job's id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "nest",
              "load"
            ],
            "description": "A nest or a load."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued"
            ],
            "description": "Waiting for a worker."
          },
          "links": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string"
              }
            },
            "additionalProperties": false,
            "required": [
              "status"
            ],
            "description": "Where to watch the job."
          }
        },
        "required": [
          "id",
          "kind",
          "status",
          "links"
        ],
        "additionalProperties": false
      },
      "JobStatus": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The job's id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "nest",
              "load"
            ],
            "description": "A nest or a load."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "done",
              "failed"
            ],
            "description": "Waiting, searching, finished with a plan, or failed."
          },
          "lane": {
            "type": "string",
            "description": "Where the search runs: within the month's pool, past it, or the Works plan's deep lane.",
            "enum": [
              "full_speed",
              "slower",
              "deep"
            ]
          },
          "search_seconds": {
            "type": "integer",
            "description": "Seconds searched, written when the job finishes or fails; 0 while it is queued or running."
          },
          "plan_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The finished plan's id, once the job is done."
          },
          "failure": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the job failed, in plain words, once it has."
          },
          "links": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string"
              },
              "plan": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "additionalProperties": false,
            "required": [
              "status",
              "plan"
            ],
            "description": "This status, and the finished plan once there is one."
          }
        },
        "required": [
          "id",
          "kind",
          "status",
          "lane",
          "search_seconds",
          "plan_id",
          "failure",
          "links"
        ],
        "additionalProperties": false
      },
      "Usage": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "bench",
              "shop",
              "works"
            ],
            "description": "The workspace's plan."
          },
          "pool_seconds": {
            "type": "integer",
            "description": "This month's full-speed search time."
          },
          "used_seconds": {
            "type": "integer",
            "description": "Full-speed search time used this month."
          },
          "left_seconds": {
            "type": "integer",
            "description": "Full-speed search time left this month."
          },
          "next_lane": {
            "type": "string",
            "enum": [
              "full_speed",
              "slower",
              "deep"
            ],
            "description": "The lane the next nest or load searches in."
          },
          "loads_limit": {
            "anyOf": [
              {
                "type": "integer",
                "description": "On a plan with a monthly load cap (Free), the loads the workspace may start this month; null on other plans."
              },
              {
                "type": "null"
              }
            ]
          },
          "loads_used": {
            "anyOf": [
              {
                "type": "integer",
                "description": "On a plan with a monthly load cap, the loads started this month, failed ones aside; null on other plans."
              },
              {
                "type": "null"
              }
            ]
          },
          "loads_left": {
            "anyOf": [
              {
                "type": "integer",
                "description": "On a plan with a monthly load cap, the loads left this month; null on other plans."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "plan",
          "pool_seconds",
          "used_seconds",
          "left_seconds",
          "next_lane",
          "loads_limit",
          "loads_used",
          "loads_left"
        ],
        "additionalProperties": false
      },
      "SavedPart": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The part's id: its key in a nest's `library`."
          },
          "label": {
            "type": "string",
            "description": "The part's name."
          },
          "width": {
            "type": "number",
            "description": "The width of the box around the part, in the listing's unit."
          },
          "height": {
            "type": "number",
            "description": "The height of the box around the part, in the listing's unit."
          },
          "holes": {
            "type": "integer",
            "description": "How many holes are cut through it."
          },
          "rotation": {
            "type": "string",
            "enum": [
              "fixed",
              "quarter",
              "eighth",
              "any"
            ],
            "description": "How it turns unless a nest says otherwise; send it as the library row's `rotation` to keep it."
          },
          "mirror": {
            "type": "boolean",
            "description": "Whether it may be flipped over unless a nest says otherwise; send it as the library row's `mirror` to keep it."
          }
        },
        "required": [
          "id",
          "label",
          "width",
          "height",
          "holes",
          "rotation",
          "mirror"
        ],
        "additionalProperties": false
      },
      "Parts": {
        "type": "object",
        "properties": {
          "unit": {
            "type": "string",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ],
            "description": "The unit sizes are written in."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SavedPart"
            },
            "description": "The workspace's saved parts, newest first."
          }
        },
        "required": [
          "unit",
          "parts"
        ],
        "additionalProperties": false
      },
      "SavedSpace": {
        "type": "object",
        "description": "A saved space as a row of a load's `spaces`, `cartons`, `pallets` or `vehicles`, typed in the listing's unit, with blank fields left out. Send it in a load with the same `unit`.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The saved space's id. A load ignores it, so the row can be sent as it is."
          },
          "kind": {
            "type": "string",
            "enum": [
              "carton",
              "pallet",
              "vehicle"
            ],
            "description": "What the space is."
          },
          "label": {
            "type": "string",
            "description": "Its name."
          },
          "length": {
            "type": "string",
            "description": "Inside length, in the listing's unit."
          },
          "width": {
            "type": "string",
            "description": "Inside width, in the listing's unit."
          },
          "height": {
            "type": "string",
            "description": "Inside height, in the listing's unit."
          },
          "own_weight": {
            "type": "string",
            "description": "Its own weight, in `weight_unit`."
          },
          "weight_limit": {
            "type": "string",
            "description": "The most weight it takes, in `weight_unit`."
          },
          "quantity": {
            "type": "string",
            "description": "How many there are; left out for as many as needed."
          },
          "price": {
            "type": "string",
            "description": "What one costs."
          },
          "loading": {
            "type": "string",
            "description": "Where its doors are; left out for the back.",
            "enum": [
              "back",
              "side"
            ]
          },
          "density": {
            "type": "string",
            "description": "The floor load limit: kg a square metre, or lb a square foot for inches."
          },
          "wall": {
            "type": "string",
            "description": "A carton's board thickness."
          },
          "deck": {
            "type": "string",
            "description": "A pallet's deck height."
          },
          "carries": {
            "type": "boolean",
            "description": "Others may stack on it once filled; left out when not."
          },
          "most_in_stack": {
            "type": "string",
            "description": "The most in one stack."
          },
          "most_above": {
            "type": "string",
            "description": "The most weight above it, in `weight_unit`."
          },
          "axles": {
            "$ref": "#/components/schemas/LoadAxles"
          }
        },
        "required": [
          "id",
          "kind"
        ],
        "additionalProperties": false
      },
      "Spaces": {
        "type": "object",
        "properties": {
          "unit": {
            "type": "string",
            "enum": [
              "mm",
              "cm",
              "m",
              "in"
            ],
            "description": "The unit lengths are typed in."
          },
          "weight_unit": {
            "type": "string",
            "enum": [
              "kg",
              "lb"
            ],
            "description": "The unit weights are typed in: kilograms, or pounds for inches."
          },
          "spaces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SavedSpace"
            },
            "description": "The workspace's saved spaces."
          }
        },
        "required": [
          "unit",
          "weight_unit",
          "spaces"
        ],
        "additionalProperties": false
      },
      "CostSummary": {
        "type": "object",
        "description": "The plan's cost in cents, once the stock is priced.",
        "properties": {
          "total_cents": {
            "type": "integer"
          },
          "waste_cents": {
            "type": "integer",
            "description": "The wasted stock's share."
          },
          "drop_value_cents": {
            "type": "integer",
            "description": "What the reusable drops are worth."
          }
        },
        "required": [
          "total_cents",
          "waste_cents",
          "drop_value_cents"
        ],
        "additionalProperties": false
      },
      "LinearCut": {
        "type": "object",
        "properties": {
          "at": {
            "type": "number",
            "description": "Where the cut starts along the bar."
          },
          "length": {
            "type": "number",
            "description": "In the request's unit."
          },
          "label": {
            "type": "string"
          },
          "rush": {
            "type": "boolean"
          }
        },
        "required": [
          "at",
          "length",
          "label",
          "rush"
        ],
        "additionalProperties": false
      },
      "LinearBlocked": {
        "type": "object",
        "properties": {
          "start": {
            "type": "number",
            "description": "In the request's unit."
          },
          "end": {
            "type": "number",
            "description": "In the request's unit."
          },
          "keep": {
            "type": "boolean",
            "description": "Kept as a drop."
          }
        },
        "required": [
          "start",
          "end",
          "keep"
        ],
        "additionalProperties": false
      },
      "LinearLeftover": {
        "type": "object",
        "properties": {
          "start": {
            "type": "number",
            "description": "In the request's unit."
          },
          "end": {
            "type": "number",
            "description": "In the request's unit."
          },
          "kind": {
            "type": "string",
            "enum": [
              "drop",
              "scrap"
            ]
          }
        },
        "required": [
          "start",
          "end",
          "kind"
        ],
        "additionalProperties": false
      },
      "LinearBar": {
        "type": "object",
        "properties": {
          "stock_row": {
            "type": "integer",
            "description": "The stock row it was cut from, from zero."
          },
          "length": {
            "type": "number",
            "description": "In the request's unit."
          },
          "material": {
            "type": "string"
          },
          "cuts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearCut"
            }
          },
          "blocked": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearBlocked"
            }
          },
          "leftovers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearLeftover"
            }
          },
          "drop": {
            "type": "number",
            "description": "In the request's unit."
          },
          "scrap": {
            "type": "number",
            "description": "In the request's unit."
          }
        },
        "required": [
          "stock_row",
          "length",
          "material",
          "cuts",
          "blocked",
          "leftovers",
          "drop",
          "scrap"
        ],
        "additionalProperties": false
      },
      "LinearUnplaced": {
        "type": "object",
        "properties": {
          "length": {
            "type": "number",
            "description": "In the request's unit."
          },
          "label": {
            "type": "string"
          },
          "quantity": {
            "type": "integer"
          }
        },
        "required": [
          "length",
          "label",
          "quantity"
        ],
        "additionalProperties": false
      },
      "LinearReport": {
        "type": "object",
        "description": "The plan: its figures, each bar's cuts and leftovers, what didn't fit, its cost, and why. Lengths are in the request's unit.",
        "properties": {
          "unit": {
            "type": "string"
          },
          "metrics": {
            "type": "object",
            "properties": {
              "bars": {
                "type": "integer"
              },
              "stock_length": {
                "type": "number",
                "description": "In the request's unit."
              },
              "parts_length": {
                "type": "number",
                "description": "In the request's unit."
              },
              "drop": {
                "type": "number",
                "description": "In the request's unit."
              },
              "blocked": {
                "type": "number",
                "description": "In the request's unit."
              },
              "scrap": {
                "type": "number",
                "description": "In the request's unit."
              },
              "utilization_percent": {
                "type": "number"
              }
            },
            "required": [
              "bars",
              "stock_length",
              "parts_length",
              "drop",
              "blocked",
              "scrap",
              "utilization_percent"
            ],
            "additionalProperties": false
          },
          "bars": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearBar"
            }
          },
          "unplaced": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinearUnplaced"
            },
            "description": "Parts that didn't fit."
          },
          "cost": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CostSummary"
              },
              {
                "type": "null"
              }
            ]
          },
          "unpriced_stock_rows": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Stock rows used without a price."
          },
          "explanation": {
            "type": "string",
            "description": "Why the plan came out as it did."
          }
        },
        "required": [
          "unit",
          "metrics",
          "bars",
          "unplaced",
          "cost",
          "unpriced_stock_rows",
          "explanation"
        ],
        "additionalProperties": false
      },
      "SheetPlaced": {
        "type": "object",
        "properties": {
          "part_row": {
            "type": "integer",
            "description": "The part row, from zero."
          },
          "label": {
            "type": "string"
          },
          "x": {
            "type": "number",
            "description": "In the request's unit."
          },
          "y": {
            "type": "number",
            "description": "In the request's unit."
          },
          "width": {
            "type": "number",
            "description": "In the request's unit."
          },
          "height": {
            "type": "number",
            "description": "In the request's unit."
          },
          "rotated": {
            "type": "boolean"
          },
          "finished_width": {
            "type": "number",
            "description": "The part's width with its edge band, in the request's unit. width and height are the size cut, as it sits on the sheet."
          },
          "finished_height": {
            "type": "number",
            "description": "The part's height with its edge band, in the request's unit."
          },
          "grain": {
            "type": "string",
            "description": "Which way the part's grain runs, as typed.",
            "enum": [
              "width",
              "height",
              "none"
            ]
          },
          "band": {
            "type": [
              "string",
              "null"
            ],
            "description": "The band type on its edges, or null."
          },
          "band_long": {
            "type": "integer",
            "description": "How many long edges are banded."
          },
          "band_short": {
            "type": "integer",
            "description": "How many short edges are banded."
          }
        },
        "required": [
          "part_row",
          "label",
          "x",
          "y",
          "width",
          "height",
          "rotated",
          "finished_width",
          "finished_height",
          "grain",
          "band",
          "band_long",
          "band_short"
        ],
        "additionalProperties": false
      },
      "SheetCut": {
        "type": "object",
        "properties": {
          "stage": {
            "type": "integer"
          },
          "axis": {
            "type": "string",
            "enum": [
              "vertical",
              "horizontal"
            ]
          },
          "at": {
            "type": "number",
            "description": "In the request's unit."
          },
          "from": {
            "type": "number",
            "description": "In the request's unit."
          },
          "to": {
            "type": "number",
            "description": "In the request's unit."
          }
        },
        "required": [
          "stage",
          "axis",
          "at",
          "from",
          "to"
        ],
        "additionalProperties": false
      },
      "SheetLeftover": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number",
            "description": "In the request's unit."
          },
          "y": {
            "type": "number",
            "description": "In the request's unit."
          },
          "width": {
            "type": "number",
            "description": "In the request's unit."
          },
          "height": {
            "type": "number",
            "description": "In the request's unit."
          },
          "kind": {
            "type": "string",
            "enum": [
              "reusable",
              "scrap"
            ]
          }
        },
        "required": [
          "x",
          "y",
          "width",
          "height",
          "kind"
        ],
        "additionalProperties": false
      },
      "SheetLayout": {
        "type": "object",
        "properties": {
          "sheet_row": {
            "type": "integer",
            "description": "The sheet row, from zero."
          },
          "width": {
            "type": "number",
            "description": "In the request's unit."
          },
          "height": {
            "type": "number",
            "description": "In the request's unit."
          },
          "material": {
            "type": "string"
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetPlaced"
            }
          },
          "cuts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetCut"
            },
            "description": "A panel saw's cuts by stage."
          },
          "leftovers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetLeftover"
            }
          },
          "grain": {
            "type": "string",
            "description": "Which way the sheet's grain runs.",
            "enum": [
              "width",
              "height",
              "none"
            ]
          }
        },
        "required": [
          "sheet_row",
          "width",
          "height",
          "material",
          "parts",
          "cuts",
          "leftovers",
          "grain"
        ],
        "additionalProperties": false
      },
      "SheetUnplaced": {
        "type": "object",
        "properties": {
          "part_row": {
            "type": "integer"
          },
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          }
        },
        "required": [
          "part_row",
          "label",
          "count"
        ],
        "additionalProperties": false
      },
      "SheetReport": {
        "type": "object",
        "description": "The plan: its figures, each sheet's parts, saw cuts, and leftovers, what didn't fit, its cost, the edge band per band type, and why.",
        "properties": {
          "unit": {
            "type": "string"
          },
          "metrics": {
            "type": "object",
            "properties": {
              "sheets": {
                "type": "integer"
              },
              "parts_placed": {
                "type": "integer"
              },
              "parts_requested": {
                "type": "integer"
              },
              "sheet_area": {
                "type": "number",
                "description": "In square units."
              },
              "part_area": {
                "type": "number"
              },
              "reusable_area": {
                "type": "number"
              },
              "reusable_leftovers": {
                "type": "integer"
              },
              "waste_area": {
                "type": "number"
              },
              "utilization_percent": {
                "type": "number"
              },
              "waste_percent": {
                "type": "number"
              }
            },
            "required": [
              "sheets",
              "parts_placed",
              "parts_requested",
              "sheet_area",
              "part_area",
              "reusable_area",
              "reusable_leftovers",
              "waste_area",
              "utilization_percent",
              "waste_percent"
            ],
            "additionalProperties": false
          },
          "sheets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetLayout"
            }
          },
          "unplaced": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetUnplaced"
            }
          },
          "cost": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CostSummary"
              },
              {
                "type": "null"
              }
            ]
          },
          "unpriced_sheet_rows": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "banding": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SheetBandTotal"
            },
            "description": "The edge band the placed parts take, one entry per band type used."
          },
          "explanation": {
            "type": "string"
          }
        },
        "required": [
          "unit",
          "metrics",
          "sheets",
          "unplaced",
          "cost",
          "unpriced_sheet_rows",
          "banding",
          "explanation"
        ],
        "additionalProperties": false
      },
      "NestPlaced": {
        "type": "object",
        "properties": {
          "part_row": {
            "type": "integer",
            "description": "The part's row, from 0, among the parts the job kept: first the saved parts asked for (a quantity other than blank or 0), in the order of the workspace's part library rather than the order the request lists them, then the typed rows that are not blank, in request order. Blank rows are dropped before counting, so this can be lower than the row's place in the request body; match a saved part by `label` or by the library's order."
          },
          "label": {
            "type": "string"
          },
          "x": {
            "type": "number",
            "description": "In the request's unit."
          },
          "y": {
            "type": "number",
            "description": "In the request's unit."
          },
          "angle_degrees": {
            "type": "number",
            "description": "Turned counter-clockwise."
          },
          "mirrored": {
            "type": "boolean"
          },
          "outline": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "number"
              },
              "description": "An `[x, y]` corner in the request's unit."
            },
            "description": "The placed outline's corners."
          },
          "holes": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "array",
                "items": {
                  "type": "number"
                },
                "description": "An `[x, y]` corner in the request's unit."
              }
            }
          }
        },
        "required": [
          "part_row",
          "label",
          "x",
          "y",
          "angle_degrees",
          "mirrored",
          "outline",
          "holes"
        ],
        "additionalProperties": false
      },
      "NestLayout": {
        "type": "object",
        "properties": {
          "sheet_row": {
            "type": "integer",
            "description": "The sheet's row, from 0, among the sheets the job kept: first the `sheets` rows that are not blank, in request order, then the `remnants`, in request order with repeats dropped. Blank sheet rows are dropped before counting, so this can be lower than the row's place in the request body."
          },
          "width": {
            "type": "number",
            "description": "In the request's unit."
          },
          "height": {
            "type": "number",
            "description": "In the request's unit."
          },
          "material": {
            "type": "string"
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NestPlaced"
            }
          }
        },
        "required": [
          "sheet_row",
          "width",
          "height",
          "material",
          "parts"
        ],
        "additionalProperties": false
      },
      "NestUnplaced": {
        "type": "object",
        "properties": {
          "part_row": {
            "type": "integer",
            "description": "The part's row, from 0, among the parts the job kept: first the saved parts asked for (a quantity other than blank or 0), in the order of the workspace's part library rather than the order the request lists them, then the typed rows that are not blank, in request order. Blank rows are dropped before counting, so this can be lower than the row's place in the request body; match a saved part by `label` or by the library's order."
          },
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          }
        },
        "required": [
          "part_row",
          "label",
          "count"
        ],
        "additionalProperties": false
      },
      "NestReport": {
        "type": "object",
        "description": "A nest: its figures, each sheet's parts with their placed outlines, what didn't fit, its cost, and why.",
        "properties": {
          "unit": {
            "type": "string"
          },
          "metrics": {
            "type": "object",
            "properties": {
              "sheets": {
                "type": "integer"
              },
              "parts_placed": {
                "type": "integer"
              },
              "parts_requested": {
                "type": "integer"
              },
              "sheet_area": {
                "type": "number"
              },
              "part_area": {
                "type": "number"
              },
              "unused_area": {
                "type": "number"
              },
              "utilization_percent": {
                "type": "number"
              }
            },
            "required": [
              "sheets",
              "parts_placed",
              "parts_requested",
              "sheet_area",
              "part_area",
              "unused_area",
              "utilization_percent"
            ],
            "additionalProperties": false
          },
          "sheets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NestLayout"
            }
          },
          "unplaced": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NestUnplaced"
            }
          },
          "cost_cents": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "explanation": {
            "type": "string"
          }
        },
        "required": [
          "unit",
          "metrics",
          "sheets",
          "unplaced",
          "cost_cents",
          "explanation"
        ],
        "additionalProperties": false
      },
      "LoadItem": {
        "type": "object",
        "properties": {
          "step": {
            "type": "integer",
            "description": "Its loading step (see loading_steps): items loaded together share one."
          },
          "item_row": {
            "type": "integer",
            "description": "The item's cargo row, from 0, among the rows the job kept: blank rows (no length, width or height sent) are dropped first, so this can be lower than the row's place in the request body. In a later linked stage the items are the stage before's cartons or pallets, numbered in that stage's order."
          },
          "label": {
            "type": "string"
          },
          "x": {
            "type": "number",
            "description": "In the request's unit."
          },
          "y": {
            "type": "number",
            "description": "In the request's unit."
          },
          "z": {
            "type": "number",
            "description": "In the request's unit."
          },
          "length": {
            "type": "number",
            "description": "In the request's unit."
          },
          "width": {
            "type": "number",
            "description": "In the request's unit."
          },
          "height": {
            "type": "number",
            "description": "In the request's unit."
          },
          "weight": {
            "type": "number",
            "description": "In kilograms, or pounds for inches."
          },
          "stop": {
            "type": "integer"
          }
        },
        "required": [
          "step",
          "item_row",
          "label",
          "x",
          "y",
          "z",
          "length",
          "width",
          "height",
          "weight",
          "stop"
        ],
        "additionalProperties": false
      },
      "LoadLayout": {
        "type": "object",
        "properties": {
          "space_row": {
            "type": "integer",
            "description": "The space's row in its stage's rows (spaces, cartons, pallets or vehicles), from 0, among the rows the job kept: blank rows (no length, width or height sent) are dropped first, so this can be lower than the row's place in the request body."
          },
          "kind": {
            "type": "string",
            "enum": [
              "carton",
              "pallet",
              "vehicle"
            ]
          },
          "label": {
            "type": "string"
          },
          "length": {
            "type": "number",
            "description": "In the request's unit."
          },
          "width": {
            "type": "number",
            "description": "In the request's unit."
          },
          "height": {
            "type": "number",
            "description": "In the request's unit."
          },
          "weight": {
            "type": "number",
            "description": "In kilograms, or pounds for inches."
          },
          "weight_limit": {
            "anyOf": [
              {
                "type": "number",
                "description": "In kilograms, or pounds for inches."
              },
              {
                "type": "null"
              }
            ]
          },
          "centre_of_gravity": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "x": {
                    "type": "number",
                    "description": "In the request's unit."
                  },
                  "y": {
                    "type": "number",
                    "description": "In the request's unit."
                  },
                  "z": {
                    "type": "number",
                    "description": "In the request's unit."
                  },
                  "offset_along": {
                    "type": "number",
                    "description": "From the middle of the floor along the length, in the request's unit: positive toward the back (where back doors are), negative toward the front."
                  },
                  "offset_across": {
                    "type": "number",
                    "description": "From the middle of the floor across the width, in the request's unit: positive toward the right, negative toward the left."
                  }
                },
                "required": [
                  "x",
                  "y",
                  "z",
                  "offset_along",
                  "offset_across"
                ],
                "additionalProperties": false,
                "description": "Where the loaded cargo balances: x, y and z from the space's front-left floor corner, and its offset from the middle of the floor."
              },
              {
                "type": "null"
              }
            ]
          },
          "axle_loads": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "middle": {
                    "type": "number",
                    "description": "In kilograms, or pounds for inches."
                  },
                  "rear": {
                    "type": "number",
                    "description": "In kilograms, or pounds for inches."
                  }
                },
                "required": [
                  "middle",
                  "rear"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadItem"
            },
            "description": "In loading order."
          },
          "loading_steps": {
            "type": "array",
            "description": "The order to load the space: the last stop's items first, then from the far end toward the doors, bottom to top, never an item before what it rests on. Each step is items of one cargo row loaded together, like one stack or one wall.",
            "items": {
              "$ref": "#/components/schemas/LoadStep"
            }
          }
        },
        "required": [
          "space_row",
          "kind",
          "label",
          "length",
          "width",
          "height",
          "weight",
          "weight_limit",
          "centre_of_gravity",
          "axle_loads",
          "items",
          "loading_steps"
        ],
        "additionalProperties": false
      },
      "LoadUnplaced": {
        "type": "object",
        "properties": {
          "item_row": {
            "type": "integer",
            "description": "The item's cargo row, from 0, among the rows the job kept: blank rows (no length, width or height sent) are dropped first, so this can be lower than the row's place in the request body. In a later linked stage the items are the stage before's cartons or pallets, numbered in that stage's order."
          },
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "reason": {
            "type": "string",
            "enum": [
              "too_big",
              "too_heavy",
              "spaces_used_up",
              "no_spot"
            ]
          },
          "why": {
            "type": "string",
            "description": "Why, in plain words."
          },
          "fix": {
            "type": "string",
            "description": "The nearest fix, with the numbers that show it, like \"2600 mm long; the longest space is 2400 mm. Add a longer space.\""
          },
          "over": {
            "anyOf": [
              {
                "type": "object",
                "description": "By how much the item missed: the side or weight to blame, what the item needs, and the most any space gives.",
                "properties": {
                  "by": {
                    "type": "string",
                    "enum": [
                      "length",
                      "width",
                      "height",
                      "longest_side",
                      "middle_side",
                      "shortest_side",
                      "weight"
                    ],
                    "description": "Upright items compare their longer floor side (length), shorter floor side (width) and height; items that may turn any way compare their longest, middle and shortest sides."
                  },
                  "needed": {
                    "type": "number",
                    "description": "In the request's unit, or kilograms (pounds for inches) for weight."
                  },
                  "most": {
                    "type": "number",
                    "description": "The roomiest space's side, or the strongest space's payload, in the same unit."
                  }
                },
                "required": [
                  "by",
                  "needed",
                  "most"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Set for too_big and too_heavy when one side or the weight is to blame."
          }
        },
        "required": [
          "item_row",
          "label",
          "count",
          "reason",
          "why",
          "fix",
          "over"
        ],
        "additionalProperties": false
      },
      "LoadStage": {
        "type": "object",
        "properties": {
          "stage": {
            "type": "string",
            "enum": [
              "load",
              "cartons",
              "pallets",
              "vehicles"
            ]
          },
          "metrics": {
            "type": "object",
            "properties": {
              "spaces": {
                "type": "integer"
              },
              "items_placed": {
                "type": "integer"
              },
              "items_requested": {
                "type": "integer"
              },
              "weight": {
                "type": "number",
                "description": "In kilograms, or pounds for inches."
              },
              "volume_percent": {
                "type": "number"
              }
            },
            "required": [
              "spaces",
              "items_placed",
              "items_requested",
              "weight",
              "volume_percent"
            ],
            "additionalProperties": false
          },
          "spaces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadLayout"
            }
          },
          "unplaced": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadUnplaced"
            }
          },
          "cost_cents": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "explanation": {
            "type": "string"
          }
        },
        "required": [
          "stage",
          "metrics",
          "spaces",
          "unplaced",
          "cost_cents",
          "explanation"
        ],
        "additionalProperties": false
      },
      "LoadReport": {
        "type": "object",
        "description": "A load: each stage's figures, spaces with items in loading order, and what didn't fit.",
        "properties": {
          "unit": {
            "type": "string"
          },
          "weight_unit": {
            "type": "string",
            "enum": [
              "kg",
              "lb"
            ]
          },
          "stages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LoadStage"
            }
          }
        },
        "required": [
          "unit",
          "weight_unit",
          "stages"
        ],
        "additionalProperties": false
      },
      "Plan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The plan's id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "nest",
              "load"
            ],
            "description": "A nest's plan or a load's."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the plan was saved."
          },
          "report": {
            "description": "The nest's report for a nest, the load's for a load.",
            "anyOf": [
              {
                "$ref": "#/components/schemas/NestReport"
              },
              {
                "$ref": "#/components/schemas/LoadReport"
              }
            ]
          },
          "exports": {
            "type": "object",
            "description": "Each export the plan offers, by format, with its URL.",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "kind",
          "created_at",
          "report",
          "exports"
        ],
        "additionalProperties": false
      },
      "LoadStep": {
        "type": "object",
        "properties": {
          "step": {
            "type": "integer",
            "description": "From 1, as the items' own step says."
          },
          "stop": {
            "type": "integer",
            "description": "The delivery stop its items come off at."
          },
          "item_row": {
            "type": "integer",
            "description": "The items' cargo row, as on the items."
          },
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "The step's items by their place in the space's items, from 0, bottom first."
          }
        },
        "required": [
          "step",
          "stop",
          "item_row",
          "label",
          "count",
          "items"
        ],
        "additionalProperties": false
      },
      "SheetBandType": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What parts call the band by, like \"PVC 2 mm\". Unique in the list.",
            "maxLength": 40
          },
          "thickness": {
            "type": "string",
            "description": "The band's thickness, in the request's unit.",
            "maxLength": 40
          }
        },
        "description": "An edge band parts may take. Fields it doesn't name are ignored."
      },
      "SheetBandTotal": {
        "type": "object",
        "properties": {
          "band": {
            "type": "string"
          },
          "thickness": {
            "type": "number",
            "description": "In the request's unit."
          },
          "edges": {
            "type": "integer",
            "description": "How many edges of the placed parts it covers."
          },
          "length": {
            "type": "number",
            "description": "Along the finished edges, in the request's unit."
          },
          "length_with_waste": {
            "type": "number",
            "description": "length with band_waste on top: the length to order."
          }
        },
        "required": [
          "band",
          "thickness",
          "edges",
          "length",
          "length_with_waste"
        ],
        "additionalProperties": false
      }
    }
  }
}
