{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "CutOptim Configurator — merchant API",
    "version": "1.0.0",
    "summary": "Quote, order and verify cut-to-size orders from the CutOptim Configurator embedded in your shop.",
    "description": "Early access. The publishable key (`?key=`) is for the shop page; the secret key (`Authorization: Bearer`) is for your server only and never belongs in a browser. Contract version v1: changes inside v1 are additive only (see /configurator/reference/versioning). Errors share one envelope `{ error, message, details? }`: branch on `error`, never on `message`.",
    "contact": {
      "name": "CutOptim",
      "url": "https://cutoptim.com/contact"
    }
  },
  "externalDocs": {
    "description": "Configurator merchant API reference",
    "url": "https://cutoptim.com/configurator/reference"
  },
  "servers": [
    {
      "url": "https://cfg.cutoptim.com",
      "description": "Configurator service"
    }
  ],
  "tags": [
    {
      "name": "Shop page",
      "description": "Publishable-key routes the embed calls from the shop page."
    },
    {
      "name": "Saved projects",
      "description": "A shopper's saved jobs: publishable key + the shopper's project token."
    },
    {
      "name": "Server",
      "description": "Secret-key routes for your server: the checks before the cart."
    }
  ],
  "paths": {
    "/cfg/v1/config/public": {
      "get": {
        "tags": [
          "Shop page"
        ],
        "operationId": "getPublicConfig",
        "summary": "The shop's published configuration (public projection)",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). No prices are in it. Cached publicly for 60 s with an ETag (a test key gets `private, no-store`).",
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/If-None-Match"
          }
        ],
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "not_found",
          "not_configured",
          "config_invalid"
        ],
        "responses": {
          "200": {
            "description": "The public configuration.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicConfig"
                }
              }
            }
          },
          "304": {
            "description": "Not modified (If-None-Match matched)."
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`, `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`, `config_invalid`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/catalog/search": {
      "get": {
        "tags": [
          "Shop page"
        ],
        "operationId": "searchCatalog",
        "summary": "Search the shop's catalogue",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). Money fields are removed from every item. ETag on shop + config version + catalogue stamp.",
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "description": "Free text, prefix-matched per word."
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{1,31}$",
              "default": "material"
            }
          },
          {
            "name": "thicknessMin",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "thicknessMax",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10000,
              "default": 0
            }
          },
          {
            "$ref": "#/components/parameters/If-None-Match"
          }
        ],
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "not_found",
          "not_configured",
          "config_invalid"
        ],
        "responses": {
          "200": {
            "description": "One page of items.",
            "headers": {
              "ETag": {
                "$ref": "#/components/headers/ETag"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogSearchResult"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`, `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`, `config_invalid`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/preview": {
      "post": {
        "tags": [
          "Shop page"
        ],
        "operationId": "preview",
        "summary": "Live plan and price (not billable, no token)",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). Solves and prices the job without issuing a quote token; it has its own per-shop rate limit. A pricing refusal does not fail the call: it arrives in `priceError` with `price: null`.",
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "not_found",
          "not_configured",
          "config_invalid",
          "sandbox_expired",
          "unknown_workflow",
          "workflow_disabled",
          "mode_mismatch",
          "unknown_mode",
          "unsupported_mode",
          "mode_unavailable",
          "customer_context_required",
          "limit_exceeded",
          "outside_catalog_slice",
          "unknown_reference",
          "busy",
          "solve_timeout"
        ],
        "responses": {
          "200": {
            "description": "The plan and the price (or the pricing refusal).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreviewResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`, `unknown_workflow`, `workflow_disabled`, `mode_mismatch`, `unknown_mode`, `unsupported_mode`, `mode_unavailable`, `limit_exceeded`, `outside_catalog_slice`, `unknown_reference`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`, `customer_context_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`, `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `busy`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`, `config_invalid`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Error envelope. Codes: `solve_timeout`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/quote": {
      "post": {
        "tags": [
          "Shop page"
        ],
        "operationId": "createQuote",
        "summary": "Signed quote (billable)",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). Solves, prices and signs the job: the answer carries `quoteToken` (valid until `expiresAt`) that your server verifies with POST /cfg/v1/quote/verify before it adds anything to a cart. A plan with unplaced parts is refused (422), never quoted.",
        "security": [
          {
            "publishableKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "not_found",
          "not_configured",
          "config_invalid",
          "sandbox_expired",
          "unknown_workflow",
          "workflow_disabled",
          "mode_mismatch",
          "unknown_mode",
          "unsupported_mode",
          "mode_unavailable",
          "customer_context_required",
          "limit_exceeded",
          "outside_catalog_slice",
          "unknown_reference",
          "busy",
          "solve_timeout",
          "test_limit",
          "tenant_unconfigured",
          "quota_exceeded",
          "final_engine_unavailable",
          "required_field_missing",
          "plan_incomplete",
          "invalid_config",
          "invalid_job",
          "item_disabled",
          "unpriced_item",
          "no_strategy",
          "unpriced_sheet",
          "cut_length_unattributable",
          "bonded_unpriced",
          "bonded_not_allowed",
          "template_value_invalid",
          "machining_length_missing",
          "machining_invalid",
          "unknown_customer_group",
          "shipping_unavailable",
          "delivery_below_minimum",
          "weight_unavailable",
          "rule_budget",
          "amount_overflow"
        ],
        "responses": {
          "200": {
            "description": "The signed quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`, `unknown_workflow`, `workflow_disabled`, `mode_mismatch`, `unknown_mode`, `unsupported_mode`, `mode_unavailable`, `limit_exceeded`, `outside_catalog_slice`, `unknown_reference`, `required_field_missing`, `plan_incomplete`, `invalid_config`, `invalid_job`, `item_disabled`, `unpriced_item`, `no_strategy`, `unpriced_sheet`, `cut_length_unattributable`, `bonded_unpriced`, `bonded_not_allowed`, `template_value_invalid`, `machining_length_missing`, `machining_invalid`, `unknown_customer_group`, `shipping_unavailable`, `delivery_below_minimum`, `weight_unavailable`, `rule_budget`, `amount_overflow`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Error envelope. Codes: `quota_exceeded`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`, `customer_context_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`, `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error envelope. Codes: `plan_incomplete`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `busy`, `test_limit`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`, `config_invalid`, `tenant_unconfigured`, `final_engine_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Error envelope. Codes: `solve_timeout`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/order": {
      "post": {
        "tags": [
          "Shop page"
        ],
        "operationId": "createOrder",
        "summary": "Redeem a quote once and store the signed order.v1",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). Redeems the quote token's nonce exactly once. A retry with the same `Idempotency-Key` returns the same status and the same bytes with `Idempotency-Replayed: true` (even after the token expired); the same key for a different quote is 409. Live orders queue the `order.created` webhook and the order e-mails in the same transaction.",
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Idempotency-Key"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderRequest"
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "idempotency_key_required",
          "sandbox_expired",
          "test_limit",
          "sandbox_order_limit",
          "not_found",
          "quote_invalid",
          "quote_already_redeemed",
          "idempotency_conflict",
          "quote_requote"
        ],
        "responses": {
          "201": {
            "description": "The stored order (canonical JSON).",
            "headers": {
              "Idempotency-Replayed": {
                "$ref": "#/components/headers/Idempotency-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderResponse"
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`, `idempotency_key_required`, `quote_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Error envelope. Codes: `quote_already_redeemed`, `idempotency_conflict`, `quote_requote`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `test_limit`, `sandbox_order_limit`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/projects": {
      "post": {
        "tags": [
          "Saved projects"
        ],
        "operationId": "createProject",
        "summary": "Save a project",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). Without an `X-Cfg-Project-Token` header the service ISSUES a project token and returns it once, as `projectToken`; keep it in the shopper's browser and send it on every later project call.",
        "security": [
          {
            "publishableKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/projectTokenOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data"
                ],
                "properties": {
                  "data": {
                    "type": "object",
                    "description": "The saved job, as the embed writes it."
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  },
                  "workflowId": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "customerRef": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "not_found",
          "not_configured",
          "config_invalid",
          "sandbox_expired",
          "unknown_workflow",
          "workflow_disabled",
          "mode_mismatch",
          "unknown_mode",
          "unsupported_mode",
          "mode_unavailable",
          "project_token_invalid",
          "project_limit"
        ],
        "responses": {
          "201": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "workflowId",
                    "mode",
                    "configVersion"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "workflowId": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "configVersion": {
                      "type": "integer"
                    },
                    "projectToken": {
                      "type": "string",
                      "pattern": "^cfgpt_[A-Za-z0-9_-]{43}$",
                      "description": "Only when this call issued the token."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`, `unknown_workflow`, `workflow_disabled`, `mode_mismatch`, `unknown_mode`, `unsupported_mode`, `mode_unavailable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`, `project_token_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`, `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `project_limit`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`, `config_invalid`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Saved projects"
        ],
        "operationId": "listProjects",
        "summary": "The shopper's projects (newest first, without data)",
        "description": "Called from the shop page with the PUBLISHABLE key in `?key=`; the page's Origin must be on the key's allowed origins (CORS reflects only a listed origin, never `*`). At most 100 projects.",
        "security": [
          {
            "publishableKey": [],
            "projectToken": []
          }
        ],
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "project_token_required",
          "project_token_invalid"
        ],
        "responses": {
          "200": {
            "description": "The list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "workflowId": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "configVersion": {
                            "type": "integer"
                          },
                          "name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "createdAt": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updatedAt": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "limit": {
                      "type": "integer",
                      "const": 100
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`, `project_token_required`, `project_token_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/projects/{id}": {
      "get": {
        "tags": [
          "Saved projects"
        ],
        "operationId": "getProject",
        "summary": "Open one project (with data)",
        "security": [
          {
            "publishableKey": [],
            "projectToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The project id (a UUID). An unknown id, another shopper's project and another shop's project all answer the same 404.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "project_token_required",
          "project_token_invalid",
          "not_found"
        ],
        "responses": {
          "200": {
            "description": "The project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`, `project_token_required`, `project_token_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Saved projects"
        ],
        "operationId": "updateProject",
        "summary": "Re-save a project (name and/or data)",
        "security": [
          {
            "publishableKey": [],
            "projectToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The project id (a UUID). An unknown id, another shopper's project and another shop's project all answer the same 404.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "minProperties": 1,
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  },
                  "data": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "project_token_required",
          "project_token_invalid",
          "not_found",
          "sandbox_expired",
          "project_limit"
        ],
        "responses": {
          "200": {
            "description": "The saved project.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`, `project_token_required`, `project_token_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `project_limit`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/projects/{id}/duplicate": {
      "post": {
        "tags": [
          "Saved projects"
        ],
        "operationId": "duplicateProject",
        "summary": "Copy a project (new id, same data, same owner)",
        "security": [
          {
            "publishableKey": [],
            "projectToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The project id (a UUID). An unknown id, another shopper's project and another shop's project all answer the same 404.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "project_token_required",
          "project_token_invalid",
          "not_found",
          "sandbox_expired",
          "project_limit"
        ],
        "responses": {
          "201": {
            "description": "The copy.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "workflowId": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "configVersion": {
                      "type": "integer"
                    },
                    "duplicatedFrom": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`, `project_token_required`, `project_token_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `project_limit`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/projects/{id}/reorder": {
      "post": {
        "tags": [
          "Saved projects"
        ],
        "operationId": "reorderProject",
        "summary": "A new quote from a saved project, at today's prices",
        "description": "Rebuilds the saved job into a quote request and quotes it on TODAY's published configuration (never the old price). Billable like POST /cfg/v1/quote; the answer is a quote plus `projectId`. The request body must be a JSON object; nothing in it is read.",
        "security": [
          {
            "publishableKey": [],
            "projectToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The project id (a UUID). An unknown id, another shopper's project and another shop's project all answer the same 404.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "origin_not_allowed",
          "rate_limited",
          "service_unavailable",
          "internal",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "not_found",
          "not_configured",
          "config_invalid",
          "sandbox_expired",
          "unknown_workflow",
          "workflow_disabled",
          "mode_mismatch",
          "unknown_mode",
          "unsupported_mode",
          "mode_unavailable",
          "customer_context_required",
          "limit_exceeded",
          "outside_catalog_slice",
          "unknown_reference",
          "busy",
          "solve_timeout",
          "test_limit",
          "tenant_unconfigured",
          "quota_exceeded",
          "final_engine_unavailable",
          "required_field_missing",
          "plan_incomplete",
          "invalid_config",
          "invalid_job",
          "item_disabled",
          "unpriced_item",
          "no_strategy",
          "unpriced_sheet",
          "cut_length_unattributable",
          "bonded_unpriced",
          "bonded_not_allowed",
          "template_value_invalid",
          "machining_length_missing",
          "machining_invalid",
          "unknown_customer_group",
          "shipping_unavailable",
          "delivery_below_minimum",
          "weight_unavailable",
          "rule_budget",
          "amount_overflow",
          "project_token_required",
          "project_token_invalid",
          "project_not_reorderable"
        ],
        "responses": {
          "200": {
            "description": "The signed quote.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/QuoteResponse"
                    },
                    {
                      "type": "object",
                      "required": [
                        "projectId"
                      ],
                      "properties": {
                        "projectId": {
                          "type": "string",
                          "format": "uuid"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`, `unknown_workflow`, `workflow_disabled`, `mode_mismatch`, `unknown_mode`, `unsupported_mode`, `mode_unavailable`, `limit_exceeded`, `outside_catalog_slice`, `unknown_reference`, `required_field_missing`, `plan_incomplete`, `invalid_config`, `invalid_job`, `item_disabled`, `unpriced_item`, `no_strategy`, `unpriced_sheet`, `cut_length_unattributable`, `bonded_unpriced`, `bonded_not_allowed`, `template_value_invalid`, `machining_length_missing`, `machining_invalid`, `unknown_customer_group`, `shipping_unavailable`, `delivery_below_minimum`, `weight_unavailable`, `rule_budget`, `amount_overflow`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`, `project_token_required`, `project_token_invalid`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Error envelope. Codes: `quota_exceeded`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Error envelope. Codes: `origin_not_allowed`, `sandbox_expired`, `customer_context_required`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`, `not_configured`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Error envelope. Codes: `plan_incomplete`, `project_not_reorderable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`, `busy`, `test_limit`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`, `config_invalid`, `tenant_unconfigured`, `final_engine_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Error envelope. Codes: `solve_timeout`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/quote/verify": {
      "post": {
        "tags": [
          "Server"
        ],
        "operationId": "verifyQuote",
        "summary": "Verify a quote token before the cart (read-only)",
        "description": "Your server's authoritative price check: the token signature is known only to CutOptim. Never consumes the token. A refused token is still a 200 with `valid: false` and a `reason`; a valid token that was already ordered comes back `valid: true, redeemed: true`, so check both. `reason: \"unavailable\"` means NOT verified. A test quote verifies only under a test secret key and a live quote only under a live one (`reason: \"test_mismatch\"`). Request body cap 64 000 bytes.",
        "security": [
          {
            "secretKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quoteToken"
                ],
                "properties": {
                  "quoteToken": {
                    "type": "string",
                    "maxLength": 4096
                  }
                }
              }
            }
          }
        },
        "x-error-codes": [
          "unauthorized",
          "rate_limited",
          "service_unavailable",
          "invalid_request",
          "too_large",
          "unsupported_media_type",
          "internal"
        ],
        "responses": {
          "200": {
            "description": "The verdict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteVerifyResult"
                }
              }
            }
          },
          "400": {
            "description": "Error envelope. Codes: `invalid_request`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Error envelope. Codes: `too_large`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Error envelope. Codes: `unsupported_media_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cfg/v1/orders/{id}": {
      "get": {
        "tags": [
          "Server"
        ],
        "operationId": "getOrder",
        "summary": "Check a stored order (no personal data)",
        "description": "Confirms that the order id the browser names is the order of the quote you verified: compare `planHash`, `quoteHash`, currency and totals with the verified token. Another shop's order, an unknown id and a malformed id are the same 404. A test order is visible to a test secret key only.",
        "security": [
          {
            "secretKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-error-codes": [
          "unauthorized",
          "rate_limited",
          "service_unavailable",
          "not_found",
          "internal"
        ],
        "responses": {
          "200": {
            "description": "The order summary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderSummary"
                }
              }
            }
          },
          "401": {
            "description": "Error envelope. Codes: `unauthorized`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Error envelope. Codes: `not_found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Error envelope. Codes: `rate_limited`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Error envelope. Codes: `internal`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Error envelope. Codes: `service_unavailable`.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "order.created": {
      "post": {
        "summary": "A new live order was stored",
        "description": "Sent to every enabled `channels.webhooks[]` endpoint subscribed to `order.created`. The body is RFC 8785 canonical JSON and byte-identical on every attempt; verify `CutOptim-Signature` over the RAW bytes before parsing. Answer 2xx fast; anything else (or a timeout or redirect) is retried. Test-key orders send no webhook.",
        "parameters": [
          {
            "$ref": "#/components/parameters/wh-CutOptim-Signature"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Event-Id"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Event-Type"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Delivery-Id"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Delivery-Attempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderCreatedEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery."
          },
          "default": {
            "description": "Any non-2xx is a failed attempt and is retried with backoff."
          }
        }
      }
    },
    "order.status_changed": {
      "post": {
        "summary": "An order moved to another status",
        "description": "Sent to every enabled `channels.webhooks[]` endpoint subscribed to `order.status_changed`. The body is RFC 8785 canonical JSON and byte-identical on every attempt; verify `CutOptim-Signature` over the RAW bytes before parsing. Answer 2xx fast; anything else (or a timeout or redirect) is retried. Test-key orders send no webhook.",
        "parameters": [
          {
            "$ref": "#/components/parameters/wh-CutOptim-Signature"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Event-Id"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Event-Type"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Delivery-Id"
          },
          {
            "$ref": "#/components/parameters/wh-CutOptim-Delivery-Attempt"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderStatusChangedEvent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery."
          },
          "default": {
            "description": "Any non-2xx is a failed attempt and is retried with backoff."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "publishableKey": {
        "type": "apiKey",
        "in": "query",
        "name": "key",
        "description": "The publishable key (`pk_live_…`, or `pk_test_…` for a test key). Public by design: it sits in the shop page and works only from its allowed origins. In the URL on purpose: a CORS preflight carries the URL but no header values."
      },
      "secretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The secret key (`sk_live_…`, or `sk_test_…`). Server side only; a browser cannot send it (Authorization is not in the CORS allow-list)."
      },
      "projectToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Cfg-Project-Token",
        "description": "The shopper's project token (`cfgpt_…`), issued once by the first POST /cfg/v1/projects."
      }
    },
    "parameters": {
      "If-None-Match": {
        "name": "If-None-Match",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "Idempotency-Key": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "One key per order attempt; a retry sends the same key.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{8,128}$"
        }
      },
      "projectTokenOptional": {
        "name": "X-Cfg-Project-Token",
        "in": "header",
        "required": false,
        "description": "Send it when the shopper already has one; omit it on the very first save.",
        "schema": {
          "type": "string",
          "pattern": "^cfgpt_[A-Za-z0-9_-]{43}$"
        }
      },
      "wh-CutOptim-Signature": {
        "name": "CutOptim-Signature",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>`; a second `v1=` while a rotated secret is in its grace window. Accept when any v1 matches one of your secrets and `t` is within 300 s."
      },
      "wh-CutOptim-Event-Id": {
        "name": "CutOptim-Event-Id",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Equals the body `id`. A retry and a redelivery repeat it: deduplicate on it."
      },
      "wh-CutOptim-Event-Type": {
        "name": "CutOptim-Event-Type",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The event name (`order.created`, `order.status_changed`)."
      },
      "wh-CutOptim-Delivery-Id": {
        "name": "CutOptim-Delivery-Id",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The delivery row; differs between a retry and a redelivery."
      },
      "wh-CutOptim-Delivery-Attempt": {
        "name": "CutOptim-Delivery-Attempt",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The attempt number, 1-based."
      }
    },
    "headers": {
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "ETag": {
        "description": "A weak validator; send it back as If-None-Match.",
        "schema": {
          "type": "string"
        }
      },
      "Idempotency-Replayed": {
        "description": "`true` when this answer replays an earlier one for the same Idempotency-Key.",
        "schema": {
          "type": "string",
          "const": "true"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "message": {
            "type": "string",
            "description": "Human-readable; may change. Branch on `error`."
          },
          "details": {
            "type": "object",
            "description": "Optional: `path` (the field), bounds (`max`, `min`, `used`, `limit`), `unplaced`."
          }
        }
      },
      "ErrorCode": {
        "type": "string",
        "enum": [
          "invalid_request",
          "idempotency_key_required",
          "quote_invalid",
          "unknown_workflow",
          "workflow_disabled",
          "mode_mismatch",
          "unknown_mode",
          "unsupported_mode",
          "limit_exceeded",
          "outside_catalog_slice",
          "unknown_reference",
          "required_field_missing",
          "unauthorized",
          "project_token_required",
          "project_token_invalid",
          "quota_exceeded",
          "customer_context_required",
          "origin_not_allowed",
          "sandbox_expired",
          "not_found",
          "not_configured",
          "idempotency_conflict",
          "quote_already_redeemed",
          "quote_requote",
          "too_large",
          "unsupported_media_type",
          "plan_incomplete",
          "project_not_reorderable",
          "rate_limited",
          "busy",
          "test_limit",
          "sandbox_order_limit",
          "project_limit",
          "internal",
          "service_unavailable",
          "config_invalid",
          "tenant_unconfigured",
          "final_engine_unavailable",
          "solve_timeout",
          "invalid_config",
          "invalid_job",
          "mode_unavailable",
          "item_disabled",
          "unpriced_item",
          "no_strategy",
          "unpriced_sheet",
          "cut_length_unattributable",
          "bonded_unpriced",
          "bonded_not_allowed",
          "template_value_invalid",
          "machining_length_missing",
          "machining_invalid",
          "unknown_customer_group",
          "shipping_unavailable",
          "delivery_below_minimum",
          "weight_unavailable",
          "rule_budget",
          "amount_overflow"
        ]
      },
      "QuoteRequest": {
        "type": "object",
        "description": "A panel2d job. Only `cutList` (or `fullSheets` / `hardware`) is needed; every price comes from the shop's published configuration, never from the request.",
        "properties": {
          "workflowId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$",
            "description": "Absent = the shop's only enabled workflow."
          },
          "mode": {
            "type": "string",
            "description": "Optional; must equal the workflow's mode (today `panel2d`)."
          },
          "cutList": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderV1/$defs/CutPart"
            },
            "description": "The parts, in the order.v1 `CutPart` shape (millimetres)."
          },
          "fullSheets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderV1/$defs/FullSheetItem"
            }
          },
          "hardware": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "type": "object",
              "required": [
                "hardwareId",
                "qty"
              ],
              "properties": {
                "hardwareId": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
                },
                "qty": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 100000
                }
              }
            }
          },
          "customerRef": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "An opaque reference of YOUR choosing (never personal data); it is bound into the quote token."
          },
          "customerGroup": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Only the shop's default group prices on a publishable key; another group needs the secret key (`customer_context_required`)."
          },
          "templateId": {
            "type": "string"
          },
          "templateValues": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "The parameter values of `templateId`."
          },
          "shipping": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "method",
                  "pickupLocationId"
                ],
                "properties": {
                  "method": {
                    "const": "pickup"
                  },
                  "pickupLocationId": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "object",
                "required": [
                  "method"
                ],
                "properties": {
                  "method": {
                    "const": "delivery"
                  },
                  "zoneId": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string"
                  },
                  "postcode": {
                    "type": "string"
                  }
                }
              }
            ]
          }
        }
      },
      "PreviewResponse": {
        "type": "object",
        "required": [
          "configVersion",
          "workflow",
          "plan",
          "price",
          "priceError",
          "leadTime"
        ],
        "properties": {
          "configVersion": {
            "type": "integer"
          },
          "workflow": {
            "type": "object",
            "required": [
              "id",
              "mode",
              "profile"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "mode": {
                "type": "string"
              },
              "profile": {
                "type": "string"
              }
            }
          },
          "plan": {
            "$ref": "#/components/schemas/OrderV1/$defs/Plan"
          },
          "price": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "priced",
                  "currency",
                  "lines",
                  "totals"
                ],
                "properties": {
                  "priced": {
                    "type": "boolean"
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[A-Z]{3}$"
                  },
                  "lines": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/OrderV1/$defs/QuoteLine"
                    }
                  },
                  "totals": {
                    "$ref": "#/components/schemas/OrderV1/$defs/Totals"
                  },
                  "display": {
                    "type": "object",
                    "description": "How the shop shows prices (net / gross)."
                  },
                  "shipping": {
                    "type": [
                      "object",
                      "null"
                    ]
                  },
                  "customerGroup": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "warnings": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "priceError": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "path": {
                "type": "string"
              }
            }
          },
          "leadTime": {
            "type": [
              "object",
              "null"
            ],
            "description": "The lead time the shop's `leadTime` setting states for the boards in this job."
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "required": [
          "quoteId",
          "quoteToken",
          "expiresAt",
          "configVersion",
          "workflow",
          "plan",
          "price",
          "warnings",
          "leadTime"
        ],
        "properties": {
          "quoteId": {
            "type": "string",
            "format": "uuid"
          },
          "quoteToken": {
            "type": "string",
            "maxLength": 4096
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "configVersion": {
            "type": "integer"
          },
          "workflow": {
            "type": "object",
            "required": [
              "id",
              "mode",
              "profile"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "mode": {
                "type": "string"
              },
              "profile": {
                "type": "string"
              }
            }
          },
          "plan": {
            "$ref": "#/components/schemas/OrderV1/$defs/Plan"
          },
          "price": {
            "type": "object",
            "required": [
              "priced",
              "currency",
              "lines",
              "totals"
            ],
            "properties": {
              "priced": {
                "type": "boolean"
              },
              "currency": {
                "type": "string",
                "pattern": "^[A-Z]{3}$"
              },
              "lines": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OrderV1/$defs/QuoteLine"
                }
              },
              "totals": {
                "$ref": "#/components/schemas/OrderV1/$defs/Totals"
              },
              "display": {
                "type": "object",
                "description": "How the shop shows prices (net / gross)."
              },
              "shipping": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "customerGroup": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "leadTime": {
            "type": [
              "object",
              "null"
            ],
            "description": "The lead time the shop's `leadTime` setting states for the boards in this job."
          }
        }
      },
      "OrderRequest": {
        "type": "object",
        "required": [
          "quoteId",
          "quoteToken"
        ],
        "properties": {
          "quoteId": {
            "type": "string",
            "format": "uuid"
          },
          "quoteToken": {
            "type": "string",
            "maxLength": 4096
          }
        }
      },
      "OrderResponse": {
        "type": "object",
        "required": [
          "orderId",
          "order"
        ],
        "properties": {
          "orderId": {
            "type": "string",
            "format": "uuid"
          },
          "order": {
            "$ref": "#/components/schemas/OrderV1"
          }
        }
      },
      "QuoteVerifyResult": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "valid",
              "redeemed",
              "claims",
              "test"
            ],
            "properties": {
              "valid": {
                "const": true
              },
              "redeemed": {
                "type": "boolean"
              },
              "claims": {
                "type": "object",
                "description": "The eleven quote-token claims.",
                "required": [
                  "tenantId",
                  "currency",
                  "customerRef",
                  "keyId",
                  "planHash",
                  "configVersion",
                  "exp",
                  "nonce",
                  "quoteHash",
                  "mode",
                  "workflowId"
                ],
                "properties": {
                  "tenantId": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "customerRef": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "keyId": {
                    "type": "string"
                  },
                  "planHash": {
                    "type": "string"
                  },
                  "configVersion": {
                    "type": "integer"
                  },
                  "exp": {
                    "type": "integer",
                    "description": "Unix seconds."
                  },
                  "nonce": {
                    "type": "string"
                  },
                  "quoteHash": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "workflowId": {
                    "type": "string"
                  }
                }
              },
              "test": {
                "type": "boolean"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "valid",
              "reason"
            ],
            "properties": {
              "valid": {
                "const": false
              },
              "reason": {
                "type": "string",
                "description": "Why the token is refused; `unavailable` = the quote store could not answer (treat as not verified); `test_mismatch` = test and live mixed."
              },
              "test": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "OrderSummary": {
        "type": "object",
        "required": [
          "orderId",
          "test",
          "tenantId",
          "currency",
          "status",
          "createdAt",
          "priced",
          "planHash",
          "quoteHash",
          "totals"
        ],
        "properties": {
          "orderId": {
            "type": "string",
            "format": "uuid"
          },
          "test": {
            "type": "boolean"
          },
          "tenantId": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "received",
              "confirmed",
              "in_production",
              "shipped",
              "completed",
              "cancelled"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "priced": {
            "type": "boolean"
          },
          "planHash": {
            "type": "string"
          },
          "quoteHash": {
            "type": "string"
          },
          "totals": {
            "type": "object",
            "required": [
              "netMinor",
              "taxMinor",
              "grossMinor"
            ],
            "properties": {
              "netMinor": {
                "type": "integer"
              },
              "taxMinor": {
                "type": "integer"
              },
              "grossMinor": {
                "type": "integer"
              }
            },
            "description": "Whole minor units of `currency`."
          }
        }
      },
      "Project": {
        "allOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "workflowId": {
                "type": "string"
              },
              "mode": {
                "type": "string"
              },
              "configVersion": {
                "type": "integer"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object"
              }
            }
          }
        ]
      },
      "PublicConfig": {
        "type": "object",
        "description": "The published configuration without prices or internal metadata: locale, unit, currency, dimensions, edging, limits, workflows, shipping zones, the enabled catalogue, brand, texts, display, lead time, and `embedPage` (the standalone page path).",
        "properties": {
          "schemaVersion": {
            "type": "integer"
          },
          "configVersion": {
            "type": "integer"
          },
          "locale": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "pricingMode": {
            "type": "string"
          },
          "workflows": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "catalog": {
            "type": "object"
          },
          "embedPage": {
            "type": "string"
          }
        }
      },
      "CatalogSearchResult": {
        "type": "object",
        "required": [
          "configVersion",
          "items",
          "total",
          "limit",
          "offset",
          "nextOffset"
        ],
        "properties": {
          "configVersion": {
            "type": "integer"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "category",
                "name",
                "active",
                "sortOrder",
                "data"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "category": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "active": {
                  "type": "boolean"
                },
                "sortOrder": {
                  "type": "integer"
                },
                "data": {
                  "type": "object"
                }
              }
            }
          },
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "nextOffset": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "OrderCreatedEvent": {
        "type": "object",
        "required": [
          "schemaVersion",
          "id",
          "type",
          "createdAt",
          "tenantId",
          "data"
        ],
        "properties": {
          "schemaVersion": {
            "const": 1
          },
          "id": {
            "type": "string",
            "description": "The event id (= CutOptim-Event-Id)."
          },
          "type": {
            "const": "order.created"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "tenantId": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "required": [
              "orderId",
              "status",
              "order"
            ],
            "properties": {
              "orderId": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "received",
                  "confirmed",
                  "in_production",
                  "shipped",
                  "completed",
                  "cancelled",
                  null
                ]
              },
              "order": {
                "$ref": "#/components/schemas/OrderV1"
              }
            }
          }
        }
      },
      "OrderStatusChangedEvent": {
        "type": "object",
        "required": [
          "schemaVersion",
          "id",
          "type",
          "createdAt",
          "tenantId",
          "data"
        ],
        "properties": {
          "schemaVersion": {
            "const": 1
          },
          "id": {
            "type": "string"
          },
          "type": {
            "const": "order.status_changed"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "tenantId": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "required": [
              "orderId",
              "status",
              "fromStatus",
              "order"
            ],
            "properties": {
              "orderId": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "received",
                  "confirmed",
                  "in_production",
                  "shipped",
                  "completed",
                  "cancelled",
                  null
                ]
              },
              "fromStatus": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "received",
                  "confirmed",
                  "in_production",
                  "shipped",
                  "completed",
                  "cancelled",
                  null
                ],
                "description": "Only on order.status_changed."
              },
              "order": {
                "$ref": "#/components/schemas/OrderV1"
              }
            }
          }
        }
      },
      "OrderV1": {
        "$schema": "https://json-schema.org/draft/2020-12/schema",
        "$id": "https://cutoptim.com/schemas/configurator/order.v1.json",
        "title": "CutOptim Configurator — order contract, order.v1",
        "description": "The signed hand-over from the configurator to the merchant's own systems (webshop plugin, callback, webhook, e-mail/PDF, ERP): the WORKFLOW it was made in, the customer's cut list, the production plan the common engine produced, the priced (or quantities-only) quote with its token, full-sheet / hardware / bonded-panel lines, file links, and provenance (`source`). It carries NO customer PII — only an opaque `customerRef`. RULES: (1) every length is millimetres (`unit` is the display unit); (2) every amount is `Money` in whole minor units and every Money.currency equals `currency`; (3) ONE ORDER = ONE WORKFLOW (D15): `workflow {id, mode, profile}` is required, and `cutList` / `fullSheets` / `bondedPanels` / `plan` are MODE-DISCRIMINATED unions selected by `workflow.mode` (today the single branch is `panel2d`; `plan.mode` repeats the tag and must equal `workflow.mode`); in the panel2d branch `plan.metrics` is the Engine API's `ApiMetrics2D` unchanged and `cutList[]` is `ApiPart2D` + {id, group, machining} (src/lib/api/types.ts — the contract test holds the field lists together); (4) `plan.planHash` = sha256 of the RFC 8785 canonical plan without `planHash` (the `mode` tag is inside, so the hash binds the mode); (5) `signature` = HMAC-SHA256 over the canonical document without `signature`; (6) the quote token binds the 11 claims of x-cfg-quote-token. A mode listed under `x-cfg-modes.reserved` is KNOWN but NOT BUILT and is rejected with `mode_unavailable`. EVOLUTION: additive only within v1 — a consumer written against this document keeps working after every later phase; a NEW MODE is a new union branch + registry entry, `order.v1` stays; a breaking change is a new `order.v2` document next to this one, never a rewrite. Validator: configurator/schema/order.ts (`validateOrder`, `verifyOrder`) dispatching through configurator/schema/modes/index.ts; gate: `npm run test:cfg-contract`.",
        "$ref": "#/$defs/Order",
        "x-cfg-evolution": "ADDITIVE ONLY. New fields are optional; existing fields never disappear or change meaning; enum sets only grow; `schemaVersion` stays 1. NEW MODE = a new `oneOf` branch on cutList.items / fullSheets.items / bondedPanels.items / plan (defs tagged `x-cfg-mode: <mode>`, the plan def carrying `mode: const <mode>`), a new `if/then` in Order.allOf, and a new registry entry in configurator/schema/modes/ — `order.v1` stays. Objects are closed (unknown keys rejected) so a typo cannot pass as a new optional field. A breaking change ships as order.v2 — a separate consumer contract.",
        "x-cfg-modes": {
          "description": "Engine modes (mirror of tenant-config.v1.json x-cfg-modes). `built` = accepted in `workflow.mode` and `plan.mode`; `reserved` = known, not built → `mode_unavailable`; anything else → `enum`.",
          "built": [
            "panel2d"
          ],
          "reserved": [
            "linear1d",
            "timber",
            "nest"
          ]
        },
        "x-cfg-quote-token": {
          "description": "Compact token `<base64url(canonical claims)>.<base64url(HMAC-SHA256)>`. The claims are canonical JSON (RFC 8785) of exactly these keys; `exp` is unix seconds and equals floor(expiresAt/1000); `customerRef` is null when the order has none; `quoteHash` = sha256 of canonical {priced, lines, totals}; `mode` = workflow.mode and `workflowId` = workflow.id (D15: the token says which workflow's rule set priced it — two workflows of one mode may price differently, and a token for one workflow cannot be presented with an order claiming another). ONE list: this, QUOTE_TOKEN_CLAIMS in order.ts, ADR-008.",
          "claims": [
            "tenantId",
            "currency",
            "customerRef",
            "keyId",
            "planHash",
            "configVersion",
            "exp",
            "nonce",
            "quoteHash",
            "mode",
            "workflowId"
          ]
        },
        "x-cfg-limits": {
          "description": "Whole-document cap: the UTF-8 size of an order, checked FIRST by validateOrder (`limit` at `$`). Measured 2026-10-05 on an order that saturates every list limit with fixture-sized items (2 000 cut parts, 500 plan sheets, 20 000 cut steps, 5 000 quote lines …): ≈ 4.4 MB; the cap is ONE number shared with tenant-config.v1 — the next power of two above twice the larger saturated document (the snapshot, 7 022 007 bytes).",
          "documentBytes": 16777216
        },
        "x-cfg-cross-field-rules": [
          "workflow.mode ∈ x-cfg-modes.built (reserved ⇒ mode_unavailable, unknown ⇒ enum); workflow.profile is a built profile that supports the mode; plan.mode = workflow.mode (checked BEFORE the plan is read by the mode's validator); cutList / fullSheets / bondedPanels / plan have the workflow mode's shape",
          "every Money.currency = $.currency",
          "source.configVersion = $.configVersion; source.engineVersion = plan.engineVersion",
          "expiresAt > createdAt; both canonical UTC ISO-8601 with milliseconds and Z",
          "plan.planHash = sha256(canonicalize(plan without planHash)); plan.metrics.sheetCount = plan.sheets.length; plan.metrics.placed ≤ plan.metrics.total; every cutPlan[].sheet indexes plan.sheets, and a plan with no sheets has no cut steps",
          "quote.totals.net = Σ amount of non-tax lines; quote.totals.tax = Σ amount of tax lines; gross = net + tax (a rounding adjustment is its own surcharge line)",
          "pricingMode = external ⇒ quote.priced = false and every amount is 0; pricingMode = platform ⇒ quote.priced = true",
          "at least one of cutList / fullSheets / hardware / bondedPanels is non-empty; ids unique within cutList, quote.lines, bondedPanels",
          "shipping.method = delivery ⇒ zoneId; = pickup ⇒ pickupLocationId",
          "QuoteLine.ref, when present, names at least one id"
        ],
        "$defs": {
          "Order": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "schemaVersion",
              "idempotencyKey",
              "tenantId",
              "configVersion",
              "workflow",
              "locale",
              "unit",
              "currency",
              "pricingMode",
              "createdAt",
              "expiresAt",
              "source",
              "cutList",
              "fullSheets",
              "hardware",
              "plan",
              "quote"
            ],
            "allOf": [
              {
                "if": {
                  "properties": {
                    "workflow": {
                      "properties": {
                        "mode": {
                          "const": "panel2d"
                        }
                      }
                    }
                  },
                  "required": [
                    "workflow"
                  ]
                },
                "then": {
                  "properties": {
                    "cutList": {
                      "items": {
                        "$ref": "#/$defs/CutPart"
                      }
                    },
                    "fullSheets": {
                      "items": {
                        "$ref": "#/$defs/FullSheetItem"
                      }
                    },
                    "bondedPanels": {
                      "items": {
                        "$ref": "#/$defs/BondedPanel"
                      }
                    },
                    "plan": {
                      "$ref": "#/$defs/Plan"
                    }
                  }
                }
              }
            ],
            "properties": {
              "schemaVersion": {
                "const": 1
              },
              "idempotencyKey": {
                "type": "string",
                "minLength": 8,
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9_-]+$",
                "description": "The caller's replay key for POST /cfg/v1/order (P2-09): the same key returns the same order."
              },
              "tenantId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "configVersion": {
                "type": "integer",
                "minimum": 1,
                "maximum": 9007199254740991,
                "description": "The tenant-config snapshot this order was priced against. Capped at 2^53−1 (one JSON text per integer, so one hash / token claim per version)."
              },
              "workflow": {
                "$ref": "#/$defs/Workflow"
              },
              "customerRef": {
                "type": "string",
                "minLength": 1,
                "maxLength": 128,
                "description": "Opaque reference into the merchant's own customer records. Never a name or an address."
              },
              "customerGroup": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$",
                "description": "The tenant-config customer group the price list was chosen for."
              },
              "locale": {
                "enum": [
                  "en",
                  "de",
                  "it",
                  "fr",
                  "pl",
                  "cs",
                  "ro",
                  "hu",
                  "es",
                  "hr"
                ]
              },
              "unit": {
                "enum": [
                  "mm",
                  "cm",
                  "in"
                ],
                "description": "Display unit at ordering time; every length in this document is millimetres."
              },
              "currency": {
                "type": "string",
                "pattern": "^[A-Z]{3}$"
              },
              "pricingMode": {
                "enum": [
                  "platform",
                  "external"
                ]
              },
              "createdAt": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$"
              },
              "expiresAt": {
                "type": "string",
                "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?Z$",
                "description": "Quote validity; equals the token's exp."
              },
              "source": {
                "$ref": "#/$defs/Source"
              },
              "cutList": {
                "type": "array",
                "maxItems": 2000,
                "items": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/CutPart"
                    }
                  ]
                },
                "description": "MODE-DISCRIMINATED by workflow.mode (Order.allOf). One branch today: panel2d CutPart."
              },
              "fullSheets": {
                "type": "array",
                "maxItems": 500,
                "items": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/FullSheetItem"
                    }
                  ]
                },
                "description": "Full stock units bought uncut. MODE-DISCRIMINATED; panel2d: whole sheets."
              },
              "hardware": {
                "type": "array",
                "maxItems": 500,
                "items": {
                  "$ref": "#/$defs/HardwareLine"
                },
                "description": "Mode-independent (catalogue hardware)."
              },
              "bondedPanels": {
                "type": "array",
                "maxItems": 200,
                "items": {
                  "oneOf": [
                    {
                      "$ref": "#/$defs/BondedPanel"
                    }
                  ]
                },
                "description": "Glued multi-layer panels (P5-05). MODE-DISCRIMINATED; panel2d only today. Shape may gain optional fields there; this is the v1 minimum."
              },
              "plan": {
                "oneOf": [
                  {
                    "$ref": "#/$defs/Plan"
                  }
                ],
                "description": "MODE-DISCRIMINATED; the plan carries its own `mode` tag (= workflow.mode), covered by planHash."
              },
              "quote": {
                "$ref": "#/$defs/Quote"
              },
              "shipping": {
                "$ref": "#/$defs/OrderShipping"
              },
              "files": {
                "type": "array",
                "maxItems": 20,
                "items": {
                  "$ref": "#/$defs/FileRef"
                }
              },
              "signature": {
                "$ref": "#/$defs/Signature"
              }
            }
          },
          "Workflow": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "mode",
              "profile"
            ],
            "description": "The tenant workflow this order was made in (tenant-config.v1 workflows[].id, or `default` for the implicit workflow). `mode` and `profile` are repeated so a consumer can dispatch without fetching the config; the quote token binds `id` and `mode`.",
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "mode": {
                "enum": [
                  "panel2d"
                ],
                "description": "A BUILT mode (x-cfg-modes.built). Reserved ⇒ `mode_unavailable`; unknown ⇒ `enum`."
              },
              "profile": {
                "enum": [
                  "furniture"
                ]
              }
            }
          },
          "Source": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "embedVersion",
              "engineVersion",
              "configVersion"
            ],
            "description": "Provenance: which embed build collected the list, which engine version produced the plan, which config snapshot priced it.",
            "properties": {
              "embedVersion": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "engineVersion": {
                "type": "string",
                "minLength": 1,
                "maxLength": 120,
                "description": "= plan.engineVersion (the Engine API's engineVersion)."
              },
              "configVersion": {
                "type": "integer",
                "minimum": 1,
                "maximum": 9007199254740991,
                "description": "= $.configVersion."
              }
            }
          },
          "CutPart": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "w",
              "h",
              "qty",
              "material"
            ],
            "x-cfg-mode": "panel2d",
            "description": "panel2d cut-list row = Engine API ApiPart2D (name, w, h, qty, rotatable, grain, grainGroup, priority, edgeBanding, material, meta) + the order's own id, group and machining. name/qty/material are REQUIRED here (optional on the API).",
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200,
                "pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f]*$",
                "description": "No C0/C1 control characters (audit P7-01 F1: a newline in a name wrote DXF entities)."
              },
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000,
                "description": "The size the engine was given (after the tenant's dimension convention / edging deduction were applied)."
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "qty": {
                "type": "integer",
                "minimum": 1,
                "maximum": 2000
              },
              "material": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$",
                "description": "Catalogue MaterialItem.id (the API's material partition key)."
              },
              "rotatable": {
                "type": "boolean"
              },
              "grain": {
                "enum": [
                  "w",
                  "h"
                ]
              },
              "grainGroup": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64,
                "pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f]*$"
              },
              "priority": {
                "type": "boolean"
              },
              "edgeBanding": {
                "$ref": "#/$defs/EdgeBanding"
              },
              "meta": {
                "type": "object",
                "description": "Opaque passthrough (= ApiPart2D.meta), ≤ 2048 bytes canonical."
              },
              "group": {
                "type": "string",
                "minLength": 1,
                "maxLength": 120,
                "pattern": "^[^\\u0000-\\u001f\\u007f-\\u009f]*$",
                "description": "Room / cabinet / project the customer assigned; printed on labels and exports."
              },
              "machining": {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "$ref": "#/$defs/MachiningOp"
                }
              }
            }
          },
          "EdgeBanding": {
            "type": "object",
            "additionalProperties": false,
            "required": [],
            "x-cfg-mode": "panel2d",
            "description": "Per-edge banding TYPE references (catalogue EdgingItem.id); an absent side is not banded (= ApiPart2D.edgeBanding).",
            "properties": {
              "top": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "right": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "bottom": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "left": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              }
            }
          },
          "MachiningOp": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "typeId",
              "qty"
            ],
            "x-cfg-mode": "panel2d",
            "description": "A machining operation on the part (catalogue MachiningType.id). `params` stays open in v1; P5-04 types its known keys additively (see params).",
            "properties": {
              "typeId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "qty": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1000
              },
              "params": {
                "type": "object",
                "propertyNames": {
                  "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$"
                },
                "additionalProperties": {
                  "type": [
                    "number",
                    "string"
                  ],
                  "maxLength": 64
                },
                "description": "Open object; the KNOWN keys are typed (P5-04, additive): corner, measure, xMm, yMm, diameterMm, radiusMm, lengthMm, widthMm, depthMm. Per catalogue kind: hole/hinge = corner+measure+xMm+yMm+diameterMm[+depthMm]; groove = corner(top-left|top-right)+xMm+lengthMm[+widthMm+depthMm]; radius = corner+radiusMm. Any other key stays valid.",
                "properties": {
                  "corner": {
                    "enum": [
                      "top-left",
                      "top-right",
                      "bottom-left",
                      "bottom-right"
                    ]
                  },
                  "measure": {
                    "enum": [
                      "centre",
                      "edge"
                    ]
                  },
                  "xMm": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 50000
                  },
                  "yMm": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 50000
                  },
                  "diameterMm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 50000
                  },
                  "radiusMm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 50000
                  },
                  "lengthMm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 50000
                  },
                  "widthMm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 50000
                  },
                  "depthMm": {
                    "type": "number",
                    "exclusiveMinimum": 0,
                    "maximum": 50000
                  }
                }
              }
            }
          },
          "FullSheetItem": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "materialId",
              "w",
              "h",
              "qty"
            ],
            "x-cfg-mode": "panel2d",
            "properties": {
              "materialId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "qty": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000
              }
            }
          },
          "HardwareLine": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "hardwareId",
              "qty"
            ],
            "properties": {
              "hardwareId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "qty": {
                "type": "integer",
                "minimum": 1,
                "maximum": 100000
              }
            }
          },
          "BondedPanel": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "w",
              "h",
              "qty",
              "layers"
            ],
            "x-cfg-mode": "panel2d",
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "qty": {
                "type": "integer",
                "minimum": 1,
                "maximum": 2000
              },
              "layers": {
                "type": "array",
                "minItems": 2,
                "maxItems": 5,
                "items": {
                  "$ref": "#/$defs/BondedLayer"
                }
              },
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              }
            }
          },
          "BondedLayer": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "materialId",
              "thicknessMm"
            ],
            "x-cfg-mode": "panel2d",
            "properties": {
              "materialId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "thicknessMm": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 200
              }
            }
          },
          "Plan": {
            "type": "object",
            "additionalProperties": false,
            "x-cfg-mode": "panel2d",
            "required": [
              "mode",
              "engine",
              "engineVersion",
              "deterministic",
              "sheets",
              "metrics",
              "cutPlan",
              "unplaced",
              "warnings",
              "guillotineValid",
              "planHash"
            ],
            "description": "The panel2d production plan as the Engine API returned it (ApiOptimize2DResponse fields), plus the `mode` tag and planHash. Numbers are the engine's own, in millimetres; sheets[].price / metrics.totalPrice are the engine's unit-less price fields, NOT money (prices live in `quote`).",
            "properties": {
              "mode": {
                "const": "panel2d",
                "description": "The mode tag — INSIDE the plan so planHash covers it; must equal $.workflow.mode (D15)."
              },
              "engine": {
                "enum": [
                  "heuristic",
                  "balanced",
                  "max"
                ]
              },
              "engineVersion": {
                "type": "string",
                "minLength": 1,
                "maxLength": 120
              },
              "deterministic": {
                "type": "boolean"
              },
              "sheets": {
                "type": "array",
                "maxItems": 500,
                "items": {
                  "$ref": "#/$defs/Sheet"
                }
              },
              "metrics": {
                "$ref": "#/$defs/Metrics2D"
              },
              "cutPlan": {
                "type": [
                  "array",
                  "null"
                ],
                "maxItems": 20000,
                "items": {
                  "$ref": "#/$defs/CutStep"
                },
                "description": "null when the layout is not guillotine."
              },
              "unplaced": {
                "type": "array",
                "maxItems": 2000,
                "items": {
                  "$ref": "#/$defs/Unplaced2D"
                }
              },
              "warnings": {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 500
                }
              },
              "guillotineValid": {
                "type": "boolean"
              },
              "planHash": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$",
                "description": "sha256 hex of canonicalize(plan without planHash) — RFC 8785; covers `mode`."
              }
            }
          },
          "Sheet": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "w",
              "h",
              "price",
              "parts"
            ],
            "x-cfg-mode": "panel2d",
            "description": "= ApiSheet (types.ts).",
            "properties": {
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "price": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 0,
                "description": "The engine's unit-less per-sheet price; the issuer passes null."
              },
              "parts": {
                "type": "array",
                "maxItems": 2000,
                "items": {
                  "$ref": "#/$defs/PlacedPart2D"
                }
              },
              "offcuts": {
                "type": "array",
                "maxItems": 5000,
                "items": {
                  "$ref": "#/$defs/Rect"
                }
              },
              "material": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "grain": {
                "enum": [
                  "w",
                  "h"
                ]
              },
              "meta": {
                "type": "object"
              }
            }
          },
          "PlacedPart2D": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "name",
              "x",
              "y",
              "w",
              "h",
              "rotated"
            ],
            "x-cfg-mode": "panel2d",
            "description": "= ApiPlacedPart2D (types.ts).",
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              },
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 50000
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 50000
              },
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "rotated": {
                "type": "boolean"
              },
              "meta": {
                "type": "object"
              }
            }
          },
          "Rect": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "x",
              "y",
              "w",
              "h"
            ],
            "x-cfg-mode": "panel2d",
            "description": "= ApiRect (types.ts).",
            "properties": {
              "x": {
                "type": "number",
                "minimum": 0,
                "maximum": 50000
              },
              "y": {
                "type": "number",
                "minimum": 0,
                "maximum": 50000
              },
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              }
            }
          },
          "Metrics2D": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "sheetCount",
              "yieldPct",
              "placed",
              "total",
              "cutLines",
              "sawPasses",
              "cutLength",
              "totalPrice"
            ],
            "x-cfg-mode": "panel2d",
            "description": "= ApiMetrics2D (types.ts), unchanged: cutLines (collinear cuts merged), sawPasses (every strip crossed), cutLength (mm, convention-independent).",
            "properties": {
              "sheetCount": {
                "type": "integer",
                "minimum": 0
              },
              "yieldPct": {
                "type": "number",
                "minimum": 0,
                "maximum": 100
              },
              "placed": {
                "type": "integer",
                "minimum": 0
              },
              "total": {
                "type": "integer",
                "minimum": 0
              },
              "cutLines": {
                "type": "integer",
                "minimum": 0
              },
              "sawPasses": {
                "type": "integer",
                "minimum": 0
              },
              "cutLength": {
                "type": "number",
                "minimum": 0
              },
              "totalPrice": {
                "type": "number",
                "minimum": 0
              }
            }
          },
          "CutStep": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "sheet",
              "step",
              "axis",
              "pos",
              "length",
              "stage"
            ],
            "x-cfg-mode": "panel2d",
            "description": "= ApiCutStep (types.ts): sheet is the 0-based index into plan.sheets, step restarts at 1 per sheet, stage is the 1-based machine pass.",
            "properties": {
              "sheet": {
                "type": "integer",
                "minimum": 0
              },
              "step": {
                "type": "integer",
                "minimum": 1
              },
              "axis": {
                "enum": [
                  "h",
                  "v"
                ]
              },
              "pos": {
                "type": "number",
                "minimum": 0,
                "maximum": 50000
              },
              "length": {
                "type": "number",
                "minimum": 0,
                "maximum": 50000
              },
              "stage": {
                "type": "integer",
                "minimum": 1
              }
            }
          },
          "Unplaced2D": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "name",
              "w",
              "h",
              "qty"
            ],
            "x-cfg-mode": "panel2d",
            "description": "= ApiUnplaced2D (types.ts).",
            "properties": {
              "name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              },
              "w": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "h": {
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 50000
              },
              "qty": {
                "type": "integer",
                "minimum": 1
              }
            }
          },
          "Quote": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "priced",
              "lines",
              "totals",
              "quoteToken"
            ],
            "properties": {
              "priced": {
                "type": "boolean",
                "description": "false under pricingMode = external: lines carry quantities, every amount is 0."
              },
              "lines": {
                "type": "array",
                "maxItems": 5000,
                "items": {
                  "$ref": "#/$defs/QuoteLine"
                }
              },
              "totals": {
                "$ref": "#/$defs/Totals"
              },
              "quoteToken": {
                "type": "string",
                "maxLength": 4096,
                "pattern": "^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$",
                "description": "See x-cfg-quote-token. The merchant's server verifies it with the tenant's key (P4-02 /quote/verify)."
              }
            }
          },
          "QuoteLine": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "type",
              "label",
              "qty",
              "unitAmount",
              "amount"
            ],
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "type": {
                "enum": [
                  "cutPart",
                  "fullSheet",
                  "hardware",
                  "machining",
                  "bondedPanel",
                  "edging",
                  "surcharge",
                  "shipping",
                  "tax"
                ]
              },
              "label": {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              },
              "qty": {
                "type": "number",
                "minimum": 0,
                "description": "Pieces, sheets, metres (edging) — the line's own unit."
              },
              "unitAmount": {
                "$ref": "#/$defs/Money"
              },
              "amount": {
                "$ref": "#/$defs/Money",
                "description": "Line total after the tenant's rounding. Σ non-tax amounts = totals.net."
              },
              "ref": {
                "$ref": "#/$defs/LineRef"
              },
              "meta": {
                "type": "object"
              }
            }
          },
          "LineRef": {
            "type": "object",
            "additionalProperties": false,
            "required": [],
            "description": "What the line prices. At least one id when present.",
            "properties": {
              "partId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "materialId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "edgingId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "hardwareId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "machiningId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "zoneId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "ruleId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "bondedPanelId": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              }
            }
          },
          "Totals": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "net",
              "tax",
              "gross"
            ],
            "properties": {
              "net": {
                "$ref": "#/$defs/Money"
              },
              "tax": {
                "$ref": "#/$defs/Money"
              },
              "gross": {
                "$ref": "#/$defs/Money"
              }
            }
          },
          "Money": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "amountMinor",
              "currency"
            ],
            "properties": {
              "amountMinor": {
                "type": "integer",
                "minimum": 0,
                "maximum": 1000000000000000
              },
              "currency": {
                "type": "string",
                "pattern": "^[A-Z]{3}$"
              }
            }
          },
          "OrderShipping": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "method"
            ],
            "description": "Ids only — the delivery address is the merchant shop's data, not the contract's.",
            "properties": {
              "method": {
                "enum": [
                  "delivery",
                  "pickup"
                ]
              },
              "zoneId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "pickupLocationId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              }
            }
          },
          "FileRef": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "kind",
              "url"
            ],
            "properties": {
              "kind": {
                "enum": [
                  "pdf",
                  "labels",
                  "csv",
                  "dxf",
                  "svg",
                  "xlsx",
                  "json"
                ]
              },
              "url": {
                "type": "string",
                "maxLength": 2048,
                "pattern": "^https://[^\\s/?#@]+(?:[/?#][^\\s]*)?$"
              },
              "sha256": {
                "type": "string",
                "pattern": "^[0-9a-f]{64}$"
              },
              "bytes": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "Signature": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "alg",
              "keyId",
              "value"
            ],
            "description": "HMAC-SHA256 over canonicalize(order without signature). keyId names the tenant key (P2-07: derived from a master, the DB holds only the id).",
            "properties": {
              "alg": {
                "const": "HMAC-SHA256"
              },
              "keyId": {
                "type": "string",
                "maxLength": 64,
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$"
              },
              "value": {
                "type": "string",
                "maxLength": 128,
                "pattern": "^[A-Za-z0-9_-]+$"
              }
            }
          }
        }
      }
    }
  }
}