{
  "components": {
    "headers": {
      "Retry-After": {
        "description": "Seconds to wait before sending the request again.",
        "schema": {
          "examples": [
            12
          ],
          "type": "integer"
        }
      },
      "X-RateLimit-Limit": {
        "description": "The ceiling on the allowance this call spent, per key, per minute. Reads and writes are counted apart, so the number moves with the verb.",
        "schema": {
          "examples": [
            600
          ],
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "What is left of that allowance, this call included. Never below zero.",
        "schema": {
          "examples": [
            597
          ],
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix seconds. The moment the allowance is whole again.",
        "schema": {
          "examples": [
            1793491260
          ],
          "type": "integer"
        }
      },
      "X-Request-Id": {
        "description": "The handle on this one call. Quote it in a support conversation, or find the call by it in the key's Usage.",
        "schema": {
          "examples": [
            "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83"
          ],
          "type": "string"
        }
      },
      "X-Tightly-Region": {
        "description": "The home that served the call. `us` for every organisation today.",
        "schema": {
          "examples": [
            "us"
          ],
          "type": "string"
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "description": "A string of your own, up to 255 characters, that makes this write safe to retry. Send the same key twice and the second request is answered with the first one's status and body instead of writing again, so a client that retried after a timeout ends holding the record's id rather than creating a second one. Reusing a key with a different body, or while the first request is still running, is refused `409`. A write that failed releases its key. Keys answer for 24 hours and belong to the API key that used them. A key longer than 255 characters is ignored and the write goes ahead without the guarantee.",
        "in": "header",
        "name": "Idempotency-Key",
        "required": false,
        "schema": {
          "examples": [
            "b8f1c0d2-6a4e-4f39-9c21-0d5f2e7a1b44"
          ],
          "maxLength": 255,
          "type": "string"
        }
      },
      "TightlyVersion": {
        "description": "The date train to answer on. One train exists today, `2026-11`, the one this reference describes and the one every key is minted on, so the call answers on it with or without this header. Send it anyway, so a client built now names its train from the start.",
        "in": "header",
        "name": "Tightly-Version",
        "required": false,
        "schema": {
          "examples": [
            "2026-11"
          ],
          "type": "string"
        }
      }
    },
    "responses": {
      "DatabaseUnavailable": {
        "content": {
          "application/json": {
            "example": {
              "code": "POSTGRES_UNAVAILABLE",
              "message": {
                "code": "postgres_unavailable",
                "desc": "The database is unavailable right now. Try again in a few seconds.",
                "doc_url": "https://app.tightly.io/docs/api/guides/errors#postgres_unavailable",
                "request_id": "req_2e7b41c0d9384fa6b15c8073ea92d1f4",
                "service": "UNKNOWN",
                "severity": "ERROR"
              }
            },
            "schema": {
              "properties": {
                "code": {
                  "description": "The same stable code as message.code, at the envelope's top level.",
                  "type": "string"
                },
                "message": {
                  "description": "severity, service, desc, and on a keyed refusal code, doc_url and request_id.",
                  "type": "object"
                }
              },
              "type": "object"
            }
          }
        },
        "description": "The database is briefly out of reach, which is what a failover or a full connection pool looks like from outside. Wait the seconds `Retry-After` names and send the request again. A read is safe to repeat. A write is not, because the connection was lost in the middle of it, so read the object back before sending it a second time. Branch on the status rather than on `code`, which is `POSTGRES_UNAVAILABLE` from one of the two seams that raise this and `SERVICE_UNAVAILABLE` from the other.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "X-Tightly-Region": {
            "$ref": "#/components/headers/X-Tightly-Region"
          }
        }
      },
      "RateLimited": {
        "content": {
          "application/json": {
            "example": {
              "code": "rate_limited",
              "message": {
                "code": "rate_limited",
                "desc": "This key has reached its limit of 600 reads a minute; try again in 12 seconds.",
                "doc_url": "https://app.tightly.io/docs/api/guides/errors#rate_limited",
                "request_id": "req_5c1a9f47b0e34d8f9a2c6e10b3d4f785",
                "service": "UNKNOWN",
                "severity": "ERROR"
              }
            },
            "schema": {
              "properties": {
                "code": {
                  "description": "The same stable code as message.code, at the envelope's top level.",
                  "type": "string"
                },
                "message": {
                  "description": "severity, service, desc, and on a keyed refusal code, doc_url and request_id.",
                  "type": "object"
                }
              },
              "type": "object"
            }
          }
        },
        "description": "The key has spent its allowance for this minute. 600 reads and 120 writes a minute, counted per key, refused calls included. `Retry-After` says how long to wait.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          },
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "X-Tightly-Region": {
            "$ref": "#/components/headers/X-Tightly-Region"
          }
        }
      }
    },
    "schemas": {
      "AddVariantsToSupplierPayload": {
        "properties": {
          "variant_suppliers": {
            "items": {
              "$ref": "#/components/schemas/VariantSupplierBasic"
            },
            "type": "array"
          }
        },
        "required": [
          "variant_suppliers"
        ],
        "type": "object"
      },
      "AddVariantsToSupplierRequest": {
        "additionalProperties": false,
        "properties": {
          "exceptions": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "filter_args": {
            "items": {
              "$ref": "#/components/schemas/ProductsFilterArg"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "AddVariantsToSupplierResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/AddVariantsToSupplierPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ApiMessage"
          }
        },
        "type": "object"
      },
      "ApiMessage": {
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "UNKNOWN",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "AttachmentDict": {
        "properties": {
          "data": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "decoded_data": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "filename": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          }
        },
        "required": [
          "filename",
          "id",
          "mime_type"
        ],
        "type": "object"
      },
      "BasketFilterArg": {
        "additionalProperties": false,
        "properties": {
          "key": {
            "type": "string"
          },
          "operation": {
            "type": "string"
          },
          "value": {
            "default": null
          }
        },
        "required": [
          "key",
          "operation"
        ],
        "type": "object"
      },
      "BulkCreateManufacturingOrdersFromReplenishmentRequest": {
        "additionalProperties": false,
        "properties": {
          "end_date": {
            "default": "",
            "type": "string"
          },
          "exceptions": {
            "items": {
              "$ref": "#/components/schemas/ManufacturingOrderExceptionItem"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "filter_args": {
            "items": {
              "$ref": "#/components/schemas/ReplenishmentFilterArg"
            },
            "type": "array"
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "start_date": {
            "default": "",
            "type": "string"
          }
        },
        "type": "object"
      },
      "BulkCreateManufacturingOrdersRequest": {
        "additionalProperties": false,
        "properties": {
          "manufacturing_orders": {
            "items": {
              "$ref": "#/components/schemas/CreateManufacturingOrderRequest"
            },
            "minItems": 1,
            "type": "array"
          }
        },
        "type": "object"
      },
      "BulkCreatePurchaseOrderDeliveryRequest": {
        "additionalProperties": false,
        "properties": {
          "deliveries": {
            "items": {
              "$ref": "#/components/schemas/CreatePurchaseOrderDeliveryRequest"
            },
            "minItems": 1,
            "type": "array"
          }
        },
        "type": "object"
      },
      "BulkCreatePurchaseOrdersRequest": {
        "additionalProperties": false,
        "properties": {
          "purchase_orders": {
            "items": {
              "$ref": "#/components/schemas/CreatePurchaseOrderWithLineItemsRequest"
            },
            "minItems": 1,
            "type": "array"
          }
        },
        "type": "object"
      },
      "BuyPlanData": {
        "additionalProperties": false,
        "properties": {
          "identity": {
            "$ref": "#/components/schemas/BuyPlanIdentityOut"
          },
          "months": {
            "items": {
              "$ref": "#/components/schemas/BuyPlanMonthOut"
            },
            "type": "array"
          },
          "profile": {
            "type": "string"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/BuyPlanRowOut"
            },
            "type": "array"
          },
          "terms": {
            "$ref": "#/components/schemas/BuyPlanTermsOut"
          }
        },
        "required": [
          "identity",
          "months",
          "profile",
          "rows",
          "terms"
        ],
        "type": "object"
      },
      "BuyPlanIdentityOut": {
        "additionalProperties": false,
        "properties": {
          "converged": {
            "type": "boolean"
          },
          "cover_wrapped_months": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "cover_wrapped_note": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "open_to_buy_note": {
            "type": "string"
          },
          "overstocked_months": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "overstocked_note": {
            "type": "string"
          },
          "profile": {
            "type": "string"
          }
        },
        "required": [
          "converged",
          "cover_wrapped_months",
          "open_to_buy_note",
          "overstocked_months",
          "overstocked_note",
          "profile"
        ],
        "type": "object"
      },
      "BuyPlanMonthOut": {
        "additionalProperties": false,
        "properties": {
          "end_exclusive": {
            "type": "string"
          },
          "fiscal_month": {
            "type": "integer"
          },
          "key": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "month": {
            "type": "integer"
          },
          "start": {
            "type": "string"
          },
          "weeks": {
            "type": "integer"
          },
          "year": {
            "type": "integer"
          }
        },
        "required": [
          "end_exclusive",
          "fiscal_month",
          "key",
          "label",
          "month",
          "start",
          "weeks",
          "year"
        ],
        "type": "object"
      },
      "BuyPlanRowOut": {
        "additionalProperties": false,
        "properties": {
          "absence": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "aggregation": {
            "type": "string"
          },
          "basis": {
            "type": "string"
          },
          "cell_basis": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "cells": {
            "items": {
              "default": null,
              "type": [
                "number",
                "null"
              ]
            },
            "type": "array"
          },
          "definition": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "measure": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "year": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "year_rule": {
            "type": "string"
          }
        },
        "required": [
          "aggregation",
          "basis",
          "cell_basis",
          "cells",
          "definition",
          "label",
          "measure",
          "unit",
          "year_rule"
        ],
        "type": "object"
      },
      "BuyPlanTermOut": {
        "additionalProperties": false,
        "properties": {
          "absence": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "basis": {
            "type": "string"
          },
          "value": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "basis"
        ],
        "type": "object"
      },
      "BuyPlanTermsOut": {
        "additionalProperties": false,
        "properties": {
          "cover_target_weeks": {
            "$ref": "#/components/schemas/BuyPlanTermOut"
          },
          "markdown_rate": {
            "$ref": "#/components/schemas/BuyPlanTermOut"
          },
          "opening_stock": {
            "$ref": "#/components/schemas/BuyPlanTermOut"
          }
        },
        "required": [
          "cover_target_weeks",
          "markdown_rate",
          "opening_stock"
        ],
        "type": "object"
      },
      "CancelSalesOrderRequest": {
        "additionalProperties": false,
        "properties": {
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "CardItem": {
        "properties": {
          "annualisable": {
            "description": "True when this metric's period figure can meaningfully be projected to a year, currently `gmroi` only. False for flows (`total_revenue`, `sold_quantity`, `sale_order_count`), whose period total IS the answer, and for ratios that are already window-independent (`sell_through_rate`, `return_rate`, `avg_sell_price`). Read this rather than hardcoding which keys annualise.\n",
            "type": "boolean"
          },
          "annualised_value": {
            "description": "`value * 365 / window_days`, the figure to PRESENT for an annualisable metric, and the only one comparable to an industry benchmark, because the industry quotes GMROI annually and every benchmark we grade against is annual. A period GMROI of 1.56 over 91 days is an annualised 6.26: the same data reads as \"barely above the minimum\" or \"above elite\" depending only on the basis. When you report this number, say it is annualised and say over what window. IMPORTANT. Null in two distinct cases, and `annualisable` tells them apart: (a) `annualisable: false`, the metric is not one we project; (b) `annualisable: true` with `annualised_value: null`, the metric should be annualised but `window_days` is under 28, the minimum window (`MIN_ANNUALISABLE_WINDOW_DAYS`). Below that the multiplier amplifies noise as much as signal, a measured tenant's 3-day GMROI of 0.01 annualises to ~1.2, which looks like a real near-target answer and is nothing. In case (b) report `value` with its window and say it is too short to annualise or grade. Do not perform the multiplication yourself.\n",
            "type": [
              "number",
              "null"
            ]
          },
          "change": {
            "description": "Percentage change from previous period. IMPORTANT. Field semantics: `value` is the actual result for the date range passed in filter_args. `previous_value` is the implicit comparison period (same calendar range one year prior). `change` is the authoritative percentage change. Always read it directly, never compute a percentage yourself from value and previous_value. Always present `value` for the period you queried; never present `previous_value` as if it were the result for a period you explicitly requested.\n",
            "type": [
              "number",
              "null"
            ]
          },
          "info": {
            "description": "Human-readable metric name",
            "type": "string"
          },
          "key": {
            "description": "Metric identifier key",
            "type": "string"
          },
          "previous_value": {
            "description": "Previous period value for comparison",
            "type": [
              "number",
              "null"
            ]
          },
          "unit": {
            "description": "Unit of measurement (count, usd, percentage, ratio)",
            "type": "string"
          },
          "value": {
            "description": "Current period value",
            "type": "number"
          },
          "window_days": {
            "description": "Length of that window in days, inclusive. IMPORTANT. `value` on every card on this endpoint is a period figure and is not annualised. This matters most for `gmroi`, which is gross margin over the window divided by average inventory at cost over the same window, so it scales with window length instead of describing a rate: the same tenant measured 0.18 over 7 days, 0.59 over 30 and 1.56 over 91. Always state the window when reporting `gmroi`, and never compare `value` to an annual GMROI benchmark (industry min 1.5 / target 3.0 / elite 4.5). Use `annualised_value` for that.\n",
            "type": [
              "integer",
              "null"
            ]
          },
          "window_end": {
            "description": "Last day of the window `value` was measured over (inclusive).",
            "type": [
              "string",
              "null"
            ]
          },
          "window_start": {
            "description": "First day of the window `value` was measured over (inclusive).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "ContactNotePayload": {
        "properties": {
          "body": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "body",
          "created_at",
          "id",
          "title",
          "updated_at"
        ],
        "type": "object"
      },
      "ContactPayload": {
        "properties": {
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "department": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "is_primary_contact": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "preferred_contact_method": {
            "default": "email",
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "type": "object"
      },
      "ContactsApiMessage": {
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "contacts",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ContactsFilterPayload": {
        "properties": {
          "cities": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "countries": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "departments": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "missing_fields": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "preferred_contact_methods": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "roles": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "suppliers": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ContactsTablePayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/ContactPayload"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "max_size",
          "offset",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "ConvertBasketToPurchaseOrdersRequest": {
        "properties": {
          "commitment_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "except_ids": {},
          "export": {
            "default": false,
            "type": "boolean"
          },
          "filter_args": {
            "items": {
              "$ref": "#/components/schemas/BasketFilterArg"
            },
            "type": "array"
          },
          "item_ids": {},
          "limit": {
            "default": 8,
            "type": "integer"
          },
          "offset": {
            "default": 0,
            "type": "integer"
          },
          "search": {
            "default": "",
            "type": "string"
          },
          "sort_args": {},
          "type": {
            "default": "variants",
            "enum": [
              "variants",
              "products",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "CreateContactNoteRequest": {
        "additionalProperties": false,
        "properties": {
          "body": {
            "type": "string"
          },
          "title": {
            "maxLength": 200,
            "type": "string"
          }
        },
        "required": [
          "body",
          "title"
        ],
        "type": "object"
      },
      "CreateContactNoteResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactNotePayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "CreateContactRequest": {
        "additionalProperties": false,
        "properties": {
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "department": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "maxLength": 1024,
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "format": "email",
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "phone": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "preferred_contact_method": {
            "type": "string"
          },
          "role": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "trading_partner_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name",
          "preferred_contact_method"
        ],
        "type": "object"
      },
      "CreateContactResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "CreateManufacturingOrderRequest": {
        "additionalProperties": false,
        "properties": {
          "expected_delivery_date": {
            "format": "date",
            "type": "string"
          },
          "location_id": {
            "minLength": 1,
            "type": "string"
          },
          "order_date": {
            "format": "date",
            "type": "string"
          },
          "quantity": {
            "minimum": 1,
            "type": "integer"
          },
          "recommended_location_id": {
            "minLength": 1,
            "type": "string"
          },
          "recommended_quantity": {
            "default": null,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          },
          "variant_id": {
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "location_id",
          "quantity",
          "recommended_location_id",
          "variant_id"
        ],
        "type": "object"
      },
      "CreatePurchaseOrderDeliveryRequest": {
        "additionalProperties": false,
        "properties": {
          "delivery_date": {
            "format": "date",
            "type": "string"
          },
          "delivery_line_items": {
            "items": {
              "$ref": "#/components/schemas/DeliveryLineItemCreate"
            },
            "minItems": 1,
            "type": "array"
          },
          "expected_delivery_date": {
            "format": "date",
            "type": "string"
          }
        },
        "type": "object"
      },
      "CreatePurchaseOrderRequest": {
        "additionalProperties": false,
        "properties": {
          "cancel_date": {
            "description": "The last day of the ship window, inclusive. After it the buyer may walk away from whatever the vendor has not shipped. Must not be before `ship_window_start`.",
            "format": "date",
            "type": "string"
          },
          "commitment_id": {
            "default": null,
            "maxLength": 250,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "expected_delivery_date": {
            "format": "date",
            "type": "string"
          },
          "location_id": {
            "minLength": 1,
            "type": "string"
          },
          "order_date": {
            "format": "date",
            "type": "string"
          },
          "order_type": {},
          "recommended_quantity": {
            "default": null,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          },
          "ship_window_start": {
            "description": "The first day the vendor may ship this order. Optional, and normally absent: most orders are placed against an expected delivery date and no window.",
            "format": "date",
            "type": "string"
          },
          "source_location_id": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      },
      "CreatePurchaseOrderWithLineItemsRequest": {
        "additionalProperties": false,
        "properties": {
          "commitment_id": {
            "default": null,
            "maxLength": 250,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "expected_delivery_date": {
            "format": "date",
            "type": "string"
          },
          "line_items": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderLineItemCreate"
            },
            "minItems": 1,
            "type": "array"
          },
          "location_id": {
            "minLength": 1,
            "type": "string"
          },
          "order_date": {
            "format": "date",
            "type": "string"
          },
          "order_type": {},
          "source_location_id": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      },
      "CreateSalesOrderRequest": {
        "additionalProperties": false,
        "properties": {
          "cancel_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "customer": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CustomerArg"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "external_reference": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "is_wholesale": {
            "default": false,
            "type": "boolean"
          },
          "lifecycle": {
            "default": "open",
            "enum": [
              "open",
              "draft"
            ],
            "type": "string"
          },
          "lines": {
            "items": {
              "$ref": "#/components/schemas/SalesOrderLineArg"
            },
            "type": "array"
          },
          "location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "origin": {
            "default": "manual",
            "enum": [
              "manual",
              "api"
            ],
            "type": "string"
          },
          "requested_ship_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sales_channel_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "ship_to": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ShipToArg"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "trading_partner_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "lines"
        ],
        "type": "object"
      },
      "CreateStocktakePayload": {
        "properties": {
          "counted_line_count": {
            "type": "integer"
          },
          "counted_on": {
            "type": "string"
          },
          "counted_total": {
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "line_count": {
            "type": "integer"
          },
          "location_names": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "posted_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "open",
              "posted"
            ],
            "type": "string"
          },
          "system_total": {
            "type": "integer"
          },
          "variance_total": {
            "type": "integer"
          },
          "variance_value_total": {
            "type": "number"
          }
        },
        "required": [
          "counted_line_count",
          "counted_on",
          "counted_total",
          "id",
          "line_count",
          "name",
          "status",
          "system_total",
          "variance_total",
          "variance_value_total"
        ],
        "type": "object"
      },
      "CreateStocktakeRequest": {
        "additionalProperties": false,
        "properties": {
          "counted_on": {
            "format": "date",
            "type": "string"
          },
          "include_all_variants": {
            "default": false,
            "type": "boolean"
          },
          "location_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "variant_ids": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "required": [
          "counted_on",
          "location_ids",
          "name"
        ],
        "type": "object"
      },
      "CreateSuppliersPayload": {
        "properties": {
          "suppliers": {
            "items": {
              "$ref": "#/components/schemas/SupplierDetailsWithContacts"
            },
            "type": "array"
          }
        },
        "required": [
          "suppliers"
        ],
        "type": "object"
      },
      "CreateSuppliersRequest": {
        "additionalProperties": false,
        "properties": {
          "suppliers": {
            "items": {
              "$ref": "#/components/schemas/SupplierPayload"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "CreateSuppliersResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CreateSuppliersPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ApiMessage"
          }
        },
        "type": "object"
      },
      "CreatedPurchaseOrderSummary": {
        "properties": {
          "expected_delivery_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "external_id": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "location_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "order_type": {},
          "source_location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "total_line_items": {
            "type": "integer"
          }
        },
        "required": [
          "external_id",
          "id",
          "location_id",
          "name",
          "total_line_items"
        ],
        "type": "object"
      },
      "CustomField": {
        "properties": {
          "access_type": {
            "description": "What the connector permits on an integration field.",
            "enum": [
              "read_only",
              "read_write"
            ],
            "examples": [
              "read_only"
            ],
            "type": "string"
          },
          "bound_to": {
            "description": "The connection filling this field and the path it reads. Null for a field nobody's connection fills, and null on this single-definition read, which does not look it up: the page read at `/variants/custom-fields` is where it is served.\n",
            "properties": {
              "connection_id": {
                "type": "string"
              },
              "field_key": {
                "examples": [
                  "product.custom_field.fabric"
                ],
                "type": "string"
              },
              "source_path": {
                "examples": [
                  "metafields.custom.fabric"
                ],
                "type": "string"
              }
            },
            "type": [
              "object",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "description": "What the field is for, where somebody wrote it down.",
            "examples": [
              "The mill's stated composition."
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "field_type": {
            "description": "The type a value of this field must parse as.",
            "enum": [
              "string",
              "decimal",
              "integer",
              "boolean",
              "date"
            ],
            "examples": [
              "string"
            ],
            "type": "string"
          },
          "grain": {
            "description": "Which bag holds the values. Null where the definition does not say, which is every definition made before a field could be added from a connection.\n",
            "enum": [
              "product",
              "variant",
              null
            ],
            "examples": [
              "product"
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "The field's id, which is what a variant's custom_fields map is keyed on.",
            "examples": [
              "123"
            ],
            "type": "string"
          },
          "is_active": {
            "description": "Whether the field is in use. An inactive field keeps its values and its history.",
            "examples": [
              true
            ],
            "type": "boolean"
          },
          "is_editable": {
            "description": "Whether the definition itself can be changed.",
            "examples": [
              true
            ],
            "type": "boolean"
          },
          "is_value_editable": {
            "description": "Whether values of this field can be written. Computed, never stored: true when source is `tightly`, otherwise true only when access_type is `read_write`.\n",
            "examples": [
              true
            ],
            "readOnly": true,
            "type": "boolean"
          },
          "label": {
            "description": "What a person calls the field. Falls back to the key humanised where nobody recorded one, so it is never null on a served row.\n",
            "examples": [
              "Fabric"
            ],
            "type": "string"
          },
          "name": {
            "description": "The field's key: what its values are stored under, what a saved filter addresses as `custom_fields.<name>`, and what a family form lists. Print `label` instead.\n",
            "examples": [
              "fabric"
            ],
            "type": "string"
          },
          "source": {
            "description": "Where the field came from: `tightly` for one declared here, otherwise the connector that brought it.\n",
            "examples": [
              "tightly"
            ],
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "used_in": {
            "description": "How many family forms and saved filters name this field. Two zeroes mean nothing reads it; null means it was not counted, which is every read but the page read at `/variants/custom-fields`.\n",
            "properties": {
              "families": {
                "type": "integer"
              },
              "saved_filters": {
                "type": "integer"
              }
            },
            "type": [
              "object",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "CustomerArg": {
        "additionalProperties": false,
        "properties": {
          "display_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "DeleteVariantsFromSupplierRequest": {
        "additionalProperties": false,
        "properties": {
          "exceptions": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "filter_args": {
            "items": {
              "$ref": "#/components/schemas/ProductsFilterArg"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_ids": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "DeliveryLineItemCreate": {
        "additionalProperties": false,
        "properties": {
          "delivered_quantity": {
            "minimum": 0,
            "type": "integer"
          },
          "expected_quantity": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "variant_id": {
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "delivered_quantity",
          "variant_id"
        ],
        "type": "object"
      },
      "DrawerIncomingPOItem": {
        "properties": {
          "delivered_quantity": {
            "default": 0,
            "type": "integer"
          },
          "expected_delivery_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "has_delivery_delay": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "line_items": {
            "items": {
              "$ref": "#/components/schemas/DrawerIncomingPOLineItem"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "ordered_quantity": {
            "default": 0,
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "supplier_signals": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "has_delivery_delay",
          "id",
          "name",
          "status"
        ],
        "type": "object"
      },
      "DrawerIncomingPOLineItem": {
        "properties": {
          "quantity": {
            "type": "integer"
          },
          "variant_id": {
            "type": "string"
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "quantity",
          "variant_id"
        ],
        "type": "object"
      },
      "DrawerIncomingPOsByType": {
        "properties": {
          "order_type": {},
          "purchase_orders": {
            "items": {
              "$ref": "#/components/schemas/DrawerIncomingPOItem"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "DrawerOverviewLocationItem": {
        "properties": {
          "capital_quadrant": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "health": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "incoming_stock": {
            "default": 0,
            "type": "integer"
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "type": "string"
          },
          "stock_on_hand": {
            "type": "integer"
          },
          "stock_value": {
            "type": "number"
          },
          "velocity": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "weeks_of_cover": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "location_id",
          "location_name",
          "stock_on_hand",
          "stock_value"
        ],
        "type": "object"
      },
      "DuplicatePurchaseOrderResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PurchaseOrderItem"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "EmailDict": {
        "properties": {
          "attachments": {
            "items": {
              "$ref": "#/components/schemas/AttachmentDict"
            },
            "type": "array"
          },
          "bcc_emails": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "bcc_names": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "body": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "body_withheld": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "cc_emails": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "cc_names": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "clean_body": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "date": {
            "default": null,
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "decision": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ThreadDecision"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "draft_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "files": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ThreadFiles"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "global_message_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "has_supplier_update_pending": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "html_body": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "in_reply_to": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "is_draft": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "is_read": {
            "type": "boolean"
          },
          "label": {
            "type": "string"
          },
          "message_id": {
            "type": "string"
          },
          "quoted_body": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "read_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "receiver_emails": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "receiver_names": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "says": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ThreadSays"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "sender_email": {
            "type": "string"
          },
          "sender_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sender_photo": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "tag": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "thread_id": {
            "type": "string"
          },
          "waits_on_you": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "with_party": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ThreadParty"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          }
        },
        "required": [
          "attachments",
          "id",
          "is_read",
          "label",
          "message_id",
          "sender_email",
          "thread_id"
        ],
        "type": "object"
      },
      "ExportPurchaseOrderPayload": {
        "additionalProperties": false,
        "properties": {
          "url": {
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "ExportPurchaseOrderResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ExportPurchaseOrderPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "GetContactNoteResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactNotePayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "GetContactNotesResponse": {
        "properties": {
          "data": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/ContactNotePayload"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "GetContactResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "GetContactsFilterResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactsFilterPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "GetContactsTableResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactsTablePayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "GetInventoryTableExportPayload": {
        "properties": {
          "url": {
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "GetInventoryTableExportResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GetInventoryTableExportPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/InventoryApiMessage"
          }
        },
        "type": "object"
      },
      "GetInventoryTableProductViewPayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "filtered_max_unique_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/InventoryTableProductRow"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "filtered_max_unique_size",
          "offset",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "GetInventoryTableProductViewResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GetInventoryTableProductViewPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/InventoryApiMessage"
          }
        },
        "type": "object"
      },
      "GetInventoryTableVariantViewPayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "filtered_max_unique_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/InventoryTableVariantRow"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "filtered_max_unique_size",
          "offset",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "GetInventoryTableVariantViewResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GetInventoryTableVariantViewPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/InventoryApiMessage"
          }
        },
        "type": "object"
      },
      "GetMFPTableResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MFPTableData"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/MFPApiMessage"
          }
        },
        "type": "object"
      },
      "GetMFPVersionsResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MFPVersionsData"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/MFPApiMessage"
          }
        },
        "type": "object"
      },
      "GetOTBRollupResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/OTBRollupData"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/OTBApiMessage"
          }
        },
        "type": "object"
      },
      "GetOrgSKUEventsPayload": {
        "additionalProperties": false,
        "properties": {
          "events": {
            "items": {
              "$ref": "#/components/schemas/OrgSKUEventItem"
            },
            "type": "array"
          },
          "total_count": {
            "type": "integer"
          }
        },
        "required": [
          "events",
          "total_count"
        ],
        "type": "object"
      },
      "GetPimTablePayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "products_count": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/PimTableRow"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "max_size",
          "offset",
          "products_count",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "GetPimVariantsTablePayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/PimVariantTableRow"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          },
          "variants_count": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "max_size",
          "offset",
          "rows",
          "size",
          "variants_count"
        ],
        "type": "object"
      },
      "GetPlanSideResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PlanSideData"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/MFPApiMessage"
          }
        },
        "type": "object"
      },
      "GetProductDrawerIncomingPOsPayload": {
        "properties": {
          "purchase_orders_grouped_by_type": {
            "items": {
              "$ref": "#/components/schemas/DrawerIncomingPOsByType"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "GetProductFiltersPayload": {
        "properties": {
          "categories": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "locations": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "categories"
        ],
        "type": "object"
      },
      "GetProductPayload": {
        "properties": {
          "category": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "category_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country_of_origin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "family": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "family_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "forecast_model": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "gallery_images": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "health_cover_by_variant": {
            "default": null,
            "items": {},
            "type": [
              "array",
              "null"
            ]
          },
          "height": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "hs_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "image_url": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "in_current_season_plan": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "in_stock": {
            "type": "integer"
          },
          "incoming_stock": {
            "type": "integer"
          },
          "inventory_value": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "is_managed": {
            "type": "boolean"
          },
          "is_seasonal": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "last_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated_by": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "launch_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "lead_time": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "length": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "min_order_quantity": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "num_variants": {
            "default": 0,
            "type": "integer"
          },
          "options": {
            "additionalProperties": {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "performance_category_by_location": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/PerformanceCategoryByLocation"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "pim_owned_fields": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "planning_attribute_sources": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "planning_gaps": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "price_band": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "price_band_reason": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "price_band_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_name": {
            "type": "string"
          },
          "product_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "replenishment_mode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "replenishment_strategies": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "sales_channels": {
            "items": {
              "$ref": "#/components/schemas/ProductSalesChannel"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "sell_price": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "selling_window_end": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "selling_window_start": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "shopify_tags": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "shopify_url": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "size_curve": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "size_curve_declared": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "subcategory": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "subcategory_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "suppliers": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/ProductVariantSupplier"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "trading_partners": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "unit_cost": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "uom": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_count": {
            "default": 0,
            "type": "integer"
          },
          "variant_options": {
            "additionalProperties": {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "vendor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          },
          "weight_unit": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            },
            "type": [
              "object",
              "null"
            ]
          }
        },
        "required": [
          "in_stock",
          "incoming_stock",
          "is_managed",
          "product_id",
          "product_name"
        ],
        "type": "object"
      },
      "GetProductPurchaseOrdersPayload": {
        "properties": {
          "purchase_orders_grouped_by_variant": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrdersGroupedByVariant"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "GetProductVariantsPayload": {
        "properties": {
          "variants": {
            "items": {
              "$ref": "#/components/schemas/ProductVariantItem"
            },
            "type": "array"
          }
        },
        "required": [
          "variants"
        ],
        "type": "object"
      },
      "GetProductsCompletenessPayload": {
        "properties": {
          "complete_products": {
            "type": "integer"
          },
          "fields": {
            "items": {
              "$ref": "#/components/schemas/PlanningFieldCompleteness"
            },
            "type": "array"
          },
          "pairs": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "products": {
            "type": "integer"
          }
        },
        "required": [
          "complete_products",
          "fields",
          "products"
        ],
        "type": "object"
      },
      "GetProductsOverviewPayload": {
        "properties": {
          "cards": {
            "items": {
              "$ref": "#/components/schemas/OverviewCardItemWithChange"
            },
            "type": "array"
          }
        },
        "required": [
          "cards"
        ],
        "type": "object"
      },
      "GetPurchaseOrderFiltersPayload": {
        "additionalProperties": false,
        "properties": {
          "filters": {
            "additionalProperties": {
              "items": {},
              "type": "array"
            },
            "type": "object"
          }
        },
        "required": [
          "filters"
        ],
        "type": "object"
      },
      "GetPurchaseOrderFiltersResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GetPurchaseOrderFiltersPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "GetPurchaseOrderResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PurchaseOrderItem"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "GetPurchaseOrdersPayload": {
        "additionalProperties": false,
        "properties": {
          "last_updated_po": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "purchase_orders": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderListItem"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          },
          "status_numbers": {
            "additionalProperties": {
              "type": "integer"
            },
            "type": "object"
          }
        },
        "required": [
          "max_size",
          "offset",
          "purchase_orders",
          "size"
        ],
        "type": "object"
      },
      "GetPurchaseOrdersResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GetPurchaseOrdersPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "GetPurchaseOrdersSupplierUpdatesPayload": {
        "additionalProperties": false,
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderSupplierUpdateItem"
            },
            "type": "array"
          },
          "total_count": {
            "type": "integer"
          }
        },
        "required": [
          "items",
          "total_count"
        ],
        "type": "object"
      },
      "GetPurchaseOrdersSupplierUpdatesResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/GetPurchaseOrdersSupplierUpdatesPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "GetSKUEventsPayload": {
        "additionalProperties": false,
        "properties": {
          "events": {
            "items": {
              "$ref": "#/components/schemas/SKUEventItem"
            },
            "type": "array"
          },
          "filtered_count": {
            "type": "integer"
          },
          "limit": {
            "default": 50,
            "type": "integer"
          },
          "offset": {
            "default": 0,
            "type": "integer"
          },
          "total_count": {
            "type": "integer"
          }
        },
        "required": [
          "events",
          "filtered_count",
          "total_count"
        ],
        "type": "object"
      },
      "GetSalesNewOverviewPayload": {
        "properties": {
          "cards": {
            "description": "Key metrics cards (from existing API)",
            "items": {
              "$ref": "#/components/schemas/CardItem"
            },
            "type": "array"
          },
          "charts": {
            "description": "All charts including existing (revenue, sales_by_variant) and new time-based charts",
            "properties": {
              "forecast": {
                "description": "Forecasted sales data (90 days future from sales_velocity) - simplified 3 fields",
                "items": {
                  "$ref": "#/components/schemas/SalesChartDataPoint"
                },
                "type": "array"
              },
              "historical": {
                "description": "Historical sales data (90 days past from sale_orders) - simplified 3 fields",
                "items": {
                  "$ref": "#/components/schemas/SalesChartDataPoint"
                },
                "type": "array"
              },
              "last_year": {
                "description": "Last year comparison (180 day window) - simplified 3 fields",
                "items": {
                  "$ref": "#/components/schemas/SalesChartDataPoint"
                },
                "type": "array"
              },
              "revenue": {
                "description": "Existing revenue chart with all metrics (9 fields)",
                "items": {
                  "type": "object"
                },
                "type": "array"
              },
              "sales_by_variant": {
                "description": "Existing sales by variant breakdown",
                "items": {
                  "type": "object"
                },
                "type": "array"
              }
            },
            "type": "object"
          }
        },
        "type": "object"
      },
      "GetStocktakeLinesTablePayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/StocktakeLineRow"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "max_size",
          "offset",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "GetStocktakePayload": {
        "properties": {
          "counted_line_count": {
            "type": "integer"
          },
          "counted_on": {
            "type": "string"
          },
          "counted_total": {
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "line_count": {
            "type": "integer"
          },
          "location_names": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "posted_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "open",
              "posted"
            ],
            "type": "string"
          },
          "system_total": {
            "type": "integer"
          },
          "variance_total": {
            "type": "integer"
          },
          "variance_value_total": {
            "type": "number"
          }
        },
        "required": [
          "counted_line_count",
          "counted_on",
          "counted_total",
          "id",
          "line_count",
          "name",
          "status",
          "system_total",
          "variance_total",
          "variance_value_total"
        ],
        "type": "object"
      },
      "GetStocktakesTablePayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/Stocktake"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "max_size",
          "offset",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "GetSupplierDetailsResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SupplierDetailsWithContacts"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ApiMessage"
          }
        },
        "type": "object"
      },
      "GetSupplierNeedsAttentionResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SupplierNeedsAttentionPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ApiMessage"
          }
        },
        "type": "object"
      },
      "GetSupplierOverviewResponse": {
        "properties": {
          "data": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/InventoryCardItem"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "message": {
            "$ref": "#/components/schemas/ApiMessage"
          }
        },
        "type": "object"
      },
      "GetSuppliersTablePayload": {
        "properties": {
          "filtered_max_size": {
            "type": "integer"
          },
          "max_size": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/SupplierDetailsWithContacts"
            },
            "type": "array"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "filtered_max_size",
          "max_size",
          "offset",
          "rows",
          "size"
        ],
        "type": "object"
      },
      "GetVarianceBySupplierPayload": {
        "properties": {
          "buckets": {
            "items": {
              "$ref": "#/components/schemas/StockVarianceBucket"
            },
            "type": "array"
          },
          "measurement": {
            "$ref": "#/components/schemas/StockVarianceMeasurement"
          },
          "totals": {
            "$ref": "#/components/schemas/StockVarianceFigures"
          }
        },
        "required": [
          "measurement",
          "totals"
        ],
        "type": "object"
      },
      "GetVariantDrawerOverviewPayload": {
        "properties": {
          "is_seasonal": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "locations": {
            "items": {
              "$ref": "#/components/schemas/DrawerOverviewLocationItem"
            },
            "type": "array"
          },
          "predecessors": {
            "items": {
              "$ref": "#/components/schemas/VariantMinimalDto"
            },
            "type": "array"
          },
          "successors": {
            "items": {
              "$ref": "#/components/schemas/VariantMinimalDto"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "GetVariantPayload": {
        "properties": {
          "barcode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "category_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country_of_origin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "custom_fields": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "forecast_model": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "gallery_images": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "gtin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "has_more_reliable_supplier": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "health_cover": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/HealthCoverByLocation"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "height": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "hs_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "image_url": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "in_current_season_plan": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "in_stock": {
            "type": "integer"
          },
          "incoming_stock": {
            "type": "integer"
          },
          "inventory_value": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "is_managed": {
            "type": "boolean"
          },
          "is_seasonal": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "last_stockout_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated_by": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "launch_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "length": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "lifecycle": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "performance_category_by_location": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/PerformanceCategoryByLocation"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "predecessors": {
            "items": {
              "$ref": "#/components/schemas/VariantMinimalDto"
            },
            "type": "array"
          },
          "prepack": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "prepack_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_name": {
            "type": "string"
          },
          "production_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "replenishment_mode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "replenishment_strategies": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "sales_channels": {
            "items": {
              "$ref": "#/components/schemas/SalesChannelInfo"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "selected_options": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "sell_price": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "selling_window_end": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "selling_window_start": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "shopify_tags": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "shopify_url": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "successors": {
            "items": {
              "$ref": "#/components/schemas/VariantMinimalDto"
            },
            "type": "array"
          },
          "suppliers": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/VariantSupplier"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "trading_partners": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "unit_cost_currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "uom": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_name": {
            "type": "string"
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "vendor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "weight_unit": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "in_stock",
          "incoming_stock",
          "is_managed",
          "product_id",
          "product_name",
          "variant_id",
          "variant_name"
        ],
        "type": "object"
      },
      "HealthCoverByLocation": {
        "additionalProperties": false,
        "properties": {
          "cover": {
            "type": "integer"
          },
          "health": {
            "type": "string"
          },
          "location": {
            "type": "string"
          }
        },
        "required": [
          "cover",
          "health",
          "location"
        ],
        "type": "object"
      },
      "HorizonFloorRange": {
        "additionalProperties": false,
        "properties": {
          "max": {
            "default": false,
            "type": "boolean"
          },
          "min": {
            "default": false,
            "type": "boolean"
          }
        },
        "type": "object"
      },
      "ImportSalesOrdersRequest": {
        "additionalProperties": false,
        "properties": {
          "mappings": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          },
          "s3_key": {
            "type": "string"
          }
        },
        "required": [
          "mappings",
          "s3_key"
        ],
        "type": "object"
      },
      "ImportStocktakeCountsPayload": {
        "properties": {
          "skipped": {
            "type": "integer"
          },
          "unmatched_skus": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "updated": {
            "type": "integer"
          }
        },
        "required": [
          "skipped",
          "updated"
        ],
        "type": "object"
      },
      "ImportStocktakeCountsRequest": {
        "additionalProperties": false,
        "properties": {
          "mappings": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          },
          "s3_key": {
            "type": "string"
          }
        },
        "required": [
          "mappings",
          "s3_key"
        ],
        "type": "object"
      },
      "InventoryApiMessage": {
        "additionalProperties": false,
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "inventory",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "InventoryCardItem": {
        "properties": {
          "info": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "key": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "value": {
            "default": null
          }
        },
        "required": [
          "key",
          "unit"
        ],
        "type": "object"
      },
      "InventoryTableProductRow": {
        "additionalProperties": false,
        "properties": {
          "available_locations": {
            "default": null,
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "batch_size": {
            "$ref": "#/components/schemas/RangeField"
          },
          "cover": {
            "$ref": "#/components/schemas/RangeField"
          },
          "cover_exceeds_horizon": {
            "$ref": "#/components/schemas/HorizonFloorRange"
          },
          "health": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "lead_time": {
            "$ref": "#/components/schemas/RangeField"
          },
          "location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location_lead_times": {
            "$ref": "#/components/schemas/RangeField"
          },
          "location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "metafields": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "min_order_quantity": {
            "$ref": "#/components/schemas/RangeField"
          },
          "missing_fields": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "next_arrival": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "performance_category": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_image": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published_at": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "sales_velocity_30_days": {
            "type": "number"
          },
          "sales_velocity_7_days": {
            "type": "number"
          },
          "sales_velocity_90_days": {
            "type": "number"
          },
          "sell_price": {
            "$ref": "#/components/schemas/RangeField"
          },
          "shopify_tags": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "suppliers": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "total_available_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "total_in_basket_quantity": {
            "type": "integer"
          },
          "total_on_order_quantity": {
            "type": "integer"
          },
          "total_reserved_quantity": {
            "type": "integer"
          },
          "total_sales_velocity": {
            "type": "number"
          },
          "total_stock_value": {
            "default": null
          },
          "unit_cost": {
            "$ref": "#/components/schemas/RangeField"
          },
          "variants": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": "array"
          },
          "weeks_of_cover": {
            "$ref": "#/components/schemas/RangeField"
          }
        },
        "required": [
          "batch_size",
          "cover",
          "lead_time",
          "min_order_quantity",
          "product_id",
          "sales_velocity_30_days",
          "sales_velocity_7_days",
          "sales_velocity_90_days",
          "sell_price",
          "suppliers",
          "total_in_basket_quantity",
          "total_on_order_quantity",
          "total_reserved_quantity",
          "total_sales_velocity",
          "unit_cost",
          "variants",
          "weeks_of_cover"
        ],
        "type": "object"
      },
      "InventoryTableVariantRow": {
        "additionalProperties": false,
        "properties": {
          "available_locations": {
            "default": null,
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "available_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "available_to_sell": {
            "default": 0,
            "type": "integer"
          },
          "barcode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "batch_size": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "capital_quadrant": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "cover": {
            "default": null
          },
          "cover_exceeds_horizon": {
            "default": false
          },
          "cover_locations": {
            "default": null,
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "custom_fields": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "has_more_reliable_supplier": {
            "default": false,
            "type": "boolean"
          },
          "health": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "in_basket_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "is_managed": {
            "type": "boolean"
          },
          "last_stockout_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location_lead_times": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": "array"
          },
          "location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "metafields": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "min_order_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "missing_fields": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "next_arrival": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "on_order": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "po_generation_enabled": {
            "type": "boolean"
          },
          "prepack": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "prepack_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_image": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "production_type": {
            "type": "string"
          },
          "published_at": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "replenishment_frequency": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "replenishment_set": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ReplenishmentSetDetails"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "reserved_quantity": {
            "type": "integer"
          },
          "sales_velocity": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "sales_velocity_30_days": {
            "type": "number"
          },
          "sales_velocity_7_days": {
            "type": "number"
          },
          "sales_velocity_90_days": {
            "type": "number"
          },
          "sales_velocity_incl_bundles": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "sell_price": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "shopify_tags": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "stock_value": {
            "default": null
          },
          "supplier_details": {
            "items": {
              "$ref": "#/components/schemas/SupplierDetails"
            },
            "type": "array"
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "weeks_of_cover": {
            "default": null
          }
        },
        "required": [
          "is_managed",
          "po_generation_enabled",
          "product_id",
          "production_type",
          "reserved_quantity",
          "sales_velocity_30_days",
          "sales_velocity_7_days",
          "sales_velocity_90_days",
          "supplier_details",
          "variant_id"
        ],
        "type": "object"
      },
      "ListMFPResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/MFPSummary"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "message": {
            "$ref": "#/components/schemas/MFPApiMessage"
          }
        },
        "type": "object"
      },
      "MFPApiMessage": {
        "additionalProperties": false,
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "mfp",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "MFPMonth": {
        "additionalProperties": false,
        "properties": {
          "is_completed": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "label": {
            "type": "string"
          },
          "month": {
            "type": "integer"
          },
          "year": {
            "type": "integer"
          }
        },
        "required": [
          "label",
          "month",
          "year"
        ],
        "type": "object"
      },
      "MFPSummary": {
        "additionalProperties": false,
        "properties": {
          "baseline_source": {
            "type": "string"
          },
          "cell_count": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "fiscal_year": {
            "type": "integer"
          },
          "fiscal_year_end_month": {
            "type": "integer"
          },
          "fiscal_year_start_month": {
            "type": "integer"
          },
          "mfp_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "total_planned_revenue": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "baseline_source",
          "fiscal_year",
          "fiscal_year_end_month",
          "fiscal_year_start_month",
          "mfp_id",
          "name",
          "status"
        ],
        "type": "object"
      },
      "MFPTableData": {
        "additionalProperties": false,
        "properties": {
          "buy_plan": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BuyPlanData"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "filters": {
            "additionalProperties": {
              "default": null,
              "type": [
                "string",
                "null"
              ]
            },
            "type": "object"
          },
          "metric": {
            "type": "string"
          },
          "mfp": {
            "$ref": "#/components/schemas/MFPSummary"
          },
          "months": {
            "items": {
              "$ref": "#/components/schemas/MFPMonth"
            },
            "type": "array"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/MFPTableRow"
            },
            "type": "array"
          },
          "totals": {
            "$ref": "#/components/schemas/MFPTableTotals"
          },
          "view": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          }
        },
        "required": [
          "filters",
          "metric",
          "mfp",
          "months",
          "rows",
          "totals"
        ],
        "type": "object"
      },
      "MFPTableRow": {
        "additionalProperties": false,
        "properties": {
          "actual": {
            "additionalProperties": {
              "default": null,
              "type": [
                "number",
                "null"
              ]
            },
            "type": "object"
          },
          "actual_total": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "category": {
            "type": "string"
          },
          "planned": {
            "additionalProperties": {
              "default": null,
              "type": [
                "number",
                "null"
              ]
            },
            "type": "object"
          },
          "planned_total": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "actual",
          "category",
          "planned"
        ],
        "type": "object"
      },
      "MFPTableTotals": {
        "additionalProperties": false,
        "properties": {
          "actual": {
            "additionalProperties": {
              "default": null,
              "type": [
                "number",
                "null"
              ]
            },
            "type": "object"
          },
          "actual_total": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "planned": {
            "additionalProperties": {
              "default": null,
              "type": [
                "number",
                "null"
              ]
            },
            "type": "object"
          },
          "planned_total": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "actual",
          "planned"
        ],
        "type": "object"
      },
      "MFPVersionSummary": {
        "additionalProperties": false,
        "properties": {
          "budget_state": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "budget_stated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "gross_margin_pct": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "is_live": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "published_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published_by": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "total_planned_cogs": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "total_planned_revenue": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "version_id": {
            "type": "integer"
          },
          "version_number": {
            "type": "integer"
          }
        },
        "required": [
          "is_live",
          "name",
          "version_id",
          "version_number"
        ],
        "type": "object"
      },
      "MFPVersionsData": {
        "additionalProperties": false,
        "properties": {
          "live_version_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "mfp": {
            "additionalProperties": {},
            "type": "object"
          },
          "versions": {
            "items": {
              "$ref": "#/components/schemas/MFPVersionSummary"
            },
            "type": "array"
          },
          "working": {
            "additionalProperties": {},
            "type": "object"
          }
        },
        "required": [
          "mfp",
          "working"
        ],
        "type": "object"
      },
      "ManufacturingOrderExceptionItem": {
        "additionalProperties": false,
        "properties": {
          "location_id": {
            "type": "string"
          },
          "recommended_location_id": {
            "type": "string"
          },
          "variant_id": {
            "type": "string"
          }
        },
        "required": [
          "location_id",
          "recommended_location_id",
          "variant_id"
        ],
        "type": "object"
      },
      "OTBApiMessage": {
        "additionalProperties": false,
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "otb",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "OTBRollupData": {
        "additionalProperties": false,
        "properties": {
          "basis": {
            "type": "string"
          },
          "channel_attribution": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "filters": {
            "additionalProperties": {},
            "type": "object"
          },
          "guardrail": {
            "additionalProperties": {},
            "type": "object"
          },
          "mfp": {
            "additionalProperties": {},
            "type": "object"
          },
          "notes": {
            "additionalProperties": {},
            "type": "object"
          },
          "phase": {
            "type": "string"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/OTBRollupRow"
            },
            "type": "array"
          },
          "scope": {
            "type": "string"
          },
          "summary": {
            "additionalProperties": {},
            "type": "object"
          },
          "totals": {
            "additionalProperties": {},
            "type": "object"
          }
        },
        "required": [
          "basis",
          "filters",
          "guardrail",
          "mfp",
          "notes",
          "phase",
          "rows",
          "scope",
          "summary",
          "totals"
        ],
        "type": "object"
      },
      "OTBRollupRow": {
        "additionalProperties": false,
        "properties": {
          "active_mode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": "string"
          },
          "chase_reserve": {
            "type": "number"
          },
          "committed": {
            "type": "number"
          },
          "committed_continuity": {
            "default": 0.0,
            "type": "number"
          },
          "committed_seasonal": {
            "default": 0.0,
            "type": "number"
          },
          "continuity_consumption_ratio": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "continuity_envelope_alert": {
            "default": false,
            "type": "boolean"
          },
          "current_excluded_amount": {
            "default": 0.0,
            "type": "number"
          },
          "estimated": {
            "default": false,
            "type": "boolean"
          },
          "exclusion_active": {
            "default": false,
            "type": "boolean"
          },
          "forward_continuity_estimate": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "forward_recommended_buy": {
            "type": "number"
          },
          "forward_seasonal_estimate": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "max_band": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "remaining": {
            "type": "number"
          },
          "remaining_free": {
            "type": "number"
          },
          "reserved": {
            "type": "number"
          },
          "reserved_continuity": {
            "default": 0.0,
            "type": "number"
          },
          "reserved_purchase_orders": {
            "items": {
              "$ref": "#/components/schemas/OTBVendorPurchaseOrder"
            },
            "type": "array"
          },
          "reserved_seasonal": {
            "default": 0.0,
            "type": "number"
          },
          "season_envelope": {
            "type": "number"
          },
          "short_week": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "substate": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "surplus_free": {
            "type": "number"
          },
          "target_amount": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "tight_week": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "variance": {
            "type": "number"
          }
        },
        "required": [
          "category",
          "chase_reserve",
          "committed",
          "forward_recommended_buy",
          "remaining",
          "remaining_free",
          "reserved",
          "season_envelope",
          "status",
          "surplus_free",
          "variance"
        ],
        "type": "object"
      },
      "OTBVendorPurchaseOrder": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "expected_delivery_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "po_id": {
            "type": "integer"
          },
          "reference": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "value": {
            "type": "number"
          }
        },
        "required": [
          "po_id",
          "reference",
          "status",
          "value"
        ],
        "type": "object"
      },
      "OrgSKUEventItem": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "created_by_user_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "event_date": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "additionalProperties": {},
            "type": "object"
          },
          "new_value": {
            "type": "string"
          },
          "previous_value": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "event_date",
          "event_type",
          "id",
          "metadata",
          "new_value",
          "variant_id"
        ],
        "type": "object"
      },
      "OverviewCardItemWithChange": {
        "properties": {
          "change": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "higher_better": {
            "default": true,
            "type": "boolean"
          },
          "info": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "key": {
            "type": "string"
          },
          "previous_value": {
            "default": null
          },
          "unit": {
            "type": "string"
          },
          "value": {
            "default": null
          }
        },
        "required": [
          "key",
          "unit"
        ],
        "type": "object"
      },
      "PatchSalesOrderLineArg": {
        "additionalProperties": false,
        "properties": {
          "order_line_item_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": "integer"
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "unit_price_cents": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "variant_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "quantity"
        ],
        "type": "object"
      },
      "PatchSalesOrderRequest": {
        "additionalProperties": false,
        "properties": {
          "cancel_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "external_reference": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "lines": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/PatchSalesOrderLineArg"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "requested_ship_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "PerformanceCategoryByLocation": {
        "additionalProperties": false,
        "properties": {
          "capital_quadrant": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location": {
            "type": "string"
          },
          "performance_category": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "location"
        ],
        "type": "object"
      },
      "PimSalesChannel": {
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "type": "object"
      },
      "PimTableRow": {
        "properties": {
          "category": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "category_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country_of_origin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "demand_group_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "demand_group_lead": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "gallery_images": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "gtin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "height": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "hs_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "is_seasonal": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "last_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated_by": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "length": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "missing_required_count": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "num_variants": {
            "type": "integer"
          },
          "planning_gaps": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": "array"
          },
          "price_band": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "price_band_reason": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "price_band_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "replenishment_mode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sales_channels": {
            "items": {
              "$ref": "#/components/schemas/PimSalesChannel"
            },
            "type": "array"
          },
          "sell_price": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "selling_window_end": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "selling_window_start": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "size_curve": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "unit_cost": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "uom": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_options": {
            "additionalProperties": {},
            "type": "object"
          },
          "vendor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "weight_unit": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          }
        },
        "required": [
          "gallery_images",
          "num_variants",
          "product_id",
          "sales_channels",
          "variant_options"
        ],
        "type": "object"
      },
      "PimVariantTableRow": {
        "properties": {
          "carton_height": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "carton_length": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "carton_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "carton_weight": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "carton_width": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "category": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country_of_origin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "gtin": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "height": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "hs_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "image_url": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_updated_by": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "length": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "physical_basis": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sales_channels": {
            "items": {
              "$ref": "#/components/schemas/PimSalesChannel"
            },
            "type": "array"
          },
          "selected_options": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "sell_price": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "unit_cbm": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "unit_kg": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "uom": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "vendor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "weight_unit": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "product_id",
          "sales_channels",
          "variant_id"
        ],
        "type": "object"
      },
      "PlanSideBaselines": {
        "additionalProperties": false,
        "properties": {
          "buying_budget": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "comparable": {
            "type": "string"
          },
          "forecast": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "single_baseline_reason": {
            "type": "string"
          }
        },
        "required": [
          "buying_budget",
          "comparable",
          "forecast",
          "plan",
          "single_baseline_reason"
        ],
        "type": "object"
      },
      "PlanSideBasisNotes": {
        "additionalProperties": false,
        "properties": {
          "budget": {
            "type": "string"
          },
          "chase_reserve": {
            "type": "string"
          },
          "intake": {
            "type": "string"
          },
          "plan_of_record": {
            "type": "string"
          },
          "plan_sales": {
            "type": "string"
          },
          "sales_plan": {
            "type": "string"
          }
        },
        "required": [
          "budget",
          "chase_reserve",
          "intake",
          "plan_of_record",
          "plan_sales",
          "sales_plan"
        ],
        "type": "object"
      },
      "PlanSideCategory": {
        "additionalProperties": false,
        "properties": {
          "budget": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "category": {
            "default": "",
            "type": "string"
          },
          "chase_reserve": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "cogs_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_committed": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_received": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_reserved": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "markdown_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "plan_closing_stock_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_markdown_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_receipts_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_sales_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "sales_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "sales_plan_last_year": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          }
        },
        "required": [
          "budget",
          "chase_reserve",
          "cogs_plan",
          "intake_committed",
          "intake_received",
          "intake_reserved",
          "markdown_plan",
          "plan_closing_stock_cost",
          "plan_markdown_cost",
          "plan_receipts_cost",
          "plan_sales_cost",
          "sales_plan",
          "sales_plan_last_year"
        ],
        "type": "object"
      },
      "PlanSideCell": {
        "additionalProperties": false,
        "properties": {
          "budget": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "category": {
            "default": "",
            "type": "string"
          },
          "chase_reserve": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "cogs_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_committed": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_received": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_reserved": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "markdown_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "period": {
            "default": "",
            "type": "string"
          },
          "plan_closing_stock_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_markdown_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_receipts_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_sales_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "sales_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "sales_plan_last_year": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          }
        },
        "required": [
          "budget",
          "chase_reserve",
          "cogs_plan",
          "intake_committed",
          "intake_received",
          "intake_reserved",
          "markdown_plan",
          "plan_closing_stock_cost",
          "plan_markdown_cost",
          "plan_receipts_cost",
          "plan_sales_cost",
          "sales_plan",
          "sales_plan_last_year"
        ],
        "type": "object"
      },
      "PlanSideCount": {
        "additionalProperties": false,
        "properties": {
          "grain": {
            "type": "string"
          },
          "population": {
            "type": "string"
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "value": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "grain",
          "population"
        ],
        "type": "object"
      },
      "PlanSideCoverage": {
        "additionalProperties": false,
        "properties": {
          "attributed_intake": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "categories": {
            "$ref": "#/components/schemas/PlanSideCount"
          },
          "cells": {
            "$ref": "#/components/schemas/PlanSideCount"
          },
          "chase_reserve_cost_estimated": {
            "default": false,
            "type": "boolean"
          },
          "months": {
            "$ref": "#/components/schemas/PlanSideCount"
          },
          "months_budgeted": {
            "$ref": "#/components/schemas/PlanSideCount"
          },
          "months_planned": {
            "$ref": "#/components/schemas/PlanSideCount"
          },
          "plan_channels": {
            "$ref": "#/components/schemas/PlanSideCount"
          },
          "unattributed_intake": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "unattributed_intake_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "window_intake": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          }
        },
        "required": [
          "attributed_intake",
          "categories",
          "cells",
          "months",
          "months_budgeted",
          "months_planned",
          "plan_channels",
          "unattributed_intake",
          "window_intake"
        ],
        "type": "object"
      },
      "PlanSideData": {
        "additionalProperties": false,
        "properties": {
          "baselines": {
            "$ref": "#/components/schemas/PlanSideBaselines"
          },
          "basis_notes": {
            "$ref": "#/components/schemas/PlanSideBasisNotes"
          },
          "buy_plan": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BuyPlanData"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "calendar": {
            "type": "string"
          },
          "categories": {
            "items": {
              "$ref": "#/components/schemas/PlanSideCategory"
            },
            "type": "array"
          },
          "coverage": {
            "$ref": "#/components/schemas/PlanSideCoverage"
          },
          "declared_terms": {
            "$ref": "#/components/schemas/PlanSideDeclaredTerms"
          },
          "full_year": {
            "$ref": "#/components/schemas/PlanSideFigures"
          },
          "grain": {
            "type": "string"
          },
          "months": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "months_detail": {
            "items": {
              "$ref": "#/components/schemas/PlanSideMonthDetail"
            },
            "type": "array"
          },
          "non_merchandise_categories": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "plan": {
            "$ref": "#/components/schemas/PlanSidePlan"
          },
          "plan_of_record": {
            "$ref": "#/components/schemas/PlanSidePlanOfRecord"
          },
          "rows": {
            "items": {
              "$ref": "#/components/schemas/PlanSideCell"
            },
            "type": "array"
          },
          "year_over_year": {
            "$ref": "#/components/schemas/PlanSideYearOverYear"
          }
        },
        "required": [
          "baselines",
          "basis_notes",
          "calendar",
          "coverage",
          "declared_terms",
          "full_year",
          "grain",
          "plan",
          "plan_of_record",
          "year_over_year"
        ],
        "type": "object"
      },
      "PlanSideDeclaredTerm": {
        "additionalProperties": false,
        "properties": {
          "declared_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "declared_by": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "key": {
            "type": "string"
          },
          "label": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "value": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "key"
        ],
        "type": "object"
      },
      "PlanSideDeclaredTerms": {
        "additionalProperties": false,
        "properties": {
          "declared": {
            "type": "integer"
          },
          "fiscal_year": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "note": {
            "type": "string"
          },
          "terms": {
            "items": {
              "$ref": "#/components/schemas/PlanSideDeclaredTerm"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "declared",
          "note",
          "total"
        ],
        "type": "object"
      },
      "PlanSideFigures": {
        "additionalProperties": false,
        "properties": {
          "budget": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "chase_reserve": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "cogs_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_committed": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_received": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "intake_reserved": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "markdown_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "plan_closing_stock_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_markdown_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_receipts_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "plan_sales_cost": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "sales_plan": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          },
          "sales_plan_last_year": {
            "$ref": "#/components/schemas/PlanSideMoneyAtNetSales"
          }
        },
        "required": [
          "budget",
          "chase_reserve",
          "cogs_plan",
          "intake_committed",
          "intake_received",
          "intake_reserved",
          "markdown_plan",
          "plan_closing_stock_cost",
          "plan_markdown_cost",
          "plan_receipts_cost",
          "plan_sales_cost",
          "sales_plan",
          "sales_plan_last_year"
        ],
        "type": "object"
      },
      "PlanSideForecastSource": {
        "additionalProperties": false,
        "properties": {
          "cycle_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "type": "string"
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "signed_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "version_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "kind"
        ],
        "type": "object"
      },
      "PlanSideMoneyAtCost": {
        "additionalProperties": false,
        "properties": {
          "basis": {
            "default": "unit_cost",
            "type": "string"
          },
          "cents": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "usd": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "PlanSideMoneyAtNetSales": {
        "additionalProperties": false,
        "properties": {
          "basis": {
            "default": "net_sales",
            "type": "string"
          },
          "cents": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "usd": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "PlanSideMonthDetail": {
        "additionalProperties": false,
        "properties": {
          "budget": {
            "$ref": "#/components/schemas/PlanSideMoneyAtCost"
          },
          "period": {
            "type": "string"
          }
        },
        "required": [
          "budget",
          "period"
        ],
        "type": "object"
      },
      "PlanSidePlan": {
        "additionalProperties": false,
        "properties": {
          "fiscal_year": {
            "type": "integer"
          },
          "fiscal_year_start_month": {
            "type": "integer"
          },
          "live_version_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "mfp_id": {
            "type": "string"
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "published": {
            "type": "boolean"
          },
          "window": {
            "$ref": "#/components/schemas/PlanSideWindow"
          }
        },
        "required": [
          "fiscal_year",
          "fiscal_year_start_month",
          "mfp_id",
          "published",
          "window"
        ],
        "type": "object"
      },
      "PlanSidePlanOfRecord": {
        "additionalProperties": false,
        "properties": {
          "budget_note": {
            "type": "string"
          },
          "calendar": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "commitments": {
            "items": {
              "$ref": "#/components/schemas/PlanSidePlanOfRecordCommitment"
            },
            "type": "array"
          },
          "coverage": {
            "$ref": "#/components/schemas/PlanSidePlanOfRecordCoverage"
          },
          "forecast_note": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "budget_note",
          "commitments",
          "coverage"
        ],
        "type": "object"
      },
      "PlanSidePlanOfRecordCommitment": {
        "additionalProperties": false,
        "properties": {
          "budget": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "budget_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "categories": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "commitment_id": {
            "type": "string"
          },
          "folded": {
            "type": "boolean"
          },
          "forecast_source": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PlanSideForecastSource"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "months": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "season_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "window": {
            "additionalProperties": {},
            "type": "object"
          }
        },
        "required": [
          "commitment_id",
          "folded",
          "name",
          "window"
        ],
        "type": "object"
      },
      "PlanSidePlanOfRecordCoverage": {
        "additionalProperties": false,
        "properties": {
          "cap": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "commitments": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "folded": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "months_dropped": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "PlanSideWindow": {
        "additionalProperties": false,
        "properties": {
          "end_exclusive": {
            "type": "string"
          },
          "start": {
            "type": "string"
          }
        },
        "required": [
          "end_exclusive",
          "start"
        ],
        "type": "object"
      },
      "PlanSideYearOverYear": {
        "additionalProperties": false,
        "properties": {
          "actuals_note": {
            "type": "string"
          },
          "actuals_read": {
            "type": "string"
          },
          "actuals_shift_days": {
            "type": "integer"
          },
          "plan_shift": {
            "type": "string"
          },
          "prior_fiscal_year": {
            "type": "integer"
          },
          "prior_plan_mfp_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "prior_plan_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "actuals_note",
          "actuals_read",
          "actuals_shift_days",
          "plan_shift",
          "prior_fiscal_year"
        ],
        "type": "object"
      },
      "PlanningFieldCompleteness": {
        "properties": {
          "affected_pairs": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "field": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "missing_products": {
            "type": "integer"
          },
          "reason": {
            "type": "string"
          },
          "review_products": {
            "type": "integer"
          }
        },
        "required": [
          "field",
          "label",
          "missing_products",
          "reason",
          "review_products"
        ],
        "type": "object"
      },
      "PostStocktakePayload": {
        "properties": {
          "counted_line_count": {
            "type": "integer"
          },
          "counted_on": {
            "type": "string"
          },
          "counted_total": {
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "line_count": {
            "type": "integer"
          },
          "location_names": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "posted_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "open",
              "posted"
            ],
            "type": "string"
          },
          "system_total": {
            "type": "integer"
          },
          "variance_total": {
            "type": "integer"
          },
          "variance_value_total": {
            "type": "number"
          }
        },
        "required": [
          "counted_line_count",
          "counted_on",
          "counted_total",
          "id",
          "line_count",
          "name",
          "status",
          "system_total",
          "variance_total",
          "variance_value_total"
        ],
        "type": "object"
      },
      "PriceEngineBlock": {
        "additionalProperties": false,
        "properties": {
          "basis": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "computed_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "confidence": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "inputs_through": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "run_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "stale_after": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "state"
        ],
        "type": "object"
      },
      "PriceSensitivityClassification": {
        "properties": {
          "basis": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "confidence": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "data_through": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "engine": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PriceEngineBlock"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "evidence": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "expected_error": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "n_events": {
            "type": "integer"
          },
          "periods_observed": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "price_variation": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "reference_basis": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "reference_elasticity": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "reference_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "required_price_variation": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "shrunk_effect_down": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "shrunk_effect_up": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "sign_agreement": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "summary": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "tier": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "worth_considering": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "n_events"
        ],
        "type": "object"
      },
      "ProductSalesChannel": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "type": "object"
      },
      "ProductVariantItem": {
        "properties": {
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "variant_id"
        ],
        "type": "object"
      },
      "ProductVariantSupplier": {
        "additionalProperties": false,
        "properties": {
          "is_default": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "supplier_id": {
            "type": "string"
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "supplier_id"
        ],
        "type": "object"
      },
      "ProductsFilterArg": {
        "additionalProperties": false,
        "properties": {
          "key": {
            "type": "string"
          },
          "operation": {
            "type": "string"
          },
          "value": {
            "default": null
          }
        },
        "required": [
          "key",
          "operation"
        ],
        "type": "object"
      },
      "PurchaseOrderAddressUpdate": {
        "additionalProperties": false,
        "properties": {
          "address_line_1": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "address_line_2": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "default": null,
            "minLength": 0,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "PurchaseOrderApiMessage": {
        "additionalProperties": false,
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "purchase_order",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "PurchaseOrderAttentionItem": {
        "properties": {
          "email": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/EmailDict"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message_key": {
            "type": "string"
          },
          "po_external_id": {
            "type": "string"
          },
          "po_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "message_key",
          "po_external_id",
          "po_id",
          "status"
        ],
        "type": "object"
      },
      "PurchaseOrderItem": {
        "properties": {
          "additional_costs": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "batch_size": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "billed_at": {
            "type": "string"
          },
          "cancel_date": {
            "default": null,
            "description": "The last day of the ship window, inclusive. After it the buyer may walk away from whatever the vendor has not shipped. Null where the order was placed without a window.",
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "comment": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "commitment_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "completed_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "completion_sentence": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "confirmation_status_updated_at": {
            "type": "string"
          },
          "container_plan": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "date_drafted": {
            "type": "string"
          },
          "delivery_address": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "delivery_status_updated_at": {
            "type": "string"
          },
          "drafted_status_updated_at": {
            "type": "string"
          },
          "expected_delivery_date": {
            "type": "string"
          },
          "external_id": {
            "type": "string"
          },
          "hold_reason": {
            "default": null,
            "description": "Why the current hold was taken, in the words whoever took it typed. Non-null whenever `on_hold` is true, because a hold with no reason is refused. Null when the order is not held. Cleared on release; the history stays in `status_audit`.",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "integrations": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "invoice_address": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "is_active": {
            "type": "boolean"
          },
          "is_billed": {
            "type": "boolean"
          },
          "is_completed": {
            "type": "boolean"
          },
          "is_proposal": {
            "type": "boolean"
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "type": "string"
          },
          "manufacturable_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_product_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "n_variants": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "on_hold": {
            "default": false,
            "description": "True while this order is held. A held order refuses every status advance except cancellation until the hold is released. Never null: an order is held or it is not.",
            "type": "boolean"
          },
          "order_type": {},
          "production_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "products": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderProductAggregate"
            },
            "type": "array"
          },
          "quantity_to_manufacture": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "recommended_location_address": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "recommended_location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "recommended_location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "recommended_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "request_status_updated_at": {
            "type": "string"
          },
          "ship_window_start": {
            "default": null,
            "description": "The first day the vendor may ship this order. This is the window the vendor agreed to, not the day the goods are expected: that is `expected_delivery_date`. Null where the order was placed without a window.",
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "shipment_status_updated_at": {
            "type": "string"
          },
          "source_address": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "source_location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "source_location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "DRAFTED",
              "FULLY_CONFIRMED",
              "FULLY_SHIPPED",
              "FULLY_DELIVERED",
              "CANCELLED",
              "IN_PRODUCTION",
              "COMPLETED"
            ],
            "type": "string"
          },
          "status_audit": {
            "description": "Append-only log of intent against this order: one entry per approve, hold, release, issue or push-to-WMS, each carrying `event`, `actor`, `reason`, `via` and `at`. Distinct from the `*_status_updated_at` ladder, which records WHEN a status changed but never WHO changed it or WHY. Empty on an order nobody has acted on.",
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": "array"
          },
          "status_updated_at": {
            "type": "string"
          },
          "supplier_contact_email": {
            "type": "string"
          },
          "supplier_contact_name": {
            "type": "string"
          },
          "supplier_contact_phone": {
            "type": "string"
          },
          "supplier_contact_preferred_method": {
            "type": "string"
          },
          "supplier_currency": {
            "type": "string"
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_min_order_value": {
            "description": "Legacy whole-currency minimum, rounded up conservatively. Use supplier_min_order_value_amount for the exact amount.",
            "type": "integer"
          },
          "supplier_min_order_value_amount": {
            "default": null,
            "description": "Exact canonical supplier minimum in supplier currency, including fractional units. Null means unstated.",
            "type": [
              "number",
              "null"
            ]
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "timeline": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "total_cost": {
            "type": "number"
          },
          "total_quantity": {
            "type": "integer"
          },
          "xero_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "xero_status": {},
          "xero_url": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "billed_at",
          "confirmation_status_updated_at",
          "date_drafted",
          "delivery_status_updated_at",
          "drafted_status_updated_at",
          "expected_delivery_date",
          "external_id",
          "id",
          "is_active",
          "is_billed",
          "is_completed",
          "is_proposal",
          "location_id",
          "location_name",
          "n_variants",
          "name",
          "products",
          "request_status_updated_at",
          "shipment_status_updated_at",
          "status",
          "status_updated_at",
          "supplier_contact_email",
          "supplier_contact_name",
          "supplier_contact_phone",
          "supplier_contact_preferred_method",
          "supplier_currency",
          "supplier_min_order_value",
          "total_cost",
          "total_quantity"
        ],
        "type": "object"
      },
      "PurchaseOrderItemUpdate": {
        "additionalProperties": false,
        "properties": {
          "confirmed_quantity": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "delivered_quantity": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "quantity": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "shipped_quantity": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "minimum": 0.0,
            "type": [
              "number",
              "null"
            ]
          },
          "variant_id": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "PurchaseOrderLineItemCreate": {
        "additionalProperties": false,
        "properties": {
          "currency": {
            "default": "USD",
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "minimum": 1,
            "type": "integer"
          },
          "unit_cost": {
            "default": null,
            "minimum": 0.0,
            "type": [
              "number",
              "null"
            ]
          },
          "variant_id": {
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "quantity",
          "variant_id"
        ],
        "type": "object"
      },
      "PurchaseOrderLineItemEntity": {
        "properties": {
          "available_to_allocate": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "barcode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "cartons": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "cbm": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "confirmed_quantity": {
            "type": "integer"
          },
          "cover": {
            "type": "integer"
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "custom_fields": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "default_supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "delivered_quantity": {
            "type": "integer"
          },
          "health": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "hs_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "in_stock": {
            "type": "integer"
          },
          "kg": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "landed_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "landed_unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "line_verdict": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "line_verdict_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "line_verdict_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "metafields": {
            "additionalProperties": {},
            "default": null,
            "type": [
              "object",
              "null"
            ]
          },
          "per_batch": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "production_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": "integer"
          },
          "recommended_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "retail_price_amount": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "shipped_quantity": {
            "type": "integer"
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "total_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "understocked_high_impact": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_name": {
            "type": "string"
          }
        },
        "required": [
          "confirmed_quantity",
          "cover",
          "delivered_quantity",
          "id",
          "in_stock",
          "quantity",
          "shipped_quantity",
          "variant_id",
          "variant_name"
        ],
        "type": "object"
      },
      "PurchaseOrderListItem": {
        "properties": {
          "billed_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "cancel_date": {
            "default": null,
            "description": "The last day of the ship window, inclusive. After it the buyer may walk away from whatever the vendor has not shipped. Null where the order was placed without a window.",
            "type": [
              "string",
              "null"
            ]
          },
          "commitment_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "commitment_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "completed_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "confirmation_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "date_created": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "date_drafted": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "delivery_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "drafted_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "expected_delivery_date": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "external_id": {
            "type": "string"
          },
          "has_delivery_delay": {
            "default": false,
            "type": "boolean"
          },
          "hold_reason": {
            "default": null,
            "description": "Why the current hold was taken, in the words whoever took it typed. Non-null whenever `on_hold` is true, because a hold with no reason is refused. Null when the order is not held.",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "integrations": {
            "additionalProperties": {},
            "type": [
              "object",
              "null"
            ]
          },
          "is_active": {
            "type": "boolean"
          },
          "is_billed": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "is_completed": {
            "type": "boolean"
          },
          "is_proposal": {
            "type": "boolean"
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "type": "string"
          },
          "manufacturable_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_product_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "manufacturable_variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "on_hold": {
            "default": false,
            "description": "True while this order is held. A held order refuses every status advance except cancellation until the hold is released. Never null: an order is held or it is not.",
            "type": "boolean"
          },
          "order_type": {},
          "production_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "quantity_to_manufacture": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "recommended_location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "recommended_location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "recommended_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "request_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "ship_window_start": {
            "default": null,
            "description": "The first day the vendor may ship this order. This is the window the vendor agreed to, not the day the goods are expected: that is `expected_delivery_date`. Null where the order was placed without a window.",
            "type": [
              "string",
              "null"
            ]
          },
          "shipment_status_updated_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "shortage": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "source_location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "source_location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "split": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "DRAFTED",
              "FULLY_CONFIRMED",
              "FULLY_SHIPPED",
              "FULLY_DELIVERED",
              "CANCELLED",
              "IN_PRODUCTION",
              "COMPLETED"
            ],
            "type": "string"
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_min_order_value": {
            "description": "Legacy whole-currency minimum, rounded up conservatively. Use supplier_min_order_value_amount for the exact amount.",
            "type": "integer"
          },
          "supplier_min_order_value_amount": {
            "default": null,
            "description": "Exact canonical supplier minimum in supplier currency, including fractional units. Null means unstated.",
            "type": [
              "number",
              "null"
            ]
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_signals": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "total_cost": {
            "type": "number"
          },
          "total_delivered_quantity": {
            "type": "integer"
          },
          "total_quantity": {
            "type": "integer"
          }
        },
        "required": [
          "external_id",
          "id",
          "is_active",
          "is_completed",
          "is_proposal",
          "location_id",
          "location_name",
          "name",
          "status",
          "supplier_min_order_value",
          "total_cost",
          "total_delivered_quantity",
          "total_quantity"
        ],
        "type": "object"
      },
      "PurchaseOrderProductAggregate": {
        "properties": {
          "cover": {
            "$ref": "#/components/schemas/PurchaseOrderProductAggregateCoverRange"
          },
          "health": {
            "type": "string"
          },
          "line_items": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderLineItemEntity"
            },
            "type": "array"
          },
          "product_id": {
            "type": "string"
          },
          "product_name": {
            "type": "string"
          },
          "total_cost": {
            "type": "number"
          },
          "total_quantity": {
            "type": "integer"
          },
          "unit_cost": {
            "$ref": "#/components/schemas/PurchaseOrderProductAggregateUnitCostRange"
          }
        },
        "required": [
          "cover",
          "health",
          "line_items",
          "product_id",
          "product_name",
          "total_cost",
          "total_quantity",
          "unit_cost"
        ],
        "type": "object"
      },
      "PurchaseOrderProductAggregateCoverRange": {
        "properties": {
          "max": {
            "type": "integer"
          },
          "min": {
            "type": "integer"
          }
        },
        "required": [
          "max",
          "min"
        ],
        "type": "object"
      },
      "PurchaseOrderProductAggregateUnitCostRange": {
        "properties": {
          "max": {
            "type": "number"
          },
          "min": {
            "type": "number"
          }
        },
        "required": [
          "max",
          "min"
        ],
        "type": "object"
      },
      "PurchaseOrderSupplierUpdateItem": {
        "properties": {
          "display_name": {
            "type": "string"
          },
          "purchase_order_id": {
            "type": "string"
          },
          "reported_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "signal_type": {
            "type": "string"
          }
        },
        "required": [
          "display_name",
          "purchase_order_id",
          "signal_type"
        ],
        "type": "object"
      },
      "PurchaseOrdersAttentionSection": {
        "properties": {
          "items": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderAttentionItem"
            },
            "type": "array"
          },
          "total_count": {
            "default": 0,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PurchaseOrdersGroupedByVariant": {
        "additionalProperties": false,
        "properties": {
          "po_count": {
            "type": "integer"
          },
          "proposal_count": {
            "type": "integer"
          },
          "purchase_orders": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": "array"
          },
          "to_count": {
            "type": "integer"
          },
          "total_delivered": {
            "type": "integer"
          },
          "total_ordered": {
            "type": "integer"
          },
          "variant_id": {
            "type": "string"
          },
          "variant_name": {
            "type": "string"
          }
        },
        "required": [
          "po_count",
          "proposal_count",
          "purchase_orders",
          "to_count",
          "total_delivered",
          "total_ordered",
          "variant_id",
          "variant_name"
        ],
        "type": "object"
      },
      "RangeField": {
        "additionalProperties": false,
        "properties": {
          "max": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "min": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "ReceiveOrderDocumentRequest": {
        "additionalProperties": false,
        "properties": {
          "file_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "mime_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "s3_key": {
            "type": "string"
          }
        },
        "required": [
          "s3_key"
        ],
        "type": "object"
      },
      "ReplenishmentFilterArg": {
        "additionalProperties": false,
        "properties": {
          "key": {
            "type": "string"
          },
          "operation": {
            "type": "string"
          },
          "value": {
            "default": null
          }
        },
        "required": [
          "key",
          "operation"
        ],
        "type": "object"
      },
      "ReplenishmentReadinessSection": {
        "properties": {
          "missing_lead_time_variants_count": {
            "default": 0,
            "type": "integer"
          },
          "missing_unit_cost_variants_count": {
            "default": 0,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "ReplenishmentSetDetails": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "item_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "type": "object"
      },
      "SKUEventItem": {
        "additionalProperties": false,
        "properties": {
          "created_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "created_by_user_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "event_date": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "location_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "additionalProperties": {},
            "type": "object"
          },
          "new_value": {
            "type": "string"
          },
          "previous_value": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "event_date",
          "event_type",
          "id",
          "metadata",
          "new_value"
        ],
        "type": "object"
      },
      "SalesChannelInfo": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "type": "object"
      },
      "SalesChartDataPoint": {
        "properties": {
          "date": {
            "description": "Date in ISO format (YYYY-MM-DD)",
            "format": "date",
            "type": "string"
          },
          "sold_quantity": {
            "description": "Number of units sold",
            "format": "float",
            "type": "number"
          },
          "total_revenue": {
            "description": "Total revenue (net sales after discounts and returns)",
            "format": "float",
            "type": "number"
          }
        },
        "type": "object"
      },
      "SalesOrderLineArg": {
        "additionalProperties": false,
        "properties": {
          "quantity": {
            "type": "integer"
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "unit_price_cents": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "variant_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "quantity"
        ],
        "type": "object"
      },
      "SalesTableVariantRow": {
        "description": "Row for variant-level sales table view (type=variants).",
        "properties": {
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_fields": {
            "type": [
              "object",
              "null"
            ]
          },
          "discounts": {
            "type": "number"
          },
          "gross_sales": {
            "type": "number"
          },
          "lost_sales_dated_days": {
            "description": "Stock-out days in the window that were priced at the rate held for that very day.\n",
            "type": [
              "integer",
              "null"
            ]
          },
          "lost_sales_latest_days": {
            "description": "Stock-out days priced at the variant's current rate because no dated rate survived for them. With lost_sales_dated_days it sums to the window's stock-out days; all latest-days means the figure equals what today's rate alone would give.\n",
            "type": [
              "integer",
              "null"
            ]
          },
          "lost_sales_units": {
            "description": "Units the empty shelf cost, each stock-out day priced at the rate stored for that day; a day with no stored rate is priced at the variant's current rate.\n",
            "type": [
              "number",
              "null"
            ]
          },
          "margin_leak": {
            "description": "Absolute discounts plus absolute returns for the period.",
            "type": [
              "number",
              "null"
            ]
          },
          "missed_revenue": {
            "type": [
              "number",
              "null"
            ]
          },
          "net_items_sold": {
            "type": "integer"
          },
          "net_items_sold_by_channel": {
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Mapping of sales_channel_id to net items sold in that channel.",
            "type": "object"
          },
          "net_sales": {
            "type": "number"
          },
          "net_sales_change": {
            "description": "Signed net sales delta against the comparison window; null without one.",
            "type": [
              "number",
              "null"
            ]
          },
          "net_sales_swing": {
            "description": "Absolute value of net_sales_change; null without a comparison window.",
            "type": [
              "number",
              "null"
            ]
          },
          "price_sensitivity": {
            "description": "The pricing engine's tier for this line. Null means the engine holds no reading for it; on THIS read a null is never coalesced to STILL_LEARNING, which is a verdict the engine reached about a line it did look at, and price_sensitivity_absence says which blank the null is. Other surfaces are not covered by that sentence: the analytics bindings behind Ask Tightly still substitute STILL_LEARNING for a missing tier.\n",
            "enum": [
              "STILL_LEARNING",
              "INCONSISTENT",
              "LOW",
              "MODERATE",
              "HIGH",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "price_sensitivity_absence": {
            "description": "Which blank a null price_sensitivity is. Set only where the tier is null, and null where a tier is served.\nnot_run: the engine has never measured this store -- no tier table on this tenant, or no run on record and no rows anywhere in it. not_scored: it has measured the store and holds no reading for this line. not_measured: a probe failed and the platform cannot say, which is never reported as not_scored.\nIt does not say whether this line's product pool could lend it a number. That split (borrowed against insufficient) needs the per-variant price evidence, which is one query per line; GET /variants/{variant_id}/price-sensitivity answers it, with the sentence, for a line a reader opens.\n",
            "enum": [
              "not_run",
              "not_scored",
              "not_measured",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "price_sensitivity_absence_word": {
            "description": "The word the cell prints for the code beside it, served rather than composed on the page, one per price_sensitivity_absence code and in the same order. Null wherever price_sensitivity_absence is null, and null for a code shipped before its copy.\n",
            "enum": [
              "Not scored yet",
              "Not scored",
              "Not measured",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "price_sensitivity_computed_at": {
            "description": "When the tier beside it was measured, ISO-8601 in UTC, so the column carries the run's own stamp rather than the reader's clock. Null where there is no tier.\n",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "product_id": {
            "type": "string"
          },
          "product_image": {
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "type": "string"
          },
          "return_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "returned_units": {
            "type": [
              "number",
              "null"
            ]
          },
          "returns": {
            "type": "number"
          },
          "sell_through_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "shopify_tags": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "stockout_days_inferred": {
            "description": "Of the window's stock-out days, how many were read out of the order history rather than snapshotted at the time. Those days are evidence, not measurement: the line sold, went silent for longer than its own selling cadence explains, and sold again. Present them as inferred and never as measured. 0 where every day was snapshotted, which is every book whose history has not been backfilled.\n",
            "type": [
              "integer",
              "null"
            ]
          },
          "taxes": {
            "type": "number"
          },
          "total_sales": {
            "type": "number"
          },
          "variant_id": {
            "type": "string"
          },
          "variant_title": {
            "type": "string"
          }
        },
        "type": "object"
      },
      "SalesVelocityEventsApiMessage": {
        "additionalProperties": false,
        "properties": {
          "desc": {
            "default": "",
            "type": "string"
          },
          "service": {
            "default": "sales_velocity_events",
            "type": "string"
          },
          "severity": {
            "default": "INFO",
            "enum": [
              "SUCCESS",
              "INFO",
              "ERROR",
              "WARNING"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "SalesVelocityEventsApiResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/SalesVelocityEventsApiMessage"
          }
        },
        "type": "object"
      },
      "ShipToArg": {
        "additionalProperties": false,
        "properties": {
          "address1": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "address2": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "company": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "postcode": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "SmartReplenishmentSection": {
        "properties": {
          "critical_variants_count": {
            "default": 0,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "StockVarianceBucket": {
        "properties": {
          "attribution": {
            "enum": [
              "default_supplier",
              "no_default_supplier",
              "no_supplier_on_file"
            ],
            "type": "string"
          },
          "attribution_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "figures": {
            "$ref": "#/components/schemas/StockVarianceFigures"
          },
          "multi_supplier_line_count": {
            "default": 0,
            "type": "integer"
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "attribution",
          "figures"
        ],
        "type": "object"
      },
      "StockVarianceFigures": {
        "properties": {
          "cost_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "counted_line_count": {
            "type": "integer"
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "gain_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "gain_line_count": {
            "type": "integer"
          },
          "gain_units": {
            "type": "integer"
          },
          "gain_units_unpriced": {
            "type": "integer"
          },
          "loss_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "loss_line_count": {
            "type": "integer"
          },
          "loss_units": {
            "type": "integer"
          },
          "loss_units_unpriced": {
            "type": "integer"
          },
          "matched_line_count": {
            "type": "integer"
          },
          "variant_count": {
            "type": "integer"
          }
        },
        "required": [
          "counted_line_count",
          "gain_line_count",
          "gain_units",
          "gain_units_unpriced",
          "loss_line_count",
          "loss_units",
          "loss_units_unpriced",
          "matched_line_count",
          "variant_count"
        ],
        "type": "object"
      },
      "StockVarianceMeasurement": {
        "properties": {
          "counted_line_count": {
            "type": "integer"
          },
          "first_counted_on": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "last_counted_on": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "no_measurement_reason": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "open_stocktake_count": {
            "type": "integer"
          },
          "posted_stocktake_count": {
            "type": "integer"
          },
          "suppliers_measured": {
            "type": "integer"
          },
          "suppliers_on_file": {
            "type": "integer"
          },
          "suppliers_with_no_reading": {
            "type": "integer"
          },
          "uncounted_line_count": {
            "type": "integer"
          },
          "variants_measured": {
            "type": "integer"
          },
          "variants_on_file": {
            "type": "integer"
          },
          "variants_with_no_reading": {
            "type": "integer"
          },
          "window_from": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "window_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "window_to": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "counted_line_count",
          "open_stocktake_count",
          "posted_stocktake_count",
          "suppliers_measured",
          "suppliers_on_file",
          "suppliers_with_no_reading",
          "uncounted_line_count",
          "variants_measured",
          "variants_on_file",
          "variants_with_no_reading"
        ],
        "type": "object"
      },
      "Stocktake": {
        "properties": {
          "counted_line_count": {
            "type": "integer"
          },
          "counted_on": {
            "type": "string"
          },
          "counted_total": {
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "line_count": {
            "type": "integer"
          },
          "location_names": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "name": {
            "type": "string"
          },
          "posted_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "enum": [
              "open",
              "posted"
            ],
            "type": "string"
          },
          "system_total": {
            "type": "integer"
          },
          "variance_total": {
            "type": "integer"
          },
          "variance_value_total": {
            "type": "number"
          }
        },
        "required": [
          "counted_line_count",
          "counted_on",
          "counted_total",
          "id",
          "line_count",
          "name",
          "status",
          "system_total",
          "variance_total",
          "variance_value_total"
        ],
        "type": "object"
      },
      "StocktakeCountEntry": {
        "additionalProperties": false,
        "properties": {
          "counted_quantity": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "location_id": {
            "type": "string"
          },
          "variant_id": {
            "type": "string"
          }
        },
        "required": [
          "location_id",
          "variant_id"
        ],
        "type": "object"
      },
      "StocktakeLineRow": {
        "properties": {
          "counted_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "id": {
            "type": "string"
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "sku": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "system_quantity": {
            "type": "integer"
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "variance": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "variance_value": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_image": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "location_id",
          "system_quantity",
          "variant_id"
        ],
        "type": "object"
      },
      "SupplierBrandRef": {
        "properties": {
          "brand_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "owned": {
            "default": false,
            "type": "boolean"
          }
        },
        "required": [
          "brand_id",
          "name"
        ],
        "type": "object"
      },
      "SupplierConfigurationSection": {
        "properties": {
          "average_lead_time_days": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "configured_lead_time_days": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "default_lead_time_configured": {
            "type": "boolean"
          },
          "lead_time_diff_days": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "min_order_value_configured": {
            "type": "boolean"
          }
        },
        "required": [
          "default_lead_time_configured",
          "min_order_value_configured"
        ],
        "type": "object"
      },
      "SupplierContactPayload": {
        "additionalProperties": false,
        "properties": {
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "department": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "maxLength": 1024,
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "format": "email",
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "preferred_contact_method": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "SupplierDetails": {
        "additionalProperties": false,
        "properties": {
          "batch_size": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "in_basket_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "is_default": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "min_order_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "otif_score": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "replenishment_frequency": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "supplier_id": {
            "type": "string"
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "supplier_id"
        ],
        "type": "object"
      },
      "SupplierDetailsWithContacts": {
        "properties": {
          "address1": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "address2": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "average_lead_time": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/InventoryCardItem"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "brands": {
            "items": {
              "$ref": "#/components/schemas/SupplierBrandRef"
            },
            "type": "array"
          },
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "contacts": {
            "items": {
              "$ref": "#/components/schemas/ContactPayload"
            },
            "type": "array"
          },
          "container_type_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "country": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "domains": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "edi": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SupplierEdi"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "id": {
            "type": "string"
          },
          "in_full": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "integrations": {
            "additionalProperties": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "object",
              "null"
            ]
          },
          "is_archived": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "kind": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "lanes": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/SupplierLane"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location_lead_times": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/SupplierLocationLeadTime"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "min_order_value": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "on_time": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "otif_score": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "payment_terms_days": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "postal_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "primary_contact": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "province": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name"
        ],
        "type": "object"
      },
      "SupplierEdi": {
        "properties": {
          "account_number": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "trading_partner_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "vendor_number": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "SupplierEdiPayload": {
        "additionalProperties": false,
        "properties": {
          "account_number": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          },
          "trading_partner_id": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          },
          "vendor_number": {
            "default": null,
            "maxLength": 255,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "SupplierLane": {
        "properties": {
          "basis": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "container_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "container_type_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "freight_per_container_cents": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "internal_cbm": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "pallet_positions": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "payload_kg": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "resolved_container_type_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "road_limit_kg": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "usable_pct": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      },
      "SupplierLaneDto": {
        "additionalProperties": false,
        "properties": {
          "container_type_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "freight_per_container_cents": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "location_id": {
            "type": "string"
          },
          "road_limit_kg": {
            "default": null,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      },
      "SupplierLocationLeadTime": {
        "properties": {
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      },
      "SupplierLocationLeadTimeDto": {
        "additionalProperties": false,
        "properties": {
          "lead_time": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "location_id": {
            "type": "string"
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      },
      "SupplierNeedsAttentionPayload": {
        "properties": {
          "purchase_orders": {
            "$ref": "#/components/schemas/PurchaseOrdersAttentionSection"
          },
          "replenishment_readiness": {
            "$ref": "#/components/schemas/ReplenishmentReadinessSection"
          },
          "smart_replenishment": {
            "$ref": "#/components/schemas/SmartReplenishmentSection"
          },
          "supplier_configuration": {
            "$ref": "#/components/schemas/SupplierConfigurationSection"
          }
        },
        "required": [
          "purchase_orders",
          "replenishment_readiness",
          "smart_replenishment",
          "supplier_configuration"
        ],
        "type": "object"
      },
      "SupplierPayload": {
        "additionalProperties": false,
        "properties": {
          "address1": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "address2": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "contacts": {
            "items": {
              "$ref": "#/components/schemas/SupplierContactPayload"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "country": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "domains": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "kind": {
            "default": null,
            "enum": [
              "factory",
              "vendor",
              "distributor",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "min_order_value": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "payment_terms_days": {
            "default": null,
            "maximum": 365,
            "minimum": -365,
            "type": [
              "integer",
              "null"
            ]
          },
          "postal_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "primary_contact": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SupplierContactPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "province": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "ThreadDecision": {
        "additionalProperties": false,
        "properties": {
          "decided_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "decided_by_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "ThreadFile": {
        "additionalProperties": false,
        "properties": {
          "filename": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "mime_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "period": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "recognised_as": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "ThreadFiles": {
        "additionalProperties": false,
        "properties": {
          "count": {
            "default": 0,
            "type": "integer"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/ThreadFile"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "ThreadParty": {
        "additionalProperties": false,
        "properties": {
          "id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "type": "string"
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "kind"
        ],
        "type": "object"
      },
      "ThreadSays": {
        "additionalProperties": false,
        "properties": {
          "party": {
            "type": "string"
          },
          "purchase_order_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "signal_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          }
        },
        "required": [
          "party",
          "type"
        ],
        "type": "object"
      },
      "UpdateContactNoteRequest": {
        "additionalProperties": false,
        "properties": {
          "body": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "default": null,
            "maxLength": 200,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UpdateContactNoteResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactNotePayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "UpdateContactRequest": {
        "additionalProperties": false,
        "properties": {
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "department": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "format": "email",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "preferred_contact_method": {
            "type": "string"
          },
          "role": {
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UpdateContactResponse": {
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/ContactsApiMessage"
          }
        },
        "type": "object"
      },
      "UpdatePOGenerationSettingsRequest": {
        "properties": {
          "enabled": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "exceptions": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "fill_container": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "filter_args": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/ProductsFilterArg"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "round_to_batch_size": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "top_up_to_moq": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "variant_ids": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "UpdatePurchaseOrderRequest": {
        "additionalProperties": false,
        "properties": {
          "additional_costs": {
            "items": {
              "additionalProperties": {},
              "type": "object"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "billed_at": {
            "format": "date",
            "type": "string"
          },
          "cancel_date": {
            "description": "The last day of the ship window, inclusive. After it the buyer may walk away from whatever the vendor has not shipped. Must not be before `ship_window_start`. Leave the field out to keep the window as it is; send null to take it off.",
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "comment": {
            "type": [
              "string",
              "null"
            ]
          },
          "commercial_resolution_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "commitment_id": {
            "maxLength": 250,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "delivery_address": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PurchaseOrderAddressUpdate"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "exceptions": {
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "expected_delivery_date": {
            "format": "date",
            "type": "string"
          },
          "filter_args": {
            "items": {
              "$ref": "#/components/schemas/ProductsFilterArg"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "include_planned_to_deliver": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "invoice_address": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PurchaseOrderAddressUpdate"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "is_active": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "is_billed": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "is_completed": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "is_proposal": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "line_items": {
            "items": {
              "$ref": "#/components/schemas/PurchaseOrderItemUpdate"
            },
            "type": "array"
          },
          "name": {
            "default": null,
            "minLength": 1,
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "default": null,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          },
          "quantity_to_manufacture": {
            "default": null,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          },
          "recommended_quantity": {
            "default": null,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "ship_window_start": {
            "description": "The first day the vendor may ship this order. Leave the field out to keep the window as it is; send null to take it off.",
            "format": "date",
            "type": [
              "string",
              "null"
            ]
          },
          "should_update_inventory": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "source_address": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PurchaseOrderAddressUpdate"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "status": {}
        },
        "type": "object"
      },
      "UpdatePurchaseOrderResponse": {
        "additionalProperties": false,
        "properties": {
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PurchaseOrderItem"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "message": {
            "$ref": "#/components/schemas/PurchaseOrderApiMessage"
          }
        },
        "type": "object"
      },
      "UpdateStocktakeCountsRequest": {
        "additionalProperties": false,
        "properties": {
          "counts": {
            "items": {
              "$ref": "#/components/schemas/StocktakeCountEntry"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "UpdateSupplierRequest": {
        "properties": {
          "address1": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "address2": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "brand_ids": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "city": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "clear_variant_supplier_lead_time": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "contacts": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/SupplierContactPayload"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "container_type_id": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "container_type_stated": {
            "default": false,
            "description": "Server-set: true when the body named container_type_id (even as null). Anything a client sends here is overwritten, so omit it.",
            "type": "boolean"
          },
          "country": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "domains": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "edi": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SupplierEdiPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "edi_stated": {
            "default": false,
            "description": "Server-set: true when the body named edi (even as null). Anything a client sends here is overwritten, so omit it.",
            "type": "boolean"
          },
          "is_archived": {
            "default": null,
            "type": [
              "boolean",
              "null"
            ]
          },
          "kind": {
            "default": null,
            "enum": [
              "factory",
              "vendor",
              "distributor",
              null
            ],
            "type": [
              "string",
              "null"
            ]
          },
          "kind_stated": {
            "default": false,
            "description": "Server-set: true when the body named kind (even as null). Anything a client sends here is overwritten, so omit it.",
            "type": "boolean"
          },
          "lanes": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/SupplierLaneDto"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "location_lead_times": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/SupplierLocationLeadTimeDto"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "min_order_value": {
            "default": null,
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "payment_terms_days": {
            "default": null,
            "maximum": 365,
            "minimum": -365,
            "type": [
              "integer",
              "null"
            ]
          },
          "postal_code": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "primary_contact": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SupplierContactPayload"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ],
            "default": null
          },
          "province": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "VariantMinimalDto": {
        "properties": {
          "product_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          },
          "variant_title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "variant_id"
        ],
        "type": "object"
      },
      "VariantSupplier": {
        "properties": {
          "batch_size": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "is_default": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "location_lead_times": {
            "default": null,
            "items": {
              "$ref": "#/components/schemas/VariantSupplierLocationLeadTime"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "min_order_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "otif_score": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "replenishment_frequency": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "replenishment_frequency_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "type": "string"
          },
          "supplier_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "unit_cost_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "supplier_id"
        ],
        "type": "object"
      },
      "VariantSupplierBasic": {
        "properties": {
          "batch_size": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "currency": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "is_default": {
            "default": false,
            "type": [
              "boolean",
              "null"
            ]
          },
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "lead_time_source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "min_order_quantity": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "supplier_id": {
            "type": "string"
          },
          "unit_cost": {
            "default": null,
            "type": [
              "number",
              "null"
            ]
          },
          "variant_id": {
            "type": "string"
          }
        },
        "required": [
          "supplier_id",
          "variant_id"
        ],
        "type": "object"
      },
      "VariantSupplierLocationLeadTime": {
        "properties": {
          "lead_time": {
            "default": null,
            "type": [
              "integer",
              "null"
            ]
          },
          "location_id": {
            "type": "string"
          },
          "location_name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "location_id"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "description": "An API key from Settings > Developer > API keys, sent as `Authorization: Bearer tly_live_...`. The key carries the organisation.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "The public Tightly API. Authenticate with an API key as a bearer token; the key carries the organisation, so no X-Organization-ID header is sent. Every operation below states the scopes a key needs.\n\nEvery response carries two headers, whatever it answers: `X-Request-Id`, the handle on that one call, and `X-Tightly-Region`, the home that served it.\n\nEvery write accepts an optional `Idempotency-Key` request header, any string up to 255 characters. Send the same key twice and the second request is answered with the first one's status and body instead of writing again, so a client that retried after a timeout ends holding the record's id rather than creating a second one. Reusing a key with a different body, or while the first request is still running, is refused `409`. A write that failed releases its key. Keys answer for 24 hours and belong to the API key that used them.\n\nAny operation can answer `503`, and it is about Tightly rather than about the request: a database briefly out of reach, which is what a failover or a full connection pool looks like from outside, or an idempotency store that could not answer. `message.desc` says which, and most carry a `Retry-After` header naming the seconds to wait. Where the sentence says nothing was written, the same request is safe to send again. Otherwise a read is safe to repeat and a write is not certain either way, because the connection was lost in the middle of it: read the object back before sending it a second time. An `Idempotency-Key` does not settle that one, and deliberately: a write that raised releases its key so the retry is a fresh attempt rather than a refusal for the next day. Branch on the status rather than on `code`, which is `POSTGRES_UNAVAILABLE` or `SERVICE_UNAVAILABLE` depending on which seam refused.",
    "title": "Tightly API",
    "version": "2026-11"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/commitments/cash-gate": {
      "get": {
        "description": "Fifty-two retail weeks of merchandise cash: what comes in from what sold and, on the weeks ahead, from the plan of record; what goes out as the open buy lands on each supplier's own billing lag; the balance walked against a declared floor; and the levers that would move the tightest weeks.\n\nIT IS MERCHANDISE CASH AND NOT A CASH-FLOW STATEMENT. Nothing here knows about payroll, rent, tax or financing. It answers one question: can the coming weeks pay for the buy we intend.\n\nTWO CASH-OUT LINES, AND ADDING THEM DOUBLE-COUNTS. `contracted_out` is the placed open buy moved onto the week each supplier actually bills, measured from that supplier's own bills and falling back to its declared terms. `planned_out` is what the plan of record still implies before a purchase order exists. They overlap by construction, because a placed order is already subtracted from the plan's need.\n\nWeeks are numbered on the organisation's own 4-5-4 grid (`fiscal_week`, FY26-W36) with the ISO week of the same Sunday beside it. A term that is not on file is null with its reason and never a guess; where the two declarations the ladder walks against are missing, `not_measured` names them.\n\nTHE FORWARD LEG CAN BE SERVED UNREAD. On a big enough book, projecting the weeks ahead can run past the time this read gives it; that leg alone is then dropped, `projected_in_source` is null and `projected_in_reason` says why. Read that field rather than inferring it from a run of nulls.\n\nScope: `cash:read`.",
        "operationId": "get_cash_gate",
        "parameters": [
          {
            "description": "Set false for a buying-plan-only read. The cash projection is explicitly absent; realised cash, purchase orders and planned/worked buying figures retain their full computation. Cash impact requires the default complete read.",
            "in": "query",
            "name": "include_projection",
            "schema": {
              "default": true,
              "type": "boolean"
            }
          },
          {
            "description": "Narrows NOTHING. The company's ladder is served whole and each group gains commitment_draw_usd, the part of its weekly draw carried by purchase orders stamped with this Commitment (the same placement over the same rows, so no week can exceed weekly_draw_usd), plus a top-level commitment block naming the commitment the request named (with a reason where no commitment on this book carries the id). Without the parameter neither key is served.\n",
            "in": "query",
            "name": "commitment_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many weeks of the ladder to serve, from the first. Narrows the ROWS and never the arithmetic: the ladder is computed over the whole year either way, because a week's balance stands on every week before it, and the slice is taken on the way out. So the figures a caller asking for four weeks sees are identical to the ones it would see asking for all of them. served_weeks says what was served out of what, and every figure outside weeks - the levers, the coverage block, the whole-year totals - still speaks for the whole year. Omitted, the whole ladder is served.\n",
            "in": "query",
            "name": "weeks",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "A company scenario's id. The same ladder is served with ONE line moved: planned_out becomes what that scenario's plan still has to buy, worked out by re-running the money grid's own identity over driver-moved terms, and the scenario's receipt shift re-dates it by whole weeks. Nothing else changes, so contracted_out, the freight, the cash in and the levers are what they are without it, and the two lines still overlap by construction. A top-level scenario block names the scenario, its state and the shift, so a ladder read under one is never taken for the plan's own. An id no scenario on this book carries is refused.\n",
            "in": "query",
            "name": "scenario",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-05",
                    "cash_floor_usd": 2500000.0,
                    "committed_in_source": "order_book",
                    "computed_at": "2026-09-05T06:12:44Z",
                    "groups": [
                      {
                        "key": "sup_2f18a0",
                        "name": "Porto Knitworks",
                        "open_buy_usd": 3120000.0,
                        "terms_days": 45,
                        "terms_measured_from": "11 billed orders",
                        "terms_source": "measured",
                        "weekly_draw_usd": [
                          902400.0,
                          1841000.0
                        ]
                      }
                    ],
                    "levers": [
                      {
                        "buy_effect_usd": 0.0,
                        "key": "porto_to_90",
                        "name": "Move Porto Knitworks to 90-day terms",
                        "reason": null,
                        "releases": [
                          {
                            "cash_usd": 1841000.0,
                            "group_key": "sup_2f18a0"
                          }
                        ],
                        "reversible": true,
                        "why": "Two weeks fall below the floor on its bills alone."
                      }
                    ],
                    "not_measured": [],
                    "opening_balance_usd": 4820000.0,
                    "planned_out_coverage": {
                      "cap": 6,
                      "commitments": 4,
                      "planned": 4,
                      "reason": null
                    },
                    "projected_in_reason": null,
                    "projected_in_source": "forward_curve",
                    "projected_through": "2027-08-28",
                    "undated_open_buy_usd": 148000.0,
                    "unlagged_open_buy_usd": 0.0,
                    "weeks": [
                      {
                        "basis": "realised",
                        "begins": "2026-08-30",
                        "committed_in": 0.0,
                        "contracted_out": 902400.0,
                        "fiscal_week": "FY26-W36",
                        "iso_week": "2026-W35",
                        "planned_out": null,
                        "planned_out_lag_source": null,
                        "settled_in": 1184200.0
                      },
                      {
                        "basis": "projected",
                        "begins": "2026-09-06",
                        "committed_in": 214000.0,
                        "contracted_out": 1841000.0,
                        "fiscal_week": "FY26-W37",
                        "iso_week": "2026-W36",
                        "planned_out": 322000.0,
                        "planned_out_lag_source": "measured",
                        "settled_in": 1102800.0
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "commitments",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "opening_balance_usd and cash_floor_usd (both DECLARED, null where nobody has said, and named in not_measured); as_of (the day the ladder stands on) and computed_at (the instant it was worked out); weeks as [{fiscal_week, iso_week, begins, settled_in, basis, committed_in, contracted_out, planned_out, planned_out_lag_source}] where fiscal_week is the org's 4-5-4 retail week (FY26-W36) and iso_week the ISO week of the same Sunday (2026-W35), and planned_out is null wherever the plan side was not read, no plan cell places money into the week, or the identity did not fire, and planned_out_lag_source is measured | declared | unmeasured (the weakest word of everything placed into that week); groups as [{key, name, open_buy_usd, terms_days, terms_source, terms_measured_from, weekly_draw_usd}] largest exposure first, each weekly_draw_usd in weeks order and null where the terms are unknown (a row of zeros means terms that pay after the window); levers as [{key, name, why, reversible, releases, buy_effect_usd, reason}] with releases [{group_key, cash_usd}] or null; unlagged_open_buy_usd and undated_open_buy_usd (open buy with no terms, and with no delivery date: said, never absorbed); projected_through; unpriced_projected_units; committed_in_source; projected_in_source (forward_curve where the projection was read, null where it was not) and projected_in_reason (the sentence beside an unread projection, null where it was read, so the two are never both set); contracted_out_source (placed | freight | placed+freight | no_open_buy, and null where NEITHER leg behind contracted_out could speak for the year -- every dollar of the open buy behind a house with no terms on file and no container plan quoting freight; every week's contracted_out is then null too, because fifty-two zeros beside an open buy of millions say the book takes nothing out of the bank all year. no_open_buy is the opposite reading and a measured zero: the order pad is empty); unpriced_committed_units; planned_out_coverage (commitments / planned / folded / measured / cap / months_dropped / cells / cells_incomplete / calendar / commitments_read / categories / reason; a capped total is never presented as the whole plan. measured is what the cap bounds -- the money reads this read PERFORMED, which is folded plus the ones that ran and then failed -- so it is never smaller than folded); planned_out_before_window_usd and planned_out_after_window_usd (planned money the ladder cannot hold, said apart: what bills before its first week, a month underway on terms shorter than the days of it already gone, and what bills past the served year)\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Cash is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Cash; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The open buy on each supplier's terms clock",
        "tags": [
          "commitments"
        ],
        "x-tightly-scopes": [
          "cash:read"
        ]
      }
    },
    "/api/v1/commitments/{commitment_id}/money": {
      "get": {
        "description": "One commitment's plan of record at cost, in integer cents, on the commitment's own 4-5-4 fiscal months (each keyed by the calendar year-month it maps to).\n\nThe plan of record is five declared terms per category and month, planned sales, planned markdown, planned closing stock, planned intake margin and the receipts they imply, and the identity that closes on them. It is the line a variance is measured against, so the figures are what a planner typed and not what an engine would say today.\n\nAN ABSENCE STAYS AN ABSENCE. A month with no modelled demand is `null` in its array, because a 0 there would read as \"plan for nothing\", while a month the order book expects nothing in is a measured `0`. Every null carries its reason in the category's `unavailable` list, in a planner's words. `open_to_commit` is receipts needed less what is already on order, and it is the same figure the Open to buy report and the cash gate's `planned_out` are built from.\n\n`company_declaration` carries the finance lead's cover target, in weeks, and markdown rate, in basis points, for the plan's fiscal year, served once for the grid rather than per cell. A category's plan cell may hold a different figure, and this block is how a reader tells the two apart; either figure reads null where the company has declared neither.\n\nFor the versions of this plan, frozen as they were locked, read `list_plan_versions`.\n\nScope: `planning:read`.",
        "operationId": "get_commitment_money",
        "parameters": [
          {
            "description": "The commitment's id.",
            "in": "path",
            "name": "commitment_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "categories": [
                      {
                        "category": "Outerwear",
                        "markdown_left_to_close_cents": 22400000,
                        "on_order_cents": [
                          48200000,
                          0
                        ],
                        "open_to_commit_cents": [
                          3200000,
                          null
                        ],
                        "planned_closing_stock_cents": [
                          98400000,
                          null
                        ],
                        "planned_markdown_cents": [
                          14720000,
                          16880000
                        ],
                        "planned_markdown_to_close_cents": 31600000,
                        "planned_sales_cents": [
                          184000000,
                          211000000
                        ],
                        "realised_markdown": [
                          9200000,
                          null
                        ],
                        "realised_markdown_at_retail": [
                          21400000,
                          null
                        ],
                        "realised_markdown_to_date_cents": 9200000,
                        "realised_markdown_units": [
                          4120,
                          null
                        ],
                        "receipts_needed_cents": [
                          51400000,
                          null
                        ],
                        "unavailable": [
                          {
                            "field": "planned_closing_stock_cents",
                            "month": "2027-03",
                            "reason": "Nobody has declared a closing stock plan for March."
                          }
                        ]
                      }
                    ],
                    "commitment_id": "cmt_9a4e21",
                    "company_declaration": {
                      "cover_target_weeks": 10.0,
                      "fiscal_year": 2027,
                      "markdown_rate_bp": 800
                    },
                    "currency": "USD",
                    "label": "Outerwear SS27",
                    "months": [
                      "2027-02",
                      "2027-03"
                    ],
                    "totals": {
                      "open_to_commit_cents": 3200000,
                      "planned_sales_cents": 395000000,
                      "receipts_needed_cents": 51400000
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "commitments",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The commitment's money position.\n\nThe plan block carries what has actually been MARKED DOWN beside what was planned, on the same months. `realised_markdown` is at COST, the plan's own basis, which is the only pair that may be subtracted; `realised_markdown_at_retail` is what the season handed over at the till, beside it and never subtracted from a plan stated at cost. `markdown_left_to_close_cents` is the subtraction, with `markdown_left_to_close_reason` where it cannot be made. A month still ahead carries null and never a zero, and `realised_markdown_reason` says why a whole row has none -- a season that has not opened, a channel scope, or a read that could not be taken.\n\nThe order book arrives in three bands on those same months, off one scan and under one refusal. `on_order` is placed purchase-order money at its expected delivery month, the envelope ladder's own definition of placed. `drafted` is purchase-order money still in draft at the same grain: written up, no envelope consumed, and its own band because a draft is not an order. `received` is the delivered subset of the placed lines at the same expected delivery month, so it is the landed slice of that month's on-order figure rather than an actual-arrival series, and it is never more than `on_order` for a month. A month the book was read for that holds nothing in a band is a measured 0; all three bands are null together, with the reason on the category, where the book cannot answer for the scope at all.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when the plan of record is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One commitment's plan of record at cost, on its own fiscal months",
        "tags": [
          "commitments"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/commitments/{commitment_id}/money/plan/versions": {
      "get": {
        "description": "Every locked version of one commitment's plan of record, newest first, with the version that is the budget flagged. Metadata only: the label, who locked it, when, and how many cells it holds.\n\nA version is the plan as it was, not today's forecast wearing its name: nothing in it is recomputed, which is the whole reason two lines exist. `budget` is the version a variance is measured against, and it is `null` WITH a reason where no version has been named the budget, never an empty object, which would read as a budget of nothing.\n\nAn empty list carries its reason, and it is not one reason: nobody has locked a version, or this organisation cannot hold one yet. The frozen grid itself is a separate read, one version at a time, because a year of weekly reforecasts would make this one a download.\n\nScope: `planning:read`.",
        "operationId": "list_plan_versions",
        "parameters": [
          {
            "description": "The commitment's id.",
            "in": "path",
            "name": "commitment_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "absence": null,
                    "budget": {
                      "label": "Budget, pre-season",
                      "version_id": 18
                    },
                    "versions": [
                      {
                        "cell_count": 96,
                        "is_budget": false,
                        "kind": "reforecast",
                        "label": "Reforecast, September",
                        "locked_at": "2026-09-01T16:40:05Z",
                        "locked_by": "Dana Whitfield",
                        "version_id": 31
                      },
                      {
                        "cell_count": 96,
                        "is_budget": true,
                        "kind": "budget",
                        "label": "Budget, pre-season",
                        "locked_at": "2026-06-12T10:21:44Z",
                        "locked_by": "Dana Whitfield",
                        "version_id": 18
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "commitments",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The locked versions of this plan",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when the plan of record is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every locked version of one commitment's plan of record",
        "tags": [
          "commitments"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/contacts": {
      "post": {
        "description": "Creates one contact and answers 201 with it. `name` and `preferred_contact_method` are required; `preferred_contact_method` is `EMAIL` or `PHONE`, and the field it names has to be in the same body (an email for EMAIL, a phone for PHONE), or the call is refused 400.\n\n`supplier_id` attaches the contact to a supplier and `trading_partner_id` attaches it to a wholesale account. They are separate fields on purpose: the two resolve through different tables, and one person can legitimately belong to both at once. An id neither table has fails the call rather than creating a person attached to nothing. A contact who is the first on an account becomes that account's main contact; nobody is promoted afterwards, because who leads is a decision somebody makes rather than an accident of insert order.\n\n`description` is capped at 1,024 characters, `department` and `role` at 255. An email another contact already has is refused 400 with \"Contact already exists. The email must be unique.\"\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "create_contact",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "city": "Porto",
                "country": "PT",
                "department": "Sales",
                "email": "alex@portoknits.example",
                "name": "Alex Example",
                "phone": "+1 202 555 0100",
                "preferred_contact_method": "EMAIL",
                "role": "Account manager",
                "supplier_id": "sup_0031"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateContactRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "city": "Porto",
                    "country": "PT",
                    "department": "Sales",
                    "description": null,
                    "email": "alex@portoknits.example",
                    "id": "412",
                    "is_primary_contact": true,
                    "name": "Alex Example",
                    "phone": "+1 202 555 0100",
                    "preferred_contact_method": "email",
                    "role": "Account manager",
                    "supplier_id": "sup_0031",
                    "supplier_name": "Porto Knits"
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CreateContactResponse"
                }
              }
            },
            "description": "The contact as created, with its supplier named.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "BAD_REQUEST",
                  "message": {
                    "desc": "Contact already exists. The email must be unique.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A required field is missing, the preferred method names a field that is not there, or the email belongs to another contact.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create a contact, attached to a supplier or to a wholesale account",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/contacts/filters": {
      "get": {
        "description": "The values a contact filter can take, read off the contacts this organisation actually has: `roles`, `departments`, `countries`, `cities`, `suppliers`, each `{id, name}`, and `missing_fields`, the field names for which at least one contact has nothing on file.\n\nTakes no parameters. An empty list means no contact carries that field, not that the filter is unsupported.\n\n`preferred_contact_methods` is the exception: it is the fixed pair `email` and `phone` rather than what is in use, because those two are the whole grammar and a book where every contact prefers email should still offer phone.\n\nPass a `missing_fields` name back to list_contacts as `{\"key\":\"missing_fields\",\"operation\":\"in\",\"value\":[\"phone\"]}` to get the contacts that lack it.\n\nScope: `suppliers:read`.",
        "operationId": "get_contacts_filters",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "cities": [
                      "Porto",
                      "Prato"
                    ],
                    "countries": [
                      "IT",
                      "PT"
                    ],
                    "departments": [
                      "Sales"
                    ],
                    "missing_fields": [
                      "department",
                      "phone"
                    ],
                    "preferred_contact_methods": [
                      "email",
                      "phone"
                    ],
                    "roles": [
                      "Account manager",
                      "Production"
                    ],
                    "suppliers": [
                      {
                        "id": "sup_0031",
                        "name": "Porto Knits"
                      },
                      {
                        "id": "sup_0044",
                        "name": "Prato Wovens"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetContactsFilterResponse"
                }
              }
            },
            "description": "Seven lists. Each is what this organisation's contacts actually carry, except `preferred_contact_methods`, which is the fixed pair.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The values a contact filter can take in this organisation",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      }
    },
    "/api/v1/contacts/table": {
      "get": {
        "description": "A page of contacts across every supplier: name, email, phone, city, country, role, department, preferred contact method, the supplier each belongs to by id and name, and `is_primary_contact`. `filtered_max_size` is the size of the filtered set and `max_size` the size of the book.\n\nPage with `limit` (1 to 10,000, default 10) and `offset` (0 to 2,147,483,647). Narrow with `filter_args`, a JSON array of `{key, operation, value, group}`, on `role`, `country`, `city`, `supplier_id` and `department` (eq, in), `preferred_contact_method` (eq) and `missing_fields` (eq, in). Order with `sort_args`, comma-separated, `-` for descending, over name, country, city, role, supplier_name, department, email and preferred_contact_method. `search` matches the name and the email.\n\n`preferred_contact_method` is `email` or `phone` and is never null: a contact with neither recorded reads `email`. `supplier_id` and `supplier_name` are null for a contact attached to no supplier, a contact created against a wholesale account rather than a supplier is one such.\n\nAsk get_contacts_filters for the values these filters can take rather than guessing them.\n\nScope: `suppliers:read`.",
        "operationId": "list_contacts",
        "parameters": [
          {
            "description": "Rows per page. Out of range is refused 400.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Rows to skip. Out of range is refused 400.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "maximum": 2147483647,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "A JSON array of filter conditions, each `{key, operation, value}` with an optional `group` of `and` (the default) or `or`. Keys and their operations: `role`, `country`, `city`, `supplier_id`, `department` (eq, in); `preferred_contact_method` (eq); `missing_fields` (eq, in), contacts with nothing on file for a field, whose value is one of email, phone, city, country, description, department, role, supplier, preferred_contact_method. An unsupported key, operation or value is refused 400 naming what was allowed.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"role\",\"operation\":\"eq\",\"value\":\"Account manager\"},{\"key\":\"missing_fields\",\"operation\":\"in\",\"value\":[\"department\",\"phone\"],\"group\":\"or\"}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated sort columns, `-` for descending and `+` or nothing for ascending. Valid columns: name, country, city, role, supplier_name, department, email, preferred_contact_method.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "supplier_name,-name"
              ],
              "type": "string"
            }
          },
          {
            "description": "Free text matched against the contact's name and email.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 12,
                    "max_size": 340,
                    "offset": 0,
                    "rows": [
                      {
                        "city": "Porto",
                        "country": "PT",
                        "department": "Sales",
                        "description": null,
                        "email": "alex@portoknits.example",
                        "id": "412",
                        "is_primary_contact": true,
                        "name": "Alex Example",
                        "phone": "+1 202 555 0100",
                        "preferred_contact_method": "email",
                        "role": "Account manager",
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits"
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetContactsTableResponse"
                }
              }
            },
            "description": "One page of contacts, with the offset requested, the number of rows served, the size of the filtered set and the size of the whole book.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "limit must be an integer between 1 and 10000",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A pagination bound, a filter key, an operation or a value is not one this table takes.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "A page of supplier contacts, with the supplier each belongs to",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      }
    },
    "/api/v1/contacts/{contact_id}": {
      "delete": {
        "description": "Deletes one contact and answers 204 with no body. The contact's link to its supplier goes with it; the supplier does not.\n\nMail already held against this address is swept in the same call, because an inbox thread with no contact behind it is a thread nothing can route.\n\n`contact_id` is a whole number, and an id no contact has is refused 404. There is no undo: use update_contact where the person has only changed role.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "delete_contact",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "The contact is gone. No body.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NO_RESULT_FOUND",
                  "message": {
                    "desc": "No result found: No row was found when one was required",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No contact of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Delete a contact",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      },
      "get": {
        "description": "One contact: name, email, phone, city, country, description, role, department, preferred contact method, and the supplier it belongs to by id and name, with `is_primary_contact` saying whether it is that supplier's main contact.\n\n`contact_id` is a whole number. A value that is not one is refused 400 before the database is touched, and one no contact has is refused 404.\n\nA contact belongs to at most one supplier here: where a person is on several, this serves the first. `supplier_id` and `supplier_name` are null for a contact attached to no supplier.\n\nScope: `suppliers:read`.",
        "operationId": "get_contact",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "city": "Porto",
                    "country": "PT",
                    "department": "Sales",
                    "description": null,
                    "email": "alex@portoknits.example",
                    "id": "412",
                    "is_primary_contact": true,
                    "name": "Alex Example",
                    "phone": "+1 202 555 0100",
                    "preferred_contact_method": "email",
                    "role": "Account manager",
                    "supplier_id": "sup_0031",
                    "supplier_name": "Porto Knits"
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetContactResponse"
                }
              }
            },
            "description": "The contact as it stands, with its supplier named.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "BAD_REQUEST",
                  "message": {
                    "desc": "'abc' is not a valid contact id.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`contact_id` is not a whole number.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Contact with id 99999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No contact of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One contact, with the supplier it belongs to",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      },
      "patch": {
        "description": "Changes one contact and answers it whole. Only the fields in the body move: a field left out is untouched, and a field sent null is cleared. The two are different, which is why this is a PATCH and not a PUT.\n\n`preferred_contact_method` is `EMAIL` or `PHONE`, and the contact must still carry the field it names once the change lands. Clearing the email of a contact who prefers email is refused 400, and so is clearing the phone of one who prefers phone; send the new method in the same body to move them both at once.\n\n`supplier_id` re-attaches the contact to a supplier. A supplier this organisation does not have fails the call. An email another contact already has is refused 400.\n\n`contact_id` is a whole number. An id no contact has does not answer 404 here. It fails as a server error, so read the contact first when the id did not come from a Tightly read.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "update_contact",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "description": null,
                "phone": "+1 202 555 0101",
                "role": "Head of sales"
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateContactRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "city": "Porto",
                    "country": "PT",
                    "department": "Sales",
                    "description": null,
                    "email": "alex@portoknits.example",
                    "id": "412",
                    "is_primary_contact": true,
                    "name": "Alex Example",
                    "phone": "+1 202 555 0101",
                    "preferred_contact_method": "email",
                    "role": "Head of sales",
                    "supplier_id": "sup_0031",
                    "supplier_name": "Porto Knits"
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/UpdateContactResponse"
                }
              }
            },
            "description": "The contact as it now stands.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "BAD_REQUEST",
                  "message": {
                    "desc": "Contact already exists. The email must be unique.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The preferred method would name a field the contact no longer carries, or the email belongs to another contact.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Change one contact, clearing a field by sending null",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/contacts/{contact_id}/notes": {
      "get": {
        "description": "Every note held against one contact, as an array: `id`, `title`, `body`, and the ISO-8601 instants the note was written and last changed. `data` is the array itself rather than an object wrapping one, and it is empty where the contact has no notes.\n\nThere is no paging and no ordering argument: notes come back in the order the database holds them, so sort on `created_at` where the order matters to a reader.\n\nA contact id no contact has answers an empty array rather than 404, because the notes are looked up by contact id and no contact is read. get_contact is the door that tells you whether the contact exists.\n\nScope: `suppliers:read`.",
        "operationId": "get_contact_notes",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "body": "Ana confirmed the knitwear block ships in two drops, the second a week later.",
                      "created_at": "2026-09-01T10:04:11.512000+00:00",
                      "id": "88",
                      "title": "Chased the SS27 confirmation",
                      "updated_at": "2026-09-01T10:04:11.512000+00:00"
                    }
                  ],
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetContactNotesResponse"
                }
              }
            },
            "description": "The contact's notes, newest not guaranteed first. Empty where there are none.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every note held against one contact",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      },
      "post": {
        "description": "Writes one note against a contact and answers 201 with it, including the id and the two instants.\n\n`title` and `body` are both required. `title` is capped at 200 characters and `body` at 200 words, counted on whitespace. A note is a line somebody reads beside a contact, not a document.\n\nThe contact id is not read before the write, but the note's foreign key is: an id no contact has fails the call rather than storing an orphan, and today that surfaces as a 500 rather than a 404. Read the contact first where the id did not come from a Tightly read.\n\nA note is append-only from this door: update_contact_note is what changes one that exists.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "create_contact_note",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "body": "Ana confirmed the knitwear block ships in two drops, the second a week later.",
                "title": "Chased the SS27 confirmation"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateContactNoteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "body": "Ana confirmed the knitwear block ships in two drops, the second a week later.",
                    "created_at": "2026-09-01T10:04:11.512000+00:00",
                    "id": "88",
                    "title": "Chased the SS27 confirmation",
                    "updated_at": "2026-09-01T10:04:11.512000+00:00"
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CreateContactNoteResponse"
                }
              }
            },
            "description": "The note as written, with its id and both instants.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "Body can have at most 200 words",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`title` is over 200 characters, `body` is over 200 words, or one of them is missing.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Write a note against a contact",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/contacts/{contact_id}/notes/{note_id}": {
      "delete": {
        "description": "Deletes one note and answers 204 with no body. The contact is untouched.\n\nAs on get_contact_note, the note is found by `note_id` alone and `contact_id` is not part of the lookup, so a note id paired with the wrong contact is still deleted. A note id nothing has is refused 404, so a second delete of the same note answers 404 rather than 204.\n\nThere is no undo, and no soft delete: the row is gone.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "delete_contact_note",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The note's id, a whole number, as `get_contact_notes` serves it.",
            "in": "path",
            "name": "note_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "The note is gone. No body.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NO_RESULT_FOUND",
                  "message": {
                    "desc": "No result found: No row was found when one was required",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No note has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Delete a note held against a contact",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      },
      "get": {
        "description": "One note: `id`, `title`, `body`, and the ISO-8601 instants it was written and last changed.\n\nThe note is found by `note_id` alone. `contact_id` is in the path because the note belongs to a contact, but it is not part of the lookup, so a note id paired with the wrong contact still answers that note. Do not read a 200 here as proof that the note belongs to the contact you named.\n\nA note id nothing has is refused 404. get_contact_notes is the door when the whole list is wanted, and the only one that answers which notes a contact really holds.\n\nScope: `suppliers:read`.",
        "operationId": "get_contact_note",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The note's id, a whole number, as `get_contact_notes` serves it.",
            "in": "path",
            "name": "note_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "body": "Ana confirmed the knitwear block ships in two drops, the second a week later.",
                    "created_at": "2026-09-01T10:04:11.512000+00:00",
                    "id": "88",
                    "title": "Chased the SS27 confirmation",
                    "updated_at": "2026-09-01T10:04:11.512000+00:00"
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetContactNoteResponse"
                }
              }
            },
            "description": "The note as it stands.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NO_RESULT_FOUND",
                  "message": {
                    "desc": "No result found: No row was found when one was required",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No note has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One note held against a contact",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      },
      "patch": {
        "description": "Changes one note and answers it whole, with `updated_at` moved.\n\n`title` and `body` are both optional and either can be sent alone. A field left out is untouched, and so is a field sent null, which this door reads as \"no change\" rather than as \"clear it\", so a note cannot be emptied through it. The caps are the ones create_contact_note applies: 200 characters of title, 200 words of body.\n\nAs on get_contact_note, the note is found by `note_id` alone and `contact_id` is not part of the lookup, so a note id paired with the wrong contact is still changed. A note id nothing has is refused 404.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "update_contact_note",
        "parameters": [
          {
            "description": "The contact's id, a whole number, as `list_contacts` serves it.",
            "in": "path",
            "name": "contact_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The note's id, a whole number, as `get_contact_notes` serves it.",
            "in": "path",
            "name": "note_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "body": "Ana confirmed two drops; the second slipped a further week on 4 September."
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateContactNoteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "body": "Ana confirmed two drops; the second slipped a further week on 4 September.",
                    "created_at": "2026-09-01T10:04:11.512000+00:00",
                    "id": "88",
                    "title": "Chased the SS27 confirmation",
                    "updated_at": "2026-09-04T08:31:02.190000+00:00"
                  },
                  "message": {
                    "desc": "",
                    "service": "contacts",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/UpdateContactNoteResponse"
                }
              }
            },
            "description": "The note as it now stands, with `updated_at` moved.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "Title must be less than 200 characters",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`title` is over 200 characters or `body` is over 200 words.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NO_RESULT_FOUND",
                  "message": {
                    "desc": "No result found: No row was found when one was required",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No note has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Change a note held against a contact",
        "tags": [
          "contacts"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/finance/reports/cash-from-the-buy": {
      "get": {
        "description": "A year of retail weeks with the buy on each supplier's terms: what settles in, what a retailer has committed, what leaves the bank, what the plan of record still implies, and the balance and headroom walked against the declared cash floor. Four bands: the weeks, the suppliers with their terms and where those terms came from, the plan's own money, and the levers.\n\nThis is merchandise cash on the terms clock and not a cash flow statement, which `state_sentence` says on the report itself. There is no payroll, tax, rent or facility in it.\n\nThe balance is walked on shipped cash. A retailer's committed order is the retailer's option, so `committed_in` is served on every week and is in no balance here.\n\nAbsence is a word in the cell rather than a zero. A week the sales ledger has no reading for reads `Not on file`, and it leaves every week after it unreadable, because a balance cannot be carried across a gap; `coverage.weeks_not_on_file` names those weeks. An organisation that has declared no opening balance or no cash floor reads `Not declared` and gets no walk at all.\n\nScope: `reports:read`.",
        "operationId": "get_cash_from_the_buy",
        "parameters": [
          {
            "description": "The day the ladder stands on, written 2026-11-08. Defaults to today. A day and not a week: the gate walks a year of weeks forward from the day it is taken.\n",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-11-08T06:05:00+00:00",
                    "basis_line": "Merchandise cash · terms clock · retail week",
                    "coverage": {
                      "not_measured": [
                        "The order book was not read, so no week carries a retailer's committed cash."
                      ],
                      "weeks_not_on_file": [
                        "FY26-W37"
                      ]
                    },
                    "head": [
                      {
                        "against": [
                          {
                            "figure": {
                              "basis": "cash",
                              "cents": 25000000,
                              "reason": null,
                              "usd": 250000.0
                            },
                            "key": "floor",
                            "label": "Declared floor"
                          }
                        ],
                        "figure": {
                          "basis": "cash",
                          "cents": -20000000,
                          "reason": null,
                          "usd": -200000.0
                        },
                        "foots_to": {
                          "column": "headroom",
                          "row": "FY26-W39",
                          "section": "weeks"
                        },
                        "key": "tightest",
                        "label": "Cash at the tightest week"
                      }
                    ],
                    "kind": "cash-from-the-buy",
                    "label": "Cash from the buy",
                    "period": "2026-11-08",
                    "period_detail": {
                      "as_of": "2026-11-08",
                      "cash_in_basis": "shipped",
                      "computed_at": "2026-11-08T06:05:00+00:00",
                      "weeks": 52,
                      "window": {
                        "end": "2027-11-06",
                        "start": "2026-11-08"
                      }
                    },
                    "sections": [
                      {
                        "absence": null,
                        "as_of": "2026-11-08T06:05:00+00:00",
                        "data_source": "pg",
                        "key": "weeks",
                        "label": "Where the window is tightest",
                        "note": null,
                        "rows": [
                          {
                            "balance": {
                              "basis": "cash",
                              "cents": 20000000,
                              "reason": null,
                              "usd": 200000.0
                            },
                            "begins": "2026-11-22",
                            "below_the_floor": true,
                            "cash_in": {
                              "basis": "cash",
                              "cents": 10000000,
                              "reason": null,
                              "usd": 100000.0
                            },
                            "cash_in_basis": "realised",
                            "cash_out": {
                              "basis": "cash",
                              "cents": 45000000,
                              "reason": null,
                              "usd": 450000.0
                            },
                            "headroom": {
                              "basis": "cash",
                              "cents": -5000000,
                              "reason": null,
                              "usd": -50000.0
                            },
                            "key": "FY26-W38",
                            "planned_out": {
                              "basis": "cash",
                              "cents": null,
                              "reason": "No plan",
                              "usd": null
                            },
                            "row_kind": "week",
                            "tightest": false,
                            "week": "FY26-W38"
                          }
                        ],
                        "unsupported_reason": null
                      },
                      {
                        "absence": null,
                        "as_of": "2026-11-08T06:05:00+00:00",
                        "data_source": "pg",
                        "key": "groups",
                        "label": "How the buy becomes cash",
                        "note": null,
                        "rows": [
                          {
                            "draws_in_window": {
                              "basis": "cash",
                              "cents": 50000000,
                              "reason": null,
                              "usd": 500000.0
                            },
                            "key": "sup-anadolu",
                            "open_buy": {
                              "basis": "unit_cost",
                              "cents": 82000000,
                              "reason": null,
                              "usd": 820000.0
                            },
                            "row_kind": "supplier",
                            "supplier": "Anadolu Tekstil",
                            "terms_days": {
                              "grain": "day",
                              "population": "days from a drop landing to this supplier's bill",
                              "reason": null,
                              "value": 60
                            },
                            "terms_source": "measured"
                          }
                        ],
                        "unsupported_reason": null
                      }
                    ],
                    "state_sentence": "Merchandise cash on the terms clock, not a cash flow statement.",
                    "verdict": "Below the floor from FY26-W38, $200K short at the worst."
                  },
                  "message": {
                    "desc": "",
                    "service": "finance_reports",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The cash from the buy report",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The day could not be read",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Reports is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Reports; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Cash from the buy, a year of weeks on the terms clock",
        "tags": [
          "finance_reports"
        ],
        "x-tightly-scopes": [
          "reports:read"
        ]
      }
    },
    "/api/v1/finance/reports/open-to-buy": {
      "get": {
        "description": "The fiscal year week by week against the plan and the envelope, as one read. Three bands: every 4-5-4 week of the year with what sold, what was placed, what arrived and the same week last year, footed by a total row; the plan of record's budget and forecast lines at cost by the Gregorian month they were written for; and the buying envelope behind both.\n\nTHE CALENDAR IS NAMED, NOT PAPERED OVER. The weeks are 4-5-4 and the plan is Gregorian months. `calendar_note` says so, and nothing here spreads a month into weeks: a weekly plan nobody wrote is a figure nobody can be held to.\n\nLEVELS DO NOT SUM. The footer totals what flows, sales and intake, and never what is held.\n\nScope: `reports:read`.",
        "operationId": "get_open_to_buy",
        "parameters": [
          {
            "description": "The fiscal year the plan is labelled by, four digits.",
            "in": "query",
            "name": "fiscal_year",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-05T06:10:02Z",
                    "basis_line": "Cost · 4-5-4 retail week · organisation calendar",
                    "calendar_note": "The weeks are 4-5-4 and the plan is by Gregorian month; neither is spread into the other.",
                    "coverage": {
                      "commitments_read": 4,
                      "reason": null
                    },
                    "head": [
                      {
                        "against": {
                          "plan": 3480000.0
                        },
                        "figure": 3120000.0,
                        "foots_to": {
                          "column": "year",
                          "row": "open_to_buy",
                          "section": "envelope"
                        },
                        "key": "open_to_buy",
                        "label": "Open to buy"
                      }
                    ],
                    "kind": "open_to_buy",
                    "label": "Open to buy",
                    "period": "FY26",
                    "period_detail": {
                      "begins": "2026-02-01",
                      "ends": "2027-01-30",
                      "fiscal_year": 2026
                    },
                    "sections": [
                      {
                        "absence": null,
                        "as_of": "2026-09-05T06:10:02Z",
                        "data_source": "pg",
                        "key": "weeks",
                        "label": "The year, week by week",
                        "note": null,
                        "rows": [
                          {
                            "fiscal_week": "FY26-W36",
                            "last_year_net_sales": 1171300.0,
                            "net_sales": 1184200.0,
                            "placed_cost": 902400.0,
                            "received_cost": 812600.0
                          },
                          {
                            "fiscal_week": "total",
                            "last_year_net_sales": 40112800.0,
                            "net_sales": 41880200.0,
                            "placed_cost": 18204000.0,
                            "received_cost": 16118200.0
                          }
                        ],
                        "unsupported_reason": null
                      },
                      {
                        "absence": null,
                        "as_of": "2026-09-05T06:10:02Z",
                        "data_source": "pg",
                        "key": "plan_of_record",
                        "label": "The plan of record, at cost",
                        "note": null,
                        "rows": [
                          {
                            "budget_cost": 1840000.0,
                            "forecast_cost": 1792400.0,
                            "last_year_cost": null,
                            "month": "2026-09"
                          }
                        ],
                        "unsupported_reason": null
                      }
                    ],
                    "verdict": "The year is 1.8 percent under its buying envelope with twelve weeks to place."
                  },
                  "message": {
                    "desc": "",
                    "service": "finance_reports",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The open to buy report",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The fiscal year could not be read",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Reports is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Reports; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The fiscal year week by week, against the plan and the envelope",
        "tags": [
          "finance_reports"
        ],
        "x-tightly-scopes": [
          "reports:read"
        ]
      }
    },
    "/api/v1/finance/reports/weekly-trade": {
      "get": {
        "description": "The Monday pack for one 4-5-4 retail week, as one read. Eight bands: the ledger's lines for this week, last week, month to date and year to date, each against the plan and against the same fiscal week last year; where the move came from, cut by category, channel or commitment; best and worst sellers; the full-price and markdown mix; size availability; aged stock; future intake by week; and this week's breached and watched commitment verdicts.\n\nEVERY HEAD FIGURE FOOTS. `foots_to` is the address of the row and column the figure was counted off, so a head can be tied to the band beneath it rather than taken on trust.\n\nEVERY BAND CARRIES ITS OWN `as_of` AND `data_source`. Eight reads taken at eight moments have eight freshnesses, and one stamp at the top would date the stalest with the freshest's.\n\nLAST YEAR IS A FISCAL WEEK, NOT A DATE. Where this year runs 53 weeks and last year ran 52, week 53 is read against last year's week 52 and `period_detail.last_year.alignment` says `restated_week_53` with the sentence beside it.\n\nABSENCE IS A WORD. A band whose read is not built yet carries `unsupported_reason`; an empty band carries one `absence` sentence; a plan cell with no line reads `No plan` and never a zero.\n\nScope: `reports:read`.",
        "operationId": "get_weekly_trade",
        "parameters": [
          {
            "description": "A fiscal year and a fiscal week, written 2026-W23.",
            "in": "query",
            "name": "week",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How the \"where the move came from\" band is broken. Defaults to category.",
            "in": "query",
            "name": "cut",
            "required": false,
            "schema": {
              "enum": [
                "category",
                "channel",
                "commitment"
              ],
              "type": "string"
            }
          },
          {
            "description": "Published plan in the selected fiscal year. Omit to use the unique published plan; multiple plans require an explicit selection.",
            "in": "query",
            "name": "plan_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-05T06:10:02Z",
                    "basis_line": "Net sales · 4-5-4 retail week · organisation calendar",
                    "calendar_note": "Weeks are 4-5-4 and Sunday-anchored; the plan is by Gregorian month.",
                    "coverage": {
                      "commitments_read": 4,
                      "reason": null
                    },
                    "head": [
                      {
                        "against": {
                          "last_year": 1171300.0,
                          "plan": 1236000.0
                        },
                        "figure": 1184200.0,
                        "foots_to": {
                          "column": "this_week",
                          "row": "net_sales",
                          "section": "ledger"
                        },
                        "key": "net_sales",
                        "label": "Net sales"
                      }
                    ],
                    "kind": "weekly_trade",
                    "label": "Weekly trade",
                    "period": "FY26-W36",
                    "period_detail": {
                      "begins": "2026-08-30",
                      "ends": "2026-09-05",
                      "last_year": {
                        "alignment": "same_fiscal_week",
                        "week": "FY25-W36"
                      },
                      "week": "FY26-W36"
                    },
                    "sections": [
                      {
                        "absence": null,
                        "as_of": "2026-09-05T06:10:02Z",
                        "data_source": "pg",
                        "key": "ledger",
                        "label": "The week",
                        "note": null,
                        "rows": [
                          {
                            "label": "Net sales",
                            "last_week": 1102800.0,
                            "last_year": 1171300.0,
                            "month_to_date": 4218400.0,
                            "plan": 1236000.0,
                            "row": "net_sales",
                            "this_week": 1184200.0,
                            "year_to_date": 41880200.0
                          }
                        ],
                        "unsupported_reason": null
                      },
                      {
                        "absence": null,
                        "as_of": null,
                        "data_source": null,
                        "key": "aged_stock",
                        "label": "Aged stock",
                        "note": null,
                        "rows": [],
                        "unsupported_reason": "Stock ageing lands with the Stock value and age report."
                      }
                    ],
                    "state_sentence": null,
                    "verdict": "Net sales are 4.2 percent behind plan for the week and 1.1 percent ahead of last year."
                  },
                  "message": {
                    "desc": "",
                    "service": "finance_reports",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The weekly trade report",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The week could not be read, or the cut is not one this report offers",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Reports is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Reports; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such report",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The Monday pack for one 4-5-4 retail week",
        "tags": [
          "finance_reports"
        ],
        "x-tightly-scopes": [
          "reports:read"
        ]
      }
    },
    "/api/v1/inventory/allocation-matrix": {
      "get": {
        "description": "Stock spread across warehouses for the variants requested: the locations as columns, one row per variant, and a cell per pair carrying on-hand quantity, days and weeks of cover and a health word. Built for finding imbalances between sites.\n\n`variant_ids` is required and takes the variants to compare. Health is `critical`, `caution` or `healthy`, the same three words get_inventory_table serves.\n\nScope: `inventory:read`.",
        "operationId": "get_allocation_matrix",
        "parameters": [
          {
            "description": "Comma-separated list of variant IDs to include in the matrix (max 50, min 1). Always required.",
            "in": "query",
            "name": "variant_ids",
            "required": true,
            "schema": {
              "examples": [
                "variant_id_1,variant_id_2,variant_id_3"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "locations": [
                      {
                        "location_id": "loc_0004",
                        "location_name": "London DC"
                      },
                      {
                        "location_id": "loc_0009",
                        "location_name": "Rotterdam DC"
                      }
                    ],
                    "variants": [
                      {
                        "cells": [
                          {
                            "days_of_cover": 18,
                            "health": "caution",
                            "location_id": "loc_0004",
                            "on_hand": 302,
                            "weeks_of_cover": 2.6
                          },
                          {
                            "days_of_cover": 0,
                            "health": "critical",
                            "location_id": "loc_0009",
                            "on_hand": 0,
                            "weeks_of_cover": 0.0
                          }
                        ],
                        "product_id": "4410092",
                        "sku": "TB-CREW-BLK-M",
                        "variant_id": "44100920011",
                        "variant_title": "Black / M"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "Allocation matrix data",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The allocation matrix",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - missing or invalid variant_ids",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stock.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Data not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get allocation matrix",
        "tags": [
          "inventory"
        ],
        "x-tightly-scopes": [
          "inventory:read"
        ]
      }
    },
    "/api/v1/inventory/forecast-accuracy": {
      "get": {
        "description": "What the weekly forecast-accuracy job measured, read back: a `trailing` window and a `latest` week, each carrying wmape, wmase and the population they were scored over, and a `weeks` series behind them. Nothing is recomputed, so the figures are the ones measured at the time.\n\nEvery headline figure is measured on the organisation's managed direct-to-consumer book and says so in `population` and `population_label`. There is no sell-in forecast to score, so an organisation with wholesale volume is not being told about its whole book.\n\n`latest.by_product_type` and `trailing.by_product_type` cut the same measurement by product type and reconcile to the headline. `by_channel` cuts it per active sales channel, each scored on its own series, and does NOT reconcile: absolute error is not additive across channels. A slice with too few selling lines to be evidence carries a `refusal` and null scores. `unattributed_variants` is the rest of the scored population, whose forecast carries no recorded model.\n\n`model_wmase[<method>]` carries each model's error on both denominators. Only `wmase_byhand`, over the by-hand four-week moving average the headline divides by, is on the headline's scale, and it is null on a week with no by-hand denominator.\n\n`weeks` bounds the trailing window, and `product_type` or `sales_channel_id` (one, not both) restricts every figure and echoes the cut in `scope`. A cut no measured week covers answers `refusal` with `trailing` and `latest` null and `weeks` empty.\n\nScope: `inventory:read`.",
        "operationId": "get_forecast_accuracy",
        "parameters": [
          {
            "description": "Window and cap, the most recent N measured whole weeks. Read `trailing.weeks` for how many were actually summed and `trailing.window_start_date` / `window_end_date` for the span they cover; a missed weekly run makes the window reach further back than N weeks.\n",
            "in": "query",
            "name": "weeks",
            "required": false,
            "schema": {
              "default": 52,
              "maximum": 260,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Narrow to measured weeks starting on or after this date.",
            "in": "query",
            "name": "start_date",
            "required": false,
            "schema": {
              "examples": [
                "2026-01-05"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Narrow to measured weeks ending on or before this date.",
            "in": "query",
            "name": "end_date",
            "required": false,
            "schema": {
              "examples": [
                "2026-09-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Opt out of the two read-time inventories, forecast coverage and model provenance, which are computed against the live catalogue on every request and are most of this endpoint's cost. Their seven fields are then absent, which every client tolerates. Ignored (treated as false) on a sliced request: they are whole-book figures and have no per-slice meaning.\n",
            "in": "query",
            "name": "current_state",
            "required": false,
            "schema": {
              "default": true,
              "type": "boolean"
            }
          },
          {
            "description": "Restrict every figure to one category from `products.product_type`, matched exactly against the stored slice. Not combinable with `sales_channel_id`, because the record measures each cut on its own axis, so a crossed cut is refused rather than approximated.\n",
            "in": "query",
            "name": "product_type",
            "required": false,
            "schema": {
              "examples": [
                "Accessories"
              ],
              "type": "string"
            }
          },
          {
            "description": "Restrict every figure to one sales channel, wholesale and retailer channels included. The channel is scored on its own series, so this never returns the direct-to-consumer figure under a channel's name; a channel with no scored week answers `refusal`.\n",
            "in": "query",
            "name": "sales_channel_id",
            "required": false,
            "schema": {
              "examples": [
                "gid://shopify/Channel/12345"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "latest": {
                      "total_actual": 18400,
                      "total_predicted": 17920,
                      "total_variants": 4120,
                      "variants_with_sales": 3880,
                      "week_end_date": "2026-08-30",
                      "week_start_date": "2026-08-24",
                      "wmape": 0.261,
                      "wmase": 0.78
                    },
                    "refusal": null,
                    "scope": null,
                    "trailing": {
                      "average_bias": -0.031,
                      "computed_at": "2026-09-01T06:31:00+00:00",
                      "false_demand_rate": 0.06,
                      "measured_days": 364,
                      "population": 4120,
                      "population_label": "variants with sales in the window",
                      "weeks": 52,
                      "weeks_missing": 0,
                      "window_end_date": "2026-08-31",
                      "window_start_date": "2025-09-08",
                      "wmape": 0.284,
                      "wmase": 0.81,
                      "wmase_baseline": "seasonal_naive"
                    },
                    "weeks": [
                      {
                        "week_start_date": "2026-08-17",
                        "wmape": 0.297,
                        "wmase": 0.84
                      },
                      {
                        "week_start_date": "2026-08-24",
                        "wmape": 0.261,
                        "wmase": 0.78
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "latest": {
                          "description": "The most recent measured week, with the detail the series omits",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "refusal": {
                          "description": "Why there is no figure for the requested cut, as a sentence to render. Present exactly when `trailing` and `latest` are null because of the cut.\n",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scope": {
                          "description": "The cut the figures were measured on, when one was requested. Null means the whole measured book.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "trailing": {
                          "description": "The whole window as one figure, sums divided once, never a mean of weekly ratios",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "weeks": {
                          "description": "The measured weeks, oldest first, so a chart plots without reversing",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "Response envelope severity, service and description",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The record. `trailing` and `latest` are null, not absent, on a book with no measured weeks, and on a refused slice.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request. An out-of-range `weeks`, or an unparseable date",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stock.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get forecast accuracy",
        "tags": [
          "inventory"
        ],
        "x-tightly-scopes": [
          "inventory:read"
        ]
      }
    },
    "/api/v1/inventory/movements": {
      "get": {
        "description": "Every plus and minus of stock, newest first, each naming the document that caused it: a receipt, a count, a fulfilment, a return, an adjustment, a transfer leg, the opening the ledger started from, or a correction the nightly sweep booked against a source that disagreed.\n\nA read must name a product AND a warehouse, or a document (`document_type` with `document_id`). One that names neither is refused: an unfiltered ledger is every stock change the organisation has ever made.\n\nSums are the point. The rows for one product at one warehouse sum to that pair's on hand, which is what `get_position` serves as one figure. `evidence` rides a `sync_correction` alone, where it carries the source's snapshot, the ledger's sum and how many nights running they disagreed.\n\nNothing writes here, ever. A movement is written by the document that moved the stock; correcting one is a reversing movement against it, not an edit.\n\nScope: `movements:read`.",
        "operationId": "list_movements",
        "parameters": [
          {
            "description": "The product. Required unless a document is named.",
            "in": "query",
            "name": "variant_id",
            "required": false,
            "schema": {
              "examples": [
                "fx-v-mar-top-m"
              ],
              "type": "string"
            }
          },
          {
            "description": "The warehouse. Required unless a document is named.",
            "in": "query",
            "name": "location_id",
            "required": false,
            "schema": {
              "examples": [
                "loc-lb"
              ],
              "type": "string"
            }
          },
          {
            "description": "One of ledger_opening, purchase_order_delivery, transfer_order_delivery, stocktake, fulfilment_request, channel_fulfilment, return, approval_request, etl_run.\n",
            "in": "query",
            "name": "document_type",
            "required": false,
            "schema": {
              "examples": [
                "purchase_order_delivery"
              ],
              "type": "string"
            }
          },
          {
            "description": "The document's own id, as text. Sent with `document_type`.",
            "in": "query",
            "name": "document_id",
            "required": false,
            "schema": {
              "examples": [
                "8841"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated movement kinds to filter to: opening, receipt, fulfilment, return, adjustment, transfer_out, transfer_in, stocktake, sync_correction.\n",
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "examples": [
                "receipt,return"
              ],
              "type": "string"
            }
          },
          {
            "description": "Only movements that occurred at or after this instant.",
            "in": "query",
            "name": "since",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Only movements that occurred at or before this instant.",
            "in": "query",
            "name": "until",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "How many movements to skip, for paging through a long ledger.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "type": "integer"
            }
          },
          {
            "description": "How many movements to return, newest first.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 10000,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 13,
                    "max_size": 13,
                    "offset": 0,
                    "rows": [
                      {
                        "actor": null,
                        "document": {
                          "id": "31",
                          "name": "RET-00000031",
                          "type": "return"
                        },
                        "document_line_id": "54",
                        "evidence": null,
                        "id": 9013,
                        "kind": "return",
                        "landed_unit_cost": {
                          "cents": null,
                          "currency": null,
                          "reason": "Not measured",
                          "usd": null
                        },
                        "location_id": "loc-lb",
                        "location_name": "Long Beach",
                        "occurred_at": "2026-09-05T02:14:00+00:00",
                        "quantity_delta": 2,
                        "recorded_at": "2026-09-05T02:14:03+00:00",
                        "reverses_movement_id": null,
                        "sequence": 1,
                        "sku": "MAR-TOP-M",
                        "source_system": "shopify",
                        "unit_cost": {
                          "cents": 1200,
                          "currency": "USD",
                          "reason": null,
                          "usd": 12.0
                        },
                        "variant_id": "fx-v-mar-top-m"
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "filtered_max_size": {
                          "type": "integer"
                        },
                        "max_size": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "size": {
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of the ledger, newest first.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "filter_needs_a_pair_or_a_document",
                  "message": {
                    "desc": "A movements read names a product and a warehouse, or a document; this one names neither.",
                    "service": "inventory",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The filter names neither a product and a warehouse nor a document (`filter_needs_a_pair_or_a_document`), or a kind, document type, instant (`since`, `until`) or page bound could not be read.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Movements.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Movements (`scope_missing`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The stock ledger, filtered to a product and warehouse or to a document",
        "tags": [
          "movements"
        ],
        "x-tightly-scopes": [
          "movements:read"
        ]
      }
    },
    "/api/v1/inventory/position": {
      "get": {
        "description": "What is there, what is held, what is coming and what is left to sell, for one product at one warehouse: `on_hand`, `reserved` broken down by why each unit is spoken for and by which order holds it, `expected_inbound` broken down by the purchase or transfer order it is coming on, and `atp`.\n\nThe identity holds on every answer: `on_hand - reserved.hard + expected_inbound.quantity + expected_inbound.returns_expected = atp`. `atp` is computed from those four and read from nowhere else, so a figure you are shown can always be counted off the rows under it.\n\n`reserved.hard` is what `atp` subtracts and `reserved.soft` warns without consuming anything. At a warehouse the connector masters, the hard figure takes the LARGER of Tightly's own order holds and the source's `committed_quantity`, and the part no Tightly document explains is served as its own `source_committed` row.\n\n`on_hand_basis` says which fact the head figure is. `source_on_hand` is the connector's own physical count; `source_available` is its available figure standing in where it serves no physical one, and that figure is already net of what the store has promised its own orders; `ledger` is the sum of the movements, which is the on hand at a warehouse Tightly masters.\n\nLeave `location_id` off for the total over every warehouse the product is known at, with a row per warehouse under `locations`. The last 20 movements ride along; the whole ledger is `list_movements`.\n\nScope: `inventory:read`.",
        "operationId": "get_position",
        "parameters": [
          {
            "description": "The product variant to read the position of.",
            "in": "query",
            "name": "variant_id",
            "required": true,
            "schema": {
              "examples": [
                "fx-v-mar-top-m"
              ],
              "type": "string"
            }
          },
          {
            "description": "The warehouse. Omit for the sum over every warehouse the product is known at, in which case the head figures are the sums of the `locations` rows.\n",
            "in": "query",
            "name": "location_id",
            "required": false,
            "schema": {
              "examples": [
                "loc-lb"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-05T06:05:12+00:00",
                    "atp": 566,
                    "expected_inbound": {
                      "by_document": [
                        {
                          "due": "2026-09-19",
                          "id": "1042",
                          "name": "PO-00001042",
                          "quantity": 200,
                          "supply_class": "firm",
                          "type": "purchase_order"
                        }
                      ],
                      "quantity": 200,
                      "returns_expected": 0
                    },
                    "location_id": "loc-lb",
                    "location_name": "Long Beach",
                    "locations": [],
                    "movements": [
                      {
                        "actor": null,
                        "document": {
                          "id": "31",
                          "name": "RET-00000031",
                          "type": "return"
                        },
                        "id": 9013,
                        "kind": "return",
                        "landed_unit_cost": {
                          "cents": null,
                          "currency": null,
                          "reason": "Not measured",
                          "usd": null
                        },
                        "occurred_at": "2026-09-05T02:14:00+00:00",
                        "quantity_delta": 2,
                        "unit_cost": {
                          "cents": 1200,
                          "currency": "USD",
                          "reason": null,
                          "usd": 12.0
                        }
                      }
                    ],
                    "movements_total": 13,
                    "on_hand": 380,
                    "on_hand_basis": "source_on_hand",
                    "reconciliation": {
                      "residual": 0,
                      "residual_seen_at": null,
                      "tied_at": "2026-09-05T03:40:11+00:00",
                      "tied_quantity": 380
                    },
                    "reserved": {
                      "by_basis": [
                        {
                          "basis": "order",
                          "documents": [
                            {
                              "id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                              "name": "#1187",
                              "quantity": 6,
                              "type": "order"
                            },
                            {
                              "id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BE",
                              "name": "#1189",
                              "quantity": 8,
                              "type": "order"
                            }
                          ],
                          "kind": "hard",
                          "quantity": 14
                        }
                      ],
                      "hard": 14,
                      "soft": 0
                    },
                    "stock_master": "source",
                    "variant_id": "fx-v-mar-top-m"
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "description": "When the read was taken.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "atp": {
                          "type": "integer"
                        },
                        "expected_inbound": {
                          "description": "The firm outstanding quantity by document, and the returns expected back.",
                          "type": "object"
                        },
                        "location_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "location_name": {
                          "description": "The warehouse's own name. Null on the sum over every warehouse.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "locations": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "movements": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "movements_total": {
                          "type": "integer"
                        },
                        "on_hand": {
                          "type": "integer"
                        },
                        "on_hand_basis": {
                          "description": "source_on_hand, source_available or ledger. Null on the sum over every warehouse.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "reconciliation": {
                          "description": "tied_at, tied_quantity, residual and residual_seen_at.",
                          "type": "object"
                        },
                        "reserved": {
                          "description": "hard, soft and the by_basis decomposition, whose quantities sum to hard + soft.",
                          "type": "object"
                        },
                        "stock_master": {
                          "description": "Who keeps this warehouse's stock, `source` or `tightly`. Null on the sum over every warehouse, where each row carries its own.\n",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "variant_id": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The position, its decomposition and the last 20 movements behind it.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No `variant_id` was sent, or a parameter could not be read.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stock.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "variant_not_on_file",
                  "message": {
                    "desc": "No product with that id is on file.",
                    "service": "inventory",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No product or no warehouse with that id is on file (`variant_not_on_file`, `location_not_on_file`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One product's position at a warehouse, decomposed",
        "tags": [
          "inventory"
        ],
        "x-tightly-scopes": [
          "inventory:read"
        ]
      }
    },
    "/api/v1/inventory/stock-on-hand": {
      "get": {
        "description": "Stock at each period boundary, per category and location: one row per period with its opening and closing level, plus the window, the grain, the calendar it was bucketed on, a coverage block and a basis note.\n\nA level and not a flow. A period's opening stock is the level on its first day and its closing stock the level on its last, and a period's closing boundary is the next period's opening boundary read from the same row, so a ladder built from these ties by construction.\n\n`start_date` and `end_date` are required and `end_date` is INCLUSIVE, as it is on get_net_sales: the service turns it into the exclusive bound, so two callers asking for 2026-01-31 and 2026-02-01 each see the period once. The window may not exceed 400 days, which is also the archive's retention, and a wider one asks for boundaries that have been deleted. `categories` and `locations` filter without changing the grain.\n\nThere is deliberately no channel filter: stock sits at a location and a unit in a warehouse is not allocated to a sales channel, so a channel argument could only be answered by inventing an allocation. Use get_inventory_table when the question is per variant rather than per period.\n\nScope: `inventory:read`.",
        "operationId": "get_stock_on_hand",
        "parameters": [
          {
            "description": "First day of the window (YYYY-MM-DD).",
            "in": "query",
            "name": "start_date",
            "required": true,
            "schema": {
              "examples": [
                "2026-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Last day of the window, inclusive (YYYY-MM-DD). At most 400 days after the start.",
            "in": "query",
            "name": "end_date",
            "required": true,
            "schema": {
              "examples": [
                "2026-06-30"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "The period the ladder is cut into. The same two keys the net sales feed serves.",
            "in": "query",
            "name": "grain",
            "required": false,
            "schema": {
              "default": "retail_week",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated categories to filter to. Filters; never changes the grain.",
            "in": "query",
            "name": "categories",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Comma-separated location ids to filter to.",
            "in": "query",
            "name": "locations",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "basis_note": "Levels at each period boundary, read from the nightly inventory archive.",
                    "calendar": "retail_454",
                    "channel_axis": null,
                    "cover_note": "Every location in scope reported on the boundary date.",
                    "coverage": {
                      "archive": "complete",
                      "carry_forward_days": 0,
                      "categories": 14,
                      "locations": 4,
                      "max_staleness_days": 1,
                      "pairs": 4120,
                      "pairs_costed": 3990,
                      "pairs_measured_at": "2026-06-30",
                      "pairs_priced": 4008,
                      "periods": 26,
                      "periods_with_closing": 26,
                      "periods_with_opening": 26,
                      "unattributed_reason": null,
                      "unattributed_units": 0,
                      "unattributed_value_at_cost": 0.0
                    },
                    "grain": "retail_week",
                    "rows": [
                      {
                        "category": "Knitwear",
                        "closing": 17120,
                        "opening": 18400,
                        "period_start": "2026-01-05"
                      },
                      {
                        "category": "Knitwear",
                        "closing": 16008,
                        "opening": 17120,
                        "period_start": "2026-01-12"
                      }
                    ],
                    "window": {
                      "end_exclusive": "2026-07-01",
                      "start": "2026-01-01"
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "basis_note": {
                          "type": "string"
                        },
                        "calendar": {
                          "type": "string"
                        },
                        "channel_axis": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "cover_note": {
                          "type": "string"
                        },
                        "coverage": {
                          "description": "Which pairs were measured, priced and costed, how stale the archive is, and what could not be attributed.",
                          "type": "object"
                        },
                        "grain": {
                          "type": "string"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "window": {
                          "description": "The window actually read, `start` and `end_exclusive`.",
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One row per period boundary and category with `opening` and `closing`, the window actually read, the calendar and grain behind it, and a coverage block that describes the population these figures were measured over.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A date could not be read, `end_date` precedes `start_date`, the window exceeds 400 days, or the grain is not one the calendar module serves.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stock.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Stock on hand at each period boundary, the level a stock ladder is anchored on",
        "tags": [
          "inventory"
        ],
        "x-tightly-scopes": [
          "inventory:read"
        ]
      }
    },
    "/api/v1/inventory/suppliers": {
      "post": {
        "description": "Creates suppliers and answers 201 with each one as get_supplier_details serves it. The body is `{\"suppliers\": [...]}`; every entry needs a `name`, and may carry the address, `currency`, `min_order_value`, `lead_time`, `payment_terms_days`, `domains`, a `contacts` list and a `primary_contact`.\n\nA supplier's id is derived from its name, so this call is idempotent on the name and never overwrites. Posting a name that already exists answers the supplier on file, unchanged, with none of the fields in the body applied, and 201 either way. update_supplier is the door for changing a supplier that exists. Two entries with the same name in one body are collapsed to the first.\n\n`contacts` are created with the supplier and deduplicated on (email, name). A contact with no `preferred_contact_method` takes email when an email is given and phone otherwise, and in that same case a contact with an email and no name takes the local part of the address, state `preferred_contact_method` yourself and the name stays null. `primary_contact` names an existing contact by `id` and does not create one.\n\n`domains` is what routes a supplier's mail: a domain like `acme.example` or a whole address like `orders@acme.example`. Anything that is neither is refused 400, because a bad entry matches nothing and nothing reports it.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "create_supplier",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "suppliers": [
                  {
                    "address1": "Rua do Bolhao 114",
                    "city": "Porto",
                    "contacts": [
                      {
                        "department": "Sales",
                        "email": "alex@portoknits.example",
                        "name": "Alex Example",
                        "phone": "+1 202 555 0100",
                        "preferred_contact_method": "EMAIL",
                        "role": "Account manager"
                      }
                    ],
                    "country": "PT",
                    "currency": "EUR",
                    "domains": [
                      "portoknits.example"
                    ],
                    "lead_time": 21,
                    "min_order_value": 5000,
                    "name": "Porto Knits",
                    "payment_terms_days": 45,
                    "postal_code": "4000-112"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateSuppliersRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "suppliers": [
                      {
                        "address1": "Rua do Bolhao 114",
                        "address2": null,
                        "city": "Porto",
                        "contacts": [
                          {
                            "city": null,
                            "country": null,
                            "department": "Sales",
                            "description": null,
                            "email": "alex@portoknits.example",
                            "id": "412",
                            "name": "Alex Example",
                            "phone": "+1 202 555 0100",
                            "preferred_contact_method": "email",
                            "role": "Account manager",
                            "supplier_id": "sup_0031"
                          }
                        ],
                        "country": "PT",
                        "currency": "EUR",
                        "domains": [
                          "portoknits.example"
                        ],
                        "id": "sup_0031",
                        "lead_time": 21,
                        "min_order_value": 5000,
                        "name": "Porto Knits",
                        "postal_code": "4000-112",
                        "province": null
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "UNKNOWN",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/CreateSuppliersResponse"
                }
              }
            },
            "description": "The suppliers as they now stand, the ones created, and unchanged for any name that already existed.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "Enter a domain like 'acme.example', or a full address like 'orders@acme.example'.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "An entry has no `name`, or a `domains` entry is neither a domain nor an address.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create suppliers and their contacts in one call",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/inventory/suppliers/filters": {
      "get": {
        "description": "The values a supplier filter can take, read off the suppliers this organisation actually has: `countries`, `currencies`, `cities`, `supplier_names`, and `missing_fields`, the field names for which at least one supplier has nothing on file.\n\nTakes no parameters. Every list is what exists now, so an empty list means no supplier carries that field rather than that the filter is unsupported. `supplier_names` is ordered with the live suppliers before the archived ones.\n\nPass one of the `missing_fields` names back to list_suppliers as `{\"key\":\"missing_fields\",\"operation\":\"in\",\"value\":[\"lead_time\"]}` to get the suppliers that lack it.\n\nScope: `suppliers:read`.",
        "operationId": "get_suppliers_filters",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "cities": [
                      "Izmir",
                      "Porto",
                      "Prato"
                    ],
                    "countries": [
                      "IT",
                      "PT",
                      "TR"
                    ],
                    "currencies": [
                      "EUR",
                      "GBP"
                    ],
                    "missing_fields": [
                      "min_order_value",
                      "postal_code"
                    ],
                    "supplier_names": [
                      "Porto Knits",
                      "Prato Wovens",
                      "Izmir Jersey"
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "cities": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "countries": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "currencies": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "missing_fields": {
                          "description": "The field names at least one supplier has nothing on file for, ready to send back as a `missing_fields` filter value.\n",
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "supplier_names": {
                          "description": "Every supplier's name, live suppliers before archived ones.",
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Five lists. Each is what this organisation's suppliers actually carry, so an empty list means nothing to filter on rather than an unsupported filter.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The values a supplier filter can take in this organisation",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      }
    },
    "/api/v1/inventory/suppliers/table": {
      "get": {
        "description": "A page of suppliers: identity and address, currency, minimum order value, the resolved lead time and where it came from, the primary contact, the per-warehouse lead times, and the OTIF, on-time and in-full rates. `filtered_max_size` is the size of the filtered set and `max_size` the size of the book, so a client can page without counting.\n\nPage with `limit` (1 to 10,000, default 10) and `offset` (0 to 2,147,483,647). Narrow with `filter_args`, a JSON array of `{key, operation, value, group}`, on `id`, `name`, `city`, `country` and `currency` (eq, in), `min_order_value` and `lead_time` (gte, lte), `is_archived` (eq) and `missing_fields` (eq, in). Order with `sort_args`, comma-separated, `-` for descending, over id, name, city, country, postal_code, min_order_value, lead_time, otif_score, on_time, in_full and primary_contact.name, .email and .phone. `search` matches the name and the address.\n\nA row's `lead_time` resolves the supplier's own figure against the organisation default, while each entry in `location_lead_times` resolves that warehouse's override against the same default. The two can differ on one row on purpose. `otif_score`, `on_time` and `in_full` are null until enough delivery lines have been scored to publish a rate.\n\nA row carries no contacts list and no lanes. get_supplier_details is the whole record for one supplier.\n\nScope: `suppliers:read`.",
        "operationId": "list_suppliers",
        "parameters": [
          {
            "description": "Return filtered_max_size and max_size without rows, delivery scoring or warehouse enrichment. size is zero and rows is empty. Counts ignore offset and limit; offset is echoed. max_size remains the full supplier-book count even when no supplier matches the filters. Cannot be combined with for_zapier. Defaults to the existing full table response.",
            "in": "query",
            "name": "counts_only",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "description": "Rows per page. Out of range is refused 400.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Rows to skip. Out of range is refused 400.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "maximum": 2147483647,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "A JSON array of filter conditions, each `{key, operation, value}` with an optional `group` of `and` (the default) or `or`. Keys and their operations: `id`, `name`, `city`, `country`, `currency` (eq, in); `min_order_value`, `lead_time` (gte, lte); `is_archived` (eq, true or false); `missing_fields` (eq, in), suppliers with nothing on file for a field, whose value is one of name, city, country, postal_code, currency, min_order_value, lead_time, contact_name, contact_email, contact_phone. `missing_fields` groups as `or` unless you say otherwise, because asking for two missing fields almost always means either. An unsupported key, operation or value is refused 400 naming what was allowed.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"country\",\"operation\":\"eq\",\"value\":\"PT\"},{\"key\":\"lead_time\",\"operation\":\"gte\",\"value\":14}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated sort columns, `-` for descending and `+` or nothing for ascending. Valid columns: id, name, city, country, postal_code, min_order_value, lead_time, otif_score, on_time, in_full, primary_contact.name, primary_contact.email, primary_contact.phone.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "-lead_time,name"
              ],
              "type": "string"
            }
          },
          {
            "description": "Free text matched against the supplier's name and address.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 38,
                    "max_size": 214,
                    "offset": 0,
                    "rows": [
                      {
                        "address1": "Rua do Bolhao 114",
                        "address2": null,
                        "average_lead_time": null,
                        "city": "Porto",
                        "contacts": [],
                        "container_type_id": null,
                        "country": "PT",
                        "currency": "EUR",
                        "domains": [],
                        "id": "sup_0031",
                        "in_full": 96.0,
                        "integrations": {},
                        "is_archived": false,
                        "lanes": null,
                        "lead_time": 21,
                        "lead_time_source": "supplier",
                        "location_lead_times": [
                          {
                            "lead_time": 21,
                            "location_id": "loc_0004",
                            "location_name": "Leeds DC",
                            "source": "supplier_location"
                          }
                        ],
                        "min_order_value": 5000,
                        "name": "Porto Knits",
                        "on_time": 94.2,
                        "otif_score": 91.4,
                        "payment_terms_days": null,
                        "postal_code": "4000-112",
                        "primary_contact": {
                          "city": null,
                          "country": null,
                          "department": null,
                          "description": null,
                          "email": "alex@portoknits.example",
                          "id": "412",
                          "is_primary_contact": null,
                          "name": "Alex Example",
                          "phone": "+1 202 555 0100",
                          "preferred_contact_method": "email",
                          "role": null,
                          "supplier_id": null,
                          "supplier_name": null
                        },
                        "province": null
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetSuppliersTablePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One page of supplier rows, with the offset requested, the number of rows served, the size of the filtered set and the size of the whole book.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "limit must be an integer between 1 and 10000",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A pagination bound, a filter key, an operation or a value is not one this table takes.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "A page of suppliers with their terms, lead times and delivery record",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      }
    },
    "/api/v1/inventory/suppliers/transfer-vendors": {
      "post": {
        "description": "Promotes the vendor names an e-commerce catalogue sync has staged into real suppliers, and answers 200 with the plain body `ok`, not JSON, and not the `{message, data}` envelope. It takes no body and no parameters.\n\nThe rows it reads are written by the catalogue sync, so it has an effect only after a sync has run and only for vendors that are not suppliers yet. A vendor already on file is left exactly as it is, so running it twice is harmless and running it on a book with nothing staged is a no-op.\n\nThis is not a general import. import_suppliers is the door for a CSV of your own, and create_supplier for suppliers named in a request body.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "transfer_vendors",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/html": {
                "example": "ok",
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "The staged vendors were promoted. The body is the two characters `ok`.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Promote the vendor names a catalogue sync has staged into suppliers",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/inventory/suppliers/upload": {
      "post": {
        "description": "Uploads a CSV of suppliers and their primary contacts. Send it as `multipart/form-data` under the field name `file`, with a content type of `text/csv` or `text/plain`; the delimiter is sniffed rather than assumed, so a semicolon or a tab file is read as readily as a comma one.\n\nRequired columns: `supplier_id`, `supplier_name`, `contact_name`, `contact_email`, `contact_phone`. Any other column the importer knows is optional. Rows with no `supplier_id` are dropped and duplicate `supplier_id`s are collapsed to the first.\n\nThe `supplier_id` here is yours, which is what separates this from create_supplier: that call derives an id from the name, this one keeps the id your system of record already uses.\n\nIt answers 200 with `{\"status\": \"success\"}`, not the `{message, data}` envelope every other operation answers. A missing file, an unsupported content type, a missing required column, an unreadable file or one that is empty after filtering is refused 400.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "import_suppliers",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "properties": {
                  "file": {
                    "description": "The CSV. Required columns: supplier_id, supplier_name, contact_name, contact_email, contact_phone.\n",
                    "format": "binary",
                    "type": "string"
                  }
                },
                "required": [
                  "file"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "status": "success"
                },
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The file was read and its rows were written. Not the standard envelope.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "error": "File format not supported. Upload a CSV file."
                },
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "error": {
                      "description": "The route's own sentence, for a missing file or an unsupported type.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc, for a file the importer could not use.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No file, a content type that is not `text/csv` or `text/plain`, a missing required column, a file that cannot be parsed, or a file with no usable row. The first two answer `{\"error\": ...}` from the route itself; the rest answer the platform's error envelope.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Import suppliers and their primary contacts from a CSV",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/inventory/suppliers/{supplier_id}": {
      "get": {
        "description": "One supplier, whole: identity and address, currency, minimum order value, payment terms in days, the resolved lead time and its source, every contact with the primary one named separately, the email domains that route this supplier's mail, the OTIF, on-time and in-full rates, and two views of its warehouses.\n\n`location_lead_times` is one entry per active warehouse carrying the RESOLVED lead time, the supplier × warehouse override if one is set, else the organisation default, with `source` saying which answered. `lanes` is the same warehouses stated whole: the lane's own override (`lead_time` null means no override), the container it names, what the ladder resolved to, `basis` naming the rung that answered (`lane`, `supplier`, `organisation`, or null for no container on file), the forwarder's quote per container in cents, and the inland leg's road limit in kg. Both lists come out of one statement, so they cannot disagree about a warehouse.\n\n`payment_terms_days` null means nobody has recorded terms, which is read as no lag rather than as zero. `container_type_id` on the supplier is the middle rung: null means \"use the organisation default\", which is not the same as no container.\n\nAn id this organisation does not have is refused 404. get_supplier_overview is the door when only the five scorecard figures are wanted.\n\nScope: `suppliers:read`.",
        "operationId": "get_supplier_details",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "address1": "Rua do Bolhao 114",
                    "address2": null,
                    "average_lead_time": {
                      "info": "Average lead time",
                      "key": "average_lead_time",
                      "unit": "days",
                      "value": 19.0
                    },
                    "city": "Porto",
                    "contacts": [
                      {
                        "city": "Porto",
                        "country": "PT",
                        "department": "Sales",
                        "description": null,
                        "email": "alex@portoknits.example",
                        "id": "412",
                        "is_primary_contact": true,
                        "name": "Alex Example",
                        "phone": "+1 202 555 0100",
                        "preferred_contact_method": "email",
                        "role": "Account manager",
                        "supplier_id": null,
                        "supplier_name": null
                      }
                    ],
                    "container_type_id": 2,
                    "country": "PT",
                    "currency": "EUR",
                    "domains": [
                      "portoknits.example"
                    ],
                    "id": "sup_0031",
                    "in_full": 96.0,
                    "integrations": {},
                    "is_archived": false,
                    "lanes": [
                      {
                        "basis": "supplier",
                        "container_name": "40ft high cube",
                        "container_type_id": null,
                        "freight_per_container_cents": 412000,
                        "internal_cbm": 76.3,
                        "lead_time": 21,
                        "lead_time_source": "supplier_location",
                        "location_id": "loc_0004",
                        "location_name": "Leeds DC",
                        "pallet_positions": 25,
                        "payload_kg": 26700,
                        "resolved_container_type_id": 2,
                        "road_limit_kg": 24000,
                        "usable_pct": 85.0
                      },
                      {
                        "basis": "supplier",
                        "container_name": "40ft high cube",
                        "container_type_id": null,
                        "freight_per_container_cents": null,
                        "internal_cbm": 76.3,
                        "lead_time": null,
                        "lead_time_source": "organization",
                        "location_id": "loc_0009",
                        "location_name": "Rotterdam 3PL",
                        "pallet_positions": 25,
                        "payload_kg": 26700,
                        "resolved_container_type_id": 2,
                        "road_limit_kg": null,
                        "usable_pct": 85.0
                      }
                    ],
                    "lead_time": 21,
                    "lead_time_source": "supplier",
                    "location_lead_times": [
                      {
                        "lead_time": 21,
                        "location_id": "loc_0004",
                        "location_name": "Leeds DC",
                        "source": "supplier_location"
                      },
                      {
                        "lead_time": 30,
                        "location_id": "loc_0009",
                        "location_name": "Rotterdam 3PL",
                        "source": "organization"
                      }
                    ],
                    "min_order_value": 5000,
                    "name": "Porto Knits",
                    "on_time": 94.2,
                    "otif_score": 91.4,
                    "payment_terms_days": 45,
                    "postal_code": "4000-112",
                    "primary_contact": {
                      "city": "Porto",
                      "country": "PT",
                      "department": "Sales",
                      "description": null,
                      "email": "alex@portoknits.example",
                      "id": "412",
                      "is_primary_contact": true,
                      "name": "Alex Example",
                      "phone": "+1 202 555 0100",
                      "preferred_contact_method": "email",
                      "role": "Account manager",
                      "supplier_id": "sup_0031",
                      "supplier_name": "Porto Knits"
                    },
                    "province": null
                  },
                  "message": {
                    "desc": "",
                    "service": "UNKNOWN",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetSupplierDetailsResponse"
                }
              }
            },
            "description": "The supplier's record: terms, contacts, domains, the delivery rates, and the warehouses as both resolved lead times and whole lanes.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Supplier with id sup_9999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One supplier whole",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      },
      "put": {
        "description": "Changes one supplier and answers it whole, as get_supplier_details serves it. Send only the fields that move: omitted or null is left alone, with the exceptions below.\n\n`location_lead_times` is a list of `{location_id, lead_time}`; each entry replaces that warehouse's setting whole, null means no override, and warehouses not listed are untouched. `lanes` states a whole warehouse instead, lead time, box, freight quote and road limit in one object, each omitted field meaning none on file. A warehouse in both lists is refused 400 naming it.\n\n`container_type_id` is the exception to \"null is omitted\": null clears it to \"use the organisation default\", not to \"no container\", so send the key to clear it and omit it to leave it.\n\n`contacts` is ignored. `primary_contact` is not: `{\"id\": <contact id>}` makes an existing contact this supplier's main one, clearing the flag from whichever contact held it and linking that contact to the supplier if it was not linked. It creates and edits nothing, create_contact and update_contact are the only doors that write a person.\n\n`is_archived` true suffixes the name \" (Archived)\" and re-points every variant this supplier is default for onto another of that variant's suppliers; false reverses the suffix. `clear_variant_supplier_lead_time` true drops the per-variant lead time across this supplier's variants. `domains` re-syncs supplier email in the background, after the answer.\n\nAn unknown supplier is refused 404 and an unknown container type 400.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "update_supplier",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "container_type_id": 2,
                "lanes": [
                  {
                    "container_type_id": null,
                    "freight_per_container_cents": 412000,
                    "lead_time": 21,
                    "location_id": "loc_0004",
                    "road_limit_kg": 24000
                  }
                ],
                "min_order_value": 6000,
                "payment_terms_days": 60
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateSupplierRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "address1": "Rua do Bolhao 114",
                    "address2": null,
                    "average_lead_time": {
                      "info": "Average lead time",
                      "key": "average_lead_time",
                      "unit": "days",
                      "value": 19.0
                    },
                    "city": "Porto",
                    "contacts": [],
                    "container_type_id": 2,
                    "country": "PT",
                    "currency": "EUR",
                    "domains": [
                      "portoknits.example"
                    ],
                    "id": "sup_0031",
                    "in_full": 96.0,
                    "integrations": {},
                    "is_archived": false,
                    "lanes": [
                      {
                        "basis": "supplier",
                        "container_name": "40ft high cube",
                        "container_type_id": null,
                        "freight_per_container_cents": 412000,
                        "internal_cbm": 76.3,
                        "lead_time": 21,
                        "lead_time_source": "supplier_location",
                        "location_id": "loc_0004",
                        "location_name": "Leeds DC",
                        "pallet_positions": 25,
                        "payload_kg": 26700,
                        "resolved_container_type_id": 2,
                        "road_limit_kg": 24000,
                        "usable_pct": 85.0
                      }
                    ],
                    "lead_time": 21,
                    "lead_time_source": "supplier",
                    "location_lead_times": [
                      {
                        "lead_time": 21,
                        "location_id": "loc_0004",
                        "location_name": "Leeds DC",
                        "source": "supplier_location"
                      }
                    ],
                    "min_order_value": 6000,
                    "name": "Porto Knits",
                    "on_time": 94.2,
                    "otif_score": 91.4,
                    "payment_terms_days": 60,
                    "postal_code": "4000-112",
                    "primary_contact": null,
                    "province": null
                  },
                  "message": {
                    "desc": "",
                    "service": "UNKNOWN",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetSupplierDetailsResponse"
                }
              }
            },
            "description": "The supplier as it now stands, in the shape `get_supplier_details` serves.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "Warehouse loc_0004 is in both 'lanes' and 'location_lead_times'. A lane carries its own lead time, so send each warehouse once.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A warehouse is in both `lanes` and `location_lead_times`, or a named container type does not exist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Supplier with id sup_9999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Change one supplier's terms, warehouses and lanes",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/inventory/suppliers/{supplier_id}/needs-attention": {
      "get": {
        "description": "What is wrong with one supplier right now, in four sections.\n\n`purchase_orders` is a worklist: one item per order needing a look, with `po_id`, the order's display number, its status, a `message_key` of `cancelled`, `delayed`, `shipped`, `quantity_changed` or `expected_delivery_date_passed`, and `email`, the supplier's message the signal was read out of, or null where the order is simply past its expected delivery date. `total_count` counts the items.\n\nAn `email` is the stored message, and its body belongs to the person whose mailbox it arrived in. A key is nobody's colleague, so it reads each mailbox at the level that mailbox shares: on the default level `body`, `html_body`, `clean_body` and `quoted_body` come back null with `body_withheld` saying so, and a mailbox shared with Tightly only keeps its messages out of the section, leaving `email` null. `says`, `files`, `decision`, `with_party`, `tag` and `waits_on_you` are answered by the Mail reads, not by this one.\n\n`replenishment_readiness` counts this supplier's variants with no lead time and with no unit cost, which are the two figures a reorder cannot be computed without. `smart_replenishment` counts the variants at critical stock. `supplier_configuration` says whether a default lead time and a minimum order value are recorded, and how far the configured lead time is from the measured one.\n\nCounts and a worklist, never rates. get_supplier_overview is the door for the figures a scorecard prints.\n\nScope: `suppliers:read`.",
        "operationId": "get_supplier_needs_attention",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "purchase_orders": {
                      "items": [
                        {
                          "email": null,
                          "message_key": "expected_delivery_date_passed",
                          "po_external_id": "PO-0000376",
                          "po_id": "376",
                          "status": "FullyConfirmed"
                        },
                        {
                          "email": {
                            "attachments": [],
                            "body": null,
                            "body_withheld": "Not shared with you",
                            "clean_body": null,
                            "date": "2026-09-02T08:41:00+00:00",
                            "decision": null,
                            "files": null,
                            "html_body": null,
                            "id": "90311",
                            "is_read": true,
                            "label": "received",
                            "message_id": "AAMkAGI2TG93AAA=",
                            "quoted_body": null,
                            "read_at": "2026-09-02T08:42:11+00:00",
                            "says": null,
                            "sender_email": "alex@textiles.example",
                            "sender_name": "Alex Example",
                            "subject": "PO-0000412: sailing pushed to the 19th",
                            "tag": null,
                            "thread_id": "t_7c1f2ab0",
                            "waits_on_you": false,
                            "with_party": null
                          },
                          "message_key": "delayed",
                          "po_external_id": "PO-0000412",
                          "po_id": "412",
                          "status": "Confirmed"
                        }
                      ],
                      "total_count": 2
                    },
                    "replenishment_readiness": {
                      "missing_lead_time_variants_count": 12,
                      "missing_unit_cost_variants_count": 8
                    },
                    "smart_replenishment": {
                      "critical_variants_count": 3
                    },
                    "supplier_configuration": {
                      "average_lead_time_days": 19,
                      "configured_lead_time_days": 14,
                      "default_lead_time_configured": true,
                      "lead_time_diff_days": 5,
                      "min_order_value_configured": false
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "UNKNOWN",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetSupplierNeedsAttentionResponse"
                }
              }
            },
            "description": "The four sections. Every count is a whole number and never null; `purchase_orders.items` is empty when there is nothing to do.\n\nAn item's `email` is the supplier's message the signal was read out of: its subject, its people, its dates, the files that came with it (`attachments`, each with `id`, `filename` and `mime_type`, and a stored file may carry keys beyond those three, which are ignored rather than refused), and the message itself where this caller may read it. `read_at` is when Tightly read the thread, or null.\n\nA connected mailbox carries one of three sharing levels, and a key is never the person who connected it, so a key reads every mailbox at the level that mailbox shares. At the default level the four body fields (`body`, `html_body`, `clean_body`, `quoted_body`) are null and `body_withheld` carries the sentence saying so; at Full messages they are served as they always were; and a mailbox shared with Tightly only has its messages left out of this section altogether, so the item carries `email: null` with the order, its status and its `message_key` unchanged.\n\n`says`, `files`, `decision`, `with_party`, `tag` and `waits_on_you` are part of the shared message shape and are null on this operation. They are read from a thread, and this section reads messages one at a time by id.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Supplier with id sup_9999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What needs doing about one supplier",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      }
    },
    "/api/v1/inventory/suppliers/{supplier_id}/overview": {
      "get": {
        "description": "Five figures for one supplier, as an array: `average_lead_time` in days, `total_pos_cost`, every purchase order placed with them, in the organisation's currency, and the `otif_score`, `on_time_score` and `in_full_score` rates. Each carries `key`, `unit`, `info` and `value`.\n\nRead the `key`, never the position: the order is stable today and is not part of the contract.\n\nA `value` of null with `info` \"Not enough delivery data\" means the rate exists but too few delivery lines have been scored to publish it, and on-time carries its own reason for the same case. A null is therefore \"not measured yet\", never zero.\n\nThe same three rates are plain fields on get_supplier_details, which is the door when the address, the contacts or the lanes are wanted in the same call.\n\nScope: `suppliers:read`.",
        "operationId": "get_supplier_overview",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "info": "Average lead time",
                      "key": "average_lead_time",
                      "unit": "days",
                      "value": 19.0
                    },
                    {
                      "info": "Total spent money",
                      "key": "total_pos_cost",
                      "unit": "dollar",
                      "value": 486320.0
                    },
                    {
                      "info": "OTIF score",
                      "key": "otif_score",
                      "unit": "percentage",
                      "value": 91.4
                    },
                    {
                      "info": "On time score",
                      "key": "on_time_score",
                      "unit": "percentage",
                      "value": 94.2
                    },
                    {
                      "info": "In full score",
                      "key": "in_full_score",
                      "unit": "percentage",
                      "value": 96.0
                    }
                  ],
                  "message": {
                    "desc": "",
                    "service": "UNKNOWN",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetSupplierOverviewResponse"
                }
              }
            },
            "description": "Five cards, each `{key, unit, info, value}`. A null `value` is a figure not measured yet, and `info` says why.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Supplier with id sup_9999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One supplier's five scorecard figures",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:read"
        ]
      }
    },
    "/api/v1/inventory/suppliers/{supplier_id}/variants": {
      "delete": {
        "description": "Detaches many variants from one supplier and answers 204 with no body.\n\nName them as `variant_ids` in a JSON body, or as a comma-separated `variant_ids` query parameter. Both are read on every request and the two lists are added together, so send one or the other rather than the same ids twice.\n\n`filter_args` and `search` in the body select by query instead, the way the variants table does, with `exceptions` naming the ids to keep. When either is sent, the explicit list is not used.\n\nDefaults are repaired the way remove_variant_from_supplier repairs them: a variant left without a default supplier takes the first of the suppliers it still has. Prepacks are pruned in the same call and a prepack left holding one variant or none is deleted.\n\nAn unknown supplier is refused 404.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "remove_variants_from_supplier",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Comma-separated variant ids, added to any `variant_ids` in the body. Omit it when the body carries the list.\n",
            "in": "query",
            "name": "variant_ids",
            "required": false,
            "schema": {
              "examples": [
                "var_88120,var_88121"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "variant_ids": [
                  "var_88120",
                  "var_88121"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/DeleteVariantsFromSupplierRequest"
              }
            }
          },
          "required": false
        },
        "responses": {
          "204": {
            "description": "The pairs are gone. No body.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "Unsupported operation 'between' for key 'unit_cost'.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A filter key, operation or value is not one the variants table takes.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Supplier with id sup_9999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Detach many variants from one supplier, by id or by filter",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      },
      "post": {
        "description": "Attaches variants to a supplier and answers 201 with the variant-supplier rows that now exist for the ids named: `unit_cost`, `currency`, `min_order_quantity`, `batch_size`, `lead_time`, where the lead time came from, and whether this supplier is that variant's default.\n\nName the variants one of two ways. `variant_ids` is an explicit list. `filter_args` and `search` select them the way the variants table does, with `exceptions` naming the ids to leave out; that arm attaches by query and answers an empty `variant_suppliers` list, so read the variants back afterwards rather than trusting the response to enumerate them.\n\nA variant that already has a default supplier keeps it; one that does not takes this supplier as its default. A pair that already exists is left exactly as it is rather than reset, so re-sending the same ids costs nothing and changes nothing.\n\nAn unknown supplier is refused 404.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "add_variants_to_supplier",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "variant_ids": [
                  "var_88120",
                  "var_88121"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/AddVariantsToSupplierRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "variant_suppliers": [
                      {
                        "batch_size": null,
                        "currency": "EUR",
                        "is_default": true,
                        "lead_time": 21,
                        "lead_time_source": "supplier",
                        "min_order_quantity": 24,
                        "supplier_id": "sup_0031",
                        "unit_cost": 11.4,
                        "variant_id": "var_88120"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "UNKNOWN",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/AddVariantsToSupplierResponse"
                }
              }
            },
            "description": "The variant-supplier rows for the ids named. Empty when the variants were selected by `filter_args` or `search`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "VALIDATION_ERROR",
                  "message": {
                    "desc": "Unsupported operation 'between' for key 'unit_cost'.",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A filter key, operation or value is not one the variants table takes.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Supplier with id sup_9999 not found",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Attach variants to a supplier",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/inventory/suppliers/{supplier_id}/variants/{variant_id}": {
      "delete": {
        "description": "Detaches one variant from one supplier and answers 204 with no body. The variant's other suppliers are untouched, and if the pair removed was that variant's default, the first supplier it still has becomes the default rather than the variant being left with none.\n\nThe variant is taken out of this supplier's prepacks in the same call, and a prepack left holding one variant or none is deleted with it.\n\nA pair that is not on file is refused 404, so a second delete of the same pair answers 404 rather than 204: treat that as \"already detached\" if you retry. Use remove_variants_from_supplier to detach several at once, or to detach by filter.\n\nAn unknown supplier is refused 404 as well.\n\nScope: `suppliers:write`, which includes `suppliers:read`.",
        "operationId": "remove_variant_from_supplier",
        "parameters": [
          {
            "description": "The supplier's id, as `list_suppliers` serves it.",
            "in": "path",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The variant to detach from this supplier.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "The pair is detached. No body.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Suppliers.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Suppliers; `ip_not_allowed` when the caller's address is outside the key's allowlist. There is no `organization_mismatch` on this resource: no supplier or contact route names an organisation in its path, so there is nothing for a key to disagree with.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "NOT_FOUND",
                  "message": {
                    "desc": "Variant with id var_0007 not found for supplier sup_0031",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The platform's error code, the exception's own name upper-cased.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No supplier of this organisation has that id, or this supplier does not carry that variant. The second is what a repeated delete answers.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Detach one variant from one supplier",
        "tags": [
          "suppliers"
        ],
        "x-tightly-scopes": [
          "suppliers:write"
        ]
      }
    },
    "/api/v1/inventory/table": {
      "get": {
        "description": "A page of stock: on hand, incoming, committed and available quantities, days of cover and a health word, with `filtered_max_size` for the filtered set's size.\n\n`type=variants` (the default) answers one row per variant and location, carrying stock_value, available_to_sell and next_arrival; `type=products` aggregates to the product. `distinct=variant_id` or `distinct=product_id` collapses the location rows, `view=combined` answers one row per variant or product across locations, and `export=true` answers a download URL with `fields` naming its columns. Narrow with `filter_args` on location_id, supplier_id, health, managed, vendor and collection_id. Health is `critical`, `caution` or `healthy`.\n\n`cover_exceeds_horizon` says whether days of cover is a measurement or a floor: true means the stock outlasted every day of forecast there was, so cover is how far the walk looked and reads as \"364+ days\" rather than a day it runs out on. It is a boolean on variant rows and a `{min, max}` pair of booleans on product rows and on `view=combined`, and it is false rather than null where the row carries no cover figure at all.\n\n`location_lead_times` is an array of `{location_id, location_name, lead_time, source}` on variant rows, where `source` names the rung that won (`variant_supplier_location`, `supplier_location`, `variant_supplier`, `supplier` or `organization`), and a `{min, max}` range with no source on product rows. It is `[]` or `{min: null, max: null}` where nothing qualifies.\n\nScope: `inventory:read`.",
        "operationId": "get_inventory_table",
        "parameters": [
          {
            "description": "The number of rows to skip before starting to return items.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "The maximum number of rows to return.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "View type - 'variants' shows individual variants, 'products' shows grouped by product.",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "default": "variants",
              "enum": [
                "variants",
                "products"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of `{key, operation, value}`. The allowlist of keys and operations, and what each one means, is on the schema below.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "description": "JSON array of filter conditions. The endpoint rejects any key not in this allowlist.\n\nAllowed keys (key · supported operations · notes):\n- variant_id · eq, in, ne, nin\n- product_id · eq, in\n- shopify_status · eq, in\n- supplier · eq, in, ne · supplier name (string)\n- supplier_id · eq, in, ne · any linked supplier\n- default_supplier_id · eq, in, ne · variant's default supplier\n- product_type · eq · values: 'continuity' | 'new'\n- category · eq, in\n- in_stock · gte, lte · integer\n- stock_value · gte, lte · numeric\n- available_to_sell · gte, lte · integer\n- next_arrival · gte, lte, gt, lt, eq · ISO date string\n- managed_status · eq · values: 'managed' | 'unmanaged'\n- tags · eq, in · values: 'high_growth' | 'declining' | 'not_selling' | 'consistent_seller' | 'stocked_out' | 'overstocked' | 'understocked' | 'new_arrival' | 'discontinued' | 'supplier_issues'. Use this key (not 'health') to filter by overstocked / understocked / stocked_out status.\n- out_of_stock · eq · boolean\n- weeks_of_cover · gte, lte · numeric\n- shopify_tags · eq, in\n- class · eq, in · performance category\n- unit_cost · gte, lte · numeric\n- sell_price · gte, lte · numeric\n- date_published · gt, lt, gte, lte, eq · ISO date string\n- lead_time · gte, lte · integer (days). On the per-location rows (type=variants with no distinct, and, since this change, type=products with no distinct) this filters the same 5-level per-warehouse resolution the row displays (variant_supplier_locations -> supplier_locations -> variant_suppliers -> suppliers -> org default). Every other shape (variants&distinct=variant_id, variants&view=combined, products&distinct=product_id, products&view=combined) filters the flat variant_suppliers -> suppliers -> org default chain, matching that shape's own flat lead_time field.\n- location · eq, in, ne · location name\n- location_id · eq, in, ne\n- health · eq, in · values: 'critical' | 'caution' | 'healthy' (case-insensitive)\n- recommended_quantity · gte, lte · integer\n- replenishment_set_id · eq, in, ne\n- collection_id · eq, in, ne, nin\n- missing_fields · eq, in · values: 'product_title' | 'price' | 'lead_time' | 'unit_cost' | 'min_order_quantity' | 'supplier' | 'location' | 'shopify_tags'\n- purchase_order_id · ne only\n- event_start_date · gte, eq · ISO date string\n- event_end_date · lte, eq · ISO date string\n- event_sales_channel_ids · eq, in\n- successor_variant_id · eq · value must be null\n- predecessor_variant_id · eq · value must be null\n- production_type · eq, in · values: 'buy_only' | 'raw_material' | 'manufacturable'\n- prepack_id · eq, ne\n\nDynamic prefixes: keys starting with 'metafields.' or 'custom_fields.' are also accepted.\n\nNot filterable here (response/sort only, do not put in filter_args): min_order_quantity, batch_size, sku, barcode, variant_title, product_title, sales_velocity, cover, on_order, in_basket_quantity.\n\nExample: '[{\"key\":\"stock_value\",\"operation\":\"gte\",\"value\":100},{\"key\":\"available_to_sell\",\"operation\":\"lte\",\"value\":5},{\"key\":\"next_arrival\",\"operation\":\"gte\",\"value\":\"2025-01-01\"}]'\nTo filter by supplier, pass filter_args: [{\"key\": \"supplier_id\", \"operation\": \"eq\", \"value\": \"<id>\"}]. Passing supplier_id as a direct query parameter is not supported and will be silently ignored.\n",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated columns to sort by; prefix a column with `-` for descending.",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "description": "Comma-separated sort columns. Prefix with + for asc, - for desc.\nSupported columns include stock_value, available_to_sell, next_arrival, total_stock_value,\nvariant_title, sku, barcode, unit_cost, lead_time, location_lead_times,\nreplenishment_frequency, supplier_name,\nlocation_name, sales_velocity, sales_velocity_7_days, sales_velocity_30_days,\nsales_velocity_90_days, cover, weeks_of_cover, in_basket_quantity, last_stockout_date,\nreserved_quantity, production_type, is_managed, replenishment_set, and others.\nExample: '-stock_value,+replenishment_set' or '+variant_title'\n\n'location_lead_times' sorts by the fastest warehouse the row reports: the smallest\n'lead_time' among the array entries on variant-grain rows, the 'min' of the range on\nproduct-grain rows. Both directions order by that same minimum ('-location_lead_times'\ndoes NOT switch to the range's 'max'), and rows reporting nothing ([] / {\"min\": null})\nsort last either way. It is NOT interchangeable with 'lead_time': on the variant-grain\n(distinct=variant_id, view=combined) and distinct=product_id shapes the 'lead_time'\ncolumn is the flat variant_suppliers -> suppliers -> org default chain, so the two sorts\nlegitimately differ. Under view=supplier_catalog it follows the filtered supplier, like\nthe range itself. It is a sort key only. 'location_lead_times' is not filterable\n(filter on 'lead_time' instead).\n",
              "type": "string"
            }
          },
          {
            "description": "Search term for filtering results.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "default": "",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated distinct columns (e.g. variant_id,location_id).",
            "in": "query",
            "name": "distinct",
            "required": false,
            "schema": {
              "enum": [
                "variant_id",
                "product_id",
                "location_id"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of fields to return in each row.",
            "in": "query",
            "name": "fields",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "When set to 'combined', returns a product-level view without requiring type=products.\nWhen set to 'supplier_catalog', 'location_lead_times' resolves for the supplier of the\nrequest's supplier_id eq filter instead of each variant's default supplier; the request\nMUST carry a supplier_id eq filter in filter_args\n(e.g. [{\"key\":\"supplier_id\",\"operation\":\"eq\",\"value\":\"<supplier_id>\"}]) or it is rejected\nwith 400. All other columns keep their default-view values.\n",
            "in": "query",
            "name": "view",
            "required": false,
            "schema": {
              "enum": [
                "combined",
                "supplier_catalog"
              ],
              "type": "string"
            }
          },
          {
            "description": "Bill of materials ID to filter by.",
            "in": "query",
            "name": "bom_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "When true, returns an export URL instead of table data.",
            "in": "query",
            "name": "export",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 4120,
                    "filtered_max_unique_size": 1284,
                    "offset": 0,
                    "rows": [
                      {
                        "available_qty": 261,
                        "committed_qty": 41,
                        "days_of_cover": 18,
                        "health": "caution",
                        "incoming_qty": 800,
                        "location_id": "loc_0004",
                        "location_name": "London DC",
                        "on_hand_qty": 302,
                        "product_id": "4410092",
                        "product_title": "Terry Crew",
                        "sku": "TB-CREW-BLK-M",
                        "variant_id": "44100920011",
                        "variant_title": "Black / M"
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/GetInventoryTableVariantViewResponse"
                    },
                    {
                      "$ref": "#/components/schemas/GetInventoryTableProductViewResponse"
                    },
                    {
                      "$ref": "#/components/schemas/GetInventoryTableExportResponse"
                    }
                  ]
                }
              }
            },
            "description": "The inventory table.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stock.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every variant with its stock, health and cover",
        "tags": [
          "inventory"
        ],
        "x-tightly-scopes": [
          "inventory:read"
        ]
      }
    },
    "/api/v1/metrics": {
      "get": {
        "description": "Every metric and dimension the platform defines, each with one sentence saying what it is, how it rolls up, the grain it is held at, the record it is read from, and the day it entered the dictionary.\n\nOne dictionary, four readers: the pages, the trade reports, the warehouse feed and Ask Tightly all resolve a figure through it, so a definition cannot mean one thing on a report head and another in a model. A tenant may rename a metric and may define its own; no tenant may redefine a platform one, which is why this read is the same for every organisation.\n\nA key that cannot be served yet is LISTED with the reason rather than left out. A reader who cannot find a key concludes Tightly does not hold the idea; a reader who finds it with \"landed cost needs a cost layer per receipt, which nothing has written yet\" knows what they are waiting for.\n\nFeed a `key` from here into `run_metric_query` to get the figure itself.\n\nScope: `metrics:read`.",
        "operationId": "get_metric_dictionary",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "domains": [
                      {
                        "data_source": "Sale orders",
                        "domain": "sales",
                        "grain": "One sale order line: a variant sold on one order.",
                        "grains": [
                          "daily",
                          "monthly",
                          "weekly"
                        ],
                        "label": "Sales",
                        "supports_time_range": true
                      }
                    ],
                    "metrics": [
                      {
                        "data_source": [
                          "Sale orders",
                          "Sale orders against the merchandise financial plan"
                        ],
                        "definition": "Sales after discounts and returns, before tax.",
                        "domains": [
                          {
                            "data_source": "Sale orders",
                            "domain": "sales",
                            "grain": "One sale order line: a variant sold on one order.",
                            "label": "Sales",
                            "served": true,
                            "unsupported_reason": null
                          },
                          {
                            "data_source": "Sale orders against the merchandise financial plan",
                            "domain": "sales_plan",
                            "grain": "One month, category and sales channel, carrying the actual and the plan together.",
                            "label": "Sales vs plan",
                            "served": true,
                            "unsupported_reason": null
                          }
                        ],
                        "format": "currency",
                        "formula": "Added up across whatever the answer is broken down by.",
                        "key": "net_sales",
                        "kind": "measure",
                        "label": "Net sales",
                        "polarity": "higher_is_better",
                        "served": true,
                        "since": "Before this dictionary",
                        "synonyms": [
                          "revenue",
                          "sales",
                          "turnover",
                          "net revenue"
                        ]
                      },
                      {
                        "data_source": [
                          "Stock levels, with the last 52 weeks of trading and the commitment that covers the style"
                        ],
                        "definition": "Stock on hand valued at what it cost to land, freight and duty included.",
                        "domains": [
                          {
                            "data_source": "Stock levels, with the last 52 weeks of trading and the commitment that covers the style",
                            "domain": "stock_finance",
                            "grain": "One variant, with its locations added together.",
                            "label": "Stock value and age",
                            "served": false,
                            "unsupported_reason": "`stock_value_landed` cannot be answered yet. Every cost in Tightly today is a STANDARD supplier cost with no freight, duty or handling in it, by declaration: landed cost is computed per purchase-order line and never folded into the variant's cost, because doing so would move every buy envelope. Valuing stock at landed cost needs a cost layer per receipt, which the operations record specifies and nothing has written yet (REC-11). Ask for `stock_value`, which is the same units at standard cost and says so on its basis line."
                          }
                        ],
                        "format": "currency",
                        "formula": "Added up across whatever the answer is broken down by.",
                        "key": "stock_value_landed",
                        "kind": "measure",
                        "label": "Stock value (landed)",
                        "polarity": "neutral",
                        "served": false,
                        "since": "2026-09-05",
                        "synonyms": [
                          "landed stock value",
                          "stock at landed cost",
                          "inventory at landed cost"
                        ]
                      }
                    ]
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "domains": {
                          "items": {
                            "properties": {
                              "data_source": {
                                "type": "string"
                              },
                              "domain": {
                                "type": "string"
                              },
                              "grain": {
                                "type": "string"
                              },
                              "grains": {
                                "items": {
                                  "type": "string"
                                },
                                "type": "array"
                              },
                              "label": {
                                "type": "string"
                              },
                              "supports_time_range": {
                                "type": "boolean"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "metrics": {
                          "items": {
                            "properties": {
                              "data_source": {
                                "items": {
                                  "type": "string"
                                },
                                "type": "array"
                              },
                              "definition": {
                                "type": "string"
                              },
                              "domains": {
                                "items": {
                                  "properties": {
                                    "data_source": {
                                      "type": "string"
                                    },
                                    "domain": {
                                      "type": "string"
                                    },
                                    "grain": {
                                      "type": "string"
                                    },
                                    "label": {
                                      "type": "string"
                                    },
                                    "served": {
                                      "type": "boolean"
                                    },
                                    "unsupported_reason": {
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    }
                                  },
                                  "type": "object"
                                },
                                "type": "array"
                              },
                              "format": {
                                "type": "string"
                              },
                              "formula": {
                                "description": "How the figure combines when rows are added together. Measures only.",
                                "type": "string"
                              },
                              "key": {
                                "type": "string"
                              },
                              "kind": {
                                "enum": [
                                  "measure",
                                  "dimension"
                                ],
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "polarity": {
                                "description": "Which way is good. Measures only.",
                                "type": "string"
                              },
                              "served": {
                                "type": "boolean"
                              },
                              "since": {
                                "type": "string"
                              },
                              "synonyms": {
                                "items": {
                                  "type": "string"
                                },
                                "type": "array"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The dictionary",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Metrics.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Metrics; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The metric dictionary",
        "tags": [
          "metrics"
        ],
        "x-tightly-scopes": [
          "metrics:read"
        ]
      }
    },
    "/api/v1/metrics/query": {
      "post": {
        "description": "Hand this the query the dictionary describes and it answers with rows, the columns, the formats each figure prints in, the window it actually ran over and the query itself, echoed back.\n\nA POST THAT WRITES NOTHING, and it costs `metrics:read` for that reason. The body is a question, not a change: the query is a nested object and a URL is the wrong place for one. Every statement it compiles is a SELECT on the read replica, and every value in the body reaches it as a bound parameter. There is no field for a table, a column or a fragment, and an unknown key is refused by name rather than ignored.\n\nTHE FIGURES ARE THE PAGE'S FIGURES. The query is resolved against the same catalog `get_metric_dictionary` publishes and compiled by the same compiler the Sales page reads through, so a warehouse job pulling net sales for a week gets the week the page shows, to the cent.\n\n`time_range` may be absolute or relative. A relative window is echoed back as it was asked and `resolved_time_range` carries the two dates it ran over, because the rows carry buckets and a bucket is an opening date, not a bound. `computed_as_of` appears where a domain serves derived figures and there is a real stamp to report; a projection with none says nothing rather than inventing \"now\".\n\nSend `{\"query\": {...}}`, or the query object at the top level. Both are read, because the query travels in a `query` field on every answer here and a caller feeding one back should not have to know which spelling to unwrap it into.\n\nScope: `metrics:read`.",
        "operationId": "run_metric_query",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "query": {
                  "dimensions": [
                    "sales_channel"
                  ],
                  "domain": "sales",
                  "grain": "week",
                  "measures": [
                    "net_sales"
                  ],
                  "time_range": {
                    "amount": 12,
                    "kind": "relative",
                    "unit": "week"
                  }
                }
              },
              "schema": {
                "properties": {
                  "query": {
                    "description": "The query. `domain` and `measures` are required; `dimensions`, `grain`, `time_range`, `filters`, `sort`, `limit` and `comparison` are optional. Every key must be one the dictionary names for that domain.\n",
                    "properties": {
                      "dimensions": {
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "domain": {
                        "type": "string"
                      },
                      "filters": {
                        "description": "`{key, operation, value}` triples, on keys the dictionary allows for the domain.",
                        "items": {
                          "type": "object"
                        },
                        "type": "array"
                      },
                      "grain": {
                        "description": "day, week, month or quarter, where the domain supports a time range.",
                        "type": "string"
                      },
                      "limit": {
                        "type": "integer"
                      },
                      "measures": {
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      "time_range": {
                        "description": "Absolute, `{kind: absolute, start, end}` as two ISO dates, or relative, `{kind: relative, unit, amount}`.\n",
                        "type": "object"
                      }
                    },
                    "type": "object"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "columns": [
                      "week",
                      "sales_channel",
                      "net_sales"
                    ],
                    "explanation": "Net sales by sales channel, by week, over the last twelve weeks.",
                    "formats": {
                      "net_sales": "currency"
                    },
                    "query": {
                      "dimensions": [
                        "sales_channel"
                      ],
                      "domain": "sales",
                      "grain": "week",
                      "measures": [
                        "net_sales"
                      ],
                      "time_range": {
                        "amount": 12,
                        "kind": "relative",
                        "unit": "week"
                      }
                    },
                    "resolved_time_range": {
                      "end": "2026-09-05",
                      "start": "2026-06-14"
                    },
                    "row_count": 2,
                    "rows": [
                      {
                        "net_sales": 842100.0,
                        "sales_channel": "Online",
                        "week": "2026-08-30"
                      },
                      {
                        "net_sales": 342100.0,
                        "sales_channel": "Wholesale",
                        "week": "2026-08-30"
                      }
                    ]
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "columns": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "computed_as_of": {
                          "description": "When derived figures in the answer were computed. Absent where there is no stamp.",
                          "type": "string"
                        },
                        "explanation": {
                          "type": "string"
                        },
                        "formats": {
                          "description": "The format each column prints in, straight from the catalog.",
                          "type": "object"
                        },
                        "query": {
                          "description": "The question as asked, round-trippable into this operation.",
                          "type": "object"
                        },
                        "resolved_time_range": {
                          "description": "The two absolute dates the statement ran over, on every query that has a window.",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "row_count": {
                          "type": "integer"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The rows, and the question that produced them",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "allowed": [
                    "net_sales",
                    "gross_profit",
                    "units"
                  ],
                  "detail": "gross_margin_landed cannot be measured in the sales domain.",
                  "error": true,
                  "field": "measures",
                  "key": "gross_margin_landed"
                }
              }
            },
            "description": "The query is the caller's to fix. `field` names the control, `key` the term that was wrong and `allowed` what may go there.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Metrics.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Metrics; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "content": {
              "application/json": {
                "example": {
                  "detail": "The query is valid and could not be run. This is a fault on Tightly's side rather than a problem with the request; retrying is safe.",
                  "error": true
                }
              }
            },
            "description": "The query was valid and the database would not answer it. Ours to fix, not the caller's.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Compile one query from the dictionary and answer with its rows",
        "tags": [
          "metrics"
        ],
        "x-tightly-scopes": [
          "metrics:read"
        ]
      }
    },
    "/api/v1/mfp": {
      "get": {
        "description": "Every buy plan this organisation holds, newest first, with the fiscal year each covers and whether it has been published. One id from here addresses every other Planning operation.\n\nA plan is the company's own sales and buying plan for a fiscal year, by category and month. It is read-only over a key: a plan is typed by a planner who can be asked why, so there is no `planning:write` and none is coming.\n\nScope: `planning:read`.",
        "operationId": "list_mfp",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "baseline_source": "last_year_actuals",
                      "cell_count": 96,
                      "created_at": "2026-08-03T09:14:22+00:00",
                      "fiscal_year": 2027,
                      "fiscal_year_end_month": 1,
                      "fiscal_year_start_month": 2,
                      "mfp_id": "mfp_7f21c9",
                      "name": "FY27 buy plan",
                      "status": "live",
                      "total_planned_revenue": 48200000.0
                    },
                    {
                      "baseline_source": "last_year_actuals",
                      "cell_count": 96,
                      "created_at": "2025-08-11T10:02:47+00:00",
                      "fiscal_year": 2026,
                      "fiscal_year_end_month": 1,
                      "fiscal_year_start_month": 2,
                      "mfp_id": "mfp_41b0da",
                      "name": "FY26 buy plan",
                      "status": "live",
                      "total_planned_revenue": 44900000.0
                    }
                  ],
                  "message": {
                    "desc": "",
                    "service": "mfp",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ListMFPResponse"
                }
              }
            },
            "description": "List of MFP summaries",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Planning.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when the planning rail is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "List the buy plans",
        "tags": [
          "mfp"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/mfp/{mfp_id}/plan-side": {
      "get": {
        "description": "The plan side of the Open to buy ledger, the weekly sales and stock intake sheet, for one plan's fiscal year, at category by calendar month, and the same figure set at three levels: each cell, each category's year, and the whole year.\n\nEVERY FIGURE CARRIES ITS OWN BASIS. `sales_plan` and `sales_plan_last_year` are at net sales; `cogs_plan`, the three intake lines, `budget`, `chase_reserve` and the four plan-of-record lines are at unit cost. A retail figure and a cost figure are both dollars and are not the same dollars, so nothing here is divided into anything else without reading the two bases first.\n\nAN ABSENCE IS NOT A ZERO. A figure that cannot be expressed carries `cents: null` with a `reason` saying why, and it is still present at every level: `budget` has no category dimension, and `chase_reserve` has no month because planned depth carries no delivery date. `plan_receipts_cost` is the only derived line and carries its own reason, because the identity behind it needs all four of its terms or none.\n\nA plan that was never published still answers: the sales plan reads absent with its reason and every other line is served.\n\nScope: `planning:read`.",
        "operationId": "get_plan_side",
        "parameters": [
          {
            "description": "The plan's id, from `list_mfp`.",
            "in": "path",
            "name": "mfp_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Read commitment plan-of-record enrichment. Set false to return the saved MFP plan without waiting for commitment money grids. The four commitment cost lines are null with a not-requested reason at every grain; all other figures retain their normal calculation.\n",
            "in": "query",
            "name": "include_plan_of_record",
            "required": false,
            "schema": {
              "default": true,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "categories": [
                      {
                        "category": "Knitwear",
                        "cells": [
                          {
                            "figures": {
                              "budget": {
                                "basis": "unit_cost",
                                "cents": null,
                                "reason": "The buying budget is declared for the whole book, not per category.",
                                "usd": null
                              },
                              "chase_reserve": {
                                "basis": "unit_cost",
                                "cents": null,
                                "reason": "Planned depth carries no delivery date, so it belongs to no month.",
                                "usd": null
                              },
                              "cogs_plan": {
                                "basis": "unit_cost",
                                "cents": 69920000,
                                "reason": null,
                                "usd": 699200.0
                              },
                              "intake_committed": {
                                "basis": "unit_cost",
                                "cents": 51400000,
                                "reason": null,
                                "usd": 514000.0
                              },
                              "plan_receipts_cost": {
                                "basis": "unit_cost",
                                "cents": null,
                                "reason": "February has no planned closing stock, so the receipt identity did not fire.",
                                "usd": null
                              },
                              "sales_plan": {
                                "basis": "net_sales",
                                "cents": 184000000,
                                "reason": null,
                                "usd": 1840000.0
                              },
                              "sales_plan_last_year": {
                                "basis": "net_sales",
                                "cents": 171200000,
                                "reason": null,
                                "usd": 1712000.0
                              }
                            },
                            "month": "2027-02"
                          }
                        ],
                        "year": {
                          "sales_plan": {
                            "basis": "net_sales",
                            "cents": 3950000000,
                            "reason": null,
                            "usd": 39500000.0
                          }
                        }
                      }
                    ],
                    "fiscal_year": 2027,
                    "months": [
                      "2027-02",
                      "2027-03"
                    ],
                    "year": {
                      "budget": {
                        "basis": "unit_cost",
                        "cents": 1831780000,
                        "reason": null,
                        "usd": 18317800.0
                      },
                      "sales_plan": {
                        "basis": "net_sales",
                        "cents": 4821000000,
                        "reason": null,
                        "usd": 48210000.0
                      }
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "mfp",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetPlanSideResponse"
                }
              }
            },
            "description": "The plan side, by category and calendar month",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Planning.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "Plan not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The plan side of the Open to buy ledger for one plan's fiscal year",
        "tags": [
          "mfp"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/mfp/{mfp_id}/table": {
      "get": {
        "description": "One buy plan's grid: the planned figure for every category and month of the fiscal year, with actuals filled in for months that have closed. `metric=revenue` reads the sales plan and `metric=margin` the margin plan.\n\n`view` decides which plan is read: `working` is the planner's tip, `live` is the published version in force, and a version id reads that frozen version. A variance you want to reproduce next quarter is read against a version id, not against `live`, because `live` moves when the plan is published again.\n\nA month still open carries a null actual rather than a partial one, and a category nobody has planned carries a null figure rather than a zero. Narrow with `filter_args` on category and sales_channel_id.\n\nScope: `planning:read`.",
        "operationId": "get_mfp_table",
        "parameters": [
          {
            "description": "The plan to read. It is the `mfp_id` a plan carries everywhere on this rail, and the same one get_otb_rollup takes. The two read one plan and cannot disagree about it.\n",
            "in": "path",
            "name": "mfp_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "URL-encoded JSON array of filters with keys category and sales_channel_id (operation eq/in), e.g. [{\"key\":\"category\",\"operation\":\"in\",\"value\":[\"Knitwear\"]}]. Omit for all categories (as rows) and all channels (aggregated).\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Value to render in each cell. Defaults to revenue.",
            "in": "query",
            "name": "metric",
            "required": false,
            "schema": {
              "enum": [
                "revenue",
                "margin"
              ],
              "type": "string"
            }
          },
          {
            "description": "Which version to read: \"working\" (default), \"live\", or a version_id. The rows and totals reflect that view; actuals are unchanged. The response echoes the active view.\n",
            "in": "query",
            "name": "view",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filters": {
                      "categories": [
                        "Knitwear"
                      ],
                      "sales_channel_ids": null
                    },
                    "hierarchy": {
                      "level": "department",
                      "reason": null,
                      "words": {
                        "category": "Class",
                        "department": "Division",
                        "subcategory": "Subclass"
                      }
                    },
                    "metric": "revenue",
                    "mfp": {
                      "baseline_source": "last_year_actuals",
                      "created_at": "2026-08-03T09:14:00+00:00",
                      "fiscal_year": 2027,
                      "fiscal_year_end_month": 1,
                      "fiscal_year_start_month": 2,
                      "mfp_id": "mfp_01J9X4",
                      "name": "FY27 Plan",
                      "status": "live"
                    },
                    "months": [
                      {
                        "is_completed": true,
                        "label": "Feb 27",
                        "month": 2,
                        "year": 2027
                      },
                      {
                        "is_completed": false,
                        "label": "Mar 27",
                        "month": 3,
                        "year": 2027
                      }
                    ],
                    "rows": [
                      {
                        "actual": {
                          "2027-02": 401220.0,
                          "2027-03": null
                        },
                        "actual_total": 401220.0,
                        "category": "Knitwear",
                        "department": "Womenswear",
                        "planned": {
                          "2027-02": 412000.0,
                          "2027-03": 388500.0
                        },
                        "planned_total": 800500.0
                      }
                    ],
                    "totals": {
                      "actual": {
                        "2027-02": 401220.0,
                        "2027-03": null
                      },
                      "actual_total": 401220.0,
                      "planned": {
                        "2027-02": 412000.0,
                        "2027-03": 388500.0
                      },
                      "planned_total": 800500.0
                    },
                    "view": {
                      "name": "Working",
                      "ref": "working",
                      "version_id": null
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "mfp",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetMFPTableResponse"
                }
              }
            },
            "description": "The plan's grid: one row per category, one column per month of the fiscal year, planned figures throughout and actuals only for months that have closed. `months[].is_completed` says which those are, and an in-progress month's actual is null rather than a part-month figure, \"the month is not over\" and \"we are behind\" are different readings and only one of them is actionable. `view` echoes the version actually read.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_2c7d0a41b98e4f36ad51e0c8f4b93d27",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Planning. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_9b40e2f1c58a4d739e2610af7c3d5b84",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when the planning rail is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The buy plan's grid, by category and month",
        "tags": [
          "mfp"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/mfp/{mfp_id}/versions": {
      "get": {
        "description": "Every published version of one buy plan, newest first, each with its totals and whether it is the version the plan currently reads as live, plus the working tip and whether it holds changes nobody has published.\n\nThis is what answers \"what did the plan say in January\". A version is frozen: its totals do not move when the live plan does, which is what makes a variance reproducible six months later. Read one version's grid with `get_mfp_table` and `view=<version_id>`.\n\nScope: `planning:read`.",
        "operationId": "get_mfp_versions",
        "parameters": [
          {
            "description": "The plan's id, from `list_mfp`.",
            "in": "path",
            "name": "mfp_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "versions": [
                      {
                        "cogs_plan_usd": 18317800.0,
                        "is_live": true,
                        "label": "Published 1 Sep 2026",
                        "published_at": "2026-09-01T16:40:05Z",
                        "published_by": "Dana Whitfield",
                        "sales_plan_usd": 48210000.0,
                        "version_id": 14
                      },
                      {
                        "cogs_plan_usd": 17922400.0,
                        "is_live": false,
                        "label": "Published 4 Aug 2026",
                        "published_at": "2026-08-04T08:55:12Z",
                        "published_by": "Dana Whitfield",
                        "sales_plan_usd": 46980000.0,
                        "version_id": 11
                      }
                    ],
                    "working": {
                      "cogs_plan_usd": 18317800.0,
                      "has_unpublished_changes": true,
                      "label": "Working",
                      "sales_plan_usd": 48210000.0
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "mfp",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetMFPVersionsResponse"
                }
              }
            },
            "description": "Version history",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Planning.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when the planning rail is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "Plan not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every published version of a buy plan, and the working tip",
        "tags": [
          "mfp"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/order-book/account-anchor": {
      "get": {
        "description": "What one account did last time and how each style is likely to perform there: what they booked last season, what their doors sold, and the baseline's grade.\n\n`trading_partner_id` and `season_code` are required. `prior_season_code` states the season compared against, `sellout_from` and `sellout_to` the window, `commitment_id` narrows to a Commitment, `as_of` reads at an instant; `prior_reason` says why where a prior season cannot be resolved.\n\n`baseline: null` means the pass never assessed this style; ABSENT means none was served, and `grades_state` says which: `served`, `not_on_plan` (with `grades_reason`) or `unreadable`. Sell-out is the account's own doors, not sell-in; a style they never carried is graded from look-alikes, which `sellout_reported: false` explains.\n\n`ordered_qty` is this season's booked units for the same account and style, off the same statement, so the gap against the prediction is one subtraction on one row. A style with no line is 0.\n\n`baseline.by_door[]` is that level per door, summing to `level_units`. Each row carries `door_id`, `name`, `units`, `share`, a `rung` (`measured`, `category_average`, `account_average`, `not_measured`, whose figure is null), a `rung_label` and a `source`: `door_baseline` for the engine's own per-door figure, with that door's evidence beside it, or `account_split` for the account level divided at read time. Absent, with `by_door_reason`, where unreadable.\n\nSold with Essentials+: without Tightly Connect, 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_account_anchor",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in apparel.",
            "in": "query",
            "name": "trading_partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The season being planned, e.g. SS27.",
            "in": "query",
            "name": "season_code",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The season to anchor against. Unstated, it is resolved from the declared season chain; when stated it wins.\n",
            "in": "query",
            "name": "prior_season_code",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "A Commitment whose scope selects the styles and whose window places the graded curve. A grade laid into one Commitment's window and read under another's dates is a curve pointing at the wrong months, so each grade carries the Commitment it was laid into.\n",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Start of the sell-out window (YYYY-MM-DD). Unstated with sellout_to, it is detected.",
            "in": "query",
            "name": "sellout_from",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End of the sell-out window (YYYY-MM-DD).",
            "in": "query",
            "name": "sellout_to",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Read the book as it stood at this instant, rather than now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "engine": {
                      "basis": null,
                      "computed_at": "2026-09-04T02:15:41+00:00",
                      "confidence": null,
                      "inputs_through": "2026-09-04T01:58:00+00:00",
                      "name": "baseline",
                      "run_id": null,
                      "stale_after": "2026-09-05T04:15:41+00:00",
                      "state": "fresh"
                    },
                    "engine_state": "fresh",
                    "grades_as_of": "2026-09-04T02:15:41+00:00",
                    "grades_reason": null,
                    "grades_state": "served",
                    "next_run_at": "2026-09-04T07:00:00+00:00",
                    "prior_season_code": "SS26",
                    "prior_season_source": "declared_chain",
                    "season_code": "SS27",
                    "sellout_reason": null,
                    "sellout_reported": true,
                    "sellout_window": {
                      "from": "2026-02-02",
                      "source": "detected",
                      "to": "2026-07-26"
                    },
                    "styles": [
                      {
                        "baseline": {
                          "basis": "account_predecessor",
                          "by_door": [
                            {
                              "availability": 0.8462,
                              "door_id": "4021",
                              "level_high": 585,
                              "level_low": 390,
                              "name": "Oxford Street",
                              "out_of_stock_days": 28.0,
                              "rung": "measured",
                              "rung_label": "Measured",
                              "share": 0.4152,
                              "sold_corrected": 465,
                              "sold_last_season": 412,
                              "source": "door_baseline",
                              "styles_reported": 1,
                              "units": 490,
                              "weeks_reported": 24
                            },
                            {
                              "availability": null,
                              "door_id": "4044",
                              "level_high": 120,
                              "level_low": 80,
                              "name": "Trafford",
                              "out_of_stock_days": null,
                              "rung": "category_average",
                              "rung_label": "Category average",
                              "share": 0.0847,
                              "sold_corrected": null,
                              "sold_last_season": null,
                              "source": "door_baseline",
                              "styles_reported": 0,
                              "units": 100,
                              "weeks_reported": null
                            }
                          ],
                          "doors_reporting": 14,
                          "evidence": "measured",
                          "expected_sell_through": 0.88,
                          "grade": "winner",
                          "level_units": 1180,
                          "pace": null,
                          "range": {
                            "high": 1410,
                            "low": 940,
                            "members": 6,
                            "reason": null
                          },
                          "sentence": "Winner at Selfridges, the predecessor sold 88% across 14 doors over 24 weeks; likely to sell 1,180 over the window, 940 to 1,410 at the edges; measured, not modelled.",
                          "state": "graded",
                          "weeks_reported": 24,
                          "window": {
                            "from": "2026-02-02",
                            "source": "detected",
                            "to": "2026-07-26"
                          }
                        },
                        "ordered_qty": 1518,
                        "prior_booked_qty": 1600,
                        "prior_sellout_qty": 1412,
                        "product_id": "4410092",
                        "style_ref": "TB-CREW-SS26"
                      },
                      {
                        "baseline": {
                          "basis": "brand_analog",
                          "doors_reporting": null,
                          "evidence": "brand",
                          "expected_sell_through": null,
                          "grade": "sleeper",
                          "level_units": 240,
                          "pace": null,
                          "range": {
                            "high": null,
                            "low": null,
                            "members": 1,
                            "reason": "No range: one past style to compare."
                          },
                          "sentence": "Sleeper at Selfridges, likely to sell 240 over the window, from one past style; graded from our own look-alikes, not from this account's doors.",
                          "state": "brand_fallback",
                          "weeks_reported": null,
                          "window": {
                            "from": "2026-02-02",
                            "source": "detected",
                            "to": "2026-07-26"
                          }
                        },
                        "ordered_qty": 0,
                        "prior_booked_qty": 0,
                        "prior_sellout_qty": 0,
                        "product_id": "4410518",
                        "style_ref": "TB-PARKA-SS26"
                      }
                    ],
                    "trading_partner_id": "tp_00417"
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "The anchor. Each `styles[]` entry carries the prior book, the prior sell-out and, where the grade is readable, `baseline`: its state, basis, evidence, grade, pace, expected sell-through, level and rate, a range with the reason it is wide or absent, the curve and its bucket size, the members it voted on and the ones it dropped, the doors and weeks reported, the window and where the window came from, one composed sentence, and `by_door`. The header carries `sellout_reported` and `sellout_reason`.\n`ordered_qty` on each style is this season's live booked units for the same account and style, off the same statement as the prior figures, so ordered against predicted is a subtraction over one row rather than a crossing of two reads taken at two instants. A style in scope with no live line carries 0 and not null: the book WAS read, and having taken none of a style is the reading a planner came for.\n`baseline.by_door[]` is the account's level delivered to each door the account has open, one row each, carrying `door_id`, `name` (the buyer's own key for the same field, character for character, so a face never has the field rename itself under it), `door_reference`, `units`, `share`, `rung`, `rung_label` and `source`. `rung` is `measured` (this door sold the styles the grade stood on), `category_average` (it reported none of them but does sell the category), `account_average` (neither, so it takes the account's average door) or `not_measured` (nothing could be sized, so the row carries a null figure and never a zero). The rows sum to `level_units` by construction, so the account's own figure and the doors under it cannot disagree.\n\n`source` SAYS WHICH READ SERVED THE ROW, and the two are not the same claim. `door_baseline` is the engine's own per-door pass: the availability correction taken on that door's own shelf, with the door's evidence beside the figure (`sold_last_season`, `sold_corrected`, `out_of_stock_days`, `availability`, `weeks_reported`, `styles_reported`). `account_split` is the INTERIM: the account's level divided at read time by each door's measured share of the styles the grade stood on, which cannot correct for availability and never carries `category_average`. The engine's rows are served wherever it has any; the interim answers only for a graded style the pass has not reached yet, so a face that branches on `source` can say which it is looking at rather than guess. An account that reports one total has no doors on file, and `by_door` is then empty rather than one synthetic door holding the whole number. The KEY IS ABSENT, with `by_door_reason` carrying the sentence, where the split could not be read at all: the grades stand either way, because the split is an addition on top of figures that are already complete. `by_door_reason` is null where the split was read.\n`basis` and `evidence` are the provenance pair, and a client switching on the grade wants both. `basis` is the rung the grade was read from, in ladder order: `account_predecessor`, `account_kit_member`, `account_cohort`, `account_family`, `brand_analog`, `sell_in_only`, `none`. The first rung carrying enough reported weeks at this account votes alone, and the outranked members are dropped and named rather than blended in at a lower weight. `evidence` is how far to trust it: `measured`, `uncorrected`, `borrowed`, `brand`, `none`. It is `evidence`, not `basis`, that carries the borrowed against measured label, because one clean predecessor of the account's own doors is a measurement and several blended is a borrowing, and the figure looks identical either way. `uncorrected` is that same own record read over weeks nobody reported a shelf for, so a slow week may be an empty shelf rather than absent demand. `brand_analog` with `evidence: brand` is the analog case named above: nothing at this account, so the brand's own answer is served at brand level and labelled as borrowed from us. `grade` is `winner`, `sleeper` or `bleeder`, and `state` is `graded`, `magnitude_only`, `brand_fallback`, `sell_in_only` or `refused`.\nThe header also says which morning the grades are from: `grades_as_of` is the completion stamp of the last grading run that succeeded, `engine_state` is that engine's state (never_ran, not_measured, running, fresh, stale, partial, failed, not_applicable), `next_run_at` is when it runs again, and `engine` carries the same four facts in the shape every engine read on the platform uses. They are stamped on every path, including the one where the grades themselves could not be read, never from the reader's clock, which is what this page used before.\n`grades_state` is a different question from `engine_state` and sits beside it: that one is about the RUN, this one is about the answer in your hands. It is `served`, `not_on_plan` or `unreadable`, and `grades_reason` carries a sentence only for `not_on_plan`, the same text a 403 `plan_excludes` carries. Three responses can hold `engine_state: fresh` and no grades at all, for three unrelated reasons, and this is the field that separates them.\nA style with no grade carries `baseline: null` where the pass simply never assessed it, and a `not_assessed` block (`state`, `reason_code`, `sentence`) where the last run named why it did not grade this ACCOUNT at all (a read that failed, reading conventions nobody has confirmed). \"Assessed by nobody\" and \"we could not read their record this morning\" send a seller to two different places.\n`baseline.by_door[]` is the same level, delivered to each of the account's shops: `door_id`, `name`, `share`, `units`, and `rung`, the word for the evidence the figure stands on. `measured` is the door's own history of the styles the grade was measured on, corrected for its own shelf; `category_average` is a door that reported none of those styles but does sell the category; `account_average` is a door that reported neither, which where nothing was measured at all is the level divided by the doors on file; `not_measured` is a door on a style the engine could not size, and it carries no figure. The units sum to `level_units` exactly, so the door column and the account figure above it cannot disagree. Each row also carries the door's own evidence: `sold_last_season` as the account reported it, `sold_corrected` with the shelf accounted for, `out_of_stock_days`, `availability`, `weeks_reported` and `styles_reported`, because a figure lifted by a third for a shop that was dark for eight weeks has to be able to say so. `availability: null` means no week at that door carried a shelf reading, so the units are taken at face value rather than corrected.\n`by_door: []` is a real answer and the commonest one on an account that reports a single total: it has no door to break down. The key ABSENT means the split could not be read, the same distinction `baseline` itself draws one level up.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or the request could not be read",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `trading_partner_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What one account did last time, and how it is likely to do now",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/account-size-curve": {
      "get": {
        "description": "One account's booked lines, folded into its own size shares in basis points summing to 10000. This is the run this retailer actually takes, beside the buy's own curve.\n\n`trading_partner_id` is required. The scope is `season_code` or `commitment_id`, one of which is required: served scopeless, a size run would read as \"this account runs no sizes\", which is a finding rather than an unstated question. Given only a Commitment, the season resolves from its declared season label.\n\nA size is read off `product_variants.selected_options` under a recognised size key. A line at style grain (a hold, \"sizes to follow\") or whose variant names no size option contributes no share; its units are counted in `unsized_units`/`unsized_lines` rather than dropped or folded in as if it had spoken.\n\n`shares_bp` is null with a `reason` where the account has no booked line in scope at all, or where every one of its lines is unsized. An absence is a sentence, never a zero.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_account_size_curve",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in this trade, and a name here would show one retailer's run under another's.\n",
            "in": "query",
            "name": "trading_partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The season. One of `season_code` or `commitment_id` is required.",
            "in": "query",
            "name": "season_code",
            "required": false,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "A Commitment whose declared season keys the book. Required where no season code is given.\n",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book as it stood at this instant (ISO-8601). Default is now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-08T06:00:00+00:00",
                    "commitment_id": null,
                    "computed_at": "2026-09-08T06:00:04+00:00",
                    "lines": 5,
                    "reason": null,
                    "scope_reason": null,
                    "season_code": "SS27",
                    "season_source": "declared",
                    "shares_bp": {
                      "L": 2500,
                      "M": 3334,
                      "S": 2500,
                      "XL": 833,
                      "XS": 833
                    },
                    "trading_partner_id": "tp_00417",
                    "units": 1200,
                    "unsized_lines": 1,
                    "unsized_units": 500
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "commitment_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "computed_at": {
                          "description": "When this fold was computed. The size run is measured fresh on every read.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "lines": {
                          "description": "The sized live line count the shares stand on.",
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "season_code": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "season_source": {
                          "description": "Whether the season was stated or resolved from the Commitment.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "shares_bp": {
                          "description": "Size -> basis points, summing to exactly 10000. Null with `reason` where the account has no sized line in scope.",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "trading_partner_id": {
                          "type": "string"
                        },
                        "units": {
                          "description": "The sized units the shares stand on.",
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "unsized_lines": {
                          "type": "integer"
                        },
                        "unsized_units": {
                          "description": "Units on live lines that named no resolvable size (holds and unsized variants).",
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The account's size shares in basis points, the units and line count they stand on, the unsized share, and when the size run itself could not be composed.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or the request could not be read",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `trading_partner_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One account's booked lines folded into its own size shares (BE-ADJ-19)",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/account-styles": {
      "get": {
        "description": "One account's book, style by style: prebook, at-once and undated quantities, the booked quantity and value, last price, line count, ship window, cancel date, status, Commitments and a `split` block naming the prebook anchor.\n\n`trading_partner_id` is required. Supply `season_code` for a seasonal book, or omit it with an explicit continuity `commitment_id` to read only lines stamped to that commitment. `as_of` and `season_start` narrow. Read it before writing a line.\n\nGrouped by product; unmatched style references remain visible. Use get_order_book_style_lines for one style across all accounts.\n\n`booked_value_usd` is summed over the same lines get_order_book_accounts sums, each at its own price, so the styles under one account add to its figure to the cent. A held unit has no price: it counts in `booked_qty` and adds nothing here. `held_qty_from_account`, `held_by_name` and `held_at` say how many of those units the account held by answering a showing, and who, and when it landed. Cancelled lines count toward nothing; `has_cancelled` is reported and `booked_qty` is null, not 0.\n\n`predicted_qty` is what we expect this account's doors to sell of the style, beside what it has booked, so the difference is one subtraction on one row. Null where unsized, absent where the sizing could not be read, never 0.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_account_styles",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in this trade and a name here would show one retailer's book under another's.\n",
            "in": "query",
            "name": "trading_partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The season, e.g. SS26. Normalised on read, so `ss26` and `SS26` are one season. An un-normalised code used to return an empty book, which reads as \"this account has taken nothing\".\n",
            "in": "query",
            "name": "season_code",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Narrow the book to one Commitment. A narrowing that could only be PARTLY resolved says so in `scope_reason` rather than serving a shorter list silently.\n",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Read the book as it stood at this instant, rather than now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "DECLARE the day the season starts shipping, which is what each row's prebook / at-once split is measured against. Unstated, it is derived from the book's earliest ship window and the response labels it as derived.\n",
            "in": "query",
            "name": "season_start",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-04T06:00:00+00:00",
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "scope_reason": null,
                    "season_code": "SS27",
                    "split": {
                      "anchor": {
                        "season_start": "2027-02-01",
                        "source": "declared"
                      },
                      "at_once": {
                        "booked_qty": 0
                      },
                      "lines_dated": 71,
                      "lines_undated": 0,
                      "prebook": {
                        "booked_qty": 5900
                      }
                    },
                    "styles": [
                      {
                        "at_once_qty": 0,
                        "booked_qty": 1400,
                        "booked_value_usd": 41300.0,
                        "commitment_ids": [
                          "cmt_0091"
                        ],
                        "first_ship_window_start": "2027-02-15",
                        "has_cancelled": false,
                        "held_at": null,
                        "held_by_name": null,
                        "held_qty_from_account": 0,
                        "last_booked_at": "2026-08-19T14:02:00+00:00",
                        "last_price": 29.5,
                        "lines": 3,
                        "next_cancel_date": "2027-01-15",
                        "prebook_qty": 1400,
                        "predicted_qty": 1780,
                        "product_id": "4410092",
                        "product_title": "Signature Wrap Coat",
                        "soft_lines": 0,
                        "soft_qty": 0,
                        "sources": [
                          "api",
                          "csv"
                        ],
                        "status": "confirmed",
                        "statuses": [
                          "confirmed"
                        ],
                        "style_ref": "TB-CREW-SS27",
                        "undated_qty": 0
                      }
                    ],
                    "trading_partner_id": "tp_00417"
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "currency": {
                          "description": "What the figures below are in. `functional` is the book's own currency and the denomination every amount here carries. `_usd` in a key name is the book's functional currency for historical reasons, never a claim of dollars. `transaction` is what the lines were agreed in where they agree on one, and null where they do not; `reporting` is what a reader of this organisation asked to see, with `rate` the dated rate between the last two, served, never applied, and dated to this read's `as_of` so a re-read of a past instant cannot move. `basis` is one of same, converted or unconverted, and `reason` carries the absence word where the block cannot say something.",
                          "type": "object"
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "season_code": {
                          "description": "The season actually read; empty for an explicit continuity commitment.",
                          "type": "string"
                        },
                        "split": {
                          "description": "The anchor the rows were split against, its declared/derived label, and the lines that could not be placed.",
                          "type": "object"
                        },
                        "styles": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "trading_partner_id": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The account's book. Each `styles[]` row carries `product_id`, `style_ref` and `product_title`, `booked_qty` and `booked_value_usd`, `last_booked_at` and `last_price`, `lines`, `has_cancelled`, `sources`, the soft share (`soft_qty`, `soft_lines`, `status`, `statuses`), the row's dates (`first_ship_window_start`, `next_cancel_date`) and its `commitment_ids`, plus this style's prebook / at-once quantities and, where the portal facts were readable, `held_qty_from_account`, `held_by_name` and `held_at`, and, where the sizing was readable, `predicted_qty`. The header carries `season_code` as actually read, `as_of`, the `split` block with its anchor and undated disclosure, and `scope_reason`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or the request could not be read",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `trading_partner_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One account's book, style by style",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/accounts": {
      "get": {
        "description": "One row per account: this season's booked quantity and value, and the same account's prior-season book read at the same number of days before the season started shipping, with each account's identity, reference and terms. `held_qty` is the hold units already inside `booked_qty`, never a figure beside it: a hold has no price, so it moves the booked quantity and not the booked value.\n\n`season_code` is required. `prior_season_code`, `season_start` and `prior_season_start` state the anchors, `commitment_id` narrows to one Commitment, `as_of` reads the book at an instant. `t_minus_days` and `anchors` exist so a half-taken book is never compared with a finished one.\n\n`trading_partner_id` is the identity and `name` only what to show, since account names repeat; `account_reference` is the account's own reference, with `account_reference_reason` where there is none.\n\n`prior` is null where the prior season has no book at all, while a zero inside it means the book exists and this account had nothing booked then. `anchors.prior_reason` says why there is no prior.\n\n`predicted_qty` is what we expect this account's doors to sell, beside `booked_qty`, so which accounts have under ordered against their prediction is this read and one subtraction. Null where nothing is sized, absent where the sizing could not be read, never 0. `doors_reporting` of `doors_on_file` is how much of the estate the prediction stands on.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_accounts",
        "parameters": [
          {
            "description": "The season being read, e.g. SS27.",
            "in": "query",
            "name": "season_code",
            "required": true,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow the book to one Commitment's scope.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book as it stood at this instant (ISO-8601). Default is now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "The season to compare against. Unstated, it is resolved from the declared season chain.",
            "in": "query",
            "name": "prior_season_code",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Declare the date this season starts shipping. Omitted, it is derived and labelled `derived`.",
            "in": "query",
            "name": "season_start",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Declare the prior season's start, so the T-minus-N cut is the same distance on both.",
            "in": "query",
            "name": "prior_season_start",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "accounts": [
                      {
                        "account_reference": "SLF-4471",
                        "account_reference_reason": null,
                        "account_tier": 1,
                        "booked_qty": 5900,
                        "booked_value_usd": 188400.0,
                        "doors_on_file": 6,
                        "doors_reporting": 4,
                        "fill_rate_target_pct": 97.0,
                        "held_qty": 800,
                        "name": "Selfridges",
                        "predicted_qty": 7200,
                        "prior": {
                          "booked_qty": 6400,
                          "booked_value_usd": 194100.0
                        },
                        "sales_channel_id": "61240442",
                        "trading_partner_id": "tp_00417"
                      }
                    ],
                    "anchors": {
                      "current": {
                        "season_start": "2027-02-01",
                        "source": "declared"
                      },
                      "prior": {
                        "read_as_of": "2025-09-05T00:00:00+00:00",
                        "season_code": "SS26",
                        "season_start": "2026-02-02",
                        "source": "derived"
                      },
                      "prior_reason": null
                    },
                    "as_of": "2026-09-04T06:00:00+00:00",
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "scope": {
                      "commitment_id": null,
                      "season_code": "SS27"
                    },
                    "t_minus_days": 150
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "accounts": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "anchors": {
                          "description": "The current and prior season starts, each labelled declared or derived, plus prior_reason.",
                          "type": "object"
                        },
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "currency": {
                          "description": "What the figures below are in. `functional` is the book's own currency and the denomination every amount here carries. `_usd` in a key name is the book's functional currency for historical reasons, never a claim of dollars. `transaction` is what the lines were agreed in where they agree on one, and null where they do not; `reporting` is what a reader of this organisation asked to see, with `rate` the dated rate between the last two, served, never applied, and dated to this read's `as_of` so a re-read of a past instant cannot move. `basis` is one of same, converted or unconverted, and `reason` carries the absence word where the block cannot say something.",
                          "type": "object"
                        },
                        "scope": {
                          "type": "object"
                        },
                        "t_minus_days": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`t_minus_days`, the two season anchors with their sources, and one row per account holding a line in either season, each with `booked_qty`, `booked_value_usd`, `prior`, `doors_reporting`, `doors_on_file`, `predicted_qty` and, where the held share was readable, `held_qty`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or a date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account calls this read, whose figure covers every account on the book; the sentence names `GET /order-book/account-styles` and `GET /order-book/account-anchor`, which are that account's own.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every account's book against its own prior season, read at the same days before start",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/curve": {
      "get": {
        "description": "How a season's book was taken over time: one point per week carrying the book as it stood at the end of that week, with booked quantity, booked value and line count.\n\n`season_code` is required; `commitment_id` and `as_of` narrow as elsewhere, and `grain` takes `week`. Weekly only: a daily curve on a book taken in showroom appointments is noise around the appointments rather than a signal about the season.\n\nIt answers whether a season is ahead of or behind the pace the last one was taken at, which a single total cannot. Use get_order_book_accounts when the question is which accounts are behind rather than whether the book as a whole is.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_curve",
        "parameters": [
          {
            "description": "The season being read, e.g. SS27.",
            "in": "query",
            "name": "season_code",
            "required": true,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow the book to one Commitment's scope.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book as it stood at this instant (ISO-8601). Default is now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "The bucket the curve is cut into. `week` is the only grain served.",
            "in": "query",
            "name": "grain",
            "required": false,
            "schema": {
              "default": "week",
              "enum": [
                "week"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "grain": "week",
                    "points": [
                      {
                        "as_of": "2026-07-06",
                        "booked_qty": 9100,
                        "booked_value_usd": 241800.0,
                        "lines": 118
                      },
                      {
                        "as_of": "2026-07-13",
                        "booked_qty": 18640,
                        "booked_value_usd": 501200.0,
                        "lines": 233
                      }
                    ],
                    "scope": {
                      "commitment_id": null,
                      "season_code": "SS27"
                    },
                    "scope_reason": null
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "currency": {
                          "description": "What the figures below are in. `functional` is the book's own currency and the denomination every amount here carries. `_usd` in a key name is the book's functional currency for historical reasons, never a claim of dollars. `transaction` is what the lines were agreed in where they agree on one, and null where they do not; `reporting` is what a reader of this organisation asked to see, with `rate` the dated rate between the last two, served, never applied, and dated to this read's `as_of` so a re-read of a past instant cannot move. `basis` is one of same, converted or unconverted, and `reason` carries the absence word where the block cannot say something.",
                          "type": "object"
                        },
                        "grain": {
                          "type": "string"
                        },
                        "points": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "scope": {
                          "type": "object"
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One point per week, each with the book as it stood at the end of that week, `as_of`, `booked_qty`, `booked_value_usd` and `lines`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or a date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account calls this read, whose figure covers every account on the book; the sentence names `GET /order-book/account-styles` and `GET /order-book/account-anchor`, which are that account's own.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "How the book was taken, booked quantity and value by the week it was booked in",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/firmness": {
      "get": {
        "description": "How much of a season's committed value is firm, how much can still walk, and how much nobody has said: the three-way split for the scope, the same split per account ordered by walkable value, and the date each piece stops being walkable.\n\nOn wholesale, committed is a retailer's option: a booked order inside its cancellation window can still walk, so open to buy computed as envelope minus committed overstates what is safe. `season_code` is required; `commitment_id` and `as_of` narrow as elsewhere.\n\nThree buckets, never two. `unknown` is value on accounts with no recorded cancellation term, or on an in-window line with no cancel date, and it is folded into neither firm (which would keep the overstatement) nor walkable (which would invent an exposure). Soft lines, drafts and holds, are in none of the three and not in committed either: they are served in their own `soft` block with value summed over priced lines only.\n\n`firmness` is null when the scope has no book at all. A season whose every line is cancelled is a true zero and is served as one.\n\nThis says what CAN walk. get_order_book_signals says what IS walking, the claims retailers have actually made, and get_order_book_summary carries the same totals beside the envelope.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_firmness",
        "parameters": [
          {
            "description": "The season being read, e.g. SS27.",
            "in": "query",
            "name": "season_code",
            "required": true,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow the book to one Commitment's scope.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book at this instant AND measure every cancel date against that day. There is deliberately no second \"as at\" parameter: two dates in one reading is how a response comes to describe a book from March against a calendar from August.\n",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "accounts": [
                      {
                        "firm": {
                          "booked_qty": 3100,
                          "lines": 44,
                          "value_usd": 96200.0
                        },
                        "name": "Selfridges",
                        "trading_partner_id": "tp_00417",
                        "unknown": {
                          "booked_qty": 0,
                          "lines": 0,
                          "value_usd": 0.0
                        },
                        "walkable": {
                          "booked_qty": 5900,
                          "lines": 71,
                          "value_usd": 188400.0
                        }
                      }
                    ],
                    "as_of": "2026-09-04T06:00:00+00:00",
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "firmness": {
                      "committed_lines": 612,
                      "committed_qty": 40210,
                      "committed_value_usd": 1284300.0,
                      "firm": {
                        "booked_qty": 23600,
                        "lines": 341,
                        "value_usd": 742800.0
                      },
                      "soft": {
                        "booked_qty": 8000,
                        "lines": 12,
                        "value_reason": "none of the 12 soft line(s) carries a price yet, so their value is unstated, not $0",
                        "value_usd": null
                      },
                      "unknown": {
                        "booked_qty": 3710,
                        "lines": 73,
                        "value_usd": 140300.0
                      },
                      "unknown_reasons": [
                        {
                          "accounts": 4,
                          "basis": "no_term",
                          "booked_qty": 3710,
                          "lines": 73,
                          "reason": "this account has no recorded cancellation term",
                          "value_usd": 140300.0
                        }
                      ],
                      "walkable": {
                        "booked_qty": 12900,
                        "lines": 198,
                        "value_usd": 401200.0
                      }
                    },
                    "measured_on": "2026-09-04",
                    "scope": {
                      "commitment_id": null,
                      "season_code": "SS27"
                    },
                    "scope_reason": null,
                    "walkable": {
                      "by_deadline": [
                        {
                          "cancel_date": "2026-11-15",
                          "lines": 96,
                          "value_usd": 211900.0
                        },
                        {
                          "cancel_date": "2026-12-01",
                          "lines": 102,
                          "value_usd": 189300.0
                        }
                      ]
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "accounts": {
                          "description": "One row per account, ordered by walkable value.",
                          "items": {
                            "type": "object"
                          },
                          "type": [
                            "array",
                            "null"
                          ]
                        },
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "currency": {
                          "description": "What the figures below are in. `functional` is the book's own currency and the denomination every amount here carries. `_usd` in a key name is the book's functional currency for historical reasons, never a claim of dollars. `transaction` is what the lines were agreed in where they agree on one, and null where they do not; `reporting` is what a reader of this organisation asked to see, with `rate` the dated rate between the last two, served, never applied, and dated to this read's `as_of` so a re-read of a past instant cannot move. `basis` is one of same, converted or unconverted, and `reason` carries the absence word where the block cannot say something.",
                          "type": "object"
                        },
                        "firmness": {
                          "description": "committed_value_usd, committed_qty, committed_lines, the three buckets, unknown_reasons and the soft block. Null where the scope holds no lines.",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "measured_on": {
                          "format": "date",
                          "type": "string"
                        },
                        "scope": {
                          "type": "object"
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "walkable": {
                          "description": "Walkable value by the date it stops being walkable.",
                          "type": [
                            "object",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The three-way split for the scope, the same split per account, and the walkable value by the date each piece stops being walkable. `measured_on` is the day the cancel dates were compared against, fixed from `as_of` rather than taken from the session's clock.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or a date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account calls this read, whose figure covers every account on the book; the sentence names `GET /order-book/account-styles` and `GET /order-book/account-anchor`, which are that account's own.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "How much of this book can still walk",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/lines": {
      "post": {
        "description": "Record existing wholesale bookings as JSON. One booking or a whole account's season. Limit: 1 MiB; above it use file import.\n\nIT RECORDS, IT DOES NOT PLACE. No account is contacted and no order is transmitted. On wholesale the rep is the actuator, and this is the write that says what they did.\n\nEach line names an account, a style, a quantity and a season. `account` is matched against a trading partner's id, name or buyer reference, case-insensitively. An account this book does not hold is refused 400 `No account named {name}.`, the whole request, before anything is written. Nothing here mints an account: a misspelt name would otherwise split one retailer's book across two spellings. A key issued to one account may record that account's lines only.\n\nRows are versioned, not overwritten: a line whose identity (account x style x season x ship window, or your own `external_line_ref`) matches one already recorded becomes its next version when anything differs, and a no-op when it does not, so re-sending a file is safe. A claim dated before the line's current version is rejected: version order is time order.\n\nA soft commitment (`status: hold`) may omit the price and the ship window; no other status may. Rejected lines come back in `rejected` with a reason and a sentence; the rest still records. US dollars only. 201 says a version was appended; a request that appended none answers 200.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:write`, which includes `order_book:read`.",
        "operationId": "record_order_book_lines",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "lines": [
                  {
                    "account": "Harrods",
                    "cancelled_qty": 24,
                    "external_line_ref": "PO-88431-L4",
                    "quantity": 240,
                    "season": "SS27",
                    "ship_window_start": "2027-02-15",
                    "shipped_at": "2027-02-20",
                    "shipped_qty": 216,
                    "sku": "FTH-HOODY-BLK-M",
                    "status": "booked",
                    "wholesale_price": 68.0
                  }
                ]
              },
              "schema": {
                "properties": {
                  "lines": {
                    "items": {
                      "properties": {
                        "account": {
                          "description": "The account's id, name or buyer reference. Matched case-insensitively.",
                          "type": "string"
                        },
                        "booked_at": {
                          "description": "The day the booking was taken. Backdate to load history.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "cancel_date": {
                          "description": "The last day the window is open, and the day walkability is judged against.",
                          "format": "date",
                          "type": "string"
                        },
                        "cancelled_qty": {
                          "description": "Units withdrawn from this line before they shipped. Omitted means nobody has reported a withdrawal; 0 records that none were withdrawn. A line withdrawn WHOLE is `status: cancelled` instead, and the two are counted apart. Anything that is not a whole number of units is refused `invalid_cancelled_qty`.",
                          "type": "integer"
                        },
                        "commitment_id": {
                          "description": "The Commitment this line belongs to, where the typing surface holds one.",
                          "type": "string"
                        },
                        "currency": {
                          "description": "USD only today.",
                          "type": "string"
                        },
                        "external_line_ref": {
                          "description": "Your own identity for this line, so amendments version it rather than opening a second.",
                          "type": "string"
                        },
                        "product_id": {
                          "description": "The style this line is a booking of, where the caller knows it.",
                          "type": "string"
                        },
                        "quantity": {
                          "description": "Units, a whole number.",
                          "type": "integer"
                        },
                        "season": {
                          "description": "The season this booking belongs to, e.g. SS27.",
                          "type": "string"
                        },
                        "ship_window_start": {
                          "description": "First delivery day. Required unless the status is draft or hold.",
                          "format": "date",
                          "type": "string"
                        },
                        "shipped_at": {
                          "description": "The day those units went out, which is what the promised window is scored against. Omitted means nobody reported a date; a quantity with no date beside it is accepted and reads as shipped undated. A date with NO quantity beside it is refused `shipped_at_without_qty`, because nothing can be reconciled from a date alone, and a date that cannot be read is refused `invalid_shipped_at`.",
                          "format": "date",
                          "type": "string"
                        },
                        "shipped_qty": {
                          "description": "Units that went out against this line. Omitted means NOBODY HAS REPORTED A SHIPMENT, which is not a shipment of nothing: send 0 to record that nothing went out. Anything that is not a whole number of units is refused `invalid_shipped_qty`.",
                          "type": "integer"
                        },
                        "sku": {
                          "description": "The style or variant this line is for.",
                          "type": "string"
                        },
                        "status": {
                          "description": "booked, hold, draft, cancelled. Defaults to booked.",
                          "type": "string"
                        },
                        "wholesale_price": {
                          "description": "Sell-in price per unit. Required unless the status is draft or hold.",
                          "type": "number"
                        }
                      },
                      "required": [
                        "account",
                        "sku",
                        "quantity",
                        "season"
                      ],
                      "type": "object"
                    },
                    "maxItems": 1000,
                    "minItems": 1,
                    "type": "array"
                  }
                },
                "required": [
                  "lines"
                ],
                "type": "object"
              }
            }
          },
          "description": "The bookings to record. `lines` holds between 1 and 1000 of them, and each is validated on its own: one malformed ship window costs that line and not the nineteen beside it.\n",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "recorded": 1,
                    "rejected": [],
                    "status": "success",
                    "unchanged": 0
                  },
                  "message": {
                    "desc": "1 order book line(s) recorded",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "recorded": {
                          "description": "Lines that appended a version.",
                          "type": "integer"
                        },
                        "rejected": {
                          "description": "One entry per refused line, carrying its `failure_reason` and a sentence.",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "status": {
                          "type": "string"
                        },
                        "unchanged": {
                          "description": "Lines identical to the version already on file. Not an error.",
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "At least one version was appended. The same body, with `recorded` at 0, is answered 200 when nothing was appended, every line a no-op against the version already on file, or rejected. `recorded` and `rejected` are what a caller branches on; the status says only whether the book moved.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "bad_request",
                  "message": {
                    "code": "bad_request",
                    "desc": "No account named Harrods.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#bad_request",
                    "request_id": "req_4a91c07f5b3e42d8ae610c9d7b28f345",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "An account this book does not hold, or a body that does not parse. The whole request is refused before anything is written. A misspelt account is never minted here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Order book.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not make this write. `scope_missing` when the key does not hold Order book at write; `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another, in a sentence naming both. That last one is the key's own binding rather than one of the seam's five, and it is its own code so a caller can branch on it.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "413": {
            "description": "Booking JSON exceeds the 1 MiB limit; use the file import for larger books.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Record bookings a person has already taken",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:write"
        ]
      }
    },
    "/api/v1/order-book/signals": {
      "get": {
        "description": "The pending claims retailers have made against their book: one row per claim with the account, the line it names, the claim type, what the retailer wants, the line's current booked value and quantity, the source text it was read from, and when it was raised, with totals by type.\n\n`season_code` is required; `commitment_id` and `as_of` narrow as elsewhere.\n\nThe money on each row is the brand's, not the retailer's: it is the line's current booked value, what is at stake if the claim is accepted, never a figure the retailer sent.\n\nOnly pending claims are counted. An approved signal has already been applied through the book's one writer, so counting it again would double the exposure, and an ignored one was judged not to be a change; both stay on file because the trail is the point. A claim naming no line is served and counted separately under `unsized_claims` rather than pooled into the money.\n\nget_order_book_firmness says what can walk, classified from the term an account agreed to. This says which of it somebody has moved on. Recording or deciding a claim is not public.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_signals",
        "parameters": [
          {
            "description": "The season being read, e.g. SS27.",
            "in": "query",
            "name": "season_code",
            "required": true,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow the book to one Commitment's scope.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book as it stood at this instant (ISO-8601). Default is now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-04T06:00:00+00:00",
                    "scope": {
                      "commitment_id": null,
                      "season_code": "SS27"
                    },
                    "scope_reason": null,
                    "signals": [
                      {
                        "account": "Selfridges",
                        "asked_for": {
                          "cancel_date": null,
                          "price_amount": null,
                          "quantity": null,
                          "ship_window_start": "2027-04-01"
                        },
                        "id": 3341,
                        "line_booked_qty": 1400,
                        "line_key": "tp_00417|SS27|4410092",
                        "line_value_usd": 41800.0,
                        "raised_at": "2026-09-01T09:12:44+00:00",
                        "signal_type": "reschedule",
                        "source_text": "Can we push the March drop to April",
                        "status": "pending",
                        "trading_partner_id": "tp_00417",
                        "unsized_reason": null
                      }
                    ],
                    "totals": {
                      "by_type": {
                        "reschedule": 1
                      },
                      "pending": 1,
                      "sized_lines": 1,
                      "sized_value_usd": 41800.0,
                      "unsized_claims": 0
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "scope": {
                          "type": "object"
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "signals": {
                          "description": "One row per pending claim, with the account, line_key, signal_type, the retailer's own words and what the retailer wants.",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "totals": {
                          "description": "pending, sized_value_usd over DISTINCT lines, sized_lines, unsized_claims and by_type.",
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every pending claim with the account, the line it names, what the retailer wants, the line's current value and, where it has none, the reason it cannot be sized; plus totals that keep the sized money and the unsized claims apart.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or a date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account calls this read, whose figure covers every account on the book; the sentence names `GET /order-book/account-styles` and `GET /order-book/account-anchor`, which are that account's own.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What is actually walking, the pending claims retailers have made against their book",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/style-lines": {
      "get": {
        "description": "One style's live lines across every account: quantity, wholesale price, status, ship window, cancel date and the confirmation verdict per account, with totals and a count of the cancelled lines.\n\n`product_id` is required, and the scope is a `season_code` or a `commitment_id`, one of which is required too: served scopeless, an empty book would read as \"nobody booked this style\", which is a finding rather than a shrug. Given only a Commitment the season is resolved from its declared season label; given both, the season wins and the disagreement is stated in `scope_reason`. A rolling Commitment without a season reads only its explicitly linked bookings across recorded seasons. Its `season_code` stays null, `season_source` is `continuity_commitment`, and `scope_reason` identifies that narrower scope.\n\n`lines` is null with a `reason` where no readable season or continuity scope could be resolved, or where the product id names nothing in the catalogue, because an unreadable style is not an unbooked one. A style with no live lines answers `lines: []` and `totals: null`, which is measured absence. A hold's price is null rather than zero, and `line_verdict` null means not yet confirmed, which is not accepted.\n\nThe transpose of get_order_book_account_styles, which is one account across every style.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_style_lines",
        "parameters": [
          {
            "description": "The style. The asking surface's rows are styles, so this is required.",
            "in": "query",
            "name": "product_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The season. One of `season_code` or `commitment_id` is required.",
            "in": "query",
            "name": "season_code",
            "required": false,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "A Commitment whose declared season keys the book. Required where no season code is given, the work-the-buy drawer holds a Commitment and no season code.\n",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book as it stood at this instant (ISO-8601). Default is now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-04T06:00:00+00:00",
                    "cancelled_lines": 1,
                    "commitment_id": null,
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "lines": [
                      {
                        "booked_qty": 1400,
                        "cancel_date": "2027-01-15",
                        "line_verdict": "accepted",
                        "name": "Selfridges",
                        "ship_window_start": "2027-02-15",
                        "status": "confirmed",
                        "trading_partner_id": "tp_00417",
                        "wholesale_price_amount": 29.5
                      },
                      {
                        "booked_qty": 600,
                        "cancel_date": null,
                        "line_verdict": null,
                        "name": "Le Bon Marché",
                        "ship_window_start": null,
                        "status": "hold",
                        "trading_partner_id": "tp_00902",
                        "wholesale_price_amount": null
                      }
                    ],
                    "product_id": "4410092",
                    "reason": null,
                    "scope_reason": null,
                    "season_code": "SS27",
                    "season_source": "stated",
                    "totals": {
                      "accounts": 2,
                      "booked_qty": 2000,
                      "booked_value_usd": 41300.0,
                      "lines": 2
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "cancelled_lines": {
                          "type": "integer"
                        },
                        "commitment_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "currency": {
                          "description": "What the figures below are in. `functional` is the book's own currency and the denomination every amount here carries. `_usd` in a key name is the book's functional currency for historical reasons, never a claim of dollars. `transaction` is what the lines were agreed in where they agree on one, and null where they do not; `reporting` is what a reader of this organisation asked to see, with `rate` the dated rate between the last two, served, never applied, and dated to this read's `as_of` so a re-read of a past instant cannot move. `basis` is one of same, converted or unconverted, and `reason` carries the absence word where the block cannot say something.",
                          "type": "object"
                        },
                        "lines": {
                          "description": "Null with a `reason` where the style or the season could not be read at all; [] where the book was read and holds nothing.",
                          "items": {
                            "type": "object"
                          },
                          "type": [
                            "array",
                            "null"
                          ]
                        },
                        "product_id": {
                          "type": "string"
                        },
                        "reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "season_code": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "season_source": {
                          "description": "Whether the season was stated or resolved from the Commitment.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "totals": {
                          "type": [
                            "object",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every live line for the style, the totals over them, the count of cancelled lines, and where the season came from.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or a date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account calls this read, whose figure covers every account on the book; the sentence names `GET /order-book/account-styles` and `GET /order-book/account-anchor`, which are that account's own.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One style's live lines across every account, the transpose of the account's book",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/order-book/summary": {
      "get": {
        "description": "The headline of a wholesale book for one season: booked quantity and value, lines, accounts, the split by status and by channel, the buy envelope the book runs against with open to buy, the prebook against at-once split, and the firm, walkable and unknown split of the committed value.\n\n`season_code` is required, `commitment_id` narrows to one Commitment, `as_of` reads the book as it stood at an instant, and `season_start` overrides the season anchor the prebook split is measured against.\n\n`book` is null when the scope has no lines at all, and so is `envelope`: a season nobody has booked into is not a season booked to zero. A hold adds its quantity to `booked_qty` and nothing to `booked_value_usd`, so `soft_qty` and `committed_qty` are served beside them, because an unpriced hold's value is unstated rather than zero.\n\nTotals only. Use get_order_book_firmness for the per-account exposure and the cancellation ladder, get_order_book_accounts for the account-by-account comparison, and get_order_book_curve for how the book was taken over time. All four read the same book, so a figure from one cannot disagree with a figure from another.\n\nThe book stays in the organisation's functional currency: `_usd` in a key name is historical, so on a sterling book `booked_value_usd` carries pounds. Every order book read serves the same `currency` block.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `order_book:read`.",
        "operationId": "get_order_book_summary",
        "parameters": [
          {
            "description": "The season being read, e.g. SS27.",
            "in": "query",
            "name": "season_code",
            "required": true,
            "schema": {
              "examples": [
                "SS27"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow the book to one Commitment's scope.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Reconstruct the book as it stood at this instant (ISO-8601). Default is now.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date-time",
              "type": "string"
            }
          },
          {
            "description": "Declare the date the season starts shipping, what the prebook / at-once split is measured against. Omitted, the anchor is derived from the earliest ship window and labelled `derived`.\n",
            "in": "query",
            "name": "season_start",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-04T06:00:00+00:00",
                    "book": {
                      "accounts": 37,
                      "booked_qty": 48210,
                      "booked_value_usd": 1284300.0,
                      "by_channel": [
                        {
                          "booked_qty": 31200,
                          "booked_value_usd": 903100.0,
                          "sales_channel_id": "61240442"
                        }
                      ],
                      "by_status": {
                        "confirmed": {
                          "qty": 40210,
                          "value_usd": 1284300.0
                        },
                        "hold": {
                          "qty": 8000,
                          "value_usd": null
                        }
                      },
                      "committed_qty": 40210,
                      "lines": 612,
                      "soft_qty": 8000,
                      "soft_value_usd": null
                    },
                    "currency": {
                      "basis": "same",
                      "functional": "USD",
                      "rate": null,
                      "reason": null,
                      "reporting": "USD",
                      "transaction": "USD"
                    },
                    "envelope": {
                      "committed_usd": 1284300.0,
                      "envelope_usd": 1600000.0,
                      "open_to_buy_usd": 315700.0
                    },
                    "firmness": {
                      "committed_value_usd": 1284300.0,
                      "firm": {
                        "booked_qty": 23600,
                        "lines": 341,
                        "value_usd": 742800.0
                      },
                      "unknown": {
                        "booked_qty": 3710,
                        "lines": 73,
                        "value_usd": 140300.0
                      },
                      "walkable": {
                        "booked_qty": 12900,
                        "lines": 198,
                        "value_usd": 401200.0
                      }
                    },
                    "scope": {
                      "commitment_id": null,
                      "season_code": "SS27"
                    },
                    "scope_reason": null,
                    "split": {
                      "anchor": {
                        "season_start": "2027-02-01",
                        "source": "declared"
                      },
                      "at_once": {
                        "booked_qty": 4110,
                        "booked_value_usd": 93900.0
                      },
                      "lines_dated": 612,
                      "lines_undated": 0,
                      "prebook": {
                        "booked_qty": 44100,
                        "booked_value_usd": 1190400.0
                      }
                    },
                    "unattributable": {
                      "lines": 0,
                      "value_usd": 0.0
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "order_book",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "format": "date-time",
                          "type": "string"
                        },
                        "book": {
                          "description": "booked_qty, booked_value_usd, lines, accounts, the soft share, by_status and by_channel. Null where the scope holds no lines.",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "currency": {
                          "description": "What the figures below are in. `functional` is the book's own currency and the denomination every amount here carries. `_usd` in a key name is the book's functional currency for historical reasons, never a claim of dollars. `transaction` is what the lines were agreed in where they agree on one, and null where they do not; `reporting` is what a reader of this organisation asked to see, with `rate` the dated rate between the last two, served, never applied, and dated to this read's `as_of` so a re-read of a past instant cannot move. `basis` is one of same, converted or unconverted, and `reason` carries the absence word where the block cannot say something.",
                          "type": "object"
                        },
                        "envelope": {
                          "description": "The buy envelope this book is measured against, or null where there is no book to compare.",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "firmness": {
                          "description": "Firm, walkable and unknown value, how much of booked_value_usd is a retailer's option.",
                          "type": "object"
                        },
                        "scope": {
                          "description": "The season code and commitment id actually read.",
                          "type": "object"
                        },
                        "scope_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "split": {
                          "description": "Prebook against at-once, with the season anchor and whether it was declared or derived.",
                          "type": "object"
                        },
                        "unattributable": {
                          "description": "Book value the scope could not attribute, and why.",
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The book, its envelope, the prebook / at-once split with the anchor it was measured against, and the firm / walkable / unknown split of the committed value.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A required parameter is missing, or a date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Order book is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Order book; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account calls this read, whose figure covers every account on the book; the sentence names `GET /order-book/account-styles` and `GET /order-book/account-anchor`, which are that account's own.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What is booked for a season, and the one envelope comparison that is like-for-like",
        "tags": [
          "order_book"
        ],
        "x-tightly-scopes": [
          "order_book:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/products/filters": {
      "get": {
        "description": "The values that exist in this organisation's catalogue, so a filter can be built from what is there rather than guessed: `category`, `supplier`, `vendor`, `product_status`, `tags`, `shopify_tags`, `class` (the performance categories), `location`, `missing_fields`, `metafields`, `custom_fields` and `country_of_origin` (ISO 3166-1 alpha-2).\n\nCalled with no `field`, it answers all twelve at once and takes no paging: it is a snapshot of the catalogue, computed once per catalogue version and served from cache until the catalogue changes.\n\nCalled with `field` set to `tags`, `shopify_tags` or `metafields`, it answers one list, paginated, as `{values, limit, offset}` and searchable with `search`. Those three are the only fields that page; any other value answers `{error: \"field '<name>' does not support pagination\"}` with a 200, not a refusal.\n\n`limit` is 1 to 500 and 8 by default, which is small on purpose: this feeds a picker.\n\nScope: `products:read`.",
        "operationId": "get_products_filters",
        "parameters": [
          {
            "description": "The organisation. On a keyed request it must be the key's own organisation: another is refused 403 organization_mismatch rather than quietly substituted.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "examples": [
                "66b2f1d4e9c3a7b5d2f81a04"
              ],
              "type": "string"
            }
          },
          {
            "description": "Return one paginated list instead of every filter. Only tags, shopify_tags and metafields page; any other name answers a 200 carrying an error string.\n",
            "in": "query",
            "name": "field",
            "required": false,
            "schema": {
              "enum": [
                "tags",
                "shopify_tags",
                "metafields"
              ],
              "examples": [
                "tags"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow the paginated list. Ignored when field is absent.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "aw26"
              ],
              "type": "string"
            }
          },
          {
            "description": "Values to skip. Ignored when field is absent.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Values to return. Ignored when field is absent.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "maximum": 500,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "category": [
                      "Outerwear",
                      "Knitwear"
                    ],
                    "class": [
                      "Best sellers",
                      "Slow movers"
                    ],
                    "country_of_origin": [
                      "PT",
                      "IT"
                    ],
                    "custom_fields": [
                      "Fabric composition"
                    ],
                    "location": [
                      {
                        "id": "61240442",
                        "name": "Rotterdam DC"
                      }
                    ],
                    "metafields": [
                      "custom.fabric"
                    ],
                    "missing_fields": [
                      "hs_code"
                    ],
                    "product_status": [
                      "ACTIVE",
                      "ARCHIVED",
                      "DRAFT",
                      "UNLISTED"
                    ],
                    "shopify_tags": [
                      "aw26"
                    ],
                    "supplier": [
                      {
                        "supplier_id": "sup_1180",
                        "supplier_name": "Atelier Norte"
                      }
                    ],
                    "tags": [
                      "aw26",
                      "carryover"
                    ],
                    "vendor": [
                      "Veja"
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "One key per filter, each an array of values. With `field` set, {values, limit, offset} instead.\n",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every filter's values, or one field's page when `field` is set.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `organization_mismatch` when the path names an organisation that is not the key's, which is refused rather than substituted; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every value the product reads can be filtered by, in one answer",
        "tags": [
          "products"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/products/overview": {
      "get": {
        "description": "Three figures for the catalogue, each as a card: `current_inventory_cost` (stock on hand at cost), `incoming_stock_cost` (what is on order, at cost) and `expected_revenue` (the same stock at its selling price).\n\nEach card carries `key`, `value`, `unit`, `previous_value`, `change` as a fraction against that previous figure, `higher_better`, and an `info` sentence. A `value` of null means the figure is unavailable, which is not the same as zero.\n\nTotals only. There are no product rows here and no search by title: for rows, read the products table.\n\n`filter_args` and `search` narrow the figures to exactly the set a table page came from, so the strip and the grid under it answer the same question.\n\nScope: `products:read`.",
        "operationId": "get_products_overview",
        "parameters": [
          {
            "description": "The organisation. On a keyed request it must be the key's own organisation: another is refused 403 organization_mismatch rather than quietly substituted.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "examples": [
                "66b2f1d4e9c3a7b5d2f81a04"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of {key, operation, value} narrowing the figures. variant_id, product_id, product_status, shopify_status, supplier, supplier_id, default_supplier_id, vendor, category and country_of_origin take eq and in; in_stock, stock_value and available_to_sell take gte and lte.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"supplier\",\"operation\":\"eq\",\"value\":\"Atelier Norte\"},{\"key\":\"product_status\",\"operation\":\"eq\",\"value\":\"ACTIVE\"}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Free text, applied to the same set the products table would match.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "overshirt"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "cards": [
                      {
                        "change": 0.0717,
                        "higher_better": true,
                        "info": "Stock on hand valued at unit cost.",
                        "key": "current_inventory_cost",
                        "previous_value": 1198400.0,
                        "unit": "USD",
                        "value": 1284300.0
                      },
                      {
                        "change": -0.2149,
                        "higher_better": true,
                        "info": "Open orders valued at unit cost.",
                        "key": "incoming_stock_cost",
                        "previous_value": 402100.0,
                        "unit": "USD",
                        "value": 315700.0
                      },
                      {
                        "change": 0.0698,
                        "higher_better": true,
                        "info": "Stock on hand valued at its selling price.",
                        "key": "expected_revenue",
                        "previous_value": 3188200.0,
                        "unit": "USD",
                        "value": 3410800.0
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductsOverviewPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The three cards, each with its previous figure and the change against it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "filter_args is not valid JSON, or names a key this read does not accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `organization_mismatch` when the path names an organisation that is not the key's, which is refused rather than substituted; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What the catalogue is worth on hand, on order and at retail",
        "tags": [
          "products"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders": {
      "get": {
        "description": "A page of purchase orders for one organisation: number, order type, status, supplier, destination location, order and expected delivery dates, total cost and line count, with `filtered_max_size` for the size of the filtered set. Header figures only, so call get_purchase_order for one order's line quantities and its deliveries.\n\n`organization_id` in the path must be the key's own organisation; a key naming another one is refused `organization_mismatch`. Page with `limit` and `offset`, narrow with `filter_args` on status, supplier_id, location_id, date_created, date_drafted, delivery_date, total_cost and has_supplier_updates, and order with `sort_args` over expected_delivery_date, name and quantity_to_manufacture.\n\nFilter the expected delivery date under the key `delivery_date`. `expected_delivery_date` is not a filter key: sent as one it is ignored rather than refused, and every order comes back.\n\nStatuses: Drafted, FullyConfirmed, InProduction, FullyShipped, FullyDelivered, Completed, Cancelled.\n\n`on_hold` says whether a row is held and `hold_reason` why, in the holder's own words. A hold with no reason is refused, so `hold_reason` is set whenever `on_hold` is true and null otherwise. A held order refuses every status advance but cancellation. The full log of who acted and why is `status_audit` on get_purchase_order, not here.\n\nScope: `purchase_orders:read`.",
        "operationId": "list_purchase_orders",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Pagination limit (defaults to 10 when neither `limit` nor `offset` is provided)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Pagination offset",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "JSON array of filter conditions. Each filter includes: - `key`: Field to filter (e.g., `id`, `supplier_id`, `location_id`, `status`, `adjustment_needed_for`, `date_created`, `date_drafted`, `delivery_date`, `total_cost`, `has_supplier_updates`) - `operation`: Filter operation (`eq`, `in`, `gt`, `gte`, `lt`, `lte`, etc.) - `value`: Filter value (can be a string, number, or array) - `group`: Optional. Grouping logic for the filter (`and` or `or`, defaults to `and`).\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                [
                  {
                    "group": "and",
                    "key": "status",
                    "operation": "eq",
                    "value": "Drafted"
                  },
                  {
                    "group": "or",
                    "key": "supplier_id",
                    "operation": "eq",
                    "value": "supplier123"
                  }
                ]
              ],
              "items": {
                "type": "object"
              },
              "type": "array"
            }
          },
          {
            "description": "Comma-separated list of sort arguments. Valid sort fields: expected_delivery_date, name, quantity_to_manufacture. Prefix with \"+\" for ascending or \"-\" for descending order. If not provided, defaults to sorting by date_created in descending order.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "+name,-expected_delivery_date"
              ],
              "type": "string"
            }
          },
          {
            "description": "Search term to filter purchase orders",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 214,
                    "offset": 0,
                    "rows": [
                      {
                        "expected_delivery_date": "2026-10-02",
                        "external_id": "PO-9014",
                        "hold_reason": "Vendor has not confirmed the navy; do not issue until they do.",
                        "id": "9014",
                        "location_id": "loc_0004",
                        "name": "PO-9014 Porto Knits",
                        "on_hold": true,
                        "order_date": "2026-09-04",
                        "order_type": "purchase",
                        "status": "issued",
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits",
                        "total_cost": 148200.0,
                        "total_line_items": 12
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetPurchaseOrdersResponse"
                }
              }
            },
            "description": "A page of purchase orders",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list purchase orders",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      },
      "post": {
        "description": "Opens one purchase order and returns it. The body names the destination `location_id` (required) and, optionally, `order_type`, `supplier_id`, `source_location_id`, `recommended_quantity`, `order_date`, `expected_delivery_date` and `commitment_id`.\n\nIt takes no line items. An order opened here is empty, and `total_line_items` on the response is 0; bulk_create_purchase_orders is the door that creates orders together with their lines, one order per supplier and location, and update_purchase_order is the one that adds lines to an order that already exists.\n\n`commitment_id` records which Commitment the order is placed against, and an id this organisation does not have is refused 400. Send it only when it came from a Commitments read.\n\nA transfer (`order_type=TRANSFER`, `source_location_id` required) moves stock that exists: a transfer drawing more of a variant than the source location holds is refused 400 naming the shortfall, and nothing is drafted. A purchase order is never checked against a shelf, because a buy asks a supplier for units nobody holds yet.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "create_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePurchaseOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "expected_delivery_date": "2026-10-02",
                    "external_id": "PO-9014",
                    "id": "9014",
                    "location_id": "loc_0004",
                    "name": "PO-9014 Porto Knits",
                    "order_type": "purchase",
                    "supplier_id": "sup_0031",
                    "total_line_items": 0
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The purchase order as created",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "examples": [
                            "Reno holds 12 available units of Alpine Down Vest · Forest · M; this transfer asks for 60. Nothing was drafted."
                          ],
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors. Also returned when a stamp names a Commitment this organization does not have, and when a TRANSFER order draws more of a variant than its source location holds. Nothing is created in either case.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "open a purchase order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/bulk": {
      "post": {
        "description": "Creates many purchase orders in one call, one per supplier and destination location, each with its own line items, and returns each created order with its id, number, line count and expected delivery date. This is the door an ERP pushes a night's worth of orders through, and the door that creates an order together with its lines.\n\nUse convert_basket_to_purchase_orders when the lines are already in a replenishment basket: that path carries the basket's own supplier split and clears the basket afterwards, and sending the same lines here would create a second set of orders beside it.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "bulk_create_purchase_orders",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkCreatePurchaseOrdersRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "purchase_orders": [
                      {
                        "expected_delivery_date": "2026-10-02",
                        "external_id": "PO-9014",
                        "id": "9014",
                        "location_id": "loc_0004",
                        "name": "PO-9014 Porto Knits",
                        "order_type": "purchase",
                        "source_location_id": null,
                        "supplier_id": "sup_0031",
                        "total_line_items": 12
                      }
                    ],
                    "total_created": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "purchase_orders": {
                          "items": {
                            "$ref": "#/components/schemas/CreatedPurchaseOrderSummary"
                          },
                          "type": "array"
                        },
                        "total_created": {
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every order created, with the id and external id an integrator stores, and how many were made.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A body field is missing or invalid; the refusal names the field.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create many purchase orders in one call, one per supplier and location",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/filters": {
      "get": {
        "description": "The values the purchase orders list can be filtered on: the statuses and order types this organisation's orders carry, and the suppliers and locations they name, each supplier and location as an id with its name. Read it to build `filter_args` for list_purchase_orders out of values that exist rather than values guessed from one page of rows.\n\nScope: `purchase_orders:read`.",
        "operationId": "get_purchase_order_filters",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "location": [
                      {
                        "location_id": "loc_0004",
                        "location_name": "London DC"
                      }
                    ],
                    "order_type": [
                      "purchase",
                      "manufacturing",
                      "transfer"
                    ],
                    "status": [
                      "draft",
                      "approved",
                      "issued",
                      "delivered"
                    ],
                    "supplier": [
                      {
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetPurchaseOrderFiltersResponse"
                }
              }
            },
            "description": "The purchase order filters",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list purchase order filters",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/from-basket": {
      "post": {
        "description": "Turns the replenishment basket into purchase orders, one per supplier, and returns each created order with its supplier, its location and the Commitment it was stamped with at conversion (`commitment_id` null where its lane names none). The basket is cleared by the conversion.\n\nThere is no preview, so review the basket before calling it. To create orders from lines you hold yourself, use bulk_create_purchase_orders.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "convert_basket_to_purchase_orders",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConvertBasketToPurchaseOrdersRequest"
              }
            }
          },
          "description": "Request object for creating purchase orders from basket",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "commitment_id": "cmt_0091",
                      "commitment_reason": null,
                      "external_id": "PO-9017",
                      "id": "9017",
                      "location_id": "loc_0004",
                      "supplier_id": "sup_0031"
                    }
                  ]
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "One entry per created purchase order.",
                      "items": {
                        "properties": {
                          "commitment_id": {
                            "description": "The commitment this order was placed for, RESOLVED from the items on its lines, never named by the caller. Present only when every line of the order belongs to the same single commitment; null otherwise, with the reason below.\n",
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "commitment_reason": {
                            "description": "Why this order records no commitment. Null when it records one.\n",
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "external_id": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          },
                          "location_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "supplier_id": {
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The purchase orders drafted from the basket",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Basket not found"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Basket not found or empty",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "ORDER_CONSTRAINTS_NOT_MET"
                      ],
                      "type": "string"
                    },
                    "data": {
                      "properties": {
                        "violations": {
                          "description": "One entry per unmet constraint, grouped by the PO that would be created.",
                          "items": {
                            "properties": {
                              "actual": {
                                "type": "number"
                              },
                              "bound": {
                                "enum": [
                                  "min",
                                  "multiple"
                                ],
                                "type": "string"
                              },
                              "currency": {
                                "type": "string"
                              },
                              "key": {
                                "description": "Which bracket is unmet. The set GROWS, so it is declared `x-extensible-enum` rather than `enum`: a bracket is one row of a table, and weight, cube, pallet-fill and truck-fill arrive as data the moment a column exists to read them. Switch on the keys you know and fall back to `label` for one you do not. A value listed here will not be removed or renamed without a new date train.\n",
                                "type": "string",
                                "x-extensible-enum": [
                                  "supplier_min_order_value",
                                  "variant_min_order_quantity",
                                  "variant_batch_size",
                                  "variant_unit_cost_missing"
                                ]
                              },
                              "label": {
                                "type": "string"
                              },
                              "limit": {
                                "type": "number"
                              },
                              "line_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "location_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "location_name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "scope": {
                                "enum": [
                                  "order",
                                  "line"
                                ],
                                "type": "string"
                              },
                              "shortfall": {
                                "description": "How much has to be added to clear the constraint.",
                                "type": "number"
                              },
                              "supplier_id": {
                                "type": "string"
                              },
                              "supplier_name": {
                                "type": "string"
                              },
                              "unit": {
                                "enum": [
                                  "amount",
                                  "eaches"
                                ],
                                "type": "string"
                              },
                              "variant_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "variant_title": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "Standard error envelope message. The generic API client renders `desc`.",
                      "properties": {
                        "desc": {
                          "description": "Human-readable summary of how many orders are short.",
                          "examples": [
                            "1 purchase order(s) cannot be placed: 2 line(s) have no unit cost on file."
                          ],
                          "type": "string"
                        },
                        "service": {
                          "examples": [
                            "purchase_order"
                          ],
                          "type": "string"
                        },
                        "severity": {
                          "examples": [
                            "ERROR"
                          ],
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Unprocessable - one or more prospective purchase orders cannot be placed. Either a supplier's ordering constraint is unmet (minimum order value, minimum order quantity, batch size), or a line carries no unit cost, which leaves the supplier's minimum order value undecidable: an unknown cost can only add value, so an order below the minimum on its priced lines alone is not known to be below it. In that case the refusal names the unpriced LINES (`variant_unit_cost_missing`) rather than reporting a minimum-order-value shortfall that could only be produced by valuing the unknown at zero. Nothing is created and the bench is left untouched.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "create purchase orders from basket",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/generation-settings": {
      "patch": {
        "description": "Patches the standing rules the order generator follows: whether it drafts orders at all, how it groups lines into orders, how far ahead it looks, and whether `fill_container` lets it top up the last container of a proposal with whole variants the ranker names. A request that omits a flag leaves that flag as it was, and the response is the settings as they now stand.\n\nSettings, not an act: nothing is generated by this call. It changes what the next generation run does, and orders an earlier run has already drafted are not rewritten.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "update_po_generation_settings",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePOGenerationSettingsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "auto_generate": true,
                    "group_by": "supplier",
                    "horizon_days": 60
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The settings as they now stand.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "update purchase order generation settings",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/manufacturing/bulk": {
      "post": {
        "description": "Creates manufacturing orders, which draw raw materials through the bill of materials rather than buying the finished item, and returns each with its id, number and line count.\n\nTwo bodies, one door. Send `manufacturing_orders` to name each order yourself, or send `filter_args` instead and the set is taken from the replenishment recommendations those filters select, which is how a filtered worklist becomes orders without listing them.\n\nManufacturable items only: a bought item sent here becomes an order nothing can produce, and bulk_create_purchase_orders is its door. Follow with auto_allocate_manufacturing_order_materials to reserve the components.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "bulk_create_manufacturing_orders",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/BulkCreateManufacturingOrdersRequest"
                  },
                  {
                    "$ref": "#/components/schemas/BulkCreateManufacturingOrdersFromReplenishmentRequest"
                  }
                ]
              }
            }
          },
          "description": "Either an explicit list of orders, or the replenishment filters that select them.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "purchase_orders": [
                      {
                        "expected_delivery_date": "2026-10-16",
                        "external_id": "MO-9015",
                        "id": "9015",
                        "location_id": "loc_0004",
                        "name": "MO-9015 Terry Crew",
                        "order_type": "manufacturing",
                        "source_location_id": "loc_0004",
                        "supplier_id": null,
                        "total_line_items": 3
                      }
                    ],
                    "total_created": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "purchase_orders": {
                          "items": {
                            "$ref": "#/components/schemas/CreatedPurchaseOrderSummary"
                          },
                          "type": "array"
                        },
                        "total_created": {
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every manufacturing order created, in the same shape the bulk purchase-order create returns.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A body field is missing or invalid; the refusal names the field.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create manufacturing orders in bulk, from a list or from a replenishment filter",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/with-supplier-updates": {
      "get": {
        "description": "The pending supplier signals for one supplier: up to `limit` rows (default 3, maximum 100), newest source email first and orders with no date last, each naming the purchase order, its display name, the signal type and the date the supplier reported it. `total_count` counts every matching pending signal rather than the page.\n\nEvery row in `items` carries `purchase_order_id`, `display_name` (the order number a person reads, such as `PO-00009014`), `signal_type`, one of `reschedule`, `change_quantity`, `ship`, `cancel`, `confirm` or `change_price`, and `reported_at`, the source email's own timestamp, which is null where that email carried no date. Rows sharing an email date fall back to the order the signals were recorded in, newest first.\n\n`supplier_id` is required. A signal is something a supplier said about an order, usually a new delivery date read out of an email, and it changes nothing on the order until somebody accepts it.\n\nScope: `purchase_orders:read`.",
        "operationId": "list_purchase_orders_with_supplier_updates",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Supplier id to scope purchase orders and pending signals",
            "in": "query",
            "name": "supplier_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Max number of supplier update rows to return (ordered newest first)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 3,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "items": [
                      {
                        "display_name": "PO-00009014",
                        "purchase_order_id": "9014",
                        "reported_at": "2026-09-02T08:11:00+00:00",
                        "signal_type": "reschedule"
                      }
                    ],
                    "total_count": 4
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetPurchaseOrdersSupplierUpdatesResponse"
                }
              }
            },
            "description": "The orders a supplier has updated",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "Bad Request. Missing supplier_id, invalid limit, or limit out of range (1 to 100)",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "description": "Internal Server Error",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list purchase orders with pending supplier updates",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}": {
      "delete": {
        "description": "Deletes one purchase order and its line items, and answers 204 with no body. Nothing is kept in its place, so use hold_purchase_order to stop an order that may come back, or a cancel through update_purchase_order to keep it and its trail.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "delete_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Unique identifier for the purchase order to delete",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "The purchase order is deleted. No body",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Purchase order not found"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Delete Purchase Order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      },
      "get": {
        "description": "One order in full: the header (supplier, destination location, order and expected delivery dates, status, currency, total cost), every line item with its ordered, confirmed and delivered quantities and unit cost, and the deliveries recorded against it.\n\n`container_plan` is the box the order was sized against: the container type and how many, the last container's fill by cube and by weight and which of the two binds, gross weight, the lane's freight quote and a per-line snapshot. It is null on an order whose lane names no container type and on every manufacturing order. Each line's cartons, cbm, kg, hs_code, landed_unit_cost and landed_reason are read from that snapshot rather than from today's variant, so a confirmed order still says what it said when it was placed, and all six are null where there is no plan.\n\n`completion_sentence` is served only on a completed manufacturing order and is null on every other order: completing a make order records that the build is done and moves no stock.\n\n`on_hold` says whether the order is held and `hold_reason` why, in the holder's own words; a hold with no reason is refused, so `hold_reason` is set whenever `on_hold` is true. `status_audit` is the append-only log of intent: one entry per approve, hold, release, issue or push-to-WMS, each with `event`, `actor`, `reason`, `via` and `at`. `actor` is a user id, or `api_key:<key_id>` for a keyed act. A held order refuses every status advance but cancellation.\n\nScope: `purchase_orders:read`.",
        "operationId": "get_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Unique identifier for the purchase order",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of sort arguments for line items. Prefix with \"+\" for ascending or \"-\" for descending. Valid sort fields: product_name, product_id, variant_name, variant_id, sku, barcode, production_type, ordered_quantity, confirmed_quantity, shipped_quantity, delivered_quantity, outstanding_quantity, unit_cost, total_cost.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "+product_name,-unit_cost"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "container_plan": {
                      "basis": "lane",
                      "binds": "cube",
                      "container_type_id": 41,
                      "container_type_name": "40' HC",
                      "containers": 1,
                      "cube_pct": 34.6,
                      "fingerprint": "3f0c9e2b7d5a4c1e8b6f0a9d2c4e6f8a1b3d5c7e9f0a2b4c6d8e0f1a3b5c7d9e",
                      "freight_per_container_cents": 718500,
                      "freight_total_cents": 718500,
                      "gross_kg": 3180.0,
                      "lines": [
                        {
                          "cartons": 40,
                          "cbm": 22.32,
                          "hs_code": "6110.20.2079",
                          "kg": 3180.0,
                          "landed_reason": null,
                          "landed_unit_cost": 15.39,
                          "variant_id": "44100920011"
                        }
                      ],
                      "total_cbm": 22.32,
                      "unmeasured_lines": 0,
                      "usable_cbm_total": 64.6,
                      "weight_pct": 12.0
                    },
                    "currency": "USD",
                    "expected_delivery_date": "2026-10-02",
                    "external_id": "PO-9014",
                    "hold_reason": "Vendor has not confirmed the navy; do not issue until they do.",
                    "id": "9014",
                    "line_items": [
                      {
                        "cartons": 40,
                        "cbm": 22.32,
                        "delivered_quantity": 0,
                        "hs_code": "6110.20.2079",
                        "kg": 3180.0,
                        "landed_reason": null,
                        "landed_unit_cost": 15.39,
                        "quantity": 240,
                        "sku": "TB-CREW-BLK-M",
                        "total_cost": 2976.0,
                        "unit_cost": 12.4,
                        "variant_id": "44100920011"
                      }
                    ],
                    "location_id": "loc_0004",
                    "name": "PO-9014 Porto Knits",
                    "on_hold": true,
                    "order_date": "2026-09-04",
                    "order_type": "purchase",
                    "status": "issued",
                    "status_audit": [
                      {
                        "actor": "66c1f0a2e4b09a3d5c7f1a02",
                        "at": "2026-09-04T11:02:19Z",
                        "event": "approved",
                        "reason": null,
                        "via": "app"
                      },
                      {
                        "actor": "66c1f0a2e4b09a3d5c7f1a02",
                        "at": "2026-09-05T08:41:03Z",
                        "event": "held",
                        "reason": "Vendor has not confirmed the navy; do not issue until they do.",
                        "via": "app"
                      }
                    ],
                    "supplier_id": "sup_0031",
                    "supplier_name": "Porto Knits",
                    "total_cost": 148200.0
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetPurchaseOrderResponse"
                }
              }
            },
            "description": "The purchase order",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "BAD_REQUEST"
                      ],
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Invalid sort column: 'bad_col'."
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "examples": [
                            "ERROR"
                          ],
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid sort column or validation error",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "NOT_FOUND"
                      ],
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Purchase order not found"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get purchase order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      },
      "patch": {
        "description": "Patches one order: its status, its dates, its notes, and its line items' quantities and costs. Line items go as `[{\"id\": 123, \"quantity\": 40, \"confirmed_quantity\": 50, \"unit_cost\": 4.99}]`, where `id` is the line item's id; `shipped_quantity` and `delivered_quantity` are accepted the same way. `is_billed: true` requires `billed_at` (YYYY-MM-DD) in the same request.\n\nStatuses: Drafted, FullyConfirmed, InProduction, FullyShipped, FullyDelivered, Completed, Cancelled. For a confirmation or a stop, approve_purchase_order and hold_purchase_order record the actor and the reason on the audit trail, which a bare status patch does not.\n\n`container_plan` is re-sized on every update that leaves the order drafted and once more on the update that confirms it; after that it is frozen, because the confirm is what the supplier works to. Changing `quantity_to_manufacture` on a manufacturing order redraws its raw material lines from the bill of materials in whole batches, with the components in force on the order date, and a cost typed on a line is never overwritten.\n\nCancelling a billed, Xero-synced order is refused 409: the bill has to be voided in Xero first, and only someone with Xero access can do that. Both doors onto a cancel are covered, `status=Cancelled` and `is_active=false`.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "update_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Unique identifier for the purchase order to update",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePurchaseOrderRequest"
              }
            }
          },
          "description": "Request object for updating a purchase order",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "expected_delivery_date": "2026-10-09",
                    "id": "9014",
                    "name": "PO-9014 Porto Knits",
                    "status": "issued",
                    "total_cost": 148200.0
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/UpdatePurchaseOrderResponse"
                }
              }
            },
            "description": "The purchase order as it now stands",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Purchase order not found"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "PO-00003219 is synced with Xero and its bill is still open. Void the bill in Xero before cancelling the order."
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Conflict - the order's state refuses this change. Cancelling an order that is synced with Xero and still carries an open bill is refused here; so is advancing an order that is on hold. Nothing was written.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "update purchase order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/approve": {
      "post": {
        "description": "Confirms a drafted order, moving it to FullyConfirmed through the same path the manual confirm takes, and records who approved it and why on the order's audit trail. The body's `reason` and `via` are optional, and `via` defaults to \"api\". The response carries the confirmed order and the audit entry that was appended.\n\nRefused 409 when the order is already confirmed or on hold, by the open-to-buy guardrail (`OTB_GUARDRAIL_HARD_STOP`), and by a Commitment envelope with no headroom left for the order's lines. get_purchase_order_commitment_draw answers the envelope question before the call.\n\n`purchase_order_id` is the internal id from list_purchase_orders, not the display number.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "approve_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order to approve.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "reason": {
                    "description": "Why this order is being approved, recorded on the audit trail.",
                    "type": "string"
                  },
                  "via": {
                    "description": "What acted (e.g. a workflow name). Defaults to \"api\".",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "audit": {
                      "actor": "66c1f0a2e4b09a3d5c7f1a02",
                      "at": "2026-09-04T09:12:00+00:00",
                      "event": "approved",
                      "reason": "Buying plan signed off for October intake",
                      "via": "api"
                    },
                    "purchase_order": {
                      "expected_delivery_date": "2026-10-02",
                      "id": "9014",
                      "name": "PO-9014 Porto Knits",
                      "status": "FullyConfirmed",
                      "total_cost": 148200.0
                    }
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "The confirmed order plus the audit entry that recorded the approval",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "description": "Already confirmed, on hold, or refused by the OTB guardrail / commitment envelope",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Approve a purchase order, with the reason and the actor stamped on it",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/auto-allocate": {
      "post": {
        "description": "Walks one manufacturing order's bill of materials and reserves the components available at its source location. The response names, per component, the quantity required, the quantity allocated and the shortfall, with `fully_allocated` for the whole order.\n\nIt reserves stock and buys none: what cannot be covered is reported rather than ordered. Only a manufacturing order has materials to allocate, so call it after bulk_create_manufacturing_orders and before issuing, while a shortfall can still be acted on.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "auto_allocate_manufacturing_order_materials",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "allocated": [
                      {
                        "allocated_quantity": 480,
                        "required_quantity": 480,
                        "shortfall": 0,
                        "variant_id": "44100000021"
                      },
                      {
                        "allocated_quantity": 180,
                        "required_quantity": 240,
                        "shortfall": 60,
                        "variant_id": "44100000022"
                      }
                    ],
                    "fully_allocated": false,
                    "purchase_order_id": 9015
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What was allocated per component, and what remains short.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The order is not a manufacturing order, or has no bill of materials to walk.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Reserve the raw materials a manufacturing order needs, from what is on hand",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/commitment-draw": {
      "get": {
        "description": "What this order WOULD draw from the Commitments its lines belong to, measured per line: one group per Commitment with the draw and the headroom left, the lines no Commitment covers with the reason, and `would_be_refused` with the sentence a refusal would carry.\n\nDisplay, never draw. Nothing is spent by reading this: the envelope moves when the order is placed, and `projection_note` says so on the response and on every group. It takes no lock and refuses nothing.\n\nThe figure is per line, because an order may legitimately span Commitments and each line is measured against its own Commitment's headroom rather than a pooled ceiling. `refused_lines` and `clear_lines` sum to `lines`, so \"some of this order is fine\" is answerable. A line no Commitment covers comes back under `unattributed` with its reason, which is a stated state rather than a problem.\n\nSold with Pro: an organisation without Commitments is refused 403 `plan_excludes`.\n\nScope: `purchase_orders:read`.",
        "operationId": "get_purchase_order_commitment_draw",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-04",
                    "clear_lines": {
                      "count": 9,
                      "grain": "line",
                      "population": "lines that would be allowed"
                    },
                    "currency": "USD",
                    "groups": [
                      {
                        "commitment_id": "cmt_0091",
                        "commitment_name": "SS27 Knitwear buy",
                        "draw_usd": 148200.0,
                        "headroom_usd": 260000.0,
                        "lines": 9,
                        "would_be_refused": false
                      }
                    ],
                    "lines": {
                      "count": 12,
                      "grain": "line",
                      "population": "lines on this order"
                    },
                    "projection_note": "Nothing here has been drawn. The envelope moves when the order is placed.",
                    "purchase_order_id": 9014,
                    "purchase_order_name": "PO-9014 Porto Knits",
                    "refusal_reason": null,
                    "refused_lines": {
                      "count": 0,
                      "grain": "line",
                      "population": "lines that would be refused"
                    },
                    "status": "draft",
                    "unattributed": {
                      "lines": 3,
                      "reason": "no Commitment declares these items in this window",
                      "value_usd": 18400.0
                    },
                    "would_be_refused": false
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "format": "date",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "clear_lines": {
                          "type": "object"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "groups": {
                          "description": "One per Commitment whose items are on this order, each with its own headroom.",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "lines": {
                          "type": "object"
                        },
                        "projection_note": {
                          "description": "That nothing here has been spent. Carried on the response and on every group.",
                          "type": "string"
                        },
                        "purchase_order_id": {
                          "type": "integer"
                        },
                        "purchase_order_name": {
                          "type": "string"
                        },
                        "refusal_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "refused_lines": {
                          "type": "object"
                        },
                        "status": {
                          "type": "string"
                        },
                        "unattributed": {
                          "description": "The lines no Commitment covers, with the reason and the sentence for each.",
                          "type": "object"
                        },
                        "would_be_refused": {
                          "type": "boolean"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The order, the Commitments its lines belong to, what each group would draw against that Commitment's headroom, the lines nothing covers, and whether placing it would be refused.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The purchase order id is not a number.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Commitments. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when this organisation's plan does not include Commitments, which is sold with Pro and is what gates this read rather than the rest of Purchase orders; `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What this order would draw from its Commitments, before it is placed",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/deliveries": {
      "get": {
        "description": "Every delivery recorded against one order, each with the date the goods arrived, the date they were expected, and its line items' delivered and expected quantities. An order carries as many partial deliveries as it needs before it is complete. get_purchase_order_delivery is the read for one of them by id.\n\nScope: `purchase_orders:read`.",
        "operationId": "get_purchase_order_deliveries",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order ID",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "delivery_date": "2026-09-18",
                      "delivery_line_items": [
                        {
                          "delivered_quantity": 240,
                          "expected_quantity": 240,
                          "variant_id": "44100920011"
                        },
                        {
                          "delivered_quantity": 180,
                          "expected_quantity": 200,
                          "variant_id": "44100920012"
                        }
                      ],
                      "expected_delivery_date": "2026-09-21",
                      "id": "8841"
                    }
                  ],
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "Every delivery recorded against this purchase order.",
                      "items": {
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every delivery on the purchase order",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get purchase order deliveries",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      },
      "post": {
        "description": "Records that goods from one order have been received, and moves stock. The body carries `delivery_date` (the day they arrived; omit it for a delivery that is still expected), an optional `expected_delivery_date`, and `delivery_line_items` as `[{\"variant_id\": \"44100920011\", \"delivered_quantity\": 240}]`, where `expected_quantity` is optional and defaults to the order line's quantity. The response is the delivery as recorded.\n\nPartial deliveries are the normal case. To correct a receipt already recorded, patch it with update_purchase_order_delivery: sent here, the correction becomes a second receipt and the order's delivered quantity counts the goods twice.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "record_purchase_order_delivery",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order ID",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "delivery_date": {
                    "description": "Date the delivery was received (ISO format). Optional.",
                    "examples": [
                      "2024-06-15"
                    ],
                    "format": "date",
                    "type": "string"
                  },
                  "delivery_line_items": {
                    "description": "List of variant deliveries in this shipment",
                    "items": {
                      "properties": {
                        "delivered_quantity": {
                          "description": "Number of units actually received",
                          "minimum": 0,
                          "type": "integer"
                        },
                        "expected_quantity": {
                          "description": "Number of units expected (optional, defaults to PO quantity)",
                          "minimum": 0,
                          "type": "integer"
                        },
                        "variant_id": {
                          "description": "The variant ID that was delivered",
                          "type": "string"
                        }
                      },
                      "required": [
                        "variant_id",
                        "delivered_quantity"
                      ],
                      "type": "object"
                    },
                    "minItems": 1,
                    "type": "array"
                  },
                  "expected_delivery_date": {
                    "description": "Expected delivery date (ISO format). Optional.",
                    "examples": [
                      "2024-06-20"
                    ],
                    "format": "date",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "delivery_date": "2026-09-18",
                    "delivery_line_items": [
                      {
                        "delivered_quantity": 240,
                        "expected_quantity": 240,
                        "variant_id": "44100920011"
                      },
                      {
                        "delivered_quantity": 180,
                        "expected_quantity": 200,
                        "variant_id": "44100920012"
                      }
                    ],
                    "exceptions_opened": [
                      {
                        "id": 4412,
                        "kind": "exception_short_receipt",
                        "title": "PO-00001042 arrived 20 units short: 180 of 200 MAR-TOP-L."
                      }
                    ],
                    "expected_delivery_date": "2026-09-21",
                    "id": "8841",
                    "movements": [
                      {
                        "id": 100241,
                        "quantity_delta": 240,
                        "variant_id": "44100920011"
                      },
                      {
                        "id": 100242,
                        "quantity_delta": 180,
                        "variant_id": "44100920012"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The delivery as recorded, with `movements` (the ledger rows this receipt wrote) and `exceptions_opened` (what it could not settle).\n",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The delivery as recorded.\nEvery recorded line writes its movement to the ledger in the same request, and what the receipt could not settle -- units over the order's quantity, units short of what it expected, a SKU the order does not carry -- opens a row in the exception queue. Both are served back here: `movements` as [{id, variant_id, quantity_delta}] (a correction carries a negative delta), `exceptions_opened` as [{id, kind, title}], where `title` is the sentence the queue prints. Both are null on a READ, which is not the same fact as an empty list.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - invalid delivery data.\n`variant_not_on_order` when a line names a product the order does not carry, and nothing was recorded for it: \"MAR-TOP-L is not on PO-00001042; nothing was recorded for it.\"\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "create purchase order delivery",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/deliveries/bulk": {
      "post": {
        "description": "Records several deliveries against one order in a single call, for a warehouse posting a day's receipts at once. Each entry is one delivery in the shape record_purchase_order_delivery takes, and the response carries them as recorded.\n\nEach entry is a NEW delivery. To correct one already recorded, use update_purchase_order_delivery: a correction sent here records a second receipt and the order's delivered quantity counts the goods twice.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "bulk_record_purchase_order_deliveries",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkCreatePurchaseOrderDeliveryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "delivery_date": "2026-09-18",
                      "delivery_line_items": [
                        {
                          "delivered_quantity": 240,
                          "expected_quantity": 240,
                          "variant_id": "44100920011"
                        },
                        {
                          "delivered_quantity": 180,
                          "expected_quantity": 200,
                          "variant_id": "44100920012"
                        }
                      ],
                      "exceptions_opened": [],
                      "expected_delivery_date": "2026-09-21",
                      "id": "8841",
                      "movements": []
                    }
                  ],
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "description": "One recorded delivery, with its dates and line items.",
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every delivery created, in order.\nEvery recorded line writes its movement to the ledger in the same request, and what the receipt could not settle -- units over the order's quantity, units short of what it expected, a SKU the order does not carry -- opens a row in the exception queue. Both are served back here: `movements` as [{id, variant_id, quantity_delta}] (a correction carries a negative delta), `exceptions_opened` as [{id, kind, title}], where `title` is the sentence the queue prints. Both are null on a READ, which is not the same fact as an empty list.\nThese are PLANNED tranches, so `movements` is an empty list on each: a promise is not a receipt and moves no stock.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A quantity is negative, a date could not be read, or no delivery was sent.\n\n`variant_not_on_order` when a line names a product the order does not carry, and nothing was recorded for it: \"MAR-TOP-L is not on PO-00001042; nothing was recorded for it.\"\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Record several deliveries against one purchase order in a single call",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/deliveries/{delivery_id}": {
      "delete": {
        "description": "Deletes one recorded delivery and its lines, answers 204, and the order's delivered quantity drops by what that delivery carried.\n\nFor a delivery that happened but was recorded wrongly, correct it with update_purchase_order_delivery instead: deleting and re-recording loses the receipt's own history, which is what a stock audit reads.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "delete_purchase_order_delivery",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The delivery on that order.",
            "in": "path",
            "name": "delivery_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted. No body.\n\nThe units come back before the document goes: a movement is never rewritten, so removing a receipt writes a full reversal per line against the delivery while it still exists, and the ledger foots to zero for it.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "error_type": "delivery_has_posted_bill",
                  "message": {
                    "desc": "The receipt of Sep 18, 2026 on PO-00001042 was billed as BILL-4471 in Xero on Sep 19, 2026; void the bill there before changing or removing the receipt.",
                    "service": "purchase_order",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`delivery_has_posted_bill` when the receipt has already been billed to Xero or QuickBooks. Void the bill in the ledger before changing or removing the receipt.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Remove a delivery recorded in error",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      },
      "get": {
        "description": "One recorded delivery: its dates, and its line items with what was expected against what arrived. Use get_purchase_order_deliveries for every delivery on the order; this is the read for one of them by id.\n\nScope: `purchase_orders:read`.",
        "operationId": "get_purchase_order_delivery",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The delivery on that order.",
            "in": "path",
            "name": "delivery_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "delivery_date": "2026-09-18",
                    "delivery_line_items": [
                      {
                        "delivered_quantity": 240,
                        "expected_quantity": 240,
                        "variant_id": "44100920011"
                      },
                      {
                        "delivered_quantity": 180,
                        "expected_quantity": 200,
                        "variant_id": "44100920012"
                      }
                    ],
                    "expected_delivery_date": "2026-09-21",
                    "id": "8841"
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The delivery, its dates, and one line per variant with the expected and delivered quantities.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One recorded delivery against a purchase order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      },
      "patch": {
        "description": "Replaces one recorded delivery's dates and line items with what is sent. This is the correction path for a receipt already recorded: a miscount, a date typed wrongly, a line that arrived later.\n\nRecording a NEW arrival is record_purchase_order_delivery. Sent here against an existing delivery, it would overwrite the first receipt with the second rather than adding to it.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "update_purchase_order_delivery",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The delivery on that order.",
            "in": "path",
            "name": "delivery_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "delivery_date": {
                    "description": "The day the goods actually arrived. Omit it for a delivery that is still expected.",
                    "format": "date",
                    "type": "string"
                  },
                  "delivery_line_items": {
                    "items": {
                      "properties": {
                        "delivered_quantity": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "expected_quantity": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "variant_id": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "variant_id",
                        "delivered_quantity"
                      ],
                      "type": "object"
                    },
                    "minItems": 1,
                    "type": "array"
                  },
                  "expected_delivery_date": {
                    "format": "date",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "delivery_date": "2026-09-18",
                    "delivery_line_items": [
                      {
                        "delivered_quantity": 240,
                        "expected_quantity": 240,
                        "variant_id": "44100920011"
                      },
                      {
                        "delivered_quantity": 180,
                        "expected_quantity": 200,
                        "variant_id": "44100920012"
                      }
                    ],
                    "exceptions_opened": [],
                    "expected_delivery_date": "2026-09-21",
                    "id": "8841",
                    "movements": [
                      {
                        "id": 100243,
                        "quantity_delta": -20,
                        "variant_id": "44100920011"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The delivery as it now stands.\nEvery recorded line writes its movement to the ledger in the same request, and what the receipt could not settle -- units over the order's quantity, units short of what it expected, a SKU the order does not carry -- opens a row in the exception queue. Both are served back here: `movements` as [{id, variant_id, quantity_delta}] (a correction carries a negative delta), `exceptions_opened` as [{id, kind, title}], where `title` is the sentence the queue prints. Both are null on a READ, which is not the same fact as an empty list.\nAn edit writes the DELTA: a line corrected from 300 to 280 books minus 20 against the row it reduces, never a second receipt of 280. A receipt whose bill has already posted to Xero or QuickBooks cannot be edited here (409 `delivery_has_posted_bill`); void the bill first.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A quantity is negative, or a date could not be read.\n`variant_not_on_order` when a line names a product the order does not carry, and nothing was recorded for it: \"MAR-TOP-L is not on PO-00001042; nothing was recorded for it.\"\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "error_type": "delivery_has_posted_bill",
                  "message": {
                    "desc": "The receipt of Sep 18, 2026 on PO-00001042 was billed as BILL-4471 in Xero on Sep 19, 2026; void the bill there before changing or removing the receipt.",
                    "service": "purchase_order",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`delivery_has_posted_bill` when the receipt has already been billed to Xero or QuickBooks. Void the bill in the ledger before changing or removing the receipt.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Correct a recorded delivery",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/duplicate": {
      "post": {
        "description": "Copies one order with all its line items and returns the copy. The copy is a new draft with its own id and number and no expected delivery date; the original is untouched.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "duplicate_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Unique identifier for the purchase order to duplicate",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "expected_delivery_date": null,
                    "external_id": "PO-9016",
                    "id": "9016",
                    "location_id": "loc_0004",
                    "name": "PO-9016 Porto Knits (copy)",
                    "order_type": "purchase",
                    "supplier_id": "sup_0031",
                    "total_line_items": 12
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/DuplicatePurchaseOrderResponse"
                }
              }
            },
            "description": "The new purchase order copied from the one named",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Purchase order not found"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "duplicate purchase order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/export": {
      "get": {
        "description": "Renders one order as a file and answers with a URL to download it. `format` is required and takes `pdf` or `csv`; `fields` names the columns a CSV carries and the order they appear in. The URL is presigned and short-lived, so fetch it rather than storing it.\n\nScope: `purchase_orders:read`.",
        "operationId": "export_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Unique identifier for the purchase order to export",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The file the export is rendered as. Defaults to PDF.",
            "in": "query",
            "name": "format",
            "required": true,
            "schema": {
              "enum": [
                "pdf",
                "csv"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated columns to export. Omit for the default column set, which is unchanged. The container columns are opt-in and appear only when named: cartons, cbm, kg and hs_code on both formats, plus landed_unit_cost and landed_reason on CSV only (freight and duty are the importer's money, not the supplier's). They are blank on an order with no container plan. A PDF export also prints one packing line under the header when the order has a plan.\n",
            "in": "query",
            "name": "fields",
            "required": false,
            "schema": {
              "examples": [
                "product_name,sku,quantity,cartons,cbm,kg,hs_code"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "url": "https://files.tightly.io/exports/po-9014.pdf"
                  },
                  "message": {
                    "desc": "",
                    "service": "purchase_order",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ExportPurchaseOrderResponse"
                }
              }
            },
            "description": "A url to download the purchase order",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Purchase order not found"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Purchase order not found",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "export purchase order",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:read"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/hold": {
      "post": {
        "description": "Puts one order on hold, or lifts a standing hold with `release: true`. A held order refuses every status advance until it is released; cancelling stays allowed. `reason` is required when holding, and is recorded on the order and on its audit trail. The response carries the order's hold state, its reason and the audit entry.\n\nRefused 409 on a hold for an order already held, and on a release for an order that is not held.\n\nA hold is reversible. Cancelling is not, and it is a status patch through update_purchase_order.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "hold_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order to put on hold.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "reason": {
                    "description": "Required when holding. A stop with no why is not auditable.",
                    "type": "string"
                  },
                  "release": {
                    "description": "true lifts the hold instead of setting one.",
                    "type": "boolean"
                  },
                  "via": {
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "audit": {
                      "actor": "66c1f0a2e4b09a3d5c7f1a02",
                      "at": "2026-09-04T09:14:00+00:00",
                      "event": "held",
                      "reason": "Waiting on the supplier's revised delivery date",
                      "via": "api"
                    },
                    "hold_reason": "Waiting on the supplier's revised delivery date",
                    "on_hold": true,
                    "purchase_order_id": "9014"
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "The order's hold state plus the audit entry",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "description": "Already on hold, or not on hold when releasing",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Stop an order and say why, or release the hold",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/issue": {
      "post": {
        "description": "Sends the order to its supplier: exports the PDF and emails it to the supplier's main contact, or to `to_email` when the body names one, through the organisation's connected email account, then records the issue on the audit trail. `message` replaces the default one-line cover note. The response says who it went to and by which channel.\n\nThis leaves the building and cannot be recalled, so gate it behind an approval. Refused 409 for transfers and manufacturing orders, for a cancelled order, for an order on hold, and when there is no recipient because the supplier has no contact email and none was passed.\n\nTransmitting an order to a warehouse is a different act: push_to_wms.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "issue_purchase_order",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order to issue to its supplier.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "message": {
                    "description": "The email body. Defaults to a one-line cover note naming the order.",
                    "type": "string"
                  },
                  "to_email": {
                    "description": "Override recipient. Defaults to the supplier's main contact email.",
                    "type": "string"
                  },
                  "via": {
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "audit": {
                      "actor": "66c1f0a2e4b09a3d5c7f1a02",
                      "at": "2026-09-04T09:20:00+00:00",
                      "event": "issued",
                      "reason": "emailed to orders@portoknits.example",
                      "via": "api"
                    },
                    "channel": "email",
                    "issued_to": "orders@portoknits.example",
                    "purchase_order_id": "9014"
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "Who it was issued to, plus the audit entry",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "description": "Wrong order type, cancelled, on hold, or no recipient resolvable",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Send the purchase order to its supplier by their channel",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/mark_delivered": {
      "post": {
        "description": "Closes an order out: every line still outstanding is recorded as delivered on one date, rather than typed in line by line. `delivery_date` defaults to today. `include_planned_to_deliver` decides whether quantities planned but not yet expected are swept in as well, and defaults to false, because a plan is not a receipt.\n\nThis records a receipt like any other, so it moves stock. Use record_purchase_order_delivery when only part of the order arrived; this closes the whole remainder.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "mark_purchase_order_as_delivered",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "delivery_date": {
                    "description": "The day everything outstanding is recorded as arriving. Defaults to today.",
                    "format": "date",
                    "type": "string"
                  },
                  "include_planned_to_deliver": {
                    "default": false,
                    "description": "Sweep in quantities planned but not yet expected. A plan is not a receipt, so this is off by default.",
                    "type": "boolean"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "delivery_date": "2026-09-18",
                    "delivery_line_items": [
                      {
                        "delivered_quantity": 240,
                        "expected_quantity": 240,
                        "variant_id": "44100920011"
                      },
                      {
                        "delivered_quantity": 180,
                        "expected_quantity": 200,
                        "variant_id": "44100920012"
                      }
                    ],
                    "exceptions_opened": [],
                    "expected_delivery_date": null,
                    "id": "8841",
                    "movements": [
                      {
                        "id": 100241,
                        "quantity_delta": 240,
                        "variant_id": "44100920011"
                      },
                      {
                        "id": 100242,
                        "quantity_delta": 180,
                        "variant_id": "44100920012"
                      }
                    ]
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The delivery this created, or null where nothing remained to deliver.\nEvery recorded line writes its movement to the ledger in the same request, and what the receipt could not settle -- units over the order's quantity, units short of what it expected, a SKU the order does not carry -- opens a row in the exception queue. Both are served back here: `movements` as [{id, variant_id, quantity_delta}] (a correction carries a negative delta), `exceptions_opened` as [{id, kind, title}], where `title` is the sentence the queue prints. Both are null on a READ, which is not the same fact as an empty list.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A date could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such purchase order, or no such delivery on it, for this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Close an order out by recording everything outstanding as delivered",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/organizations/{organization_id}/purchase-orders/{purchase_order_id}/push-to-wms": {
      "post": {
        "description": "Creates or amends this order's inbound at the connected warehouse integration, which today means Helm WMS (`helm-wms-direct`). The first push creates the inbound purchase order; a pushed order is amended in place under the reference it was created with. The response carries one `results` entry per warehouse integration with what happened to it, the `references` the warehouse holds for this order, and the audit entry.\n\nIt fans only to warehouse integrations, never to the accounting or webhook ones. Refused 409 when no connected integration accepts purchase orders, and when the order is cancelled or on hold. A failure at the warehouse itself comes back as a `results` entry with `action: failed` and its error, rather than as a refusal.\n\nScope: `purchase_orders:write`, which includes `purchase_orders:read`.",
        "operationId": "push_to_wms",
        "parameters": [
          {
            "description": "The organisation. On the key path it must be the key's own organisation. A key naming another one is refused `organization_mismatch` rather than quietly served its own rows.\n",
            "in": "path",
            "name": "organization_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The purchase order to push.",
            "in": "path",
            "name": "purchase_order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "via": {
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "audit": {
                      "actor": "66c1f0a2e4b09a3d5c7f1a02",
                      "at": "2026-09-04T09:22:00+00:00",
                      "event": "pushed_to_wms",
                      "reason": "helm-wms-direct: created",
                      "via": "api"
                    },
                    "purchase_order_id": "9014",
                    "references": {
                      "helm-wms-direct": {
                        "id": "447102",
                        "reference": "PO-9014"
                      }
                    },
                    "results": [
                      {
                        "action": "created",
                        "integration": "helm-wms-direct"
                      }
                    ]
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "Per-integration results (created / updated / failed) and the warehouse references",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Purchase orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Purchase orders; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "description": "No warehouse connected, or the order is cancelled / on hold",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Create or amend an inbound at the warehouse",
        "tags": [
          "purchase_orders"
        ],
        "x-tightly-scopes": [
          "purchase_orders:write"
        ]
      }
    },
    "/api/v1/otb/{mfp_id}/rollup": {
      "get": {
        "description": "Open to buy for one season, category by category: the envelope the plan declares, what is already committed, what is reserved against it, what is left, the forward recommended buy and the chase reserve split.\n\nA SEASON POOL RATHER THAN A WEEKLY BUDGET. The budget is category x season, so a single week cannot breach on its own; `scope` selects SS or FW and there is no fiscal-year scope, because rolling two disjoint selling windows into one running variance produced a crossing-week signal that meant nothing. `scope=FY` is refused 400. Every figure is at cost.\n\nThere is no stored budget and no build step: the envelope is read live from the plan's own MFP on every call, so this and get_mfp_table cannot disagree about the same season.\n\nSold with Pro: an organisation without the planning rail is refused 403 `plan_excludes`.\n\nScope: `planning:read`.",
        "operationId": "get_otb_rollup",
        "parameters": [
          {
            "description": "The plan whose envelope this rollup is read against. The same `mfp_id` get_mfp_table takes: the envelope is read live from that plan on every call, so the two cannot disagree.\n",
            "in": "path",
            "name": "mfp_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "SS (Spring/Summer, the default) or FW (Fall/Winter), each narrows to a single season's half-open window. FY was REMOVED as a client-selectable scope (TIG-2216, 2026-07-22): the rollup is now a per-season pool view, so ?scope=FY returns 400 (code VALIDATION_ERROR). SS is the default because the request-validation boundary has no plan/date context to resolve the plan's currently-active season. Implication for the FE: /risk-timeline is a fixed FY forward horizon (its scope selector was also removed, TIG-2216), so the rollup is a per-season pool (SS|FW) while the risk-timeline is a whole-FY view (no per-season risk-timeline).\n",
            "in": "query",
            "name": "scope",
            "required": false,
            "schema": {
              "default": "SS",
              "enum": [
                "SS",
                "FW"
              ],
              "type": "string"
            }
          },
          {
            "description": "URL-encoded JSON array of filters with keys category and sales_channel_id (operation eq/in), e.g. [{\"key\":\"category\",\"operation\":\"in\",\"value\":[\"Knitwear\"]}]. The channel filter narrows only the Envelope (Committed / Reserved / forward Recommended Buy are channel-agnostic).\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Optional (TIG-2216). Comma-separated sort spec applied as a STABLE multi-key sort over the rows, e.g. -variance,category (variance descending, then category ascending). Prefix - = descending, + / none = ascending. The sort column is constrained to the rollup's sortable columns: category, season_envelope, committed, reserved, remaining, forward_recommended_buy, variance, status (an unknown column returns 400). status sorts on the status string; the rest on the numeric row value. Omitted preserves the default category-ascending order. It orders ONLY the rows array - summary / totals / guardrail stay whole-scope aggregates.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Optional (TIG-2216). Case-insensitive substring matched against the category NAME to filter the rows (e.g. search=knit). Blank / omitted is a no-op (all rows). Like sort_args it narrows only the rows array, never the summary / totals (so summary still ties out to /header). Additive and backward-compatible.\n",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "basis": "cost",
                    "filters": {
                      "categories": null,
                      "sales_channel_ids": null
                    },
                    "guardrail": {
                      "absolute_ceiling_ratio": null,
                      "amber_threshold_ratio": 0.1,
                      "guardrail_tolerance_ratio": 0.0,
                      "mode": "soft_warning",
                      "surplus_threshold_ratio": 0.15
                    },
                    "mfp": {
                      "fiscal_year": 2027,
                      "mfp_id": "mfp_01J9X4",
                      "name": "FY27 Plan"
                    },
                    "notes": {
                      "category_scope": "mfp_selected_only",
                      "continuity_forward_estimate": "open_replen_demand_forecast_cost",
                      "cost_basis": "po_unit_cost_standard_not_landed"
                    },
                    "phase": "pre_season",
                    "rows": [
                      {
                        "active_mode": null,
                        "category": "Knitwear",
                        "chase_reserve": 91000.0,
                        "committed": 622400.0,
                        "committed_continuity": 210800.0,
                        "committed_seasonal": 411600.0,
                        "continuity_consumption_ratio": 0.32,
                        "continuity_envelope_alert": false,
                        "current_excluded_amount": 0.0,
                        "estimated": false,
                        "exclusion_active": false,
                        "forward_continuity_estimate": 61400.0,
                        "forward_recommended_buy": 168200.0,
                        "forward_seasonal_estimate": 106800.0,
                        "max_band": null,
                        "remaining": 213500.0,
                        "remaining_free": 122500.0,
                        "reserved": 74100.0,
                        "reserved_continuity": 22300.0,
                        "reserved_purchase_orders": [
                          {
                            "created_at": "2026-08-28T11:02:00+00:00",
                            "expected_delivery_date": "2027-02-19",
                            "po_id": 41882,
                            "reference": "PO-00041882",
                            "status": "DRAFTED",
                            "value": 74100.0
                          }
                        ],
                        "reserved_seasonal": 51800.0,
                        "season_envelope": 910000.0,
                        "short_week": null,
                        "status": "healthy",
                        "substate": null,
                        "surplus_free": 45300.0,
                        "target_amount": null,
                        "tight_week": null,
                        "variance": 45300.0
                      }
                    ],
                    "scope": "SS",
                    "summary": {
                      "committed": 1704300.0,
                      "continuity_envelope_alerts": 0,
                      "fw_envelope": 2210000.0,
                      "remaining": 686800.0,
                      "reserved": 218900.0,
                      "scope_envelope": 2610000.0,
                      "ss_envelope": 2610000.0,
                      "total_envelope": 4820000.0
                    },
                    "totals": {
                      "committed": 1704300.0,
                      "continuity_envelope_alerts": 0,
                      "forward_recommended_buy": 512400.0,
                      "remaining": 686800.0,
                      "reserved": 218900.0,
                      "season_envelope": 2610000.0,
                      "status": "healthy",
                      "substate": null,
                      "variance": 174400.0
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "otb",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetOTBRollupResponse"
                }
              }
            },
            "description": "The season pool, category by category. Every figure is at cost. `remaining` is `season_envelope - (committed + reserved)`; `variance` is `remaining - forward_recommended_buy` and is the signal a planner acts on. `tight_week` and `short_week` are fiscal weeks and are null while the pool never crosses in the window. The `notes` block says what the figures leaned on, the cost basis, the category scope, and whether a forward estimate was available at all.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "validation_error",
                  "message": {
                    "code": "validation_error",
                    "desc": "scope[0]: Must be one of: SS, FW.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#validation_error",
                    "request_id": "req_5e83c1470ab24f9d8c60e2153af7b9d4",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`scope=FY` is refused: the rollup is a per-season pool, and rolling two disjoint selling windows into one running variance produced a crossing week that meant nothing. An unknown sort column is refused here too.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_c1f60b8e3d724a05be9271034fa8d6c5",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Planning. It is sold with Pro.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_7a29d40e6c1b48f593ae0d827b1f6534",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when the planning rail is sold with Pro and this organisation's plan does not include it; `scope_missing` when the key does not hold Planning; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get otb rollup",
        "tags": [
          "otb"
        ],
        "x-tightly-scopes": [
          "planning:read"
        ]
      }
    },
    "/api/v1/pim/v1/products": {
      "get": {
        "description": "A page of products with their product data: the family each belongs to, whether that family has a template at all, how many required attributes are still empty, and which fields Tightly owns. `family_id` narrows to one family, and `limit` and `offset` page, with `total` for the whole set.\n\n`has_template` is not a completeness figure. Where a product has no family, nothing can be called missing, so a reader that showed a clean bill of health here would be asserting something nobody established.\n\nThis is the product data door, included with Essentials+ and Pro, and it is its own resource: a `pim:read` key reaches these two operations and none of the catalogue reads. It resolves its organisation from the key inside its own decorator, so an `X-Organization-ID` header is ignored entirely rather than merely unused, and its refusals answer `data.error` rather than the coded envelope every other operation serves. Use get_pim_product for one product's attribute values; this list carries the counts.\n\nScope: `pim:read`.",
        "operationId": "list_pim_products",
        "parameters": [
          {
            "description": "Only products in this family.",
            "in": "query",
            "name": "family_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Rows per page. Clamped to 500.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 500,
              "type": "integer"
            }
          },
          {
            "description": "Rows to skip.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "limit": 100,
                    "offset": 0,
                    "rows": [
                      {
                        "family_id": "knitwear",
                        "has_template": true,
                        "missing_required_count": 2,
                        "owned_fields": [
                          "composition",
                          "care"
                        ],
                        "product_id": "4410092",
                        "title": "Terry Crew"
                      }
                    ],
                    "total": 1284
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "limit": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "total": {
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of products with `total`, `limit` and `offset`, each row carrying its family, whether that family has a template, how many required attributes are missing, and the owned fields.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "error": "The API key is not valid."
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "A single `error` sentence.",
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, in the sentence the seam's `key_invalid` carries, because telling a caller which is which maps the surface for them. This door resolves the key in its own decorator rather than at the seam, so the body carries `data.error` rather than `message.code`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "error": "This organisation's plan does not include Product data. It is sold with Essentials+."
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "A single `error` sentence naming the plan it is sold with.",
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key is valid and the organisation's plan does not include Product data, which is included with Essentials+ and Pro. Specific on purpose: it is only ever shown to the holder of a valid key, so it leaks nothing and it tells an integrator to renew the plan rather than rotate a key that was never the problem. The plan is re-checked on every request, because a key minted on Pro keeps resolving after a downgrade.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The product catalogue with its family attributes, for a system that wants to read it",
        "tags": [
          "pim"
        ],
        "x-tightly-scopes": [
          "pim:read"
        ]
      }
    },
    "/api/v1/pim/v1/products/{product_id}": {
      "get": {
        "description": "One product's attributes, resolved against its family template: every attribute the family declares, grouped as the family groups them, each with whether it is required, whether it is filled, its value, and whether it is a value the family does not declare at all (`not_in_family`, kept and shown rather than dropped).\n\nBeside the groups: what is missing and required, what is missing and optional, the values that sit outside the family, the fields Tightly owns, and the owned fields whose value a storefront displays but write-back has not reached yet.\n\nA product this key's organisation does not have answers 404 \"Not found\", never \"product X not found\", which would tell anyone holding any valid key which ids exist in someone else's catalogue.\n\nIncluded with Essentials+ and Pro. Like list_pim_products it resolves its organisation from the key inside its own decorator and answers `data.error` on a refusal.\n\nScope: `pim:read`.",
        "operationId": "get_pim_product",
        "parameters": [
          {
            "description": "The product.",
            "in": "path",
            "name": "product_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "awaiting_write_back": [
                      "composition"
                    ],
                    "family_id": "knitwear",
                    "groups": {
                      "Fabric": [
                        {
                          "attribute": "composition",
                          "filled": true,
                          "not_in_family": false,
                          "required": true,
                          "value": "80% cotton, 20% polyester"
                        },
                        {
                          "attribute": "gsm",
                          "filled": false,
                          "not_in_family": false,
                          "required": true,
                          "value": null
                        }
                      ]
                    },
                    "has_template": true,
                    "missing_optional": [
                      "origin"
                    ],
                    "missing_required": [
                      "gsm",
                      "care"
                    ],
                    "not_in_family": [
                      "legacy_swatch_code"
                    ],
                    "owned_fields": [
                      "composition",
                      "care"
                    ],
                    "product_id": "4410092",
                    "title": "Terry Crew"
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "awaiting_write_back": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "family_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "groups": {
                          "description": "Attribute rows keyed by the family's own group names.",
                          "type": "object"
                        },
                        "has_template": {
                          "description": "False where the product has no family. Nothing can be called missing then.",
                          "type": "boolean"
                        },
                        "missing_optional": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "missing_required": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "not_in_family": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "owned_fields": {
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "product_id": {
                          "type": "string"
                        },
                        "title": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The product, its family, its grouped attributes, and the four absence lists, missing required, missing optional, not in family, and awaiting write-back.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "error": "The API key is not valid."
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "A single `error` sentence.",
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, in the sentence the seam's `key_invalid` carries, because telling a caller which is which maps the surface for them. This door resolves the key in its own decorator rather than at the seam, so the body carries `data.error` rather than `message.code`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "error": "This organisation's plan does not include Product data. It is sold with Essentials+."
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "A single `error` sentence naming the plan it is sold with.",
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key is valid and the organisation's plan does not include Product data, which is included with Essentials+ and Pro. Specific on purpose: it is only ever shown to the holder of a valid key, so it leaks nothing and it tells an integrator to renew the plan rather than rotate a key that was never the problem. The plan is re-checked on every request, because a key minted on Pro keeps resolving after a downgrade.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No such product for this key's organisation. Deliberately not \"product X not found\".\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One product's attributes, resolved against its family template",
        "tags": [
          "pim"
        ],
        "x-tightly-scopes": [
          "pim:read"
        ]
      }
    },
    "/api/v1/product-subcategories": {
      "get": {
        "description": "The active subcategories belonging to one category, each `{subcategory_id, name, category_id, is_active}`. Read it to resolve a subcategory name to the id a product carries, or to offer the choices under a category.\n\n`category` is required and takes either the category's name or its id; there is no way to list every subcategory in the organisation in one call. Omitting it is refused 400 with \"category query parameter is required.\"\n\nOnly active subcategories are served. A category that exists but has no active subcategory answers an empty array, which is the empty case and not an error.\n\nScope: `products:read`.",
        "operationId": "list_product_subcategories",
        "parameters": [
          {
            "description": "The category name or category_id whose subcategories to retrieve.",
            "in": "query",
            "name": "category",
            "required": true,
            "schema": {
              "examples": [
                "Outerwear"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "category_id": "cat_outerwear",
                      "is_active": true,
                      "name": "Shirt jackets",
                      "subcategory_id": "sub_shirt_jackets"
                    },
                    {
                      "category_id": "cat_outerwear",
                      "is_active": true,
                      "name": "Parkas",
                      "subcategory_id": "sub_parkas"
                    }
                  ],
                  "message": {
                    "desc": "The subcategories on file",
                    "service": "inventory",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "items": {
                        "properties": {
                          "category_id": {
                            "type": "string"
                          },
                          "is_active": {
                            "type": "boolean"
                          },
                          "name": {
                            "type": "string"
                          },
                          "subcategory_id": {
                            "type": "string"
                          }
                        },
                        "type": "object"
                      },
                      "type": "array"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The active subcategories under that category.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The category query parameter is missing.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No category in this organisation carries that name or id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The subcategories under one category",
        "tags": [
          "products"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/product/{product_id}": {
      "get": {
        "description": "One product as the catalogue holds it: title, description, vendor, images, category, subcategory and family, status and published date, country of origin, HS code, unit of measure, and the sales channels it is sold through.\n\nFigures that differ variant by variant are served as `{min, max}` pairs rather than a single number: `unit_cost`, `sell_price`, `lead_time`, `min_order_quantity`, `weight`, `length`, `width` and `height` all take that shape. `in_stock`, `incoming_stock` and `inventory_value` are totals across the product's variants, and `variant_count` and `num_variants` both carry the count.\n\n`location_id` narrows the stock figures to one warehouse. Without it they are the total across every warehouse.\n\n`pim_owned_fields` names the columns the PIM has taken over, which the connector defers to on sync; it is `[]` for an organisation that has not opted in. `planning_gaps` is what planning is missing on this product, each entry `{field, label, reason, severity}` with `severity` of `missing` or `review`, and `size_curve` is the effective curve (a declaration beats a measurement beats the labelled default) while `size_curve_declared` is the raw declaration. Where the effective curve is the labelled default because the engine refused to measure one, `size_curve.fallback_reason_code` and `size_curve.fallback_reason` say which refusal and in what words, and `price_band_reason` says the same for the price band.\n\nA product id that does not exist in this organisation is refused 404.\n\nScope: `products:read`.",
        "operationId": "get_product",
        "parameters": [
          {
            "description": "The product this reads.",
            "in": "path",
            "name": "product_id",
            "required": true,
            "schema": {
              "examples": [
                "7412095483953"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow in_stock, incoming_stock and inventory_value to one warehouse.",
            "in": "query",
            "name": "location_id",
            "required": false,
            "schema": {
              "examples": [
                "61240442"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "category": "Outerwear",
                    "category_id": "cat_outerwear",
                    "country_of_origin": "PT",
                    "currency": "USD",
                    "description": "A brushed wool overshirt cut for layering.",
                    "family": "Tops",
                    "family_id": "fam_tops",
                    "gallery_images": [
                      "https://cdn.tightly.io/p/7412095483953.jpg"
                    ],
                    "hs_code": "620331",
                    "image_url": "https://cdn.tightly.io/p/7412095483953.jpg",
                    "in_current_season_plan": true,
                    "in_stock": 1840,
                    "incoming_stock": 600,
                    "inventory_value": 71760.0,
                    "is_managed": true,
                    "is_seasonal": true,
                    "last_updated_at": "2026-09-01T11:42:00+00:00",
                    "last_updated_by": "ana.ruiz@example.com",
                    "launch_date": "2026-02-14",
                    "lead_time": {
                      "max": 60.0,
                      "min": 45.0
                    },
                    "min_order_quantity": {
                      "max": 120.0,
                      "min": 120.0
                    },
                    "num_variants": 8,
                    "options": {
                      "Colour": [
                        "Charcoal",
                        "Sand"
                      ],
                      "Size": [
                        "S",
                        "M",
                        "L",
                        "XL"
                      ]
                    },
                    "pim_owned_fields": [
                      "description",
                      "country_of_origin"
                    ],
                    "planning_gaps": [
                      {
                        "field": "cost",
                        "label": "Unit cost",
                        "reason": "No cost: margin floors can't compute, so buy suggestions can't respect margin.",
                        "severity": "missing"
                      }
                    ],
                    "price_band": "better",
                    "price_band_source": "declared",
                    "product_id": "7412095483953",
                    "product_name": "Alpine Wool Overshirt",
                    "product_status": "ACTIVE",
                    "product_title": "Alpine Wool Overshirt",
                    "published_at": "2026-02-14",
                    "published_status": "ACTIVE",
                    "replenishment_mode": "seasonal",
                    "sales_channels": [
                      {
                        "id": "61240442",
                        "name": "Online Store"
                      }
                    ],
                    "sell_price": {
                      "max": 145.0,
                      "min": 129.0
                    },
                    "selling_window_end": "2027-01-31",
                    "selling_window_start": "2026-08-01",
                    "shopify_tags": [
                      "aw26"
                    ],
                    "size_curve": {
                      "label": "Declared",
                      "source": "declared",
                      "values": {
                        "L": 2,
                        "M": 2,
                        "S": 1,
                        "XL": 1
                      }
                    },
                    "status": "ACTIVE",
                    "subcategory": "Shirt jackets",
                    "subcategory_id": "sub_shirt_jackets",
                    "suppliers": [
                      {
                        "is_default": true,
                        "supplier_id": "sup_1180",
                        "supplier_name": "Atelier Norte"
                      }
                    ],
                    "unit_cost": {
                      "max": 39.0,
                      "min": 34.0
                    },
                    "uom": "each",
                    "variant_count": 8,
                    "variant_options": {
                      "Size": [
                        "S",
                        "M",
                        "L",
                        "XL"
                      ]
                    },
                    "vendor": "Veja",
                    "weight": {
                      "max": 0.74,
                      "min": 0.62
                    },
                    "weight_unit": "kg"
                  },
                  "message": {
                    "desc": "OK",
                    "service": "product",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The product, its ranges across variants, and its planning attributes.\n\n`size_curve.fallback_reason_code` is one of `no_stock_history`, `a_run_size_never_observed`, `a_sold_size_outside_the_run`, `run_broken_every_day`, `too_few_clean_days`, `too_few_clean_units` or `one_size_sold`, and `size_curve.fallback_reason` is that code in words, safe to render as it stands. `price_band_reason` carries a `reason` of `no_category`, `no_price`, `category_too_thin` or `no_spread`, with the counts behind it. Both are null where nothing was refused.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No product in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One product with its stock and the ranges its variants span",
        "tags": [
          "product"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/product/{product_id}/filters": {
      "get": {
        "description": "The filter values that exist for one product: `categories`, the category names its variants carry, and `locations`, the warehouses it is stocked in. Read it to populate a picker before narrowing the product's own reads, rather than sending a value the catalogue does not hold and getting an empty page back.\n\nBoth are plain string arrays and both can be empty, which means the product has no variant carrying that field, not that the read failed.\n\n`locations` is on its way out and is served for compatibility; the warehouse list an integrator should read is the one on the stock reads.\n\nA product id that does not exist in this organisation is refused 404.\n\nScope: `products:read`.",
        "operationId": "get_product_filters",
        "parameters": [
          {
            "description": "The product whose filter values this reads.",
            "in": "path",
            "name": "product_id",
            "required": true,
            "schema": {
              "examples": [
                "7412095483953"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "categories": [
                      "Outerwear"
                    ],
                    "locations": [
                      "Rotterdam DC",
                      "New Jersey DC"
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "product",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductFiltersPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The categories and locations this product's rows can be narrowed by.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No product in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The values a product's own rows can be filtered by",
        "tags": [
          "product"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/product/{product_id}/incoming-pos": {
      "get": {
        "description": "Open orders carrying any variant of one product, grouped under the variant rather than under the order: one entry per variant that has something inbound, each with `po_count`, `to_count` (transfer orders), `proposal_count` and the orders themselves.\n\nEach order in `purchase_orders` carries its id and name, `order_type` (`PURCHASE`, `TRANSFER` or `MANUFACTURING`), `is_proposal`, `status`, `expected_delivery_date`, supplier and location, the `line_items` and `deliveries` for that variant alone, `ordered_quantity` and `delivered_quantity` for that variant alone, `has_delivery_delay` (true once the expected date is in the past) and `supplier_signals`. `total_ordered` and `total_delivered` on the group are those two summed across its orders and are being retired; sum the orders yourself rather than depending on them.\n\nFully delivered and cancelled orders are not here. A product with nothing inbound answers `purchase_orders_grouped_by_variant: []`, which is the empty case and not an error.\n\nThis is the product's own view. For the same question against a single variant, read the variant's incoming purchase orders; for the orders themselves, with their whole line set, read the purchase orders resource.\n\nScope: `products:read`.",
        "operationId": "get_product_incoming_purchase_orders",
        "parameters": [
          {
            "description": "The product whose inbound orders this reads.",
            "in": "path",
            "name": "product_id",
            "required": true,
            "schema": {
              "examples": [
                "7412095483953"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "purchase_orders_grouped_by_variant": [
                      {
                        "po_count": 1,
                        "proposal_count": 0,
                        "purchase_orders": [
                          {
                            "delivered_quantity": 0,
                            "deliveries": null,
                            "expected_delivery_date": "2026-10-12",
                            "has_delivery_delay": false,
                            "id": "48120",
                            "is_proposal": false,
                            "line_items": [
                              {
                                "quantity": 240,
                                "variant_id": "42318902722609",
                                "variant_title": "Charcoal / S"
                              }
                            ],
                            "location_id": "61240442",
                            "location_name": "Rotterdam DC",
                            "order_name": "PO-00048120",
                            "order_type": "PURCHASE",
                            "ordered_quantity": 240,
                            "status": "FullyConfirmed",
                            "supplier_id": "sup_1180",
                            "supplier_name": "Atelier Norte",
                            "supplier_signals": []
                          }
                        ],
                        "to_count": 0,
                        "total_delivered": 0,
                        "total_ordered": 240,
                        "variant_id": "42318902722609",
                        "variant_name": "Charcoal / S"
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "product",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductPurchaseOrdersPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One group per variant with something inbound, each carrying its open orders.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No product in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What is still on order for a product, grouped by the variant it is coming for",
        "tags": [
          "product"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/product/{product_id}/incoming-pos/drawer": {
      "get": {
        "description": "The same open orders as the per-variant read, grouped the other way: by `order_type`, in the fixed order `PURCHASE`, `TRANSFER`, `MANUFACTURING`, with one entry per order rather than one per variant.\n\nEach order carries `id`, `name`, `expected_delivery_date`, `status`, `has_delivery_delay`, `supplier_signals`, `ordered_quantity` and `delivered_quantity` totalled across this product's variants, and `line_items` of `{variant_id, variant_title, quantity}` for the variants of this product only.\n\nProposals, cancelled orders and fully delivered orders are excluded, so what is here is what is genuinely still coming. A product with nothing inbound answers `purchase_orders_grouped_by_type: []`.\n\nTake this one when a product's inbound needs counting by order type; take the per-variant read when the question is which variant the stock is coming for.\n\nScope: `products:read`.",
        "operationId": "get_product_incoming_purchase_orders_by_type",
        "parameters": [
          {
            "description": "The product whose inbound orders this reads.",
            "in": "path",
            "name": "product_id",
            "required": true,
            "schema": {
              "examples": [
                "7412095483953"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "purchase_orders_grouped_by_type": [
                      {
                        "order_type": "PURCHASE",
                        "purchase_orders": [
                          {
                            "delivered_quantity": 0,
                            "expected_delivery_date": "2026-10-12",
                            "has_delivery_delay": false,
                            "id": "48120",
                            "line_items": [
                              {
                                "quantity": 240,
                                "variant_id": "42318902722609",
                                "variant_title": "Charcoal / S"
                              },
                              {
                                "quantity": 360,
                                "variant_id": "42318902755377",
                                "variant_title": "Charcoal / M"
                              }
                            ],
                            "name": "PO-00048120",
                            "ordered_quantity": 600,
                            "status": "FullyConfirmed",
                            "supplier_signals": []
                          }
                        ]
                      },
                      {
                        "order_type": "TRANSFER",
                        "purchase_orders": []
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "product",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductDrawerIncomingPOsPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The product's open orders, grouped by order type.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No product in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What is still on order for a product, grouped by order type",
        "tags": [
          "product"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/product/{product_id}/variants": {
      "get": {
        "description": "The product's variants, three fields each: `variant_id`, `variant_title` and `sku`. It is the list to walk when you hold a product and need the variant ids underneath it.\n\nDeliberately thin, and not paginated: every variant of the product is in one answer. For prices, costs, stock or dimensions per variant, read the variants table or one variant.\n\n`sku` and `variant_title` are null where the catalogue has none; `variant_id` never is.\n\nA product id that does not exist in this organisation is refused 404.\n\nScope: `products:read`.",
        "operationId": "list_product_variants",
        "parameters": [
          {
            "description": "The product whose variants this lists.",
            "in": "path",
            "name": "product_id",
            "required": true,
            "schema": {
              "examples": [
                "7412095483953"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "variants": [
                      {
                        "sku": "AWO-CHR-S",
                        "variant_id": "42318902722609",
                        "variant_title": "Charcoal / S"
                      },
                      {
                        "sku": "AWO-CHR-M",
                        "variant_id": "42318902755377",
                        "variant_title": "Charcoal / M"
                      },
                      {
                        "sku": "AWO-SND-S",
                        "variant_id": "42318902788145",
                        "variant_title": "Sand / S"
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "product",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductVariantsPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Every variant of the product, id, title and SKU.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No product in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every variant of one product",
        "tags": [
          "product"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/products/completeness": {
      "get": {
        "description": "A statement about a whole scope rather than a page of it: how many products it holds, how many are planning-complete, and one row per planning attribute saying how many products are missing it.\n\nThe six attributes are `category`, `price_band`, `size_curve`, `selling_window`, `cost` and `lifecycle`. Each row carries the `label` a face prints, the `reason` the gap matters, `missing_products` (planning should ask for this), `review_products` (declared and detected disagree, worth a look) and `affected_pairs`, the variant-by-sales-channel pairs those products cover.\n\n`affected_pairs` and `pairs` are null, not 0, for an organisation with no channel pairs at all. Absence and zero are different answers and are served differently.\n\nIt takes the same `filter_args` and `search` as the products table, so a completeness figure can be read for exactly the set a table page came from. It is not paginated: `offset` and `limit` are accepted and ignored, because completeness describes the scope and not a page of it.\n\nScope: `products:read`.",
        "operationId": "get_products_completeness",
        "parameters": [
          {
            "description": "Free text over product title, SKU and vendor, as on the products table.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "overshirt"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of {key, operation, value}, the same keys the products table accepts.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"category\",\"operation\":\"eq\",\"value\":\"Outerwear\"}]"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "complete_products": 268,
                    "fields": [
                      {
                        "affected_pairs": 0,
                        "field": "category",
                        "label": "Category",
                        "missing_products": 0,
                        "reason": "No category: analog matching and price banding fall back to the whole catalog.",
                        "review_products": 0
                      },
                      {
                        "affected_pairs": 742,
                        "field": "price_band",
                        "label": "Price band",
                        "missing_products": 61,
                        "reason": "No price band: suggestions can't band by architecture (good / better / best).",
                        "review_products": 0
                      },
                      {
                        "affected_pairs": 1109,
                        "field": "size_curve",
                        "label": "Size curve",
                        "missing_products": 88,
                        "reason": "No size curve: buys will use the bell default (1-2-2-1).",
                        "review_products": 0
                      },
                      {
                        "affected_pairs": 301,
                        "field": "selling_window",
                        "label": "Selling window",
                        "missing_products": 24,
                        "reason": "Seasonal without a selling window: replenishment can't hold to the season and plans can't phase.",
                        "review_products": 0
                      },
                      {
                        "affected_pairs": 144,
                        "field": "cost",
                        "label": "Unit cost",
                        "missing_products": 12,
                        "reason": "No cost: margin floors can't compute, so buy suggestions can't respect margin.",
                        "review_products": 0
                      },
                      {
                        "affected_pairs": 88,
                        "field": "lifecycle",
                        "label": "Lifecycle",
                        "missing_products": 7,
                        "reason": "Sales look seasonal but the lifecycle is undeclared: declare core or seasonal so plans and replenishment agree.",
                        "review_products": 19
                      }
                    ],
                    "pairs": 5104,
                    "products": 412
                  },
                  "message": {
                    "desc": "OK",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetProductsCompletenessPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The scope's product count, its complete count, and one row per planning attribute.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "filter_args is not valid JSON, or names a key the products table does not accept.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What the scoped products are missing for planning, and how many each gap costs",
        "tags": [
          "products"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/products/table": {
      "get": {
        "description": "A page of products with the catalogue fields an integrator syncs on: title, vendor, category, `num_variants`, status, GTIN, description, currency, HS code, country of origin, unit of measure, `sales_channels`, and `last_updated_at` and `last_updated_by`.\n\nFigures that differ variant by variant are `{min, max}` pairs, not single numbers: `unit_cost`, `sell_price`, `weight`, `length`, `width` and `height`. `variant_options` is the option map `{name: [values]}` and `gallery_images` the image list.\n\nPaging is `offset` and `limit`, limit at most 10,000 and 8 by default. Four counts come back with the rows: `products_count` and `size` for this page, `filtered_max_size` for the filtered set and `max_size` for the catalogue. Page on `filtered_max_size`, never on `max_size`, or a filtered walk never ends.\n\nNarrow with `filter_args`: product_id, vendor, category, product_status, sales_channel_id, country_of_origin and collection_id take `eq` and `in`; unit_cost and sell_price take `gte` and `lte`. `search` matches title, SKU and vendor. Sort with `sort_args`: comma-separated columns, `-` for descending, bare or `+` for ascending.\n\n`export=true` answers a download URL in `data.url` instead of rows.\n\nPlanning attributes ride along where the PIM holds them: `replenishment_mode` is the declared lifecycle and `is_seasonal` the detected one, `size_curve` is the effective curve, and `planning_gaps` is what planning is missing, each `{field, label, reason, severity}`.\n\nScope: `products:read`.",
        "operationId": "list_products",
        "parameters": [
          {
            "description": "Rows to skip before this page.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "examples": [
                0
              ],
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Rows in this page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "examples": [
                50
              ],
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Free text over product title, SKU and vendor.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "overshirt"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of {key, operation, value}. product_id, vendor, category, product_status, sales_channel_id, country_of_origin and collection_id take eq and in; unit_cost and sell_price take gte and lte. product_status is one of ACTIVE, ARCHIVED, DRAFT, UNLISTED. collection_id is a collection as the store syncs it, not a curated collection, so a curated collection's id matches nothing here. A product can sit in several, so in answers every product in any of the ids given.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"category\",\"operation\":\"in\",\"value\":[\"Outerwear\"]},{\"key\":\"product_status\",\"operation\":\"eq\",\"value\":\"ACTIVE\"}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated sort columns, `-` for descending and `+` or nothing for ascending. Sortable: product_id, product_title, vendor, category, product_status, num_variants, unit_cost, sell_price, last_updated_at, last_updated_by, weight, length, width, height, description, gtin, currency, uom, weight_unit, hs_code, country_of_origin, sales_channels.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "-last_updated_at,vendor"
              ],
              "type": "string"
            }
          },
          {
            "description": "Answer a download URL for the filtered set instead of a page of rows.",
            "in": "query",
            "name": "export",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 412,
                    "max_size": 1806,
                    "offset": 0,
                    "products_count": 1,
                    "rows": [
                      {
                        "category": "Outerwear",
                        "category_id": "cat_outerwear",
                        "country_of_origin": "PT",
                        "currency": "USD",
                        "description": "A brushed wool overshirt cut for layering.",
                        "gallery_images": [
                          "https://cdn.tightly.io/p/7412095483953.jpg"
                        ],
                        "gtin": "05012345678900",
                        "height": null,
                        "hs_code": "620331",
                        "is_seasonal": true,
                        "last_updated_at": "2026-09-01T11:42:00+00:00",
                        "last_updated_by": "ana.ruiz@example.com",
                        "length": null,
                        "num_variants": 8,
                        "planning_gaps": [],
                        "price_band": "better",
                        "price_band_source": "declared",
                        "product_id": "7412095483953",
                        "product_status": "ACTIVE",
                        "product_title": "Alpine Wool Overshirt",
                        "replenishment_mode": "seasonal",
                        "sales_channels": [
                          {
                            "id": "61240442",
                            "name": "Online Store"
                          }
                        ],
                        "sell_price": {
                          "max": 145.0,
                          "min": 129.0
                        },
                        "selling_window_end": "2027-01-31",
                        "selling_window_start": "2026-08-01",
                        "size_curve": {
                          "label": "Declared",
                          "source": "declared",
                          "values": {
                            "L": 2,
                            "M": 2,
                            "S": 1,
                            "XL": 1
                          }
                        },
                        "unit_cost": {
                          "max": 39.0,
                          "min": 34.0
                        },
                        "uom": "each",
                        "variant_options": {
                          "Size": [
                            "S",
                            "M",
                            "L",
                            "XL"
                          ]
                        },
                        "vendor": "Veja",
                        "weight": {
                          "max": 0.74,
                          "min": 0.62
                        },
                        "weight_unit": "kg",
                        "width": null
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "OK",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetPimTablePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of products, with the page's counts and the filtered set's size.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "offset or limit is outside its range, or filter_args is not valid JSON.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "A page of the product catalogue, one row per product",
        "tags": [
          "products"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/sales/analytics": {
      "get": {
        "description": "Revenue trend and period-over-period comparison, grouped: each group carries `revenue_total` and `revenue_monthly` keyed by month, with `Overall` beside the groups.\n\n`start_date` and `end_date` are required unless `in_fiscal_year` is sent, which takes `this` or `last` as a shorthand for the organisation's own fiscal year. `categories` narrows the groups and `fields` narrows the measures returned.\n\nScope: `sales:read`.",
        "operationId": "get_sales_analytics",
        "parameters": [
          {
            "description": "this or last. Shortcut for getting this fiscal year's or last fiscal year's data. If not provided, start_date and end_date are expected.",
            "in": "query",
            "name": "in_fiscal_year",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Start date for analytics period (ISO date format)",
            "in": "query",
            "name": "start_date",
            "required": true,
            "schema": {
              "examples": [
                "2024-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End date for analytics period (ISO date format)",
            "in": "query",
            "name": "end_date",
            "required": true,
            "schema": {
              "examples": [
                "2024-12-31"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Comma separated categories to filter the budget",
            "in": "query",
            "name": "categories",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Comma separated fields to be fetched",
            "in": "query",
            "name": "fields",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "When true, returns sample/demo data instead of real analytics",
            "in": "query",
            "name": "is_sample",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "Overall": {
                      "revenue_monthly": {
                        "2026-01-01": 164000.0,
                        "2026-02-01": 171200.0
                      },
                      "revenue_total": 984000.0
                    },
                    "cash cows": {
                      "revenue_monthly": {
                        "2026-01-01": 68800.0,
                        "2026-02-01": 71400.0
                      },
                      "revenue_total": 412800.0
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The sales analytics metrics",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales analytics",
        "tags": [
          "sales"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/customers": {
      "get": {
        "description": "Everyone the record knows, from a connected channel or from an order somebody typed. Deliberately thin: a display name, the email's domain, a country and a coarsened region, and never a street address, a phone number or an email in the clear. Do not call this to build a marketing list, and do not call it to find one person you already have the id for: `GET /sales/customers/{customer_id}` serves that one with their orders and returns.\n\nScope: `orders:read`.",
        "operationId": "list_customers",
        "parameters": [
          {
            "description": "Only this account's own customer record.",
            "in": "query",
            "name": "trading_partner_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "A term containing an `@` is read as an email address and matched against its pseudonym, exactly. Anything else matches the display name or the email's domain.",
            "in": "query",
            "name": "search",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many customers to skip. The list is ordered by the most recent order, newest first.",
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "type": "integer"
            }
          },
          {
            "description": "How many customers to return, at most 100.",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 8,
              "maximum": 100,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 2,
                    "offset": 0,
                    "rows": [
                      {
                        "account": {
                          "name": "Coastline DS",
                          "trading_partner_id": "tp-coastline"
                        },
                        "country_code": "US",
                        "customer_id": "cus_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                        "display_name": "Coastline DS",
                        "display_name_words": "Coastline DS",
                        "email_domain": "coastline.example",
                        "first_order_at": "Jul 14, 2026",
                        "kind": "account",
                        "last_order_at": "Sep 5, 2026",
                        "orders_count": 3,
                        "redacted_at": null,
                        "region": "CA",
                        "source_system": "tightly"
                      },
                      {
                        "account": null,
                        "country_code": "US",
                        "customer_id": "cus_7QK2V0X9MB3D5T8HAJ1NCR6FZ4",
                        "display_name": null,
                        "display_name_words": "Not on file",
                        "email_domain": null,
                        "first_order_at": "Sep 2, 2026",
                        "kind": "consumer",
                        "last_order_at": "Sep 2, 2026",
                        "orders_count": 1,
                        "redacted_at": "Sep 4, 2026",
                        "region": "NY",
                        "source_system": "shopify"
                      }
                    ],
                    "size": 2,
                    "withheld_words": null
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "rows, offset, size, filtered_max_size, and `withheld_words`, which reads `Not held` on a tenant that never holds a person and null on one that may.\n",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The page of customers, and how many the filter matched in all.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "`offset` or `limit` is outside the range the list offers.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and named another (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list customers",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      }
    },
    "/api/v1/sales/customers/{customer_id}": {
      "get": {
        "description": "The person, their orders in the same row shape the Orders list serves, their returns, and what has been refunded to them across every channel they bought on. Call it to answer a question about a buyer; do not call it in a loop over a list, which is what the list itself is for. There is no write: a customer is created by the order writers and by the sync, never by this API.\n\n`refund_status.refunded` is a money leg summed off the orders' own refunded amounts, which are the channel's figures and are never recomputed from lines. `cents` null with a reason beside it means no order of theirs has been refunded, which is absence and not a refund of nothing. `redacted_at` set means the identity was cleared on a channel's erasure event: the name, the email pseudonym and the domain are gone, and the orders, their lines and their figures were all kept.\n\nScope: `orders:read`.",
        "operationId": "get_customer",
        "parameters": [
          {
            "description": "The customer's id, as `List customers` and an order's `customer` block serve it.",
            "in": "path",
            "name": "customer_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account": {
                      "name": "Coastline DS",
                      "trading_partner_id": "tp-coastline"
                    },
                    "country_code": "US",
                    "customer_id": "cus_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                    "display_name": "Coastline DS",
                    "display_name_words": "Coastline DS",
                    "email_domain": "coastline.example",
                    "first_order_at": "Sep 5, 2026",
                    "kind": "account",
                    "last_order_at": "Sep 5, 2026",
                    "orders": [
                      {
                        "account": {
                          "name": "Coastline DS",
                          "trading_partner_id": "tp-coastline"
                        },
                        "channel": {
                          "name": "Wholesale",
                          "sales_channel_id": "wholesale"
                        },
                        "commitment_id": null,
                        "created_at": "2026-09-05T09:00:00+00:00",
                        "customer": {
                          "customer_id": "cus_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                          "display_name": "Coastline DS"
                        },
                        "exceptions_open": 0,
                        "fulfilled_units": 0,
                        "is_wholesale": true,
                        "lifecycle": "allocated",
                        "lifecycle_updated_at": "2026-09-05T09:12:00+00:00",
                        "location": {
                          "location_id": "loc-lb",
                          "name": "Long Beach"
                        },
                        "name": "ORD-00000412",
                        "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                        "origin": "manual",
                        "units": 900,
                        "value": {
                          "cents": 3710000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 37100.0
                        }
                      }
                    ],
                    "orders_count": 1,
                    "redacted_at": null,
                    "refund_status": {
                      "orders_refunded": 0,
                      "refunded": {
                        "cents": null,
                        "currency": null,
                        "reason": "No order of theirs has been refunded",
                        "usd": null
                      }
                    },
                    "region": "CA",
                    "returns": [],
                    "source_system": "tightly"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "the customer, orders, returns and refund_status.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The customer, their orders, their returns and what has been refunded.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The path carries no customer id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and the record is not that account's (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "customer_not_on_file",
                  "message": {
                    "code": "customer_not_on_file",
                    "desc": "No customer with that id is on file.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#customer_not_on_file",
                    "request_id": "req_2f7a5c9038be4d1197c6a0e4b5d38f21",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No customer with that id is on file (`customer_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get customer",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      }
    },
    "/api/v1/sales/filters": {
      "get": {
        "description": "The values the sales table can be filtered on: categories, tags and custom fields as flat lists. Send `field` to page one field's values with `limit`, `offset` and `search`, which is what a long tag list needs; omit it and every filter comes back at once. Read it to build `filter_args` for get_sales_table out of values that exist.\n\nScope: `sales:read`.",
        "operationId": "get_sales_filters",
        "parameters": [
          {
            "description": "Optional field name to retrieve paginated values (e.g., 'shopify_tags'). Omit to get all filters.",
            "in": "query",
            "name": "field",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Optional search term to filter results (only applicable when field is provided)",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Number of items to skip for pagination (only applicable when field is provided)",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Maximum number of items to return per page (only applicable when field is provided)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "categories": [
                      "Knitwear",
                      "Outerwear"
                    ],
                    "custom_fields": [
                      "fabric_weight"
                    ],
                    "shopify_tags": [
                      "core",
                      "seasonal"
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The sales filters",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales filters",
        "tags": [
          "sales"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/invoices": {
      "get": {
        "description": "The wholesale invoices Tightly has raised, newest first, with what the books did with each and what the network did with its 810. Use it to reconcile your accounts receivable against what shipped. It does not include consumer orders: a storefront's own payment is not an invoice Tightly raises.\n\nScope: `orders:read`.",
        "operationId": "list_invoices",
        "parameters": [
          {
            "description": "Only this account's invoices.",
            "in": "query",
            "name": "trading_partner_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The document's own state, which is not the ledger's and not the network's.",
            "in": "query",
            "name": "state",
            "schema": {
              "enum": [
                "draft",
                "issued",
                "voided"
              ],
              "type": "string"
            }
          },
          {
            "description": "Only the invoices raised against this order.",
            "in": "query",
            "name": "order_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many invoices to skip, for the page after this one.",
            "in": "query",
            "name": "offset",
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "How many invoices one page carries, capped where every list is capped.",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 1,
                    "max_size": 50,
                    "offset": 0,
                    "rows": [
                      {
                        "account": {
                          "name": "Tidewater Surf Co.",
                          "trading_partner_id": "fx-tp-tidewater"
                        },
                        "currency": "USD",
                        "edi": {
                          "document_id": 45,
                          "sent_at": "2026-09-06T17:30:01+00:00",
                          "sentence": null,
                          "state": "sent"
                        },
                        "invoice_id": 4,
                        "issued_at": "2026-09-06T17:30:00+00:00",
                        "ledger": {
                          "external_url": "https://go.xero.com/AccountsReceivable/Edit.aspx?InvoiceID=8d1f0f1c",
                          "ledger": "xero",
                          "posted_at": "2026-09-06T17:30:02+00:00",
                          "sentence": null,
                          "state": "posted"
                        },
                        "name": "INV-00000004",
                        "order": {
                          "name": "TIDE-5581",
                          "order_id": "fx-ord-tide-5581"
                        },
                        "state": "issued",
                        "terms": "net_30",
                        "total": {
                          "cents": 780000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 7800.0
                        },
                        "units": 200
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The invoices, newest first.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "An offset or limit outside the bounds every list keeps.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and named another (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list invoices",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      }
    },
    "/api/v1/sales/invoices/{invoice_id}": {
      "get": {
        "description": "One wholesale invoice: what was billed, at what price, against which order and shipment, what your books did with it and what the network did with its 810. Use it to answer an account's query about a document they hold.\n\nThree facts are served apart and none of them is the others. `state` is the document's own, `issued` or `voided`. `ledger` is what Xero or QuickBooks did, and null there means no ledger is connected, so nothing was posted and nothing is owed in anybody's books. `edi` is what SPS did with the 810, and null means none was queued, which is what an account that takes none looks like.\n\nScope: `orders:read`.",
        "operationId": "get_invoice",
        "parameters": [
          {
            "description": "The invoice's id.",
            "in": "path",
            "name": "invoice_id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account": {
                      "name": "Tidewater Surf Co.",
                      "trading_partner_id": "fx-tp-tidewater"
                    },
                    "created_by": "usr_01J8V3H2Q9",
                    "currency": "USD",
                    "edi": {
                      "document_id": 45,
                      "sent_at": "2026-09-06T17:30:01+00:00",
                      "sentence": null,
                      "state": "sent"
                    },
                    "freight": {
                      "cents": 0,
                      "currency": "USD",
                      "reason": null,
                      "usd": 0.0
                    },
                    "invoice_id": 4,
                    "issued_at": "2026-09-06T17:30:00+00:00",
                    "ledger": {
                      "external_id": "8d1f0f1c-0f2a-4f3e-9a11-6c2f5f0e7a10",
                      "external_url": "https://go.xero.com/AccountsReceivable/Edit.aspx?InvoiceID=8d1f0f1c",
                      "ledger": "xero",
                      "posted_at": "2026-09-06T17:30:02+00:00",
                      "sentence": null,
                      "state": "posted"
                    },
                    "lines": [
                      {
                        "amount": {
                          "cents": 468000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 4680.0
                        },
                        "buyer_sku": "4472-S",
                        "line_number": 1,
                        "order_line_item_id": "fx-ord-tide-5581:1",
                        "product_title": "Marina Bottom",
                        "quantity": 120,
                        "sku": "MAR-BTM-S",
                        "unit_price": {
                          "cents": 3900,
                          "currency": "USD",
                          "reason": null,
                          "usd": 39.0
                        },
                        "variant_id": "fx-var-btm-s",
                        "variant_title": "S"
                      },
                      {
                        "amount": {
                          "cents": 312000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 3120.0
                        },
                        "buyer_sku": "4472-L",
                        "line_number": 2,
                        "order_line_item_id": "fx-ord-tide-5581:2",
                        "product_title": "Marina Bottom",
                        "quantity": 80,
                        "sku": "MAR-BTM-L",
                        "unit_price": {
                          "cents": 3900,
                          "currency": "USD",
                          "reason": null,
                          "usd": 39.0
                        },
                        "variant_id": "fx-var-btm-l",
                        "variant_title": "L"
                      }
                    ],
                    "name": "INV-00000004",
                    "order": {
                      "name": "TIDE-5581",
                      "order_id": "fx-ord-tide-5581"
                    },
                    "shipment": {
                      "name": "SHP-00000007",
                      "shipment_id": 7
                    },
                    "state": "issued",
                    "subtotal": {
                      "cents": 780000,
                      "currency": "USD",
                      "reason": null,
                      "usd": 7800.0
                    },
                    "tax": {
                      "cents": 0,
                      "currency": "USD",
                      "reason": null,
                      "usd": 0.0
                    },
                    "terms": "net_30",
                    "total": {
                      "cents": 780000,
                      "currency": "USD",
                      "reason": null,
                      "usd": 7800.0
                    },
                    "units": 200
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The invoice, whole.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and the record is not that account's (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "invoice_not_on_file",
                  "message": {
                    "desc": "No invoice with that id is on file.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No invoice with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get an invoice",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      }
    },
    "/api/v1/sales/net-sales": {
      "get": {
        "description": "Net sales bucketed by category, channel type and period: one row per combination with `net_sales` and `net_items_sold`, plus the window, the grain, the calendar, a `coverage` block counting the orders and dimensions behind the rows, and a basis note. This is the coarse actuals feed a plan is measured against.\n\n`start_date` and `end_date` are required and `end_date` is INCLUSIVE, as it is on get_stock_on_hand: the service turns it into the exclusive bound, so two callers asking for 2026-01-31 and 2026-02-01 each see the period once. The window may not exceed 400 days; a caller wanting more asks twice.\n\n`categories` and `channel_types` filter and never change the grain, and `coverage` then describes the filtered population, so a filtered total is honest about its own denominator instead of reading as the whole book.\n\nUse get_sales_table when the question is per variant. This is the same book at a coarser grain, keyed the way a plan is keyed.\n\nScope: `sales:read`.",
        "operationId": "get_net_sales",
        "parameters": [
          {
            "description": "Scope to this commitment's exact merchandise membership and dates. Channel commitments also restrict the sales channel. Rolling commitments use the requested bounded window. The response confirms commitment_scope; an unknown id is refused, never treated as the company.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "maxLength": 250,
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "description": "First day of the window (YYYY-MM-DD).",
            "in": "query",
            "name": "start_date",
            "required": true,
            "schema": {
              "examples": [
                "2026-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Last day of the window, inclusive (YYYY-MM-DD). At most 400 days after the start.",
            "in": "query",
            "name": "end_date",
            "required": true,
            "schema": {
              "examples": [
                "2026-06-30"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "The period the rows are bucketed into.",
            "in": "query",
            "name": "grain",
            "required": false,
            "schema": {
              "default": "month",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated categories to filter to. Filters; never changes the grain.",
            "in": "query",
            "name": "categories",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Comma-separated channel types to filter to.",
            "in": "query",
            "name": "channel_types",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "basis_note": "Net of refunds, over the organisation's own order book.",
                    "calendar": "gregorian",
                    "coverage": {
                      "categories": 14,
                      "channel_types": 3,
                      "orders": 41208,
                      "unattributed_orders": 0,
                      "unattributed_reason": null
                    },
                    "grain": "month",
                    "rows": [
                      {
                        "category": "Knitwear",
                        "channel_type": "wholesale",
                        "net_items_sold": 14020,
                        "net_sales": 412800.0,
                        "period_start": "2026-01-01"
                      },
                      {
                        "category": "Knitwear",
                        "channel_type": "direct",
                        "net_items_sold": 2410,
                        "net_sales": 188400.0,
                        "period_start": "2026-01-01"
                      }
                    ],
                    "window": {
                      "end_exclusive": "2026-07-01",
                      "start": "2026-01-01"
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "basis_note": {
                          "type": "string"
                        },
                        "calendar": {
                          "type": "string"
                        },
                        "commitment_scope": {
                          "description": "Exact commitment membership and date bounds applied to these actuals; null for an unscoped read.",
                          "properties": {
                            "commitment_id": {
                              "type": "string"
                            },
                            "membership_basis": {
                              "type": "string"
                            },
                            "membership_date": {
                              "format": "date",
                              "type": "string"
                            },
                            "name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "sales_channel_id": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "variants": {
                              "type": "integer"
                            },
                            "window_end_exclusive": {
                              "format": "date",
                              "type": "string"
                            },
                            "window_start": {
                              "format": "date",
                              "type": "string"
                            }
                          },
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "coverage": {
                          "description": "The population behind the totals, what was measured, what was priced, and what could not be attributed.",
                          "type": "object"
                        },
                        "grain": {
                          "type": "string"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "window": {
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One row per category × channel type × period, the window actually read, the calendar and grain behind it, and a coverage block describing the population these totals were measured over.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A date could not be read, `end_date` precedes `start_date`, the window exceeds 400 days, or the grain is not one the query can strike.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Net sales at category × channel type × period, the actuals feed for finance",
        "tags": [
          "sales"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/new-overview": {
      "get": {
        "description": "The sales overview a dashboard is built from: `cards` carrying a value, the previous period's value and the change per measure, and `charts` carrying `revenue` (the measured series), `historical` (the requested range), `forecast` (the same length forward) and `last_year` (the same range shifted back 365 days).\n\nAll three of historical, forecast and last_year are the same length as the range requested: historical is the window `filter_args` sets, forecast starts the day after it ends, and last_year is that window a year earlier. Forecast points are predictions rather than measurements, and are answered on the same shape as the rest.\n\n`period` sets the bucket, and `cards`, `charts` and `product_option` narrow what is computed, which is worth doing: this read is the most expensive one on Sales.\n\nScope: `sales:read`.",
        "operationId": "get_sales_new_overview",
        "parameters": [
          {
            "description": "JSON array of filter conditions. Valid filter keys with their allowed operations and value types:\n- `date` (string date)\n  - Operations: `gte`, `lte`\n  - Example value: \"2024-12-01\"\n- `location_id` (string or array)\n  - Operations: `eq`, `in`\n  - Example value: \"60904046659\" or [\"60904046659\", \"63148523587\"]\n- `variant_id` (string)\n  - Operations: `eq`\n  - Example value: \"123456789\"\n  - Use for variant-level aggregation\n- `product_id` (string)\n  - Operations: `eq`\n  - Example value: \"987654321\"\n  - Use for product-level aggregation (aggregates all variants)\n- `unit_cost`, `sell_price`, `sales_amount`, `cost_of_goods_sold`, `total_gross_profit`, `quantity_sold` (numbers)\n  - Operations: `gte`, `lte`\n  - Example value: 100.50\n\nExample:\n```json\n[\n  {\"key\": \"date\", \"operation\": \"gte\", \"value\": \"2024-12-01\"},\n  {\"key\": \"location_id\", \"operation\": \"in\", \"value\": [\"60904046659\", \"63148523587\"]},\n  {\"key\": \"product_id\", \"operation\": \"eq\", \"value\": \"987654321\"}\n]\n```\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"date\",\"operation\":\"gte\",\"value\":\"2024-12-01\"},{\"key\":\"location_id\",\"operation\":\"in\",\"value\":[\"60904046659\",\"63148523587\"]},{\"key\":\"variant_id\",\"operation\":\"eq\",\"value\":\"123456789\"}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Aggregation granularity only. Controls how chart data is bucketed. This does NOT set the date range. To filter by date range, use filter_args with date gte/lte conditions. Valid values: daily, weekly.\n",
            "in": "query",
            "name": "period",
            "required": false,
            "schema": {
              "enum": [
                "daily",
                "weekly"
              ],
              "examples": [
                "daily"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of card metrics to include. Available cards: sold_quantity, total_cogs, total_revenue, gross_profit, sell_through_rate, gmroi, return_rate, avg_sell_price, sale_order_count\n",
            "in": "query",
            "name": "cards",
            "required": false,
            "schema": {
              "examples": [
                "sold_quantity,total_revenue,gross_profit"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of charts to include. Available charts: revenue, sales_by_variant\n",
            "in": "query",
            "name": "charts",
            "required": false,
            "schema": {
              "examples": [
                "revenue,sales_by_variant"
              ],
              "type": "string"
            }
          },
          {
            "description": "Product option for filtering and analysis",
            "in": "query",
            "name": "product_option",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "cards": [
                    {
                      "change": 244.59,
                      "info": "Quantity Sold",
                      "key": "sold_quantity",
                      "previous_value": 379,
                      "unit": "count",
                      "value": 1306
                    },
                    {
                      "change": 263.46,
                      "info": "Sales Amount",
                      "key": "total_revenue",
                      "previous_value": 8685.65,
                      "unit": "dollar",
                      "value": 31569.22
                    }
                  ],
                  "charts": {
                    "forecast": [
                      {
                        "date": "2025-10-27",
                        "sold_quantity": 18.09,
                        "total_revenue": 408.35
                      },
                      {
                        "date": "2025-11-03",
                        "sold_quantity": 10.22,
                        "total_revenue": 230.65
                      }
                    ],
                    "historical": [
                      {
                        "date": "2025-07-28",
                        "sold_quantity": 302.0,
                        "total_revenue": 7001.44
                      },
                      {
                        "date": "2025-08-04",
                        "sold_quantity": 342.0,
                        "total_revenue": 7321.85
                      }
                    ],
                    "last_year": [
                      {
                        "date": "2024-07-29",
                        "sold_quantity": 329.0,
                        "total_revenue": 6989.04
                      },
                      {
                        "date": "2024-08-05",
                        "sold_quantity": 240.0,
                        "total_revenue": 5538.99
                      }
                    ],
                    "revenue": [
                      {
                        "avg_sell_price": 30.35,
                        "date": "2025-10-13T00:00:00+00:00",
                        "gmroi": 0.0,
                        "gross_profit": 3486.64,
                        "return_rate": 0.0,
                        "sale_order_count": 140,
                        "sell_through_rate": 0.0,
                        "sold_quantity": 200,
                        "total_cogs": 1392.0,
                        "total_revenue": 4878.64
                      }
                    ]
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GetSalesNewOverviewPayload"
                }
              }
            },
            "description": "The sales overview",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales new overview",
        "tags": [
          "sales"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/orders": {
      "get": {
        "description": "Every order Tightly holds, whatever it came in by: a connected channel, a person, a file, this API, an EDI 850 or the buyer portal. `status_numbers` counts the whole filtered book per lifecycle, not the page, so a total shown above the rows can be relied on. Do not call this to find one order you already have the id for: `GET /sales/orders/{order_id}` serves that one whole, and this list deliberately carries no address.\n\nScope: `orders:read`.",
        "operationId": "list_sales_orders",
        "parameters": [
          {
            "description": "Only orders in this state.",
            "in": "query",
            "name": "lifecycle",
            "schema": {
              "enum": [
                "draft",
                "open",
                "allocated",
                "fulfilment_requested",
                "fulfilled",
                "cancelled"
              ],
              "type": "string"
            }
          },
          {
            "description": "Only orders somebody has to answer for: a wholesale draft nobody has confirmed, or an order carrying an open queue row. Not expressible as a lifecycle, which is why it is a filter of its own.",
            "in": "query",
            "name": "needs_you",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Only orders that came in by this door.",
            "in": "query",
            "name": "origin",
            "schema": {
              "enum": [
                "channel",
                "manual",
                "csv",
                "api",
                "edi",
                "portal"
              ],
              "type": "string"
            }
          },
          {
            "description": "True for orders an account placed, false for orders a shopper placed.",
            "in": "query",
            "name": "is_wholesale",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "Only orders for this account.",
            "in": "query",
            "name": "trading_partner_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only orders on this channel.",
            "in": "query",
            "name": "sales_channel_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Matches an order's name, its reference, or the account it is for.",
            "in": "query",
            "name": "search",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Only orders placed on or after this day.",
            "in": "query",
            "name": "since",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "How many orders to skip, for paging.",
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "type": "integer"
            }
          },
          {
            "description": "How many orders to return. Above the maximum the read is REFUSED rather than trimmed: a caller handed 500 of 4,000 rows with no warning has wrong data. `filtered_max_size` states the book's true size, so a caller can always tell a page from the whole of it.",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 8,
              "maximum": 500,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-05T12:00:00+00:00",
                    "filtered_max_size": 6,
                    "max_size": 6,
                    "offset": 0,
                    "rows": [
                      {
                        "account": {
                          "name": "Coastline DS",
                          "trading_partner_id": "tp-coastline"
                        },
                        "channel": {
                          "name": "Wholesale",
                          "sales_channel_id": "wholesale"
                        },
                        "commitment_id": null,
                        "created_at": "2026-09-05T09:00:00+00:00",
                        "customer": null,
                        "exceptions_open": 0,
                        "fulfilled_units": 0,
                        "is_wholesale": true,
                        "lifecycle": "allocated",
                        "lifecycle_updated_at": "2026-09-05T09:12:00+00:00",
                        "location": {
                          "location_id": "loc-lb",
                          "name": "Long Beach"
                        },
                        "name": "ORD-00000412",
                        "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                        "origin": "manual",
                        "units": 900,
                        "value": {
                          "cents": 3710000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 37100.0
                        }
                      }
                    ],
                    "size": 6,
                    "status_numbers": {
                      "allocated": 4,
                      "fulfilment_requested": 1,
                      "open": 1
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The house paging shape (offset, size, max_size, filtered_max_size, rows), status_numbers and as_of.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The page of orders, and the counts the head ties to.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A filter carries a value the list does not offer, or `since` is not a date written YYYY-MM-DD (`date_unreadable`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and named another (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list sales orders",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      },
      "post": {
        "description": "Creates one order Tightly owns from the moment it exists: its lines, the channel or the account it belongs to, where it ships and what it is worth. Send an `Idempotency-Key` header; the same key with the same body answers the same order, the same key with a different body is refused, and a key sent while the first attempt is still running is refused with `Retry-After`. Tightly keeps a key 30 days.\n\nOrders that come from a connected channel are never created here: they arrive on the sync and changing them is done in the channel. A file of orders goes through `POST /sales/orders/import`, and a buyer's 850 arrives as a draft through EDI.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "create_sales_order",
        "parameters": [
          {
            "description": "A key you choose, up to 255 characters; Tightly keeps it 30 days.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "cancel_date": "2026-10-06",
                "currency": "USD",
                "external_reference": "WEB-88231",
                "is_wholesale": true,
                "lifecycle": "open",
                "lines": [
                  {
                    "quantity": 400,
                    "unit_price_cents": 4400,
                    "variant_id": "fx-v-mar-top-m"
                  },
                  {
                    "quantity": 500,
                    "sku": "MAR-BTM-M",
                    "unit_price_cents": 3900
                  }
                ],
                "location_id": "loc-lb",
                "origin": "manual",
                "requested_ship_date": "2026-09-22",
                "sales_channel_id": "wholesale",
                "ship_to": {
                  "address1": "700 Queensway Drive",
                  "city": "Long Beach",
                  "country_code": "US",
                  "name": "Coastline DS",
                  "postcode": "90802",
                  "region": "CA"
                },
                "trading_partner_id": "tp-coastline"
              },
              "schema": {
                "$ref": "#/components/schemas/CreateSalesOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account": {
                      "name": "Coastline DS",
                      "trading_partner_id": "tp-coastline"
                    },
                    "channel": {
                      "name": "Wholesale",
                      "sales_channel_id": "wholesale"
                    },
                    "commitment_id": null,
                    "created_at": "2026-09-05T09:00:00+00:00",
                    "customer": null,
                    "exceptions_open": 0,
                    "fulfilled_units": 0,
                    "is_wholesale": true,
                    "lifecycle": "open",
                    "lifecycle_updated_at": "2026-09-05T09:00:00+00:00",
                    "lines": [
                      {
                        "fulfilled_quantity": 0,
                        "line_number": 1,
                        "line_total": {
                          "cents": 1760000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 17600.0
                        },
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                        "part_of_bundle": null,
                        "quantity": 400,
                        "reservations": [],
                        "returned_quantity": 0,
                        "sku": "MAR-TOP-M",
                        "unit_price": {
                          "cents": 4400,
                          "currency": "USD",
                          "reason": null,
                          "usd": 44.0
                        },
                        "variant_id": "fx-v-mar-top-m"
                      }
                    ],
                    "location": {
                      "location_id": "loc-lb",
                      "name": "Long Beach"
                    },
                    "name": "ORD-00000412",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                    "origin": "manual",
                    "ship_to": {
                      "address1": "700 Queensway Drive",
                      "city": "Long Beach",
                      "country_code": "US",
                      "name": "Coastline DS",
                      "postcode": "90802",
                      "region": "CA"
                    },
                    "units": 900,
                    "value": {
                      "cents": 3710000,
                      "currency": "USD",
                      "reason": null,
                      "usd": 37100.0
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The order, in the shape GET /sales/orders/{order_id} serves.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The order as recorded.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "variant_unknown",
                  "message": {
                    "desc": "MAR-BTM-M is not a product Tightly knows.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "A line has no product or no positive quantity (`quantity_not_positive`, `variant_unknown`); the body could not be read; no Idempotency-Key was sent (`idempotency_key_required`), or the one sent is longer than 255 characters (`idempotency_key_too_long`); no account, channel or warehouse is on file for the one named (`account_unknown`, `channel_unknown`, `location_unknown`); a line has no price on file, or its price on file is in another currency than the order's (`price_unknown`); a date is not written YYYY-MM-DD (`date_unreadable`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Orders; `organization_mismatch` when the request names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another, in a sentence naming both.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "idempotency_key_reused",
                  "message": {
                    "desc": "Idempotency-Key ord-88231 was already used for a different request; use a new key.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`idempotency_key_reused` when the key was already used for a different body; `idempotency_key_in_flight` when the first attempt on this key is still running, and the response carries `Retry-After`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "create sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/documents": {
      "get": {
        "description": "Every purchase order a retailer has sent you, whichever door it came through: a mail attachment, a followed link, an upload, a buyer's own send through their seat, or an 850 over EDI. Each row says what state the paper is in, what Tightly read off it, and which draft order it became.\n\nFilter by `state` to find the papers waiting on somebody, by `lane` to find one door's, or by `trading_partner_id` for one account's.\n\nScope: `orders:read`.",
        "operationId": "list_order_documents",
        "parameters": [
          {
            "description": "received is not read yet; applied made a draft; refused created nothing and says why.",
            "in": "query",
            "name": "state",
            "schema": {
              "enum": [
                "received",
                "parsed",
                "applied",
                "refused",
                "queued",
                "sent",
                "failed"
              ],
              "type": "string"
            }
          },
          {
            "description": "The door the paper came through: an upload, a mail attachment, a link in a mail, the buyer's own seat, or the EDI network.",
            "in": "query",
            "name": "lane",
            "schema": {
              "enum": [
                "upload",
                "email",
                "link",
                "portal",
                "sps_commerce"
              ],
              "type": "string"
            }
          },
          {
            "description": "One account's papers only.",
            "in": "query",
            "name": "trading_partner_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Papers received on or after this day.",
            "in": "query",
            "name": "since",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Rows per page.",
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Rows to skip, for the next page.",
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 2,
                    "rows": [
                      {
                        "account": {
                          "name": "Juniper & Fable",
                          "trading_partner_id": "tp-juniper-fable"
                        },
                        "document_id": 41,
                        "file_name": "order-88231.pdf",
                        "lane": "upload",
                        "needs_verification": true,
                        "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                        "order_name": "ORD-00000413",
                        "po_number": "88231",
                        "read_word": "Read from pages 1 and 2",
                        "received_at": "Sep 5, 2026",
                        "refusal": null,
                        "state": "applied",
                        "type": "pdf",
                        "units": 186,
                        "verification_words": [
                          "1 new code"
                        ],
                        "verified_at": null,
                        "verified_by": null,
                        "words": [
                          "Totals tie"
                        ]
                      }
                    ],
                    "state_numbers": {
                      "applied": 1,
                      "refused": 1
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The papers, and the counts a head ties to.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when this organisation's plan does not include Tightly Connect, which the arriving order file needs and which is sold with Essentials+; `scope_missing` when the key does not hold Orders; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another, or a paper that is not that account's.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every order file that has arrived, newest first",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      },
      "post": {
        "description": "Files one purchase order a retailer sent you, as a PDF, a CSV, a workbook or a photographed page, against the account you name. Upload the file first with the presigned upload and send its key here. Tightly reads the paper on a worker and makes one draft sales order from it, with a line per garment resolved through that account's own codes; the answer comes back immediately with the paper in `received`, and the draft appears on it when the read lands.\n\nName the account in `account_id`. Tightly never takes the account off the letterhead: where the paper names a different account you have on file, nothing is created and the paper says why.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "receive_order_document",
        "parameters": [
          {
            "description": "The account this order is for. Required; never read off the paper.",
            "in": "query",
            "name": "account_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "file_name": "order-88231.pdf",
                "mime_type": "application/pdf",
                "s3_key": "5f2c/u_88/2f2a1c94-0f61-4c1b-9a2e-6d0b5c47e0a1_order-88231.pdf"
              },
              "schema": {
                "$ref": "#/components/schemas/ReceiveOrderDocumentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account": {
                      "name": "Juniper & Fable",
                      "trading_partner_id": "tp-juniper-fable"
                    },
                    "document_id": 41,
                    "facts": {},
                    "file_name": "order-88231.pdf",
                    "header_match": {},
                    "lane": "upload",
                    "lines": [],
                    "matched": [],
                    "needs_verification": true,
                    "order_id": null,
                    "order_name": null,
                    "po_number": null,
                    "read_word": "Not read",
                    "reading": {},
                    "received_at": "Sep 5, 2026",
                    "refusal": null,
                    "source": {
                      "page_count": null,
                      "pages_read": [],
                      "type": "pdf",
                      "url": "https://uploads.example/…"
                    },
                    "state": "received",
                    "template_version_id": null,
                    "type": "pdf",
                    "units": null,
                    "unmatched_lines": 0,
                    "verification_words": [],
                    "verified_at": null,
                    "verified_by": null,
                    "words": []
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The paper, in the shape GET /sales/orders/documents/{document_id} serves.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The paper as recorded, before it has been read.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "account_unknown",
                  "message": {
                    "desc": "Name the account this order is for; Tightly never reads it off the paper.",
                    "service": "inventory",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No account was named (`account_unknown`); the file is larger than 20 MB or is a kind Tightly does not read (`file_not_readable`); the key is not one of this organisation's uploads.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when this organisation's plan does not include Tightly Connect, which the arriving order file needs and which is sold with Essentials+; `scope_missing` when the key does not hold Orders; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another, or a paper that is not that account's.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No account with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "edi_document_duplicate",
                  "message": {
                    "desc": "This file was already received on Sep 3, 2026 as Juniper & Fable's order 88231; nothing was created twice.",
                    "service": "inventory",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`edi_document_duplicate`, these bytes were already received; `file_names_another_account`, the paper names a different account you have on file.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "File one order a retailer sent, and queue its read",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/documents/{document_id}": {
      "get": {
        "description": "One purchase order as it arrived, and what Tightly read off it: the header facts, a line per garment with the codes as printed, and where on the paper each figure was read from, a page and the passage cited on it or a sheet row and column. `source.url` is a short lived link to the paper itself, so a person can stand the extracted lines beside it.\n\n`matched` says which rung answered for each printed row: the account's own code, a barcode, your SKU, a pack code or a size grid row. `unmatched_lines` is how many rows still wait on somebody; while it is above zero, confirming the draft is refused.\n\nScope: `orders:read`.",
        "operationId": "get_order_document",
        "parameters": [
          {
            "description": "The order file's id, as `List order documents` serves it.",
            "in": "path",
            "name": "document_id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account": {
                      "name": "Juniper & Fable",
                      "trading_partner_id": "tp-juniper-fable"
                    },
                    "document_id": 41,
                    "facts": {
                      "po_number": {
                        "read_word": "Read from page 1",
                        "span": {
                          "cited_text": "Order No. 88231",
                          "kind": "page",
                          "page": 1
                        },
                        "value": "88231"
                      }
                    },
                    "file_name": "order-88231.pdf",
                    "header_match": {
                      "currency": "USD",
                      "currency_differs": false,
                      "door_word": null,
                      "payment_terms": "Net 30",
                      "trading_partner_warehouse_id": 7
                    },
                    "lane": "upload",
                    "lines": [
                      {
                        "buyer_sku": "JF-4471-BLK-M",
                        "document_line": 1,
                        "line_number": 1,
                        "quantity": 12,
                        "read_word": "Read from page 1",
                        "size": "M",
                        "span": {
                          "cited_text": "JF-4471-BLK M 12",
                          "kind": "page",
                          "page": 1
                        },
                        "unit_price": 180.0,
                        "words": []
                      }
                    ],
                    "matched": [
                      {
                        "document_line": 1,
                        "draft_lines": [
                          {
                            "prepack_id": null,
                            "quantity": 12,
                            "size": "M",
                            "unit_price": 180.0,
                            "variant_id": "v-moor-blk-m",
                            "word": "Matched by Juniper & Fable's code"
                          }
                        ],
                        "learn": [],
                        "open_cells": [],
                        "printed_codes": [
                          "JF-4471-BLK-M"
                        ],
                        "rung": "account_code",
                        "word": "Matched by Juniper & Fable's code"
                      }
                    ],
                    "needs_verification": true,
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                    "order_name": "ORD-00000413",
                    "po_number": "88231",
                    "read_word": "Read from pages 1 and 2",
                    "received_at": "Sep 5, 2026",
                    "refusal": null,
                    "source": {
                      "page_count": 2,
                      "pages_read": [
                        1,
                        2
                      ],
                      "type": "pdf",
                      "url": "https://uploads.example/…"
                    },
                    "state": "applied",
                    "template_version_id": null,
                    "type": "pdf",
                    "units": 186,
                    "unmatched_lines": 1,
                    "verification_words": [
                      "1 new code"
                    ],
                    "verified_at": null,
                    "verified_by": null,
                    "words": [
                      "Totals tie"
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The paper, whole. Alongside the fields below the answer carries `reading`, the reader's own stored frame, which `facts`, `lines`, `matched` and `header_match` are each drawn from and which also holds the totals it tied and the pages it read. The example leaves it out for length rather than because it is absent; where you want one field, read the four above it, and where you want the reading as the reader wrote it, read `reading`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when this organisation's plan does not include Tightly Connect, which the arriving order file needs and which is sold with Essentials+; `scope_missing` when the key does not hold Orders; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another, or a paper that is not that account's.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "document_not_on_file",
                  "message": {
                    "desc": "No order file with that id is on file.",
                    "service": "inventory",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No order file with that id is on file (`document_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One order file, its reading and the source to check it against",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      }
    },
    "/api/v1/sales/orders/documents/{document_id}/take": {
      "post": {
        "description": "Takes a paper Tightly refused and makes it the order instead. Use it when a retailer sends the same order number again with different lines: the first draft still stands, and this cancels it with the reason and reads the newer paper in its place, in one call.\n\nRe-sending the file cannot do this: those bytes are already on the refused paper, so Tightly would answer that it already has them and create nothing.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "take_order_document",
        "parameters": [
          {
            "description": "The order file's id, as `List order documents` serves it.",
            "in": "path",
            "name": "document_id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "document_id": 42,
                    "lane": "upload",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BE",
                    "order_name": "ORD-00000414",
                    "po_number": "88231",
                    "read_word": "Read from page 1",
                    "state": "applied",
                    "type": "pdf",
                    "units": 198,
                    "unmatched_lines": 0
                  },
                  "message": {
                    "desc": "",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The paper, read again, with the draft it now carries, in the same whole shape `GET /sales/orders/documents/{document_id}` serves: the reading, the matcher's answer per document line, the source to check it against, and the account. The example shows the fields the take moves rather than the whole answer.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when this organisation's plan does not include Tightly Connect, which the arriving order file needs and which is sold with Essentials+; `scope_missing` when the key does not hold Orders; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another, or a paper that is not that account's.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order file with that id is on file (`document_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "document_still_reading",
                  "message": {
                    "desc": "Order 88231 is still being read; open it in a moment.",
                    "service": "inventory",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`document_still_reading`, the paper has not been read yet; `document_not_refused`, it was not refused, so there is nothing to take; `document_replaces_nothing`, it was refused for something other than a standing draft; `standing_order_confirmed`, the order it would replace is no longer a draft; `file_names_another_account`, the paper's letterhead names another account on file.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Take a refused order file as the new draft",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/import": {
      "post": {
        "description": "Turns a file already uploaded and column-mapped into owned orders: one document per order number and channel, one line per SKU. Re-importing the same file updates the same orders rather than making a second set, so a corrected file is sent again rather than cleaned up by hand. Rows that cannot be written come back in `failed_rows` with the reason in words, and nothing in the file is written twice.\n\nThis is the door for a file of orders you owe. A backfill of sales HISTORY is a different entity and a different door: it writes lines, not documents, and tells Tightly nothing about what is still to ship.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "import_sales_orders",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "mappings": {
                  "account": "Customer",
                  "channel": "Channel",
                  "order_number": "PO Number",
                  "quantity": "Qty",
                  "requested_ship_date": "Ship date",
                  "sku": "Style",
                  "unit_price": "Price"
                },
                "s3_key": "64f/9a1/orders-aw26.csv"
              },
              "schema": {
                "$ref": "#/components/schemas/ImportSalesOrdersRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "counts": {
                      "line": 148,
                      "order": 12
                    },
                    "failed_rows": [
                      {
                        "failure_reason": "sku_not_resolved",
                        "failure_reason_words": "No product with that SKU is on file.",
                        "order_number": "PO-8841",
                        "quantity": "4",
                        "sku": "MAR-BTM-XS"
                      }
                    ],
                    "upload_id": "64f/9a1/orders-aw26.csv"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "upload_id, counts and failed_rows.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What the import wrote, and what it refused.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The body names no file, or maps none of the order number, the SKU and the quantity.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account, which a file of orders cannot be narrowed to (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "import sales orders",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/{order_id}": {
      "get": {
        "description": "One order and everything hanging off it: its lines with what has been fulfilled and returned, what each line holds and where, the fulfilment requests sent to a warehouse, the shipments, returns and invoices against it, its open exceptions, and the booking it was born from. `ship_to` is served here and on no list or export, because an address is the document's and not the page's.\n\nScope: `orders:read`.",
        "operationId": "get_sales_order",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "book": null,
                    "edi": null,
                    "exceptions": [],
                    "exceptions_open": 0,
                    "financial": {
                      "subtotal": {
                        "cents": 3710000,
                        "currency": "USD",
                        "reason": null,
                        "usd": 37100.0
                      }
                    },
                    "fulfilled_units": 0,
                    "fulfilment_requests": [],
                    "invoices": [],
                    "lifecycle": "allocated",
                    "lines": [],
                    "name": "ORD-00000412",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                    "origin": "manual",
                    "returns": [],
                    "ship_to": {
                      "city": "Long Beach",
                      "country_code": "US",
                      "name": "Coastline DS"
                    },
                    "shipments": [],
                    "units": 900,
                    "withheld_words": null
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The order, its lines and the documents against it.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The order.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and the record is not that account's (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "order_not_on_file",
                  "message": {
                    "desc": "No order with that id is on file.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:read"
        ]
      },
      "patch": {
        "description": "Changes what a person may change on an order Tightly owns: its ship date, its cancel date, your own reference, the warehouse it will ship from, and its lines. `lines` names only the lines you are changing, each by its `order_line_item_id` (as the read serves it) or by `variant_id` or `sku`. A `quantity` of 0 removes one, a `variant_id` the order does not carry adds one, and a line you do not name is left alone, so two people editing one order do not delete each other's work.\n\nAllowed in `draft`, `open` and `allocated`, and it re-allocates. An order that came from a connected channel is never changed here; change it in the channel. An order a warehouse already holds is refused with the day it was sent, because the pick list is already there.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "update_sales_order",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "lines": [
                  {
                    "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                    "quantity": 360
                  }
                ],
                "requested_ship_date": "2026-09-29"
              },
              "schema": {
                "$ref": "#/components/schemas/PatchSalesOrderRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "lifecycle": "allocated",
                    "name": "ORD-00000412",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                    "units": 860
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The order as it now stands.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "line_not_on_order",
                  "message": {
                    "desc": "MAR-BTM-M is not on ORD-00000412.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "A line names a product the order does not carry (`line_not_on_order`), a product Tightly does not know (`variant_unknown`), or a quantity below zero (`quantity_not_positive`); the warehouse named is not on file (`location_unknown`); a date is not written YYYY-MM-DD (`date_unreadable`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and the record is not that account's (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "order_locked_for_fulfilment",
                  "message": {
                    "desc": "ORD-00000412 was sent to Long Beach on Sep 5, 2026; nothing on it can change until Long Beach answers.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`channel_order_is_the_channels`, `order_locked_for_fulfilment`, `order_already_fulfilled`, `order_already_cancelled`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "update sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/{order_id}/allocate": {
      "post": {
        "description": "Holds stock for one open order. Turning reservations on does not reach back over the orders that were open before it, so this is the act that allocates one of them. An order already allocated, sent, fulfilled or cancelled is refused with the state it is in; an organisation that does not hold stock for orders is refused with where to turn it on; and an order whose channel has no warehouse linked is refused with what to link.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "allocate_sales_order",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "lifecycle": "allocated",
                    "lines": [
                      {
                        "is_backordered": false,
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                        "quantity": 400,
                        "reservations": [
                          {
                            "location_id": "loc-lb",
                            "location_name": "Long Beach",
                            "quantity": 400,
                            "shortfall": 0,
                            "state": "held"
                          }
                        ],
                        "sku": "MAR-TOP-M"
                      }
                    ],
                    "location": {
                      "location_id": "loc-lb",
                      "name": "Long Beach"
                    },
                    "name": "ORD-00000412",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The order, allocated.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and the record is not that account's (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "order_not_open",
                  "message": {
                    "desc": "ORD-00000412 is allocated; only an open order can be allocated.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`order_not_open`, `order_reservations_off`, `no_serving_location`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "allocate sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/{order_id}/cancel": {
      "post": {
        "description": "Cancels an order Tightly owns and releases every unit it was holding, so the stock is sellable again in the same request. A reason is optional and is kept on the document.\n\nAllowed from `draft`, `open` and `allocated`, and from `fulfilment_requested` only while the warehouse request is still queued or has failed, or once the warehouse confirms the cancel.\n\nAn order that came from a connected channel is cancelled in the channel, not here; an order a warehouse already holds is refused until the warehouse answers.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "cancel_sales_order",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "reason": "The account moved the season"
              },
              "schema": {
                "$ref": "#/components/schemas/CancelSalesOrderRequest"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "cancel_reason": "The account moved the season",
                    "lifecycle": "cancelled",
                    "name": "ORD-00000412",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The order, cancelled.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or is issued to one account and the record is not that account's (`account_mismatch`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "order_already_cancelled",
                  "message": {
                    "desc": "ORD-00000412 was cancelled on Sep 5, 2026.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`channel_order_is_the_channels`, `order_locked_for_fulfilment`, `order_already_fulfilled`, `order_already_cancelled`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "cancel sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/{order_id}/confirm": {
      "post": {
        "description": "Records what you are agreeing to on every line of a `draft` order and opens it, or moves it straight to `allocated` where the organisation holds stock for orders. Each line is accepted as asked, accepted at a different quantity, or rejected; every line must carry one, so nothing is agreed to on your behalf. On an order that arrived over EDI the acknowledgement (855) goes back to the account where they take one, and where they do not the decision is recorded and nothing is sent. A line the account named with a code that is not on their product list has to be matched or rejected first; until it is, the confirm is refused with how many lines are waiting.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "confirm_sales_order",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "lines": [
                  {
                    "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                    "quantity": 8000,
                    "reason": "the run covers 8,000",
                    "verdict": "quantity_changed"
                  },
                  {
                    "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:2",
                    "verdict": "accepted"
                  },
                  {
                    "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:3",
                    "verdict": "accepted"
                  },
                  {
                    "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:4",
                    "verdict": "rejected"
                  }
                ]
              },
              "schema": {
                "properties": {
                  "lines": {
                    "items": {
                      "properties": {
                        "order_line_item_id": {
                          "type": "string"
                        },
                        "quantity": {
                          "description": "Required with `quantity_changed`; ignored otherwise.",
                          "minimum": 1,
                          "type": "integer"
                        },
                        "reason": {
                          "description": "Your own words, carried onto the book line and the acknowledgement.",
                          "type": "string"
                        },
                        "verdict": {
                          "enum": [
                            "accepted",
                            "quantity_changed",
                            "rejected"
                          ],
                          "type": "string"
                        }
                      },
                      "required": [
                        "order_line_item_id",
                        "verdict"
                      ],
                      "type": "object"
                    },
                    "type": "array"
                  }
                },
                "required": [
                  "lines"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "book": {
                      "commitment_id": "cmt_ss27",
                      "line_key": null,
                      "season": "SS27",
                      "version": 1
                    },
                    "edi": {
                      "acknowledgement": {
                        "document_id": 42,
                        "sent_at": "2026-09-05T17:30:00+00:00",
                        "sentence": null,
                        "state": "sent"
                      },
                      "document_id": 41,
                      "po_number": "CDS-90114",
                      "state": "applied"
                    },
                    "lifecycle": "open",
                    "lines": [
                      {
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                        "quantity": 8000,
                        "requested_quantity": 8500,
                        "sku": "MAR-TOP-M",
                        "verdict": "quantity_changed"
                      },
                      {
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:4",
                        "quantity": 0,
                        "requested_quantity": 100,
                        "sku": "MAR-BTM-L",
                        "verdict": "rejected"
                      }
                    ],
                    "name": "ORD-00000412",
                    "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                    "origin": "edi",
                    "units": 9400,
                    "value": {
                      "cents": 40910000,
                      "currency": "USD",
                      "reason": null,
                      "usd": 409100.0
                    }
                  },
                  "message": {
                    "desc": "An acknowledgement was sent to Coastline Department Stores.",
                    "service": "sales",
                    "severity": "SUCCESS"
                  }
                }
              }
            },
            "description": "The order, open, with every verdict on its lines.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "verdict_missing_on_a_line",
                  "message": {
                    "desc": "Line 2 of ORD-00000412 has no verdict; accept it, change its quantity or reject it before confirming.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "A line has no verdict (`verdict_missing_on_a_line`); a changed quantity is not a whole number above zero (`quantity_not_positive`); the body names a line the order does not have (`line_not_on_order`); the body could not be read.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or the organisation's plan does not include wholesale and the draft arrived over EDI, or the key is issued to one account and the order is another's (`account_mismatch`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "draft_has_unmatched_lines",
                  "message": {
                    "desc": "ORD-00000412 still has 1 line with no match on Coastline Department Stores's product list; match or reject it before confirming.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`order_not_a_draft`, `draft_has_unmatched_lines`, `order_already_cancelled`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "confirm sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/{order_id}/invoice": {
      "post": {
        "description": "Raises the account's invoice for the quantities that were actually fulfilled, at the price the order was agreed at, and sends it as an 810 where the account takes one. It is also posted to Xero or QuickBooks as a receivable where a ledger is connected. Tax and freight are figures you send; Tightly never calculates either.\n\nThree states are served apart: the document's own, what the books did (`ledger.state`, null where no ledger is connected) and what the network did with the 810 (`edi.state`, null where the account takes none). Neither the ledger nor the network can fail the invoice.\n\nCall it after the goods have shipped. Do not call it to invoice what was ordered rather than what shipped, and do not call it twice: one live invoice stands per order, and the second call names the one that stands.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "invoice_sales_order",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "freight_cents": 0,
                "tax_cents": 0,
                "terms": "net_30"
              },
              "schema": {
                "properties": {
                  "freight_cents": {
                    "description": "Freight as invoiced, in whole cents.",
                    "minimum": 0,
                    "type": "integer"
                  },
                  "tax_cents": {
                    "description": "Tax as invoiced, in whole cents. Tightly calculates none.",
                    "minimum": 0,
                    "type": "integer"
                  },
                  "terms": {
                    "description": "The payment terms this invoice carries. Omit it where the account's terms are not on file; the ledger then applies its own standing terms for the contact.",
                    "enum": [
                      "net_30",
                      "net_60",
                      "net_90",
                      "due_on_receipt"
                    ],
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account": {
                      "name": "Tidewater Surf Co.",
                      "trading_partner_id": "fx-tp-tidewater"
                    },
                    "currency": "USD",
                    "edi": {
                      "document_id": 45,
                      "sent_at": "2026-09-06T17:30:01+00:00",
                      "sentence": null,
                      "state": "sent"
                    },
                    "freight": {
                      "cents": 0,
                      "currency": "USD",
                      "reason": null,
                      "usd": 0.0
                    },
                    "invoice_id": 4,
                    "issued_at": "2026-09-06T17:30:00+00:00",
                    "ledger": {
                      "external_id": "8d1f0f1c-0f2a-4f3e-9a11-6c2f5f0e7a10",
                      "external_url": "https://go.xero.com/AccountsReceivable/Edit.aspx?InvoiceID=8d1f0f1c-0f2a-4f3e-9a11-6c2f5f0e7a10",
                      "ledger": "xero",
                      "posted_at": "2026-09-06T17:30:02+00:00",
                      "sentence": null,
                      "state": "posted"
                    },
                    "lines": [
                      {
                        "amount": {
                          "cents": 468000,
                          "currency": "USD",
                          "reason": null,
                          "usd": 4680.0
                        },
                        "buyer_sku": "4472-S",
                        "line_number": 1,
                        "order_line_item_id": "fx-ord-tide-5581:1",
                        "quantity": 120,
                        "sku": "MAR-BTM-S",
                        "unit_price": {
                          "cents": 3900,
                          "currency": "USD",
                          "reason": null,
                          "usd": 39.0
                        },
                        "variant_id": "fx-var-btm-s"
                      }
                    ],
                    "name": "INV-00000004",
                    "order": {
                      "name": "TIDE-5581",
                      "order_id": "fx-ord-tide-5581"
                    },
                    "shipment": {
                      "name": "SHP-00000007",
                      "shipment_id": 7
                    },
                    "state": "issued",
                    "subtotal": {
                      "cents": 780000,
                      "currency": "USD",
                      "reason": null,
                      "usd": 7800.0
                    },
                    "tax": {
                      "cents": 0,
                      "currency": "USD",
                      "reason": null,
                      "usd": 0.0
                    },
                    "terms": "net_30",
                    "total": {
                      "cents": 780000,
                      "currency": "USD",
                      "reason": null,
                      "usd": 7800.0
                    },
                    "units": 200
                  },
                  "message": {
                    "desc": "INV-00000004 was invoiced to Tidewater Surf Co. and sent.",
                    "service": "sales",
                    "severity": "SUCCESS"
                  }
                }
              }
            },
            "description": "The invoice, as `GET /api/v1/sales/invoices/{invoice_id}` serves it.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "terms_not_known",
                  "message": {
                    "desc": "terms takes net_30, net_60, net_90, due_on_receipt; net_45 is not one of them.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`terms_not_known`, `tax_cents_not_whole_cents`, `freight_cents_not_whole_cents`, `tax_cents_negative`, `freight_cents_negative`, or a body that could not be read.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or the organisation's plan does not include wholesale, or the key is issued to one account and the order is another's (`account_mismatch`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "invoice_already_issued",
                  "message": {
                    "desc": "TIDE-5581 was invoiced on Sep 6, 2026 as INV-00000004; void it before issuing another.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`order_is_not_an_accounts`, `nothing_fulfilled_to_invoice`, `invoice_already_issued`, `line_without_a_price` (a fulfilled line with no price on it and no sell-in price on the account; nothing is invoiced at $0.00).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "invoice a sales order",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/orders/{order_id}/shipping-notice": {
      "post": {
        "description": "Sends the account an 856 shipping notice built from what the warehouse actually shipped: the cartons, the barcode on each, the carrier and the tracking number. Call it once the warehouse has confirmed the shipment. It is refused where the warehouse reported no cartons, because a notice with a guessed pack structure is what a retailer receives against and charges you for; type the carton list against the shipment first.\n\nThe notice's state under `shipments[].shipping_notice` moves `queued` to `sent`, or to `failed` with the network's own sentence on it, and a retry re-sends that document rather than creating a second one.\n\nDo not call it for accounts that do not take shipping notices over EDI, and do not call it twice for one shipment.\n\nScope: `orders:write`, which includes `orders:read`.",
        "operationId": "send_shipping_notice",
        "parameters": [
          {
            "description": "The order's id.",
            "in": "path",
            "name": "order_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "fulfilled_units": 200,
                    "is_wholesale": true,
                    "lifecycle": "fulfilled",
                    "name": "TIDE-5581",
                    "order_id": "fx-ord-tide-5581",
                    "origin": "manual",
                    "shipments": [
                      {
                        "carrier": "UPSN",
                        "carton_count": 2,
                        "cartons": [
                          {
                            "carton_number": 1,
                            "sscc": "00000123456789012345",
                            "units": 120
                          },
                          {
                            "carton_number": 2,
                            "sscc": "00000123456789012352",
                            "units": 80
                          }
                        ],
                        "name": "SHP-00000007",
                        "shipment_id": 7,
                        "shipping_notice": {
                          "document_id": 44,
                          "sent_at": "2026-09-06T17:30:00+00:00",
                          "sentence": null,
                          "state": "sent"
                        },
                        "tracking_number": "1Z999AA10123456784",
                        "units": 200
                      }
                    ],
                    "units": 200
                  },
                  "message": {
                    "desc": "A shipping notice was sent to Tidewater Surf Co.",
                    "service": "sales",
                    "severity": "SUCCESS"
                  }
                }
              }
            },
            "description": "The order, with the notice's state on its shipment.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Orders.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Orders (`scope_missing`), or the organisation's plan does not include wholesale, or the key is issued to one account and the order is another's (`account_mismatch`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "shipment_without_cartons",
                  "message": {
                    "desc": "Long Beach reported no cartons for TIDE-5581, so no shipping notice was sent; type the carton list against the shipment or ask Long Beach for it.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`order_is_not_an_accounts` (the order belongs to no account), `partner_takes_no_856` (the account does not take one, or nobody has asked), `nothing_shipped_to_notify`, `shipment_without_cartons`, `cartons_exceed_what_shipped`, `partner_not_connected`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "send shipping notice",
        "tags": [
          "orders"
        ],
        "x-tightly-scopes": [
          "orders:write"
        ]
      }
    },
    "/api/v1/sales/performance/channels": {
      "get": {
        "description": "Revenue per sales channel over a window: each channel with its total revenue, the difference against the previous period of the same length, the change as a percentage, and a dated series at the requested granularity.\n\n`start_date` and `end_date` are required and the window may not exceed one year. `sales_channel_ids` narrows the channels, and `granularity` sets the series' step.\n\nScope: `sales:read`.",
        "operationId": "get_sales_channel_performance",
        "parameters": [
          {
            "description": "Start date for the current period (ISO date format YYYY-MM-DD)",
            "in": "query",
            "name": "start_date",
            "required": true,
            "schema": {
              "examples": [
                "2025-11-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End date for the current period (ISO date format YYYY-MM-DD). Maximum range is 1 year from start_date.",
            "in": "query",
            "name": "end_date",
            "required": true,
            "schema": {
              "examples": [
                "2025-11-08"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of sales channel IDs to filter. If not provided, returns all channels.",
            "example": "ch_123,ch_456,ch_789",
            "in": "query",
            "name": "sales_channel_ids",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Time granularity for the data points (day, week, or month)",
            "in": "query",
            "name": "granularity",
            "required": false,
            "schema": {
              "default": "day",
              "enum": [
                "day",
                "week",
                "month"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "channels": [
                      {
                        "change_percentage": 12.5,
                        "channel_name": "Amazon",
                        "data": [
                          {
                            "date": "2025-11-01",
                            "revenue": 45000.0
                          },
                          {
                            "date": "2025-11-02",
                            "revenue": 52000.0
                          }
                        ],
                        "period_difference": 34000.25,
                        "sales_channel_id": "ch_123",
                        "total_revenue": 453000.5
                      },
                      {
                        "change_percentage": 8.3,
                        "channel_name": "Shopify",
                        "data": [
                          {
                            "date": "2025-11-01",
                            "revenue": 32000.0
                          },
                          {
                            "date": "2025-11-02",
                            "revenue": 35000.0
                          }
                        ],
                        "period_difference": 54000.0,
                        "sales_channel_id": "ch_456",
                        "total_revenue": 324000.0
                      }
                    ]
                  },
                  "message": {
                    "desc": "Success",
                    "service": "sales",
                    "severity": "info"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "channels": {
                          "items": {
                            "properties": {
                              "change_percentage": {
                                "description": "Percentage change compared to previous period",
                                "examples": [
                                  12.5
                                ],
                                "format": "float",
                                "type": "number"
                              },
                              "channel_name": {
                                "description": "Display name of the sales channel",
                                "examples": [
                                  "Amazon"
                                ],
                                "type": "string"
                              },
                              "data": {
                                "description": "Time-series data points for the channel",
                                "items": {
                                  "properties": {
                                    "date": {
                                      "description": "Date of the data point",
                                      "examples": [
                                        "2025-11-01"
                                      ],
                                      "format": "date",
                                      "type": "string"
                                    },
                                    "revenue": {
                                      "description": "Revenue for this date",
                                      "examples": [
                                        45000.0
                                      ],
                                      "format": "float",
                                      "type": "number"
                                    }
                                  },
                                  "type": "object"
                                },
                                "type": "array"
                              },
                              "period_difference": {
                                "description": "Revenue difference compared to previous period",
                                "examples": [
                                  34000.25
                                ],
                                "format": "float",
                                "type": "number"
                              },
                              "sales_channel_id": {
                                "description": "Unique identifier for the sales channel",
                                "examples": [
                                  "ch_123"
                                ],
                                "type": "string"
                              },
                              "total_revenue": {
                                "description": "Total revenue for the current period",
                                "examples": [
                                  453000.5
                                ],
                                "format": "float",
                                "type": "number"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Success"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "examples": [
                            "sales"
                          ],
                          "type": "string"
                        },
                        "severity": {
                          "examples": [
                            "info"
                          ],
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Performance by sales channel",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "examples": [
                            "Date range cannot exceed 1 year (365 days)"
                          ],
                          "type": "string"
                        },
                        "service": {
                          "examples": [
                            "sales"
                          ],
                          "type": "string"
                        },
                        "severity": {
                          "examples": [
                            "error"
                          ],
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid parameters (e.g., date range exceeds 1 year, invalid date format)",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Error description",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales channel performance",
        "tags": [
          "sales"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/returns": {
      "get": {
        "description": "Every return, newest first, with what is expected back and what has arrived. `state_numbers` counts the WHOLE filtered book per state, not the page you were served, so a heading built on it ties to the rows underneath. Filter by state, origin, order or creation date.\n\nScope: `returns:read`.",
        "operationId": "list_returns",
        "parameters": [
          {
            "description": "Returns in this state only.",
            "in": "query",
            "name": "state",
            "schema": {
              "enum": [
                "expected",
                "received",
                "closed",
                "exception",
                "cancelled"
              ],
              "type": "string"
            }
          },
          {
            "description": "Returns that came in by this door only.",
            "in": "query",
            "name": "origin",
            "schema": {
              "enum": [
                "channel",
                "manual",
                "api",
                "csv",
                "portal"
              ],
              "type": "string"
            }
          },
          {
            "description": "Returns against this order only.",
            "in": "query",
            "name": "order_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Returns created on or after it.",
            "in": "query",
            "name": "since",
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Rows to skip before the page starts.",
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Rows on the page.",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 8,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 3,
                    "rows": [
                      {
                        "created_at": "2026-09-05T09:00:00+00:00",
                        "expected_on": "2026-09-12",
                        "id": 33,
                        "location": {
                          "location_id": "loc-lb",
                          "name": "Long Beach"
                        },
                        "name": "RET-00000033",
                        "order": {
                          "name": "ORD-00000412",
                          "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                        },
                        "origin": "manual",
                        "received_at": null,
                        "refund": {
                          "cents": null,
                          "currency": null,
                          "reason": "No refund is recorded on this return",
                          "usd": null
                        },
                        "refund_status": "none",
                        "state": "expected",
                        "units_expected": 1,
                        "units_received": 0
                      }
                    ],
                    "state_numbers": {
                      "closed": 2,
                      "expected": 1
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "filtered_max_size": {
                          "type": "integer"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "state_numbers": {
                          "additionalProperties": {
                            "type": "integer"
                          },
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The page, and the counts a heading has to tie to.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A filter names a value that is not one of its own (`state`, `origin`), or a date that could not be read.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Returns (`scope_missing`), names another organisation, or is outside its allowlist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list returns",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:read"
        ]
      },
      "post": {
        "description": "Records one return: the order the goods come back from, the products and how many of each, where they are expected and why. The return is EXPECTED until a warehouse receives it, and an expected return moves no stock: it counts as inbound on the position read and nothing else. Send an `Idempotency-Key` header; the same key with the same body answers the same return, the same key with a different body is refused, and Tightly keeps a key 30 days.\n\nReturns that come from a connected channel are never created here: Tightly reads the channel's refund on the sync and builds the return from it. The refund on a return is the channel's fact too, so a refund sent for an order the channel owns is refused.\n\nScope: `returns:write`, which includes `returns:read`.",
        "operationId": "create_return",
        "parameters": [
          {
            "description": "A key you choose, up to 255 characters; Tightly keeps it 30 days.",
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "maxLength": 255,
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "expected_on": "2026-09-12",
                "lines": [
                  {
                    "expected_quantity": 1,
                    "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1"
                  }
                ],
                "location_id": "loc-lb",
                "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD",
                "origin": "manual",
                "reason": "wrong_size",
                "refund": {
                  "status": "none"
                }
              },
              "schema": {
                "properties": {
                  "exchange_order_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "expected_on": {
                    "description": "YYYY-MM-DD.",
                    "format": "date",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lines": {
                    "items": {
                      "properties": {
                        "expected_quantity": {
                          "minimum": 1,
                          "type": "integer"
                        },
                        "order_line_item_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "sku": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "variant_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "expected_quantity"
                      ],
                      "type": "object"
                    },
                    "minItems": 1,
                    "type": "array"
                  },
                  "location_id": {
                    "description": "Where it is expected back. Absent, Tightly reads the order's own warehouse, then your single active one; an organisation with several and nothing said is refused.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "order_id": {
                    "description": "The order the goods come back from.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "origin": {
                    "default": "manual",
                    "description": "Which door this return came in by. `channel` is not among them.",
                    "enum": [
                      "manual",
                      "api",
                      "csv",
                      "portal"
                    ],
                    "type": "string"
                  },
                  "reason": {
                    "enum": [
                      "wrong_size",
                      "damaged",
                      "not_as_described",
                      "changed_mind",
                      "late",
                      "other"
                    ],
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "reason_note": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "refund": {
                    "description": "What the money did. A fact you record, never one Tightly issues.",
                    "properties": {
                      "amount_cents": {
                        "type": [
                          "integer",
                          "null"
                        ]
                      },
                      "currency": {
                        "maxLength": 3,
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "refunded_at": {
                        "format": "date-time",
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "status": {
                        "default": "none",
                        "enum": [
                          "none",
                          "partial",
                          "full"
                        ],
                        "type": "string"
                      },
                      "timing": {
                        "enum": [
                          "before_receipt",
                          "after_receipt"
                        ],
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    },
                    "type": [
                      "object",
                      "null"
                    ]
                  }
                },
                "required": [
                  "lines"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "closed_at": null,
                    "exceptions": [],
                    "expected_on": "2026-09-12",
                    "id": 33,
                    "ledger_written_at": null,
                    "lines": [
                      {
                        "disposition": null,
                        "expected_quantity": 1,
                        "id": 71,
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                        "received_quantity": 0,
                        "refund_line_item_id": null,
                        "restocked_quantity": 0,
                        "sku": "MAR-TOP-S",
                        "variant_id": "fx-v-mar-top-s"
                      }
                    ],
                    "location": {
                      "location_id": "loc-lb",
                      "name": "Long Beach"
                    },
                    "movements": [],
                    "name": "RET-00000033",
                    "order": {
                      "name": "ORD-00000412",
                      "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                    },
                    "origin": "manual",
                    "reason": "wrong_size",
                    "received_at": null,
                    "refund": {
                      "cents": null,
                      "currency": null,
                      "reason": "No refund is recorded on this return",
                      "usd": null
                    },
                    "refund_status": "none",
                    "source_system": "tightly",
                    "state": "expected",
                    "units_expected": 1,
                    "units_received": 0,
                    "units_restocked": 0
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The return, in the shape GET /sales/returns/{return_id} serves.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The return as recorded, expected back.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "return_needs_an_order_or_a_sku",
                  "message": {
                    "desc": "A return names the order it comes back from, or at least one product; this one names neither.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "A line names no product or no positive quantity (`quantity_not_positive`, `variant_unknown`); a line names a product the order does not carry (`variant_not_on_order`); the return names neither an order nor a product (`return_needs_an_order_or_a_sku`); a partial or full refund carries no amount (`refund_needs_an_amount`); a date is not written YYYY-MM-DD (`date_unreadable`); no Idempotency-Key was sent (`idempotency_key_required`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing`: the key does not hold Returns; `organization_mismatch`: the request names an organisation that is not the key's; `ip_not_allowed`: the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No order with that id is on file (`order_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "return_more_than_sold",
                  "message": {
                    "desc": "ORD-00000412 shipped 2 of MAR-TOP-S and 1 came back; a return of 2 is more than is out there. Record what arrived as an exception if the warehouse counted it.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`return_more_than_sold`: more is coming back than the order still has out there; `return_before_shipment`: nothing on the order has shipped; `refund_is_the_channels`: the refund on this order is recorded in the channel; `return_needs_a_place`: you keep more than one warehouse and nobody named which; `idempotency_key_reused`: the key was already used for a different body.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "create return",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:write"
        ]
      }
    },
    "/api/v1/sales/returns/import": {
      "post": {
        "description": "Turns a file you have already mapped into returns expected back: one document per order number, one line per SKU. Idempotent per order number, so a corrected file updates the same returns rather than minting a second set. Upload the file first and confirm its column mapping, then send the key and the mapping here.\n\n`mappings` maps your column headings onto `order_number`, `sku` and `quantity`, which are required, and onto `reason`, `location` and `expected_on`, which are not.\n\nA file is a brand saying what is on its way back, not a receipt. The units are EXPECTED until a warehouse records what arrived, through `POST /sales/returns/{return_id}/receive`.\n\nScope: `returns:write`, which includes `returns:read`.",
        "operationId": "import_returns",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "mappings": {
                  "Order": "order_number",
                  "Qty": "quantity",
                  "Reason": "reason",
                  "Style": "sku"
                },
                "s3_key": "uploads/org-1/returns-2026-09.csv"
              },
              "schema": {
                "properties": {
                  "mappings": {
                    "additionalProperties": {
                      "type": "string"
                    },
                    "type": "object"
                  },
                  "s3_key": {
                    "description": "The key the upload answered with.",
                    "type": "string"
                  }
                },
                "required": [
                  "s3_key",
                  "mappings"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "counts": {
                      "line": 19,
                      "return": 12
                    },
                    "failed_rows": [
                      {
                        "reason": "sku_not_resolved",
                        "row": 7,
                        "words": "No product with that SKU is on file."
                      }
                    ],
                    "upload_id": "uploads/org-1/returns-2026-09.csv"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "counts": {
                          "additionalProperties": {
                            "type": "integer"
                          },
                          "type": "object"
                        },
                        "failed_rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "upload_id": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What the file did, and every row it could not.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No `s3_key`, or the order number, SKU and quantity columns are not all mapped. Row-level refusals are not 400s: they come back in `failed_rows` as `missing_required_fields`, `invalid_quantity`, `order_not_found`, `sku_not_resolved`, `variant_not_on_order`, `return_more_than_sold`, `return_already_received` (the file names a return the warehouse has already received; a file updates a promise, never a receipt), `location_unknown` or `date_unreadable`.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Returns (`scope_missing`), names another organisation, or is outside its allowlist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "return_before_shipment",
                  "message": {
                    "desc": "Nothing on ORD-00000412 has shipped, so nothing can come back from it yet; cancel or change the order instead.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "A refusal the whole file cannot get past, in one of §4.3's sentences with its code.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "import returns",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:write"
        ]
      }
    },
    "/api/v1/sales/returns/summary": {
      "get": {
        "description": "The return rate over the last N days, and both figures it is made of, so nothing you build has to compute it. `rate_points` is `returned_units / fulfilled_units` over the same window, IN POINTS: 2.4 means 2.4%. Where nothing shipped in that window `rate_points` is null with a reason beside it, because a return rate over no shipments is not 0%.\n\nScope: `returns:read`.",
        "operationId": "get_returns_summary",
        "parameters": [
          {
            "description": "How many days back the rate is struck over, ending today.",
            "in": "query",
            "name": "days",
            "schema": {
              "default": 30,
              "maximum": 365,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "as_of": "2026-09-05T12:00:00+00:00",
                    "days": 30,
                    "fulfilled_units": 420,
                    "rate_points": 0.7143,
                    "rate_reason": null,
                    "returned_units": 3,
                    "returns_opened": 3,
                    "since": "2026-08-06T12:00:00+00:00"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "as_of": {
                          "description": "When the two counts were read.",
                          "format": "date-time",
                          "type": "string"
                        },
                        "days": {
                          "type": "integer"
                        },
                        "fulfilled_units": {
                          "type": "integer"
                        },
                        "rate_points": {
                          "description": "Points, not a fraction: 2.4 is 2.4%.",
                          "type": [
                            "number",
                            "null"
                          ]
                        },
                        "rate_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "returned_units": {
                          "type": "integer"
                        },
                        "returns_opened": {
                          "type": "integer"
                        },
                        "since": {
                          "description": "The window's start.",
                          "format": "date-time",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The two figures and the rate between them.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "`days` is outside 1 to 365.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Returns (`scope_missing`), names another organisation, or is outside its allowlist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get returns summary",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:read"
        ]
      }
    },
    "/api/v1/sales/returns/{return_id}": {
      "get": {
        "description": "One return with everything on it: its lines, the ledger rows it wrote, and anything about it a person still has to decide. Call it after a receipt only if you did not read the receipt's own answer, which is this same shape.\n\nScope: `returns:read`.",
        "operationId": "get_return",
        "parameters": [
          {
            "description": "The return's own id.",
            "in": "path",
            "name": "return_id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "closed_at": "2026-09-05T09:00:00+00:00",
                    "exceptions": [],
                    "external_id": "gid://shopify/Refund/9912",
                    "id": 31,
                    "ledger_written_at": "2026-09-05T09:00:04+00:00",
                    "lines": [
                      {
                        "disposition": "restock",
                        "expected_quantity": 2,
                        "id": 68,
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                        "received_quantity": 2,
                        "refund_line_item_id": "gid://shopify/RefundLineItem/551",
                        "restocked_quantity": 2,
                        "sku": "MAR-TOP-M",
                        "variant_id": "fx-v-mar-top-m"
                      }
                    ],
                    "location": {
                      "location_id": "loc-lb",
                      "name": "Long Beach"
                    },
                    "movements": [
                      {
                        "cost_basis": "left_at",
                        "id": 4471,
                        "kind": "return",
                        "location_id": "loc-lb",
                        "location_name": "Long Beach",
                        "occurred_at": "2026-09-05T09:00:00+00:00",
                        "quantity_delta": 2,
                        "sequence": 1,
                        "sku": "FX-MAR-TOP-M",
                        "unit_cost": {
                          "cents": 1200,
                          "currency": "USD",
                          "reason": null,
                          "usd": 12.0
                        },
                        "variant_id": "fx-v-mar-top-m",
                        "variant_title": "Marlow Top / M"
                      }
                    ],
                    "name": "RET-00000031",
                    "order": {
                      "name": "#1187",
                      "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                    },
                    "origin": "channel",
                    "received_at": "2026-09-05T09:00:00+00:00",
                    "refund": {
                      "cents": 17600,
                      "currency": "USD",
                      "reason": null,
                      "usd": 176.0
                    },
                    "refund_status": "full",
                    "refunded_at": "2026-09-05T09:00:00+00:00",
                    "source_system": "shopify",
                    "state": "closed",
                    "units_expected": 2,
                    "units_received": 2,
                    "units_restocked": 2
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The return, its lines, its movements and its open exceptions.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The return as it now stands.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Returns (`scope_missing`), names another organisation, or is outside its allowlist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "code": "return_not_on_file",
                  "message": {
                    "desc": "No return with that id is on file.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No return with that id is on file (`return_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get return",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:read"
        ]
      }
    },
    "/api/v1/sales/returns/{return_id}/cancel": {
      "post": {
        "description": "Calls off a return nobody has received: the units stop counting as inbound and the document is finished. From `expected` only; a return a warehouse has already received is refused with the date it arrived, because the goods are in the building whatever anybody decides afterwards. Cancelling a return that is already cancelled answers the same document again and refuses nothing.\n\nScope: `returns:write`, which includes `returns:read`.",
        "operationId": "cancel_return",
        "parameters": [
          {
            "description": "The return's own id.",
            "in": "path",
            "name": "return_id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "closed_at": null,
                    "exceptions": [],
                    "id": 33,
                    "ledger_written_at": null,
                    "lines": [],
                    "movements": [],
                    "name": "RET-00000033",
                    "origin": "manual",
                    "received_at": null,
                    "refund": {
                      "cents": null,
                      "currency": null,
                      "reason": "No refund is recorded on this return",
                      "usd": null
                    },
                    "refund_status": "none",
                    "source_system": "tightly",
                    "state": "cancelled",
                    "units_expected": 1,
                    "units_received": 0,
                    "units_restocked": 0
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The return, in the shape GET /sales/returns/{return_id} serves.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The return, cancelled.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Returns (`scope_missing`), names another organisation, or is outside its allowlist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No return with that id is on file (`return_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "return_already_received",
                  "message": {
                    "desc": "RET-00000031 was received on Sep 5, 2026; nothing on it can change.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`return_already_received`: the return has been received and nothing on it can change.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "cancel return",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:write"
        ]
      }
    },
    "/api/v1/sales/returns/{return_id}/receive": {
      "post": {
        "description": "The one act on a return that moves stock. Say what arrived at the dock and how much of it went back on the shelf; the rest came back and moved nothing sellable, which is what `disposition` is for. Tightly writes the ledger for the restocked units the order can admit, at the cost those units left at, and closes the return when every line is in.\n\nUnits beyond what the order shipped, and products that were never on it, are still recorded -- the shelf is the shelf -- but they do not enter stock here. They open an exception a person decides, and enter as an adjustment only if that person accepts them.\n\nScope: `returns:write`, which includes `returns:read`.",
        "operationId": "receive_return",
        "parameters": [
          {
            "description": "The return's own id.",
            "in": "path",
            "name": "return_id",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "lines": [
                  {
                    "disposition": "restock",
                    "received_quantity": 1,
                    "restocked_quantity": 1,
                    "sku": "MAR-TOP-S"
                  }
                ],
                "received_at": "2026-09-12T17:00:00Z"
              },
              "schema": {
                "properties": {
                  "lines": {
                    "items": {
                      "properties": {
                        "disposition": {
                          "enum": [
                            "restock",
                            "damaged",
                            "quarantine",
                            "destroyed"
                          ],
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "received_quantity": {
                          "minimum": 0,
                          "type": "integer"
                        },
                        "restocked_quantity": {
                          "description": "Absent is all of what arrived.",
                          "minimum": 0,
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "sku": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "variant_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "received_quantity"
                      ],
                      "type": "object"
                    },
                    "minItems": 1,
                    "type": "array"
                  },
                  "location_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "received_at": {
                    "description": "Absent is now.",
                    "format": "date-time",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "lines"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "closed_at": "2026-09-12T17:00:00+00:00",
                    "exceptions": [],
                    "id": 33,
                    "ledger_written_at": "2026-09-12T17:00:01+00:00",
                    "lines": [
                      {
                        "disposition": "restock",
                        "expected_quantity": 1,
                        "id": 71,
                        "order_line_item_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD:1",
                        "received_quantity": 1,
                        "refund_line_item_id": null,
                        "restocked_quantity": 1,
                        "sku": "MAR-TOP-S",
                        "variant_id": "fx-v-mar-top-s"
                      }
                    ],
                    "location": {
                      "location_id": "loc-lb",
                      "name": "Long Beach"
                    },
                    "movements": [
                      {
                        "cost_basis": "left_at",
                        "id": 4488,
                        "kind": "return",
                        "location_id": "loc-lb",
                        "location_name": "Long Beach",
                        "occurred_at": "2026-09-12T17:00:00+00:00",
                        "quantity_delta": 1,
                        "sequence": 1,
                        "sku": "FX-MAR-TOP-M",
                        "unit_cost": {
                          "cents": 1200,
                          "currency": "USD",
                          "reason": null,
                          "usd": 12.0
                        },
                        "variant_id": "fx-v-mar-top-s",
                        "variant_title": "Marlow Top / M"
                      }
                    ],
                    "name": "RET-00000033",
                    "order": {
                      "name": "ORD-00000412",
                      "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                    },
                    "origin": "manual",
                    "received_at": "2026-09-12T17:00:00+00:00",
                    "refund": {
                      "cents": null,
                      "currency": null,
                      "reason": "No refund is recorded on this return",
                      "usd": null
                    },
                    "refund_status": "none",
                    "source_system": "tightly",
                    "state": "closed",
                    "units_expected": 1,
                    "units_received": 1,
                    "units_restocked": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The return, in the shape GET /sales/returns/{return_id} serves.",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The return in `received`, `closed` or `exception`, with the movements it wrote.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "example": {
                  "code": "restocked_beyond_received",
                  "message": {
                    "desc": "MAR-TOP-S arrived 1 unit and 2 went back on the shelf; the shelf cannot take more than arrived.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "A line names no product Tightly knows (`variant_unknown`); a line records a negative quantity (`quantity_not_positive`); more went on the shelf than arrived (`restocked_beyond_received`); `received_at` could not be read (`date_unreadable`).\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Returns.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key does not hold Returns (`scope_missing`), names another organisation, or is outside its allowlist.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No return with that id is on file (`return_not_on_file`).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "code": "return_already_received",
                  "message": {
                    "desc": "RET-00000033 was received on Sep 12, 2026; nothing on it can change.",
                    "service": "sales",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "`return_already_received`: the return is closed and nothing on it can change; `return_needs_a_place`: you keep more than one warehouse and nobody named which.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "receive return",
        "tags": [
          "returns"
        ],
        "x-tightly-scopes": [
          "returns:write"
        ]
      }
    },
    "/api/v1/sales/table": {
      "get": {
        "description": "A page of sales per variant or per product: gross sales, discounts, returns, taxes, net sales, net items sold, returned units, return rate, sell-through rate, and the units and revenue lost to stockouts, with `filtered_max_size` for the size of the filtered set. A variant row also carries `price_sensitivity`, the pricing engine's tier, and `price_sensitivity_absence`, which says which blank a null tier is; a product row carries neither.\n\n`type=variants` is the default and `type=products` aggregates to the product. Page with `limit` and `offset`, narrow with `filter_args` on date, location, sales channel, vendor, collection and custom fields, order with `sort_args`, and set `export=true` to receive a download URL instead of rows. `comparison_date_gte` and `comparison_date_lte` add the comparison period the change figures are measured against.\n\nScope: `sales:read`.",
        "operationId": "get_sales_table",
        "parameters": [
          {
            "description": "Pagination limit",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Pagination offset",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "JSON array of filter conditions. Filter keys: date, sales_channel_id, location_id, variant_id, product_id, net_items_sold, net_items_sold_by_channel, gross_sales, net_sales, total_sales, returned_units, return_rate, sell_through_rate, lost_sales_units, missed_revenue, category, shopify_tags. Numeric keys support gte/lte. variant_id/product_id/sales_channel_id/location_id support eq/in. Example - filter variants with missed revenue >= $100: ```json [\n    {\"key\": \"date\", \"operation\": \"gte\", \"value\": \"2024-12-01\"},\n    {\"key\": \"date\", \"operation\": \"lte\", \"value\": \"2024-12-31\"},\n    {\"key\": \"missed_revenue\", \"operation\": \"gte\", \"value\": 100}\n] ```\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"date\",\"operation\":\"gte\",\"value\":\"2024-12-01\"},{\"key\":\"date\",\"operation\":\"lte\",\"value\":\"2024-12-31\"},{\"key\":\"missed_revenue\",\"operation\":\"gte\",\"value\":100}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of sort arguments (e.g., \"+sales_amount,-variant_title\"). Valid sort fields: variant_title, product_title, sku, product_id, variant_id, custom_fields, net_items_sold, gross_sales, discounts, returns, net_sales, taxes, total_sales, returned_units, return_rate, sell_through_rate, lost_sales_units, missed_revenue, margin_leak, net_sales_change, net_sales_swing. margin_leak is |discounts| + |returns|, what discounting and returns ate of the period. net_sales_change (signed) and net_sales_swing (absolute) measure net sales against the comparison_date_gte/lte window and are NULL without one, so sorting by them then has no effect, always pass the comparison window with these two. Note: custom_fields is sorted lexicographically as text (e.g. \"10\" sorts before \"2\"). Never use gross_profit as a sort field. It is not supported and will be silently ignored, causing results to return in default order. Use gross_sales or net_sales to sort by revenue.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "sales_amount,-variant_title"
              ],
              "type": "string"
            }
          },
          {
            "description": "Search term to filter sales",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "View type - 'variants' shows individual variants, 'products' shows grouped by product.",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "default": "variants",
              "enum": [
                "variants",
                "products"
              ],
              "type": "string"
            }
          },
          {
            "description": "If true, returns an export URL instead of paginated table data",
            "in": "query",
            "name": "export",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "description": "Start of comparison date range. On JSON responses it drives the per-row net_sales_change / net_sales_swing columns; on exports it adds side-by-side period columns. Pass both bounds or neither.\n",
            "in": "query",
            "name": "comparison_date_gte",
            "required": false,
            "schema": {
              "examples": [
                "2023-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End of comparison date range. See comparison_date_gte.",
            "in": "query",
            "name": "comparison_date_lte",
            "required": false,
            "schema": {
              "examples": [
                "2023-12-31"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 4120,
                    "offset": 0,
                    "rows": [
                      {
                        "category": "Knitwear",
                        "discounts": 2140.0,
                        "gross_sales": 41880.0,
                        "lost_sales_units": 40,
                        "missed_revenue": 1180.0,
                        "net_items_sold": 1280,
                        "net_sales": 37760.0,
                        "price_sensitivity": "MODERATE",
                        "price_sensitivity_absence": null,
                        "price_sensitivity_absence_word": null,
                        "price_sensitivity_computed_at": "2026-09-01T06:31:00Z",
                        "product_id": "4410092",
                        "product_title": "Terry Crew",
                        "return_rate": 0.048,
                        "returned_units": 62,
                        "returns": 1980.0,
                        "sell_through_rate": 0.86,
                        "shopify_tags": [
                          "core"
                        ],
                        "sku": "TB-CREW-BLK-M",
                        "taxes": 0.0,
                        "total_sales": 37760.0,
                        "variant_id": "44100920011",
                        "variant_title": "Black / M"
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "sales",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "A page of rows with `offset`, `size` and `filtered_max_size`, or, with `export=true`, a single `url` to download instead.\n",
                      "properties": {
                        "filtered_max_size": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "rows": {
                          "items": {
                            "$ref": "#/components/schemas/SalesTableVariantRow"
                          },
                          "type": "array"
                        },
                        "size": {
                          "type": "integer"
                        },
                        "url": {
                          "description": "Present only on an export.",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The sales table, or an export url",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "description": "Detailed validation error information",
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Human-readable error description",
                          "type": "string"
                        },
                        "service": {
                          "description": "Service name",
                          "type": "string"
                        },
                        "severity": {
                          "description": "Error severity level",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales table",
        "tags": [
          "sales"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/events/suggested": {
      "get": {
        "description": "The events this organisation's industry is offered for a year, such as Black Friday: each with the real event window, a recommended preparation window (`suggested_start_date` and `suggested_end_date`), the lift range to expect, and whether it has already been dismissed.\n\n`year` picks the year; the set is filtered to the organisation's industry vertical. These are suggestions: nothing here is an event until one is created.\n\nScope: `sales:read`.",
        "operationId": "get_suggested_events",
        "parameters": [
          {
            "description": "Year to retrieve suggested events for. Defaults to the current year.\n",
            "in": "query",
            "name": "year",
            "required": false,
            "schema": {
              "examples": [
                2026
              ],
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "events": [
                      {
                        "adjustment_max": 50,
                        "adjustment_min": 30,
                        "end_date": "2026-11-27",
                        "event_key": "black_friday_2026",
                        "is_dismissed": false,
                        "name": "Black Friday",
                        "start_date": "2026-11-27",
                        "suggested_end_date": "2026-11-27",
                        "suggested_start_date": "2026-11-13"
                      },
                      {
                        "adjustment_max": 40,
                        "adjustment_min": 25,
                        "end_date": "2026-11-30",
                        "event_key": "cyber_monday_2026",
                        "is_dismissed": false,
                        "name": "Cyber Monday",
                        "start_date": "2026-11-30",
                        "suggested_end_date": "2026-11-30",
                        "suggested_start_date": "2026-11-27"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity_events",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/SalesVelocityEventsApiResponse"
                }
              }
            },
            "description": "List of suggested events for the organization's industry",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid parameters",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get suggested events",
        "tags": [
          "sales_velocity_events"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/events/table": {
      "get": {
        "description": "A page of sales velocity events with their windows, statuses, adjustment types and item counts. Page with `limit` and `offset`, narrow with `filter_args` and `search`, order with `sort_args`.\n\nBeside the page it serves the whole filtered book's breakdown, so a figure row above a calendar costs no second read. `status_counts` carries `{active, upcoming, past, cancelled}` and honours every filter and the search term EXCEPT a status filter, so the four sum to `filtered_max_size` whether or not a status is selected, and they are served even on a page with no rows. `awaiting_input_count` is how many of those events are custom events, not yet past, with no values entered anywhere in their window, which is the same fact as `awaiting_input` on each row, and it honours every filter except an `awaiting_input` filter.\n\nEvery row carries `exclude_from_training`, whether the baseline has been told not to learn from that window. A planner sets the mark when the event is created or edited in Tightly, and no operation in this reference sets it. It reads false on an organisation whose events table cannot record the mark, which is the truth there.\n\nScope: `sales:read`.",
        "operationId": "get_sales_velocity_events_table",
        "parameters": [
          {
            "description": "Number of rows to retrieve in the response.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "examples": [
                10
              ],
              "type": "integer"
            }
          },
          {
            "description": "Number of rows to skip in the response.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "examples": [
                0
              ],
              "type": "integer"
            }
          },
          {
            "description": "JSON array of filter conditions. Each filter includes: - `key`: one of `status` (`eq`, `in`), `sales_channel_id` (`eq`, `in`), `start_date` and `end_date` (`gte`, `lte`, `gt`, `lt`), `awaiting_input` (`eq`, a boolean). Any other key is refused with a 400 naming the ones that are allowed. - `operation`: the operation the key allows, from the list above. - `value`: Filter value (a string, a boolean, or an array for `in`). Example: ```json [\n    {\"key\": \"status\", \"operation\": \"eq\", \"value\": \"active\"},\n    {\"key\": \"awaiting_input\", \"operation\": \"eq\", \"value\": true}\n] ```\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"status\",\"operation\":\"eq\",\"value\":\"active\"},{\"key\":\"awaiting_input\",\"operation\":\"eq\",\"value\":true}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of sort arguments. Prefix with \"+\" for ascending or \"-\" for descending. Valid sort fields: name, status, created_at, adjustment_type, date_range, variants_count, items_count.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "start_date,-created_at"
              ],
              "type": "string"
            }
          },
          {
            "description": "Search keyword to filter event names or descriptions.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "Black Friday"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "awaiting_input_count": 2,
                    "filtered_max_size": 12,
                    "max_size": 12,
                    "offset": 0,
                    "rows": [
                      {
                        "adjustment_type": "uplift_percent",
                        "awaiting_input": false,
                        "end_date": "2026-11-30",
                        "exclude_from_training": false,
                        "id": 412,
                        "items_count": 184,
                        "name": "Black Friday 2026",
                        "start_date": "2026-11-27",
                        "status": "scheduled",
                        "variants_count": 184
                      }
                    ],
                    "size": 1,
                    "status_counts": {
                      "finished": 7,
                      "running": 1,
                      "scheduled": 4
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity_events",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/SalesVelocityEventsApiResponse"
                }
              }
            },
            "description": "The sales velocity events table",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales velocity events table",
        "tags": [
          "sales_velocity_events"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/events/{event_id}": {
      "get": {
        "description": "One sales velocity event: its name and description, its window, the adjustment it applies and that adjustment's configuration, the channels it covers, its status and how many items and variants it holds.\n\n`exclude_from_training` says whether the baseline has been told not to learn from this window. A planner sets the mark when the event is created or edited in Tightly, and it is what makes a window already in the past worth recording at all. No operation in this reference sets it. It reads false on an organisation whose events table cannot record the mark, which is the truth there, and on that organisation a save asking for the mark is refused rather than saved without it.\n\nScope: `sales:read`.",
        "operationId": "get_sales_velocity_event",
        "parameters": [
          {
            "description": "Unique identifier of the sales velocity event",
            "example": "123",
            "in": "path",
            "name": "event_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "adjustment_config": {
                      "uplift_percent": 45
                    },
                    "adjustment_type": "uplift_percent",
                    "awaiting_input": false,
                    "created_at": "2026-08-02T11:20:00+00:00",
                    "description": "Site-wide promotion",
                    "end_date": "2026-11-30",
                    "id": 412,
                    "is_active": true,
                    "is_overridden": false,
                    "items_count": 184,
                    "name": "Black Friday 2026",
                    "sales_channel_id": "61240442",
                    "sales_channels": [
                      {
                        "id": "61240442",
                        "name": "Wholesale"
                      }
                    ],
                    "start_date": "2026-11-27",
                    "status": "scheduled",
                    "suggested_event_key": null,
                    "variants_count": 184
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity_events",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/SalesVelocityEventsApiResponse"
                }
              }
            },
            "description": "The sales velocity event",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid event ID format",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not Found - Sales velocity event with specified ID does not exist",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales velocity event",
        "tags": [
          "sales_velocity_events"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/events/{event_id}/analytics": {
      "get": {
        "description": "How one sales velocity event performed: baseline (the forecast without it), target (the forecast adjusted for it) and actual units, revenue, profit and return on investment, with an accuracy figure.\n\n`event_id` is the event. Omit `group_by` for one aggregate row over the event period, or send `group_by=date` for one row per calendar day.\n\nScope: `sales:read`.",
        "operationId": "get_event_analytics",
        "parameters": [
          {
            "description": "Unique identifier of the sales velocity event",
            "example": "123",
            "in": "path",
            "name": "event_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Grouping dimension. Omit for a single summary row, or pass \"date\" for a row per day.\n",
            "example": "date",
            "in": "query",
            "name": "group_by",
            "required": false,
            "schema": {
              "enum": [
                "date"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "group_by": "date",
                    "rows": [
                      {
                        "accuracy": 78,
                        "actual_profit": 180.0,
                        "actual_revenue": 450.0,
                        "actual_roi": 66.67,
                        "actual_unit": 9.0,
                        "baseline_profit": 210.0,
                        "baseline_revenue": 525.0,
                        "baseline_unit": 10.5,
                        "group_by_col": "2025-12-01",
                        "target_profit": 231.0,
                        "target_revenue": 577.5,
                        "target_roi": 66.67,
                        "target_unit": 11.55
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity_events",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/SalesVelocityEventsApiResponse"
                }
              }
            },
            "description": "The event's analytics",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid group_by value",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Not Found - Sales velocity event with specified ID does not exist",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get event analytics",
        "tags": [
          "sales_velocity_events"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/filters": {
      "get": {
        "description": "The values the sales velocity table can be filtered on: locations and suppliers as id and name, events, the modified-status words, tags, and `{min, max}` ranges for the numeric columns. Send `field` to page one field's values with `limit`, `offset` and `search`; omit it and every filter comes back at once.\n\nScope: `sales:read`.",
        "operationId": "get_sales_velocity_filters",
        "parameters": [
          {
            "description": "Optional field name to retrieve paginated values (e.g., 'shopify_tags'). Omit to get all filters.",
            "in": "query",
            "name": "field",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Optional search term to filter results (only applicable when field is provided)",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Number of items to skip for pagination (only applicable when field is provided)",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Maximum number of items to return per page (only applicable when field is provided)",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "event_id": [
                      {
                        "id": "412",
                        "name": "Black Friday 2026"
                      }
                    ],
                    "location_id": [
                      {
                        "id": "loc_0004",
                        "name": "London DC"
                      }
                    ],
                    "modified_status": [
                      "all",
                      "modified",
                      "unmodified"
                    ],
                    "sales_velocity_30_days_range": [
                      0.0,
                      18.4
                    ],
                    "shopify_tags": [
                      "core",
                      "seasonal"
                    ],
                    "supplier": [
                      {
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits"
                      }
                    ]
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "event_id": {
                          "description": "The sales-velocity events a row can be filtered to.",
                          "items": {
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "location_id": {
                          "description": "Available locations for filtering",
                          "items": {
                            "properties": {
                              "id": {
                                "description": "Location ID",
                                "examples": [
                                  "gid://shopify/Location/12345678"
                                ],
                                "type": "string"
                              },
                              "name": {
                                "description": "Location name",
                                "examples": [
                                  "Main Warehouse"
                                ],
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "modified_status": {
                          "description": "Available modification status filter options",
                          "examples": [
                            [
                              "all",
                              "modified",
                              "unmodified"
                            ]
                          ],
                          "items": {
                            "enum": [
                              "all",
                              "modified",
                              "unmodified"
                            ],
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "sales_velocity_30_days_range": {
                          "description": "Min and max sales velocity (last 30 days) values for range filtering",
                          "examples": [
                            [
                              0.0,
                              15.75
                            ]
                          ],
                          "items": {
                            "format": "float",
                            "type": "number"
                          },
                          "maxItems": 2,
                          "minItems": 2,
                          "type": "array"
                        },
                        "shopify_tags": {
                          "description": "Every tag carried by a product in this book.",
                          "items": {
                            "type": "string"
                          },
                          "type": "array"
                        },
                        "supplier": {
                          "description": "Available suppliers for filtering",
                          "items": {
                            "properties": {
                              "supplier_id": {
                                "description": "Supplier ID",
                                "examples": [
                                  "supplier_123"
                                ],
                                "type": "string"
                              },
                              "supplier_name": {
                                "description": "Supplier name",
                                "examples": [
                                  "ABC Supply Co"
                                ],
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The filter options",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "code": {
                      "examples": [
                        "POSTGRES_ERROR"
                      ],
                      "type": "string"
                    },
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "properties": {
                        "desc": {
                          "description": "Generic error message",
                          "type": "string"
                        }
                      },
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales velocity filters",
        "tags": [
          "sales_velocity"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/forecast-config": {
      "get": {
        "description": "The forecast model this organisation has selected and the configuration behind it: the model, the minimum sales and minimum weeks with sales a variant needs to be forecast, the base and pattern filters, the time windows, and any model-specific parameters under `model_parameters`.\n\nThe models the engine carries are `moving_average`, `seasonal_naive`, `seasonal_tsb` and `lightgbm`, and none of them is plan-gated. This read answers the configuration; changing it is not public.\n\nScope: `sales:read`.",
        "operationId": "get_forecast_model_config",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "min_total_sales": 30,
                    "min_weeks_with_sales": 8,
                    "model": "auto_arima"
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "The selected model and the filters that decide which variants it is fitted on. Model-specific parameters ride under `model_parameters`.\n",
                      "properties": {
                        "min_total_sales": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "min_weeks_with_sales": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "model": {
                          "description": "moving_average, auto_arima, sarimax, and so on.",
                          "type": "string"
                        },
                        "model_parameters": {
                          "type": [
                            "object",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Current forecast model configuration",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get forecast model config",
        "tags": [
          "sales_velocity"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/table": {
      "get": {
        "description": "A page of sales velocity per variant or product, the rate demand forecasting and days of cover are computed from: total demand and total sales for the window, the forecast model chosen for the row, whether it is seasonal, price, channel, supplier, bundle count and a confidence tier, with `confidence_tier_counts` for the filtered book.\n\n`type=variants` is the default and `type=products` aggregates. Page with `limit` and `offset`, narrow with `filter_args` on location_id, sales_velocity_30_days, modified_status, variant_id, product_id, supplier_id, event_id, shopify_tags and sales_channel_id, and order with `sort_args` over variant_title, product_title, sales_velocity_30_days, sales_velocity_30_days_incl_bundles, current, next and next_2. `start_date` and `end_date` set the window and must be sent together. `export=true` answers a download URL.\n\n`data_source` says which store served the read, `ch` for the analytics mirror and `pg` for PostgreSQL, and `demand_as_of` is when the demand figures were last computed. An `export=true` response carries `data_source` too: the export dispatches to the same two stores and falls back the same way.\n\n`row_grain` says what grain the rows in the response are, `variants` or `products`. It is a property of the envelope, not an echo of the `type` you sent, so a client can check the answer against its own request rather than inferring the grain from which counter it can find.\n\nScope: `sales:read`.",
        "operationId": "get_sales_velocity_table",
        "parameters": [
          {
            "description": "Number of rows to retrieve in the response.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "examples": [
                10
              ],
              "type": "integer"
            }
          },
          {
            "description": "Number of rows to skip in the response.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "examples": [
                0
              ],
              "type": "integer"
            }
          },
          {
            "description": "JSON array of filter conditions. Each filter includes: - `key`: Field to filter (e.g., `sales_velocity_30_days`, `location_id`, `event_id`). - `operation`: Filter operation (`eq`, `gte`, `lte`, `in`, etc.). - `value`: Filter value (can be a string, number, or array).\nAvailable filter keys: - `sales_velocity_30_days`: Filter by sales velocity (last 30 days) value - `location_id`: Filter by location ID - `variant_id`: Filter by variant ID - `product_id`: Filter by product ID - `supplier_id`: Filter by supplier ID - `sales_channel_id`: Filter by sales channel ID - `event_id`: Filter by sales velocity event ID - `modified_status`: Filter by modification status (`modified`, `unmodified`, `all`)\nExample: ```json [\n    {\"key\": \"sales_velocity_30_days\", \"operation\": \"gte\", \"value\": 3},\n    {\"key\": \"location_id\", \"operation\": \"in\", \"value\": [\"60904046659\", \"63148523587\"]},\n    {\"key\": \"event_id\", \"operation\": \"eq\", \"value\": 123}\n] ```\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"sales_velocity_30_days\",\"operation\":\"gte\",\"value\":3},{\"key\":\"location_id\",\"operation\":\"in\",\"value\":[\"60904046659\",\"63148523587\"]}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of sort arguments. Use `-` for descending order. Example: `sales_velocity_30_days,-variant_title` (sorts by `sales_velocity_30_days` ascending and `variant_title` descending).\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "sales_velocity_30_days,-variant_title"
              ],
              "type": "string"
            }
          },
          {
            "description": "Search keyword to filter product or variant titles.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "Water Bottle"
              ],
              "type": "string"
            }
          },
          {
            "description": "If true, returns an export URL instead of paginated table data",
            "in": "query",
            "name": "export",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "description": "Start date for sales velocity analysis period",
            "in": "query",
            "name": "start_date",
            "required": false,
            "schema": {
              "examples": [
                "2024-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End date for sales velocity analysis period",
            "in": "query",
            "name": "end_date",
            "required": false,
            "schema": {
              "examples": [
                "2024-12-31"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "View type - 'variants' shows individual variants, 'products' shows grouped by product.",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "default": "variants",
              "enum": [
                "variants",
                "products"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated list of demand/sales columns to include. Available columns: user_defined_demand, calculated_demand, plan_change_percent, min_date, max_date, sales, last_period, effective_demand, events\n",
            "in": "query",
            "name": "demand_sales_columns",
            "required": false,
            "schema": {
              "examples": [
                "user_defined_demand,calculated_demand,effective_demand"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "confidence_tier_counts": {
                      "high": 1840,
                      "low": 640,
                      "medium": 1204,
                      "new": 436,
                      "scored": 3684,
                      "total": 4120
                    },
                    "data_source": "ch",
                    "demand_as_of": "2026-09-04T02:41:00+00:00",
                    "filtered_max_size": 4120,
                    "filtered_variants_size": 1284,
                    "max_size": 4120,
                    "offset": 0,
                    "row_grain": "variants",
                    "rows": [
                      {
                        "bundle_count": 2,
                        "confidence_tier": "high",
                        "forecast_model": "auto_arima",
                        "is_modified": false,
                        "is_seasonal": true,
                        "price": 79.0,
                        "product_id": "4410092",
                        "product_title": "Terry Crew",
                        "sales_channel_id": "61240442",
                        "sales_channel_name": "Wholesale",
                        "shopify_tags": [
                          "core"
                        ],
                        "sku": "TB-CREW-BLK-M",
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits",
                        "total_demand": 412.0,
                        "total_sales": 388.0,
                        "typical_miss_pct": 0.14,
                        "variant_id": "44100920011",
                        "variant_title": "Black / M"
                      }
                    ],
                    "size": 1,
                    "successors_size": 0
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "A page of velocity rows with `offset`, `size`, `filtered_max_size`, `data_source` (`ch` or `pg`) and `row_grain` (`variants` or `products`), or, with `export=true`, a single download `url` and the same `data_source`.\n",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The sales velocity table, or an export url",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid request parameters or validation errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error - Database connection or processing errors",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales velocity table",
        "tags": [
          "sales_velocity"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/table/totals": {
      "get": {
        "description": "The sales velocity table's totals over every filtered row, as one entry per period: units sold and revenue, forecast demand and its revenue, effective demand (forecast adjusted for stockouts), the same period a year earlier, and the velocity rate the forecast used.\n\nIt takes the same `filter_args` and `search` as get_sales_velocity_table, so the totals match the rows a caller is looking at. `start_date` and `end_date` set the window, `include_last_year` adds the prior-year figures and `aggregation_mode` sets how periods are summed.\n\n`first_forecast_date` is set only on the period holding the day forecasting first ran for this organisation, and is null on every other. Missing forecasts remain null rather than zero. Historical partial forecasts retain their recorded amount and expose `forecast_coverage` on commitment-scoped PostgreSQL reads.\n\nScope: `sales:read`.",
        "operationId": "get_sales_velocity_totals",
        "parameters": [
          {
            "description": "JSON array of filter conditions. Same filter keys as the sales velocity table: location_id, sales_velocity_30_days, modified_status, variant_id, product_id, supplier_id, event_id, shopify_tags, sales_channel_id.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"location_id\",\"operation\":\"in\",\"value\":[\"60904046659\",\"63148523587\"]}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Search keyword to filter by product or variant title",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Start date for the sales velocity analysis period",
            "in": "query",
            "name": "start_date",
            "required": false,
            "schema": {
              "examples": [
                "2024-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End date for the sales velocity analysis period. Must be provided together with start_date.",
            "in": "query",
            "name": "end_date",
            "required": false,
            "schema": {
              "examples": [
                "2024-12-31"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "When true, includes last year comparison data in the totals",
            "in": "query",
            "name": "include_last_year",
            "required": false,
            "schema": {
              "default": true,
              "type": "boolean"
            }
          },
          {
            "description": "Time bucket for period columns. Auto-selected based on date range when omitted: daily (≤14 days), weekly (≤90 days), monthly (otherwise).\n",
            "in": "query",
            "name": "aggregation_mode",
            "required": false,
            "schema": {
              "enum": [
                "daily",
                "weekly",
                "monthly"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "data_source": "pg",
                    "demand_as_of": "2026-09-04T02:41:00+00:00",
                    "scope": {
                      "pairs": 4120,
                      "variants": 1284
                    },
                    "scope_unavailable_reason": null,
                    "totals": [
                      {
                        "calculated_demand": 18820.0,
                        "effective_demand": 19100.0,
                        "last_period_sales": 17600.0,
                        "max_date": "2026-09-13",
                        "min_date": "2026-09-07",
                        "sales": 18400.0,
                        "sales_revenue": 984000.0,
                        "week_grid": "MON:2026-09-07"
                      }
                    ],
                    "week_grids": {
                      "MON:2026-09-07": {
                        "aligned": false,
                        "days_in_next_planning_week": 1,
                        "days_in_planning_week": 6,
                        "planning_fiscal_week": 32,
                        "planning_fiscal_year": 2026,
                        "planning_week_end": "2026-09-12",
                        "planning_week_start": "2026-09-06",
                        "planning_week_start_dow": "SUN",
                        "shift_days": 1,
                        "week_end": "2026-09-13",
                        "week_start": "2026-09-07",
                        "week_start_dow": "MON"
                      }
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sales_velocity",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "description": "One entry per time bucket in `totals`, the `data_source` that served them (`ch` or `pg`), and the `scope` the denominators were counted over, or the reason there is none, which is not the same as a scope of nothing. On a weekly chart each bucket also names its week's key in `week_grids`, which converts that Monday-keyed week to the plan's 4-5-4 Sunday week: the plan week it falls inside, that week's fiscal year and number, and how the seven days split between it and the next (6 and 1). That split describes the keyed week itself, not the bucket: a bucket clipped by `start_date` or `end_date` covers only part of its week, and `min_date` / `max_date` remain the bucket's own bounds. Daily and monthly buckets carry `week_grid: null` and `week_grids` is empty, those buckets are not weeks.\n",
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The sales velocity totals",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - invalid parameters",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get sales velocity table totals",
        "tags": [
          "sales_velocity"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sales/velocity/variants/{variant_id}/bundle-contributions": {
      "get": {
        "description": "How much of one variant's demand comes from each bundle that contains it, over a window: one entry per bundle with the bundle's product and variant titles and its contribution, plus `total_count` and `total_demand`.\n\n`variant_id` is in the path; `sales_channel_id`, `start_date` and `end_date` are all required. It uses the same effective-demand figures as get_sales_velocity_table. At most 20 bundles come back, largest contribution first, and where there are more the last entry is an `Others` row summing the rest.\n\n`data_source` says which store served the read, `ch` for the analytics mirror and `pg` for PostgreSQL, exactly as on get_sales_velocity_table. The figures are the same either way; the field is there because a fallback to PostgreSQL is otherwise invisible.\n\nScope: `sales:read`.",
        "operationId": "get_variant_bundle_contributions",
        "parameters": [
          {
            "description": "The component variant whose bundle contributions are requested.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Sales channel to scope the demand data to.",
            "in": "query",
            "name": "sales_channel_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Start of the date range (inclusive).",
            "in": "query",
            "name": "start_date",
            "required": true,
            "schema": {
              "examples": [
                "2026-04-24"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End of the date range (inclusive).",
            "in": "query",
            "name": "end_date",
            "required": true,
            "schema": {
              "examples": [
                "2026-10-24"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "bundle_contributions": [
                      {
                        "contribution": 42.0,
                        "product_title": "Bundle Product A",
                        "variant_title": "Bundle A"
                      },
                      {
                        "contribution": 18.0,
                        "product_title": "Bundle Product B",
                        "variant_title": "Bundle Set B"
                      },
                      {
                        "contribution": 8.0,
                        "product_title": "Others",
                        "variant_title": null
                      }
                    ],
                    "data_source": "ch",
                    "total_count": 3,
                    "total_demand": 60.0
                  },
                  "message": {
                    "service": "sales_velocity",
                    "success": true
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bundle demand contributions for the variant",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Bad Request - Invalid parameters",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Sales.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sales; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "Internal Server Error",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "get variant bundle contributions",
        "tags": [
          "sales_velocity"
        ],
        "x-tightly-scopes": [
          "sales:read"
        ]
      }
    },
    "/api/v1/sandbox/seed": {
      "post": {
        "description": "Writes a small starter set into the sandbox the key belongs to: two locations, two sales channels, two suppliers, three products with two variants each, and a stock level for every variant at both locations. Enough that every published read answers rows and every published write has a real channel, location and variant to name.\n\nSend it with a `tly_test_` key and no body. It answers `200` whether it wrote anything or not: `seeded` says whether this call wrote, `already_seeded` says whether an earlier one did, and `written` counts the rows this call inserted, per table, which is zero once the sandbox is filled. So a partner's CI can call it before every run and read one shape back.\n\nNothing is truncated, updated or deleted: every row is inserted on conflict do nothing, so a sandbox somebody has since loaded their own data into keeps it. The ids it writes are stable and prefixed `sbx-`, and they are not ids to build against: read your ids off `variants/table` and `inventory/table` as the examples do, because a sandbox seeded with something else holds different ones.\n\nThis is not the multi-season demo book. That is composed from bundles by Tightly and comes through support; this is the starter set an integrator needs on their first afternoon.\n\nA live organisation is refused `409`, whatever key was used, and nothing is written.\n\nScope: `sandbox:write`, which includes `sandbox:read`.",
        "operationId": "seed_sandbox",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {},
              "schema": {
                "description": "No fields. The sandbox is the key's own organisation and the set is fixed.",
                "type": "object"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "already_seeded": false,
                    "read_next": "GET /api/v1/variants/table?limit=5",
                    "seeded": true,
                    "written": {
                      "inventory_levels": 12,
                      "locations": 2,
                      "product_categories": 1,
                      "product_variants": 6,
                      "products": 3,
                      "sales_channels": 2,
                      "suppliers": 2,
                      "variant_sales_channels": 6,
                      "variant_suppliers": 6
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "sandbox",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "already_seeded": {
                          "description": "Whether an earlier call had already filled this sandbox.",
                          "type": "boolean"
                        },
                        "read_next": {
                          "description": "The read to make next, so the answer names the way on.",
                          "type": "string"
                        },
                        "seeded": {
                          "description": "Whether this call wrote anything.",
                          "type": "boolean"
                        },
                        "written": {
                          "description": "Rows this call inserted, keyed by table. Every count is zero on a repeat.",
                          "type": "object"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The sandbox as this call left it. `written` counts the rows this call inserted, per table, so the first call reports the starter set and every call after it reports zeros beside `already_seeded: true`. One success code either way, because a repeat creates nothing and a client should read what happened out of the body rather than out of the status.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8e3b603fdcf44993b618b5ff7a2868ad",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Sandbox.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_391944cd161d4b5a97359d0d06d0b28d",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Sandbox for writing; `plan_excludes` when the organisation's plan does not include the public API; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "409": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "already_seeded": false,
                    "seeded": false
                  },
                  "message": {
                    "desc": "This is not a sandbox organisation, so it cannot be seeded. Make a sandbox with POST /api/v1/developer/sandbox, mint a test key in it, and send this with that key.",
                    "service": "sandbox",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "This organisation is not a sandbox, so it cannot be seeded, and nothing was written. The sentence names the way out: make a sandbox with POST /api/v1/developer/sandbox, mint a test key in it, and send this with that key.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Fill this sandbox with a starter catalogue so every other operation has something to answer",
        "tags": [
          "sandbox"
        ],
        "x-tightly-scopes": [
          "sandbox:write"
        ]
      }
    },
    "/api/v1/sell-out/coverage": {
      "get": {
        "description": "Which accounts reported which weeks, and at what grain: the week axis once, and each account's reported weeks keyed against it with the units and variants it sent, its last reported week and its grain. `weeks` bounds the window.\n\nCoverage is account against time, and a table of totals cannot show that an account stopped reporting four weeks ago.\n\nA week an account did not report is ABSENT from its map, never a row with zero units. The source data does not agree on that point: one retailer lists every product every week and means its zeros, another lists only movement, so filling the gaps would decide the argument wrongly for one of them, and a reader seeing \"0 units\" could not tell a dead week from a week nobody sent. Inside a reported week `units` can still be null, because a stock-only week has no sales figure.\n\nDerived rows, the brand's own curve scaled by an account's share, are excluded: a coverage grid counting them would claim an account sent weeks nobody sent.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `sell_out:read`.",
        "operationId": "get_sell_out_coverage",
        "parameters": [
          {
            "description": "How many weeks of axis to return. Clamped to 1 to 52.",
            "in": "query",
            "name": "weeks",
            "required": false,
            "schema": {
              "default": 8,
              "maximum": 52,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Optional tenant-local book. Restricts reported variants to its canonical current cohort (future boxed books use their declared window). The response roster contains reported-activity accounts only, not expected reporters; expected_accounts is null. The organization-wide default is unchanged.",
            "in": "query",
            "name": "commitment_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ISO date for commitment scope; requires commitment_id. Defaults to today.",
            "in": "query",
            "name": "as_of",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "accounts": [
                      {
                        "account": "Selfridges",
                        "account_id": "tp_00417",
                        "coverage_sentence": "2 weeks reported",
                        "grain": "store",
                        "last_reported": "2026-08-24",
                        "period_grain": "week",
                        "periods_reported": 2,
                        "reported": {
                          "2026-08-10/2026-08-16": {
                            "grain": "week",
                            "period_days": 7,
                            "period_end": "2026-08-16",
                            "period_start": "2026-08-10",
                            "units": 1841,
                            "variants": 96
                          },
                          "2026-08-24/2026-08-30": {
                            "grain": "week",
                            "period_days": 7,
                            "period_end": "2026-08-30",
                            "period_start": "2026-08-24",
                            "units": null,
                            "variants": 96
                          }
                        },
                        "weekly_rate_absent": null
                      },
                      {
                        "account": "Le Bon Marché",
                        "account_id": "tp_00902",
                        "coverage_sentence": null,
                        "grain": null,
                        "last_reported": null,
                        "period_grain": null,
                        "periods_reported": 0,
                        "reported": {},
                        "weekly_rate_absent": null
                      },
                      {
                        "account": "New Balance Deutschland",
                        "account_id": "tp_01188",
                        "coverage_sentence": "1 month reported",
                        "grain": "account",
                        "last_reported": "2026-08-01",
                        "period_grain": "month",
                        "periods_reported": 1,
                        "reported": {
                          "2026-08-01/2026-08-31": {
                            "grain": "month",
                            "period_days": 31,
                            "period_end": "2026-08-31",
                            "period_start": "2026-08-01",
                            "units": 4211,
                            "variants": 214
                          }
                        },
                        "weekly_rate_absent": "Reported monthly, so a weekly rate is not measured."
                      }
                    ],
                    "reporting": 2,
                    "total_accounts": 3,
                    "weeks": [
                      "2026-08-10",
                      "2026-08-17",
                      "2026-08-24"
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "sell_out",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "accounts": {
                          "items": {
                            "properties": {
                              "account": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "account_id": {
                                "type": "string"
                              },
                              "coverage_sentence": {
                                "description": "\"4 months reported\", \"12 weeks reported\", or both. Null where nothing was reported.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "grain": {
                                "description": "The LOCATION grain. What a period is is `period_grain`.",
                                "enum": [
                                  "store",
                                  "account",
                                  null
                                ],
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "last_reported": {
                                "format": "date",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "period_axis": {
                                "description": "Exact cell keys and bounds, preserving distinct weekly and monthly periods with the same start.",
                                "items": {
                                  "properties": {
                                    "grain": {
                                      "type": "string"
                                    },
                                    "key": {
                                      "type": "string"
                                    },
                                    "period_days": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "period_end": {
                                      "format": "date",
                                      "type": [
                                        "string",
                                        "null"
                                      ]
                                    },
                                    "period_start": {
                                      "format": "date",
                                      "type": "string"
                                    }
                                  },
                                  "type": "object"
                                },
                                "type": "array"
                              },
                              "period_grain": {
                                "enum": [
                                  "week",
                                  "month",
                                  "mixed",
                                  null
                                ],
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "periods_reported": {
                                "type": "integer"
                              },
                              "reported": {
                                "additionalProperties": {
                                  "properties": {
                                    "grain": {
                                      "enum": [
                                        "week",
                                        "month"
                                      ],
                                      "type": "string"
                                    },
                                    "period_days": {
                                      "description": "How many days this period covers, both ends counted. A face draws one band this wide.",
                                      "type": "integer"
                                    },
                                    "period_end": {
                                      "format": "date",
                                      "type": "string"
                                    },
                                    "period_start": {
                                      "format": "date",
                                      "type": "string"
                                    },
                                    "units": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    },
                                    "variants": {
                                      "type": "integer"
                                    }
                                  },
                                  "type": "object"
                                },
                                "description": "The periods this account reported, KEYED ON THE PERIOD: unambiguous weekly start dates and otherwise start/end dates, `2026-08-01/2026-08-31`. Not on the start alone, because a month and a week that open on the same day are two periods and one would replace the other. A period the account did not report is ABSENT, never a cell of zeroes.",
                                "type": "object"
                              },
                              "sales_channel_id": {
                                "description": "Stable channel identity, present only in scoped reads.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "weekly_rate_absent": {
                                "description": "Why a per-week rate is not measured, where the grain is the reason.",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "as_of": {
                          "format": "date",
                          "type": "string"
                        },
                        "commitment_id": {
                          "description": "Only present for the optional scoped read.",
                          "type": "string"
                        },
                        "expected_accounts": {
                          "description": "Always null for scoped reads; no expected-reporting membership exists.",
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "period_axis": {
                          "description": "Exact cell keys and bounds, preserving distinct weekly and monthly periods with the same start.",
                          "items": {
                            "properties": {
                              "grain": {
                                "type": "string"
                              },
                              "key": {
                                "type": "string"
                              },
                              "period_days": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "period_end": {
                                "format": "date",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "period_start": {
                                "format": "date",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "period_policy": {
                          "type": "string"
                        },
                        "population": {
                          "properties": {
                            "end_date": {
                              "format": "date",
                              "type": "string"
                            },
                            "kind": {
                              "enum": [
                                "as_of",
                                "future_book_window"
                              ],
                              "type": "string"
                            },
                            "start_date": {
                              "format": "date",
                              "type": "string"
                            }
                          },
                          "type": "object"
                        },
                        "reported_window": {
                          "properties": {
                            "end_date": {
                              "format": "date",
                              "type": "string"
                            },
                            "start_date": {
                              "format": "date",
                              "type": "string"
                            }
                          },
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "reported_window_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "reporting": {
                          "description": "How many accounts reported at least one week in the window.",
                          "type": "integer"
                        },
                        "roster_basis": {
                          "description": "Scoped accounts are evidence-bearing reporters, not an expected roster.",
                          "enum": [
                            "reported_activity"
                          ],
                          "type": "string"
                        },
                        "total_accounts": {
                          "type": "integer"
                        },
                        "weeks": {
                          "description": "The period axis, one ISO date per period start. It steps at the cadence each account actually reports: seven days for a weekly feed, one CALENDAR MONTH for an account that files months, and both where accounts differ. It runs to the last COMPLETED period, so a period still running is not yet an account's silence.",
                          "items": {
                            "format": "date",
                            "type": "string"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The week axis, one row per account with the weeks it reported and the grain it reports at, and the two counts a verdict is written from, how many accounts reported at all, and how many there are.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "Empty commitment_id, invalid as_of, or as_of without commitment_id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No book with that ID in the authorized tenant.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Which accounts reported which periods, and at what grain",
        "tags": [
          "sell_out"
        ],
        "x-tightly-scopes": [
          "sell_out:read"
        ]
      }
    },
    "/api/v1/sell-out/declaration": {
      "get": {
        "description": "The four answers one account has confirmed about its own sell-out reports, or null when nobody has answered yet: which day its week ends on, whether the figures cover stores, distribution centres or the whole account, the currency and whether prices include tax, and whether a product missing from a file means it sold none or means the retailer did not report it. `account_id` is required.\n\nNull is the meaningful case. The declaration is a precondition of an import rather than a setting, so an account with none has its files refused until a person answers.\n\nThe four exist because every default is wrong for about half of all accounts. One retailer writes an explicit zero for a product that did not sell; another omits the row. Guess wrong and half the book shows healthy products as dead, with the row count looking correct either way.\n\nEvery machine value is served beside the sentence a person reads for it (`covers`, `prices_are`, `missing_row_means`, `week_ends_on`), so no surface has to keep its own translation table. `confirmed_by` is the author's id and `confirmed_by_name` the name resolved for it, with the name null rather than the id where there is none.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `sell_out:read`.",
        "operationId": "get_sell_out_declaration",
        "parameters": [
          {
            "description": "The account. An id, never a name. \"Intersport\" is a dozen buying groups, and a name here would apply one retailer's week anchor to another retailer's file with every screen afterwards looking normal.\n",
            "in": "query",
            "name": "account_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "absence_convention": "row_omitted",
                    "confirmed_at": "2026-08-24T10:41:03+00:00",
                    "confirmed_by": "66c1f0a2e4b09a3d5c7f1a02",
                    "confirmed_by_name": "Priya Raman",
                    "covers": "Each store",
                    "currency": "GBP",
                    "location_grain": "store",
                    "missing_row_means": "The retailer did not report it",
                    "price_tax_basis": "inclusive",
                    "prices_are": "Including tax",
                    "week_anchor_dow": 6,
                    "week_ends_on": "Sunday"
                  },
                  "message": {
                    "desc": "OK",
                    "service": "sell_out",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "The confirmed declaration, `week_ends_on` / `week_anchor_dow`, `covers` / `location_grain`, `currency`, `prices_are` / `price_tax_basis`, `missing_row_means` / `absence_convention`, `confirmed_by`, `confirmed_by_name`, `confirmed_at`, or `data: null` when nobody has confirmed for this account yet.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No account was named",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What one account has confirmed about its own sell-out reports",
        "tags": [
          "sell_out"
        ],
        "x-tightly-scopes": [
          "sell_out:read"
        ]
      },
      "post": {
        "description": "Records a person's answers for one account: `location_grain` (`store`, `dc` or `none`), `currency`, `price_tax_basis` and `absence_convention`, all four required in one call, with `account_id` in the query. Re-confirming REPLACES them, which is how a retailer that has changed its reporting week is corrected.\n\nAll four or none. A declaration missing one of them would have to be completed by a default, and the reason these four exist is that every default is wrong for about half of all accounts.\n\nValues are checked here and again by the database, and the database constraints are the ones that hold; this validation exists so a person gets a sentence rather than a constraint violation.\n\nCall it once per account before import_sell_out, and read it back with get_sell_out_declaration.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `sell_out:write`, which includes `sell_out:read`.",
        "operationId": "confirm_sell_out_declaration",
        "parameters": [
          {
            "description": "The account these answers are for. An id, never a name.",
            "in": "query",
            "name": "account_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "absence_convention": {
                    "description": "Whether a product missing from the file sold none (`explicit_zero`, they write a zero, so an absent row is genuinely absent data) or was simply not reported (`row_omitted`). This is the answer that cannot be defaulted.\n",
                    "enum": [
                      "explicit_zero",
                      "row_omitted"
                    ],
                    "type": "string"
                  },
                  "currency": {
                    "description": "Three-letter code, e.g. GBP or EUR.",
                    "type": "string"
                  },
                  "location_grain": {
                    "description": "What a row covers, one store, one distribution centre, or the whole account.",
                    "enum": [
                      "store",
                      "dc",
                      "none"
                    ],
                    "type": "string"
                  },
                  "price_tax_basis": {
                    "description": "Whether the prices in the file include tax.",
                    "enum": [
                      "inclusive",
                      "exclusive",
                      "unknown"
                    ],
                    "type": "string"
                  },
                  "source_profile_key": {
                    "description": "The format profile these answers were read from, when a file proposed them.",
                    "type": "string"
                  },
                  "week_anchor_dow": {
                    "description": "The day their week ends on, 0 for Monday through 6 for Sunday. Absent when the export carries no date at all and there is no anchor to declare, inventing one would be the same defect the field exists to prevent.\n",
                    "maximum": 6,
                    "minimum": 0,
                    "type": "integer"
                  }
                },
                "required": [
                  "location_grain",
                  "currency",
                  "price_tax_basis",
                  "absence_convention"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "absence_convention": "row_omitted",
                    "confirmed_at": "2026-08-24T10:41:03+00:00",
                    "confirmed_by": "66c1f0a2e4b09a3d5c7f1a02",
                    "confirmed_by_name": "Priya Raman",
                    "covers": "Each store",
                    "currency": "GBP",
                    "location_grain": "store",
                    "missing_row_means": "The retailer did not report it",
                    "price_tax_basis": "inclusive",
                    "prices_are": "Including tax",
                    "week_anchor_dow": 6,
                    "week_ends_on": "Sunday"
                  },
                  "message": {
                    "desc": "recorded; this account's reports can now be imported",
                    "service": "sell_out",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "type": "object"
                }
              }
            },
            "description": "The stored declaration, in the same shape `GET /sell-out/declaration` serves, including `confirmed_by` and the `confirmed_by_name` resolved for it. This account's reports can now be imported.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No account was named, or one of the four is missing or outside its closed list. The refusal says which and what to answer.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Confirm the four things Tightly must know about an account's reports, once per account",
        "tags": [
          "sell_out"
        ],
        "x-tightly-scopes": [
          "sell_out:write"
        ]
      }
    },
    "/api/v1/sell-out/doors": {
      "get": {
        "description": "One account's sell-out by door: units sold, stock on hand, variants and weeks reported, the last week reported and the sell-through rate per door, ranked by what they sold, with the account's totals beside.\n\n`account_id` is required and `weeks` bounds the window. \"24 doors clearing 98%\" and \"3 doors carrying the whole number\" are the same headline and opposite buys.\n\nFour optional narrowings, composing with AND: `variant_id` is the SKU, `product_id` the style, `family_id` the product family, `category` the catalogue category matched on its id or its name, exactly and case-sensitively. Every per-door figure narrows, `by_week` included, and all four are echoed. Unstated, nothing narrows.\n\nThe door list is the same under every filter: a door with nothing in scope keeps its row and reads `units: 0`, `on_hand` and `sell_through` null. `scope_reason` says why a narrowing could not be made.\n\n`sell_through` is null rather than 0 wherever either side is unreported, since folding an unreported side to zero manufactures the 100% and 0% rates the write path refuses. `grain` is read from the rows: an account reporting one total answers `grain: account` and an empty `doors` list.\n\n`units` and `on_hand` are not summed the same way. `units` is a flow, so a window of it is that window's sales. `on_hand` is a level: each variant's LAST reported reading at that door, summed within the door.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `sell_out:read`.",
        "operationId": "get_sell_out_doors",
        "parameters": [
          {
            "description": "The account. An id, never a name. Nothing in a retailer's export reliably says who sent it, a filename is not evidence, and a wrong guess files a whole retailer's sell-through under another account's name.\n",
            "in": "query",
            "name": "account_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many weeks back to read. Clamped to 1 to 52.",
            "in": "query",
            "name": "weeks",
            "required": false,
            "schema": {
              "default": 12,
              "maximum": 52,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Narrow every figure to one style, resolved to all of its variants. A plan is decided per garment per door, and unstated this read answers for the whole account. Sent empty it is unstated, not a style with no id.\n",
            "in": "query",
            "name": "product_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Narrow every figure to one SKU. Bound to what the account reported rather than to the catalogue, so a SKU nobody has mapped still answers.\n",
            "in": "query",
            "name": "variant_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Narrow every figure to one product family. A catalogue with no families answers a page of zeros with `scope_reason` saying so, rather than a filter that quietly matched everything.\n",
            "in": "query",
            "name": "family_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Narrow every figure to one catalogue category, matched on the category's id or on its name, because the word a merchandiser has is the name. The match is exact and case-sensitive: \"knitwear\" does not find \"Knitwear\".\n",
            "in": "query",
            "name": "category",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "account_total": {
                      "by_week": {
                        "2026-06-08/2026-06-14": {
                          "grain": "week",
                          "on_hand": 742,
                          "period_days": 7,
                          "period_end": "2026-06-14",
                          "period_start": "2026-06-08",
                          "units": 201,
                          "variants": 96
                        },
                        "2026-06-15/2026-06-21": {
                          "grain": "week",
                          "on_hand": 701,
                          "period_days": 7,
                          "period_end": "2026-06-21",
                          "period_start": "2026-06-15",
                          "units": 188,
                          "variants": 96
                        },
                        "2026-06-22/2026-06-28": {
                          "grain": "week",
                          "on_hand": 663,
                          "period_days": 7,
                          "period_end": "2026-06-28",
                          "period_start": "2026-06-22",
                          "units": 176,
                          "variants": 95
                        },
                        "2026-06-29/2026-07-05": {
                          "grain": "week",
                          "on_hand": 612,
                          "period_days": 7,
                          "period_end": "2026-07-05",
                          "period_start": "2026-06-29",
                          "units": 231,
                          "variants": 131
                        },
                        "2026-07-06/2026-07-12": {
                          "grain": "week",
                          "on_hand": 571,
                          "period_days": 7,
                          "period_end": "2026-07-12",
                          "period_start": "2026-07-06",
                          "units": 213,
                          "variants": 127
                        },
                        "2026-07-13/2026-07-19": {
                          "grain": "week",
                          "on_hand": 528,
                          "period_days": 7,
                          "period_end": "2026-07-19",
                          "period_start": "2026-07-13",
                          "units": 201,
                          "variants": 125
                        },
                        "2026-07-20/2026-07-26": {
                          "grain": "week",
                          "on_hand": 489,
                          "period_days": 7,
                          "period_end": "2026-07-26",
                          "period_start": "2026-07-20",
                          "units": 194,
                          "variants": 131
                        },
                        "2026-07-27/2026-08-02": {
                          "grain": "week",
                          "on_hand": 447,
                          "period_days": 7,
                          "period_end": "2026-08-02",
                          "period_start": "2026-07-27",
                          "units": 184,
                          "variants": 123
                        },
                        "2026-08-03/2026-08-09": {
                          "grain": "week",
                          "on_hand": 404,
                          "period_days": 7,
                          "period_end": "2026-08-09",
                          "period_start": "2026-08-03",
                          "units": 174,
                          "variants": 120
                        },
                        "2026-08-10/2026-08-16": {
                          "grain": "week",
                          "on_hand": 368,
                          "period_days": 7,
                          "period_end": "2026-08-16",
                          "period_start": "2026-08-10",
                          "units": 168,
                          "variants": 121
                        },
                        "2026-08-17/2026-08-23": {
                          "grain": "week",
                          "on_hand": 331,
                          "period_days": 7,
                          "period_end": "2026-08-23",
                          "period_start": "2026-08-17",
                          "units": 169,
                          "variants": 116
                        },
                        "2026-08-24/2026-08-30": {
                          "grain": "week",
                          "on_hand": 302,
                          "period_days": 7,
                          "period_end": "2026-08-30",
                          "period_start": "2026-08-24",
                          "units": 154,
                          "variants": 112
                        }
                      },
                      "currency": "GBP",
                      "on_hand": 302,
                      "returned": 47,
                      "returns_absent": null,
                      "sell_through": 0.882,
                      "units": 2253,
                      "value_absent": null,
                      "value_sold_cents": 4963200
                    },
                    "category": null,
                    "doors": [
                      {
                        "by_week": {
                          "2026-06-08/2026-06-14": {
                            "grain": "week",
                            "on_hand": 742,
                            "period_days": 7,
                            "period_end": "2026-06-14",
                            "period_start": "2026-06-08",
                            "units": 201,
                            "variants": 96
                          },
                          "2026-06-15/2026-06-21": {
                            "grain": "week",
                            "on_hand": 701,
                            "period_days": 7,
                            "period_end": "2026-06-21",
                            "period_start": "2026-06-15",
                            "units": 188,
                            "variants": 96
                          },
                          "2026-06-22/2026-06-28": {
                            "grain": "week",
                            "on_hand": 663,
                            "period_days": 7,
                            "period_end": "2026-06-28",
                            "period_start": "2026-06-22",
                            "units": 176,
                            "variants": 95
                          },
                          "2026-06-29/2026-07-05": {
                            "grain": "week",
                            "on_hand": 612,
                            "period_days": 7,
                            "period_end": "2026-07-05",
                            "period_start": "2026-06-29",
                            "units": 169,
                            "variants": 96
                          },
                          "2026-07-06/2026-07-12": {
                            "grain": "week",
                            "on_hand": 571,
                            "period_days": 7,
                            "period_end": "2026-07-12",
                            "period_start": "2026-07-06",
                            "units": 158,
                            "variants": 94
                          },
                          "2026-07-13/2026-07-19": {
                            "grain": "week",
                            "on_hand": 528,
                            "period_days": 7,
                            "period_end": "2026-07-19",
                            "period_start": "2026-07-13",
                            "units": 150,
                            "variants": 93
                          },
                          "2026-07-20/2026-07-26": {
                            "grain": "week",
                            "on_hand": 489,
                            "period_days": 7,
                            "period_end": "2026-07-26",
                            "period_start": "2026-07-20",
                            "units": 146,
                            "variants": 96
                          },
                          "2026-07-27/2026-08-02": {
                            "grain": "week",
                            "on_hand": 447,
                            "period_days": 7,
                            "period_end": "2026-08-02",
                            "period_start": "2026-07-27",
                            "units": 140,
                            "variants": 92
                          },
                          "2026-08-03/2026-08-09": {
                            "grain": "week",
                            "on_hand": 404,
                            "period_days": 7,
                            "period_end": "2026-08-09",
                            "period_start": "2026-08-03",
                            "units": 132,
                            "variants": 90
                          },
                          "2026-08-10/2026-08-16": {
                            "grain": "week",
                            "on_hand": 368,
                            "period_days": 7,
                            "period_end": "2026-08-16",
                            "period_start": "2026-08-10",
                            "units": 128,
                            "variants": 91
                          },
                          "2026-08-17/2026-08-23": {
                            "grain": "week",
                            "on_hand": 331,
                            "period_days": 7,
                            "period_end": "2026-08-23",
                            "period_start": "2026-08-17",
                            "units": 131,
                            "variants": 88
                          },
                          "2026-08-24/2026-08-30": {
                            "grain": "week",
                            "on_hand": 302,
                            "period_days": 7,
                            "period_end": "2026-08-30",
                            "period_start": "2026-08-24",
                            "units": 122,
                            "variants": 86
                          }
                        },
                        "coverage_sentence": "12 weeks reported",
                        "currency": "GBP",
                        "door_id": "884",
                        "last_reported": "2026-08-30",
                        "name": "Oxford Street",
                        "on_hand": 302,
                        "period_grain": "week",
                        "periods_reported": 12,
                        "returned": 47,
                        "returns_absent": null,
                        "sell_through": 0.859,
                        "units": 1841,
                        "value_absent": null,
                        "value_sold_cents": 4963200,
                        "variants": 96,
                        "weekly_rate_absent": null,
                        "weeks_reported": 12
                      },
                      {
                        "by_week": {
                          "2026-06-29/2026-07-05": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-07-05",
                            "period_start": "2026-06-29",
                            "units": 62,
                            "variants": 61
                          },
                          "2026-07-06/2026-07-12": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-07-12",
                            "period_start": "2026-07-06",
                            "units": 55,
                            "variants": 60
                          },
                          "2026-07-13/2026-07-19": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-07-19",
                            "period_start": "2026-07-13",
                            "units": 51,
                            "variants": 58
                          },
                          "2026-07-20/2026-07-26": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-07-26",
                            "period_start": "2026-07-20",
                            "units": 48,
                            "variants": 61
                          },
                          "2026-07-27/2026-08-02": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-08-02",
                            "period_start": "2026-07-27",
                            "units": 44,
                            "variants": 57
                          },
                          "2026-08-03/2026-08-09": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-08-09",
                            "period_start": "2026-08-03",
                            "units": 42,
                            "variants": 56
                          },
                          "2026-08-10/2026-08-16": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-08-16",
                            "period_start": "2026-08-10",
                            "units": 40,
                            "variants": 59
                          },
                          "2026-08-17/2026-08-23": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-08-23",
                            "period_start": "2026-08-17",
                            "units": 38,
                            "variants": 55
                          },
                          "2026-08-24/2026-08-30": {
                            "grain": "week",
                            "on_hand": null,
                            "period_days": 7,
                            "period_end": "2026-08-30",
                            "period_start": "2026-08-24",
                            "units": 32,
                            "variants": 52
                          }
                        },
                        "coverage_sentence": "9 weeks reported",
                        "currency": null,
                        "door_id": "887",
                        "last_reported": "2026-08-30",
                        "name": "Trafford Centre",
                        "on_hand": null,
                        "period_grain": "week",
                        "periods_reported": 9,
                        "returned": null,
                        "returns_absent": "Not reported",
                        "sell_through": null,
                        "units": 412,
                        "value_absent": "Not reported",
                        "value_sold_cents": null,
                        "variants": 61,
                        "weekly_rate_absent": null,
                        "weeks_reported": 9
                      }
                    ],
                    "family_id": null,
                    "grain": "store",
                    "product_id": null,
                    "scope_reason": null,
                    "trading_partner_id": "tp_00417",
                    "variant_id": null,
                    "weeks": 12
                  },
                  "message": {
                    "desc": "OK",
                    "service": "sell_out",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "account_total": {
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "category": {
                          "description": "The catalogue category every figure is about, echoed, or null.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "doors": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "family_id": {
                          "description": "The product family every figure is about, echoed, or null.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "grain": {
                          "description": "`store` where doors are named, `account` where one total is reported, null where nothing is.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "period_axis": {
                          "description": "Exact cell keys and bounds, preserving distinct weekly and monthly periods with the same start.",
                          "items": {
                            "properties": {
                              "grain": {
                                "type": "string"
                              },
                              "key": {
                                "type": "string"
                              },
                              "period_days": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "period_end": {
                                "format": "date",
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "period_start": {
                                "format": "date",
                                "type": "string"
                              }
                            },
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "product_id": {
                          "description": "The style every figure is about, echoed, or null for the whole account.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scope_reason": {
                          "description": "Why a narrowing could not be made, or null. A catalogue with no product families cannot answer `family_id`, and a filter that matched nothing looks exactly like one this catalogue cannot answer once every figure is a zero.\n",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "trading_partner_id": {
                          "type": "string"
                        },
                        "variant_id": {
                          "description": "The SKU every figure is about, echoed, or null for the whole account.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "weeks": {
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One row per door with units, on-hand, variants, weeks reported and sell-through, plus the money the account reported and the units that came back, the account total, the grain the account actually reports at, the four narrowings echoed (`product_id`, `variant_id`, `family_id`, `category`), each null for the whole account, and `scope_reason` where a narrowing could not be made. A door with nothing in the scope is a row with `units: 0`, never a dropped row.\n\n`by_week` is the periods behind each row, KEYED ON THE PERIOD: unambiguous weekly start dates and otherwise start/end dates, `2026-08-01/2026-08-31`, and not on the start alone, because a month and a week that open on the same day are two periods and one would silently replace the other. Each cell holds `units`, `on_hand` and `variants`, and states its own extent with `period_start`, `period_end`, `period_days` and `grain`, so a face draws ONE band across a month rather than four cells the account never sent. A period the account did not report is absent from the map, never present as a zero: a dead week and a week nobody sent are different facts. A week that carried stock and no sales is present with a null `units`. The cells' `units` add up to the row's `units`; a cell's `on_hand` is that week's shelf reading and adds up to nothing, since the row's own `on_hand` is each variant's last reading. The account total carries the same map, which for an account reporting one total is the only period series it has.\n\nEVERY ROW NAMES ITS GRAIN. `weeks_reported` counts PERIODS and its name predates the accounts that file whole calendar months; `period_grain` says what a period is, `periods_reported` is the same count without a unit in its name, `coverage_sentence` is what a face prints (\"4 months reported\"), and `weekly_rate_absent` carries the sentence a per-week rate is refused with on a monthly account. A month is never divided by 4.33, and an account that reports both keeps its weekly rate because it has weeks.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No account was named.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One account's sell-out by door, the grain a plan is actually delivered at",
        "tags": [
          "sell_out"
        ],
        "x-tightly-scopes": [
          "sell_out:read"
        ]
      }
    },
    "/api/v1/sell-out/import": {
      "post": {
        "description": "Import a retailer report for the explicitly named account_id; the account is never inferred. The account declaration and mapping must be confirmed. Reports without a date require week_start (and week_end where appropriate). Unrecognized formats or incomplete declarations are refused. Existing idempotency and account-bound authorization apply to both input sources. Send original CSV/XLSX/XLS bytes inline (up to 8 MiB), or use /files/presigned-url and send application/json with exactly {\"s3_key\":\"organization/user/uploaded-file.csv\"} (up to 100 MiB). Uploaded keys must belong to the authenticated organization; foreign keys and URLs are refused. Both sources use the same native retailer parser and preserve its period and provenance. Use confirm_sell_out_declaration to confirm account answers before importing; use preview_sell_out to inspect the same file without writing. not_matched.by_reason counts every row by resolver outcome, including matches; not_matched.total counts refused rows, and not_matched.by_reason_words explains each outcome. Repeated files report both rows submitted and rows actually changed. Requires Tightly Connect; unavailable plans return 403 plan_excludes.\n\nScope: `sell_out:write`, which includes `sell_out:read`.",
        "operationId": "import_sell_out",
        "parameters": [
          {
            "description": "The account that sent this report. An id, never a name. \"Intersport\" is a dozen buying groups.\n",
            "in": "query",
            "name": "account_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The week this file covers (YYYY-MM-DD), for the retailers whose export carries no date anywhere in it. Required on those files rather than defaulted: writing \"this week\" for a file that never said so invents a week nobody reported.\n",
            "in": "query",
            "name": "week_start",
            "required": false,
            "schema": {
              "format": "date",
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "s3_key": {
                    "description": "Key from the authenticated organization's existing presigned upload flow.",
                    "maxLength": 1024,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "s3_key"
                ],
                "type": "object"
              }
            },
            "application/octet-stream": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            },
            "application/vnd.ms-excel": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            },
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            },
            "text/csv": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "description": "Original retailer bytes up to 8 MiB inline, or a tenant-owned upload key up to 100 MiB.\n",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "confirmed": {
                      "absence_convention": "row_omitted",
                      "confirmed_at": "2026-08-24T10:41:03+00:00",
                      "confirmed_by": "66c1f0a2e4b09a3d5c7f1a02",
                      "confirmed_by_name": "Priya Raman",
                      "covers": "Each store",
                      "currency": "GBP",
                      "location_grain": "store",
                      "missing_row_means": "The retailer did not report it",
                      "price_tax_basis": "inclusive",
                      "prices_are": "Including tax",
                      "week_anchor_dow": 6,
                      "week_ends_on": "Sunday"
                    },
                    "needs_you": [],
                    "not_matched": {
                      "by_reason": {
                        "matched": 62916,
                        "unknown_product": 118
                      },
                      "by_reason_words": {
                        "matched": "we carry this product",
                        "unknown_product": "a valid barcode for a product that is not in your catalogue"
                      },
                      "examples": [
                        "5010029000016"
                      ],
                      "total": 118
                    },
                    "period": {
                      "end": "2026-08-30",
                      "start": "2026-08-24"
                    },
                    "recognised_as": "Intersport weekly sell-through",
                    "refused": null,
                    "reorder_rules": null,
                    "rows_changed": 4118,
                    "rows_read": 63034,
                    "rows_written": 62916,
                    "we_read_it_as": null
                  },
                  "message": {
                    "desc": "62,916 rows recorded, 4,118 changed",
                    "service": "sell_out",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "confirmed": {
                          "description": "What this account has already confirmed, so a drift reads as a difference rather than a fresh interrogation.",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "needs_you": {
                          "description": "The confirmations still outstanding, each a question with the answer we would propose.",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "not_matched": {
                          "description": "Every row under the outcome the resolver reached. `total` is the rows no product could be resolved for. `by_reason` is keyed by the resolver's own tokens (`matched`, `matched_on_sku_only`, `invalid_barcode`, `unknown_product`, `no_identifier`) and counts all of them, so the counts sum to `rows_read`. `by_reason_words` carries one sentence per counted key, and `examples` names the values that could not be placed.\n",
                          "type": "object"
                        },
                        "period": {
                          "description": "The week the file resolved to, as start and end. Where the account's own column mapping reads the file, the week is taken from its period-end column when one is mapped and from its period-start column otherwise, and either way it is widened to the week the account confirmed its weeks end on.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "recognised_as": {
                          "description": "What read the file: the format profile that claimed it, or, where none did, the column mapping confirmed for this account, named with its version.\n",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "refused": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "reorder_rules": {
                          "description": "The account's OWN reorder policy, where their file carried one: `read` is how many (account, variant) rules landed and `not_stated` how many of their rows could not state one. NULL rather than a zero for an account that sends no policy sheet at all, which is almost all of them: measured across nine real retailer exports, one does. \"0 rules read\" on every import would read as a failure of ours rather than as a thing that account does not do, so absence is absence, and a zero beside a refusal count means their sheet was there and none of it landed.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "rows_changed": {
                          "description": "What actually moved. Re-sending a week you already sent is a no-op, and this says so.",
                          "type": "integer"
                        },
                        "rows_read": {
                          "type": "integer"
                        },
                        "rows_written": {
                          "type": "integer"
                        },
                        "we_read_it_as": {
                          "description": "The declaration the file itself proposes, to accept or correct. `week_ends_on` and `week_anchor_dow` name the day the retailer's reporting week ends, whichever day the file's own dates carry: a retailer who dates each week by its first day is proposed the last day of that week, which is the question the account was asked.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What landed. `recognised_as` and `period`, `rows_read` / `rows_written` / `rows_changed`, `not_matched` (`total`, `by_reason`, `by_reason_words`, `examples`), the account's `confirmed` declaration, the `we_read_it_as` the profile proposed, and `reorder_rules` where the file carried the retailer's own reorder policy.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No file, no account, a format we do not recognise yet, a file too large to send inline, or a declaration this account has not confirmed, each with its own sentence, and the same payload so the four questions can be answered without re-uploading.\n\nAn account whose column mapping is confirmed but cannot read this particular file is refused with `data.reason`: `not_declared`, `no_door` (the account reports by door and no column holds the door code), `mixed_currency` (the file prices in two currencies), `unreadable_file`, `no_columns` or `nothing_to_read`. A retailer already mapped is never turned away as one we do not recognise; the reason names the step that is actually missing.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Land a retailer's own sell-out report against one named account",
        "tags": [
          "sell_out"
        ],
        "x-tightly-scopes": [
          "sell_out:write"
        ]
      }
    },
    "/api/v1/sell-out/preview": {
      "post": {
        "description": "Preview the recognized profile, period, matched rows and refusal reasons without importing sell-out. account_id is optional; when supplied, the account's confirmed declaration and mapping are used. Unrecognized account reports return a mapping proposal for review. Preview outcome counts use the same resolver as import; rows_written and rows_changed remain zero. Send original CSV/XLSX/XLS bytes inline (up to 8 MiB), or use /files/presigned-url and send application/json with exactly {\"s3_key\":\"organization/user/uploaded-file.csv\"} (up to 100 MiB). Uploaded keys must belong to the authenticated organization; foreign keys and URLs are refused. Both sources use the same native retailer parser and preserve its period and provenance. Use import_sell_out to record the report; preview never imports it. Use get_sell_out_declaration to check the account declaration without supplying a file. Requires Tightly Connect; unavailable plans return 403 plan_excludes.\n\nScope: `sell_out:write`, which includes `sell_out:read`.",
        "operationId": "preview_sell_out",
        "parameters": [
          {
            "description": "The account, when it is known. Given it, the preview asks only what has moved since this account last confirmed, and a file no profile recognises comes back as a column mapping to confirm rather than as a refusal.\n",
            "in": "query",
            "name": "account_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "What the file was called, recorded against the mapping proposal so a person reviewing it later can tell which upload it was matched from. Ignored on a file a profile already recognises.\n",
            "in": "query",
            "name": "file_name",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "properties": {
                  "s3_key": {
                    "description": "Key from the authenticated organization's existing presigned upload flow.",
                    "maxLength": 1024,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "s3_key"
                ],
                "type": "object"
              }
            },
            "application/octet-stream": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            },
            "application/vnd.ms-excel": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            },
            "text/csv": {
              "schema": {
                "type": "string"
              }
            }
          },
          "description": "Original retailer bytes up to 8 MiB inline, or a tenant-owned upload key up to 100 MiB.\n",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "confirmed": null,
                    "needs_you": [
                      {
                        "detail": "This retailer lists only movement in the weeks we have seen.",
                        "kind": "absence_convention",
                        "proposed": "row_omitted",
                        "question": "Does a product missing from this file mean it sold none?"
                      }
                    ],
                    "not_matched": {
                      "by_reason": {
                        "matched": 62916,
                        "unknown_product": 118
                      },
                      "by_reason_words": {
                        "matched": "we carry this product",
                        "unknown_product": "a valid barcode for a product that is not in your catalogue"
                      },
                      "examples": [
                        "5010029000016"
                      ],
                      "total": 118
                    },
                    "period": {
                      "end": "2026-08-30",
                      "start": "2026-08-24"
                    },
                    "recognised_as": "Intersport weekly sell-through",
                    "refused": null,
                    "rows_changed": 0,
                    "rows_read": 63034,
                    "rows_written": 0,
                    "we_read_it_as": {
                      "absence_convention": "row_omitted",
                      "confirmed_at": "2026-08-24T10:41:03+00:00",
                      "confirmed_by": "66c1f0a2e4b09a3d5c7f1a02",
                      "confirmed_by_name": "Priya Raman",
                      "covers": "Each store",
                      "currency": "GBP",
                      "location_grain": "store",
                      "missing_row_means": "The retailer did not report it",
                      "price_tax_basis": "inclusive",
                      "prices_are": "Including tax",
                      "week_anchor_dow": 6,
                      "week_ends_on": "Sunday"
                    }
                  },
                  "message": {
                    "desc": "preview only; nothing has been imported",
                    "service": "sell_out",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "confirmed": {
                          "description": "What this account has already confirmed, so a drift reads as a difference rather than a fresh interrogation. It travels beside `mapping` as well: an account that answered the four and has no profile yet sees its own answers on the proposal rather than a blank slate.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "mapping": {
                          "description": "Present only on a named account no profile recognises. `decisions` holds one entry per field sell-out is stored in, keyed by field, each with the source column proposed, a state, a confidence and the reason for it; `sample_values` holds up to three real values per proposed column; `summary` counts what is matched and what still needs a decision; `declared_terms` carries the account's four answers as rows in the same grammar; `sample` reports how much of the file was read and archived. Absent, or null, whenever a profile claimed the file.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "needs_you": {
                          "description": "The confirmations still outstanding, each a question with the answer we would propose.",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "not_matched": {
                          "description": "What the import will find, counted by the resolver the import runs. `total` is the rows no product could be resolved for. `by_reason` counts EVERY row under its outcome, keyed by the resolver's own tokens (`matched`, `matched_on_sku_only`, `invalid_barcode`, `unknown_product`, `no_identifier`), so the counts sum to `rows_read`. `by_reason_words` carries one sentence per counted key, and `examples` names the values that could not be placed.\n",
                          "type": "object"
                        },
                        "period": {
                          "description": "The week the file resolved to, as start and end. Where the account's own column mapping reads the file, the week is taken from its period-end column when one is mapped and from its period-start column otherwise, and either way it is widened to the week the account confirmed its weeks end on.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "recognised_as": {
                          "description": "The format profile that claimed the file.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "refused": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "rows_changed": {
                          "description": "What actually moved. Re-sending a week you already sent is a no-op, and this says so.",
                          "type": "integer"
                        },
                        "rows_read": {
                          "type": "integer"
                        },
                        "rows_written": {
                          "type": "integer"
                        },
                        "we_read_it_as": {
                          "description": "The declaration the file itself proposes, to accept or correct. `week_ends_on` and `week_anchor_dow` name the day the retailer's reporting week ends, whichever day the file's own dates carry: a retailer who dates each week by its first day is proposed the last day of that week, which is the question the account was asked.\n",
                          "type": [
                            "object",
                            "null"
                          ]
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What the import would do. `rows_written` and `rows_changed` are zero here by construction, this call writes nothing.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "No file was received, the file is over 8 MB, or the format is not recognised yet. The refusal says what to do next. An unrecognised file is a request for a profile, not a dead end.\n\nAn account whose column mapping is confirmed but cannot read this particular file is refused here too, and that refusal carries `data.reason`: `not_declared` (the four have not been answered), `no_door` (the account reports by door and no column holds the door code), `mixed_currency` (the file prices in two currencies), `unreadable_file`, `no_columns` or `nothing_to_read`. Each reason names the next step; none of them means the retailer is unknown.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Sell-out is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Sell-out; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in `account_id`, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Recognise a retailer's report and say what would happen, writes nothing",
        "tags": [
          "sell_out"
        ],
        "x-tightly-scopes": [
          "sell_out:write"
        ]
      }
    },
    "/api/v1/stocktakes": {
      "post": {
        "description": "Opens one count and returns it. The body takes `name`, `counted_on`, `location_ids` (at least one) and then either `variant_ids` or `include_all_variants: true`, one or the other, and sending both is refused 400.\n\nEach line snapshots the system quantity at the moment the count is created, so the variance a warehouse reviews cannot move under it while the shelf is being counted. The count opens `status: open` with every `counted_quantity` null and `counted_line_count` 0.\n\n`include_all_variants` covers every variant holding stock at the chosen locations. A selection resolving to more than 20,000 lines is refused 400 naming the figure, and nothing is created; narrow it by location or name the variants. A selection resolving to no lines at all is refused the same way.\n\n`counted_on` is the date the shelf was counted, not the date of this call, so a count keyed in the morning after can still be dated correctly. It does not have to be today.\n\nThen set the counts, update_stocktake_counts by hand, or import_stocktake_counts from a file, and close it with post_stocktake. Nothing moves a stock level until it is posted.\n\nScope: `stocktakes:write`, which includes `stocktakes:read`.",
        "operationId": "create_stocktake",
        "parameters": [
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "counted_on": "2026-08-10",
                "include_all_variants": false,
                "location_ids": [
                  "loc1"
                ],
                "name": "Cycle count of fast movers",
                "variant_ids": [
                  "v1",
                  "v2"
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/CreateStocktakeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "counted_line_count": 0,
                    "counted_on": "2026-08-10",
                    "counted_total": 0,
                    "id": "1",
                    "line_count": 24,
                    "location_names": [
                      "Collect"
                    ],
                    "name": "Cycle count of fast movers",
                    "posted_at": null,
                    "status": "open",
                    "system_total": 0,
                    "variance_total": 0,
                    "variance_value_total": 0.0
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CreateStocktakePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The count, opened, with its lines snapshotted and nothing counted yet.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "An empty name; no location; neither `variant_ids` nor `include_all_variants`, or both; a location this organisation does not have; a scope resolving to no lines; or one resolving to more than 20,000 lines, refused with the figure, \"This selection would create 24,310 lines. Narrow it to 20,000 or fewer.\"\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8e3b603fdcf44993b618b5ff7a2868ad",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_391944cd161d4b5a97359d0d06d0b28d",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key holds Stocktakes for reading only, or not at all; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Open a stock count over a set of locations and a product selection",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:write"
        ]
      }
    },
    "/api/v1/stocktakes/table": {
      "get": {
        "description": "One page of counts, newest scope first, each row carrying what the count covers and what it found: `line_count` and `counted_line_count`, the `system_total` and `counted_total` over the counted lines only, and `variance_total` (which is exactly `counted_total - system_total`) with `variance_value_total` beside it.\n\n`status` is `open` or `posted`. An open count is still being counted and its figures move; a posted one is frozen and carries `posted_at`. Filter on it with `filter_args=[{\"key\":\"status\",\"operation\":\"eq\",\"value\":\"open\"}]`, the only filterable key.\n\n`search` matches the count's name. `sort_args` takes `name`, `status`, `counted_on`, `line_count`, `variance_total` or `variance_value_total`, prefixed `-` for descending. `limit` defaults to 10 and `offset` to 0; `max_size` is every count on file and `filtered_max_size` is how many the search and filters left, so a caller pages on the second.\n\n`id` is a string on the wire and is what the by-id operations take. `location_names` is for display only. A count is scoped by location id at creation and the names are resolved on read.\n\nScope: `stocktakes:read`.",
        "operationId": "get_stocktakes_table",
        "parameters": [
          {
            "description": "Match against the count's name. Omitted, every count is in scope.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "Cycle count"
              ],
              "type": "string"
            }
          },
          {
            "description": "How many rows to return. Defaults to 10; the platform cap is 10,000.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "examples": [
                10
              ],
              "maximum": 10000,
              "type": "integer"
            }
          },
          {
            "description": "How many rows to skip. Page against `filtered_max_size`, not `max_size`.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "examples": [
                0
              ],
              "type": "integer"
            }
          },
          {
            "description": "Sort fields, comma-separated, `-` for descending and `+` or nothing for ascending. One of name, status, counted_on, line_count, variance_total, variance_value_total.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "-counted_on"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of `{key, operation, value}`. `status` is the only key, taking eq, ne, in or nin against `open` and `posted`; `in` and `nin` take an array as the value.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"status\",\"operation\":\"eq\",\"value\":\"open\"}]"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 2,
                    "max_size": 2,
                    "offset": 0,
                    "rows": [
                      {
                        "counted_line_count": 6,
                        "counted_on": "2026-08-10",
                        "counted_total": 71,
                        "id": "1",
                        "line_count": 24,
                        "location_names": [
                          "Collect"
                        ],
                        "name": "Cycle count of fast movers",
                        "posted_at": null,
                        "status": "open",
                        "system_total": 59,
                        "variance_total": 12,
                        "variance_value_total": 288.0
                      },
                      {
                        "counted_line_count": 1840,
                        "counted_on": "2026-06-30",
                        "counted_total": 41065,
                        "id": "2",
                        "line_count": 1840,
                        "location_names": [
                          "Collect",
                          "London 3PL"
                        ],
                        "name": "Quarter-end full count",
                        "posted_at": "2026-06-30T18:12:00+00:00",
                        "status": "posted",
                        "system_total": 41220,
                        "variance_total": -155,
                        "variance_value_total": -3720.0
                      }
                    ],
                    "size": 2
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetStocktakesTablePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One page of counts, with the totals each one found.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "`filter_args` is not JSON, names a key other than `status`, or carries a value that is not `open` or `posted`; or `limit` is over 10,000.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_888aaabb50584d329fda2ff1fa2db684",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_86097699e909475eaf35070acc778a4b",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stocktakes; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every stock count on file, with its totals and the variance it found",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:read"
        ]
      }
    },
    "/api/v1/stocktakes/variance/by-supplier": {
      "get": {
        "description": "Reads `system_quantity` against `counted_quantity` on POSTED counts only, and puts each counted line to the variant's default supplier. Open counts are never read: an unfinished count is not a measurement.\n\nLosses and gains are served separately and never netted, so there is no net field to reach for. A location that loses 500 and finds 480 has a counting problem, not a variance of 20. Nothing here is a shrink figure either, counting short can be theft, damage, a mis-shipment, a receiving error or a miscount, and counting long is found stock.\n\nEvery counted line lands in exactly one `buckets` entry, `default_supplier`, `no_default_supplier` or `no_supplier_on_file`, so the buckets sum to `totals` on every unit and line figure. The two unattributed buckets carry `attribution_reason` instead of a supplier and are never folded into one. On an attributed bucket, `multi_supplier_line_count` says how many of its lines came from variants carrying more than one supplier.\n\n`loss_cost` and `gain_cost` are null, never 0, where there is nothing priceable, and `cost_reason` says why; `*_units_unpriced` says how many units the money leaves out even when it is populated. `currency` is null unless one denomination covers the lot.\n\nA supplier with no posted count is not a supplier with clean stock: it is counted in `measurement.suppliers_with_no_reading` rather than served as a zero row. For the lines behind one count, use get_stocktake_lines_table.\n\nScope: `stocktakes:read`.",
        "operationId": "get_stock_variance_by_supplier",
        "parameters": [
          {
            "description": "Include only counts whose `counted_on` is on or after this date. Omit both dates to read every posted count on file; `measurement.window_source` reports which was used.\n",
            "in": "query",
            "name": "counted_from",
            "required": false,
            "schema": {
              "examples": [
                "2026-01-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Include only counts whose `counted_on` is on or before this date. A `counted_from` after `counted_to` is refused 400.\n",
            "in": "query",
            "name": "counted_to",
            "required": false,
            "schema": {
              "examples": [
                "2026-06-30"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Limit the read to these locations. Repeatable (`?location_id=a&location_id=b`), bracket-repeatable (`?location_id[]=a&location_id[]=b`) or comma-separated. Omit for every location.\n",
            "in": "query",
            "name": "location_id",
            "required": false,
            "schema": {
              "examples": [
                "loc1,loc2"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "buckets": [
                      {
                        "attribution": "default_supplier",
                        "attribution_reason": null,
                        "figures": {
                          "cost_reason": null,
                          "counted_line_count": 640,
                          "currency": "USD",
                          "currency_reason": null,
                          "gain_cost": 2304.0,
                          "gain_line_count": 21,
                          "gain_units": 96,
                          "gain_units_unpriced": 0,
                          "loss_cost": 19488.0,
                          "loss_line_count": 74,
                          "loss_units": 812,
                          "loss_units_unpriced": 0,
                          "matched_line_count": 545,
                          "variant_count": 210
                        },
                        "multi_supplier_line_count": 38,
                        "supplier_id": "sup_88",
                        "supplier_name": "Northbound Textiles"
                      },
                      {
                        "attribution": "no_default_supplier",
                        "attribution_reason": "These products are bought from more than one supplier and none is marked as the main one, so the variance cannot be put to a single supplier.",
                        "figures": {
                          "cost_reason": "60 of the 161 units that moved have no unit cost on file, so the value covers only the rest.",
                          "counted_line_count": 96,
                          "currency": "USD",
                          "currency_reason": null,
                          "gain_cost": 414.0,
                          "gain_line_count": 4,
                          "gain_units": 18,
                          "gain_units_unpriced": 0,
                          "loss_cost": 1992.0,
                          "loss_line_count": 11,
                          "loss_units": 143,
                          "loss_units_unpriced": 60,
                          "matched_line_count": 81,
                          "variant_count": 31
                        },
                        "multi_supplier_line_count": 96,
                        "supplier_id": null,
                        "supplier_name": null
                      },
                      {
                        "attribution": "no_supplier_on_file",
                        "attribution_reason": "No supplier is recorded against these products, so the variance cannot be attributed.",
                        "figures": {
                          "cost_reason": "No unit cost is on file for any of these products, so the variance is shown in units only.",
                          "counted_line_count": 48,
                          "currency": null,
                          "currency_reason": "There is no amount to denominate, because no unit cost is on file.",
                          "gain_cost": null,
                          "gain_line_count": 2,
                          "gain_units": 9,
                          "gain_units_unpriced": 9,
                          "loss_cost": null,
                          "loss_line_count": 6,
                          "loss_units": 55,
                          "loss_units_unpriced": 55,
                          "matched_line_count": 40,
                          "variant_count": 19
                        },
                        "multi_supplier_line_count": 0,
                        "supplier_id": null,
                        "supplier_name": null
                      }
                    ],
                    "measurement": {
                      "counted_line_count": 784,
                      "first_counted_on": "2026-02-14",
                      "last_counted_on": "2026-06-30",
                      "no_measurement_reason": null,
                      "open_stocktake_count": 1,
                      "posted_stocktake_count": 3,
                      "suppliers_measured": 12,
                      "suppliers_on_file": 31,
                      "suppliers_with_no_reading": 19,
                      "uncounted_line_count": 56,
                      "variants_measured": 260,
                      "variants_on_file": 1284,
                      "variants_with_no_reading": 1024,
                      "window_from": "2026-01-01",
                      "window_source": "declared",
                      "window_to": "2026-06-30"
                    },
                    "totals": {
                      "cost_reason": "124 of the 1,133 units that moved have no unit cost on file, so the value covers only the rest.",
                      "counted_line_count": 784,
                      "currency": "USD",
                      "currency_reason": null,
                      "gain_cost": 2718.0,
                      "gain_line_count": 27,
                      "gain_units": 123,
                      "gain_units_unpriced": 9,
                      "loss_cost": 21480.0,
                      "loss_line_count": 91,
                      "loss_units": 1010,
                      "loss_units_unpriced": 115,
                      "matched_line_count": 666,
                      "variant_count": 260
                    }
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetVarianceBySupplierPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What was measured, the totals over every counted line, and one bucket per attribution. The buckets sum to the totals.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A date could not be read, or `counted_from` is after `counted_to`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_289e254cfa8b445190e9a82161d09342",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_c4681405dcd9478285d14c036ff849b2",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stocktakes; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Stock variance measured from posted counts, attributed to each variant's default supplier",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:read"
        ]
      }
    },
    "/api/v1/stocktakes/{stocktake_id}": {
      "delete": {
        "description": "Deletes an open count and its lines outright. Takes no body, and answers 204 with none.\n\nOnly an open count can be discarded. A posted count is refused 400. It is a record of what a warehouse found on a date, and deleting it would remove the evidence behind every variance figure measured from it. To correct a posted count, open a new one.\n\nNothing about stock changes either way: an open count has never moved a level, so discarding it leaves the shelf exactly as it was.\n\nThis is not the way to close a count. post_stocktake records what was found; this throws it away, and a count discarded by mistake has to be created and counted again from nothing.\n\nScope: `stocktakes:write`, which includes `stocktakes:read`.",
        "operationId": "delete_stocktake",
        "parameters": [
          {
            "description": "The open count to discard.",
            "in": "path",
            "name": "stocktake_id",
            "required": true,
            "schema": {
              "examples": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "The count and its lines are gone. No body.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "The count has already been posted, \"This count has already been posted and can no longer be changed\".\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_953be509b8fb425ba10c9ea3dabd47a8",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_73ae26d866d74aecb80ff17683bffe28",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key holds Stocktakes for reading only, or not at all; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No count with this id in this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Discard an open count and every line in it",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:write"
        ]
      },
      "get": {
        "description": "One count by id, in the shape the table serves it: `status`, `counted_on`, the locations it covers, `line_count` and `counted_line_count`, and the totals, `system_total`, `counted_total`, `variance_total` and `variance_value_total`.\n\nThe totals cover COUNTED lines only, so `counted_total - system_total` is exactly `variance_total`. An uncounted line has no reading and is in `line_count` but not in `counted_line_count`, which is how a caller tells \"counted and matched\" from \"not yet counted\": the two are the same zero on a total and different facts about a shelf.\n\n`status` is `open` while counting and `posted` once the count is closed, when `posted_at` is stamped and the figures stop moving. Use get_stocktake_lines_table for the lines behind these totals.\n\nA count this organisation does not have is 404, never an empty body.\n\nScope: `stocktakes:read`.",
        "operationId": "get_stocktake",
        "parameters": [
          {
            "description": "The count's id, as `id` on every stocktake row.",
            "in": "path",
            "name": "stocktake_id",
            "required": true,
            "schema": {
              "examples": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "counted_line_count": 6,
                    "counted_on": "2026-08-10",
                    "counted_total": 71,
                    "id": "1",
                    "line_count": 24,
                    "location_names": [
                      "Collect"
                    ],
                    "name": "Cycle count of fast movers",
                    "posted_at": null,
                    "status": "open",
                    "system_total": 59,
                    "variance_total": 12,
                    "variance_value_total": 288.0
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetStocktakePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The count, with the locations it covers and the totals it found.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_815b7107f5714375a5b2f8c2b9bd382a",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_34eeeb57a25a4f0c9486db19059f1daa",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stocktakes; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No count with this id in this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One stock count, with its totals and the variance it found",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:read"
        ]
      }
    },
    "/api/v1/stocktakes/{stocktake_id}/counts": {
      "patch": {
        "description": "Writes counted quantities onto lines of an open count. The body is `counts`, a non-empty array of `{variant_id, location_id, counted_quantity}`; the pair addresses the line, so a variant counted at two locations is two entries.\n\nA `counted_quantity` of null clears a line back to uncounted, which is not the same as counting zero: a zero is a reading that says the shelf is empty, and a null says nobody has looked. A negative quantity is refused 400.\n\nPartial by design, send only the lines that were counted, as often as needed, and the rest stay as they were. Repeating a pair inside one body keeps the last entry rather than refusing, which is what a grid posting edits in order expects. A pair that is not in the count's scope is ignored rather than created: the scope is fixed when the count is opened.\n\nRefused 400 once the count is posted. A posted count is frozen, and a correction is a new count. Answers 204 with no body; read the count back with get_stocktake for the new totals. To apply a whole file of counts instead of naming lines, use import_stocktake_counts.\n\nScope: `stocktakes:write`, which includes `stocktakes:read`.",
        "operationId": "update_stocktake_counts",
        "parameters": [
          {
            "description": "The open count whose lines to write.",
            "in": "path",
            "name": "stocktake_id",
            "required": true,
            "schema": {
              "examples": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "counts": [
                  {
                    "counted_quantity": 37,
                    "location_id": "loc1",
                    "variant_id": "v1"
                  },
                  {
                    "counted_quantity": 0,
                    "location_id": "loc1",
                    "variant_id": "v2"
                  },
                  {
                    "counted_quantity": null,
                    "location_id": "loc1",
                    "variant_id": "v3"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/UpdateStocktakeCountsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "The counts were applied. No body; read the count back for its new totals.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "An empty `counts` array, a negative `counted_quantity`, or the count has already been posted, \"This count has already been posted and can no longer be changed\".\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_ffb6f7affcc14b26930164eb0ab699b1",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_a617e8e99538433e85b3cb097229c31d",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key holds Stocktakes for reading only, or not at all; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No count with this id in this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Set counted quantities on the lines of an open count",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:write"
        ]
      }
    },
    "/api/v1/stocktakes/{stocktake_id}/import": {
      "post": {
        "description": "Applies a whole file of counts to an open count. The body is `s3_key`, a file already uploaded through the shared presigned-URL flow, and `mappings`, a `{our_field: their_header}` object in the same shape `GET /files/mappings` returns. `sku` and `counted_quantity` are both required in the mapping; `location` is optional.\n\nRows match the count's lines on SKU, case-insensitively and ignoring surrounding spaces, and on location name as well when a `location` column is mapped. A SKU held at several locations in the count's scope with no location column is reported in `unmatched_skus` rather than guessed at: putting a count on the wrong shelf is worse than not putting it anywhere.\n\nThe response is a summary, not the lines: `updated` is how many lines took a count, `skipped` is how many rows were readable but changed nothing, and `unmatched_skus` lists what the file names and the count ignored, a SKU outside the scope, or an ambiguous one. A quantity that is not a number is skipped, not zeroed.\n\nRefused 400 once the count is posted. To set a handful of lines without a file, use update_stocktake_counts.\n\nScope: `stocktakes:write`, which includes `stocktakes:read`.",
        "operationId": "import_stocktake_counts",
        "parameters": [
          {
            "description": "The open count the file's rows apply to.",
            "in": "path",
            "name": "stocktake_id",
            "required": true,
            "schema": {
              "examples": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "mappings": {
                  "counted_quantity": "Counted",
                  "location": "Warehouse",
                  "sku": "SKU"
                },
                "s3_key": "uploads/counts.csv"
              },
              "schema": {
                "$ref": "#/components/schemas/ImportStocktakeCountsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "skipped": 4,
                    "unmatched_skus": [
                      "NOT-IN-COUNT"
                    ],
                    "updated": 812
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ImportStocktakeCountsPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "What the file changed, and what it names that the count does not cover.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A blank `s3_key`; a mapping missing the SKU or the counted-quantity column; a file that is empty or cannot be read; or the count has already been posted.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_7a445dfc1a8242f7a7e327fd0f0aaec1",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_58e7a688b8f646a8a1b02b31b9888e6a",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key holds Stocktakes for reading only, or not at all; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No count with this id in this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Apply counted quantities to an open count from an uploaded CSV or Excel file",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:write"
        ]
      }
    },
    "/api/v1/stocktakes/{stocktake_id}/post": {
      "post": {
        "description": "Closes the count. Its status becomes `posted`, `posted_at` is stamped, and the totals stop moving: from here the count is a record of what was found rather than a working document. Takes no body. The count is identified entirely by its path.\n\nTightly records the adjustment. It does not write the correction back to the connected store, so a Shopify or ERP level is unchanged by this call and the next sync from that system is unaffected. Correcting the source of record is a separate act in that system.\n\nRefused 400 if no line carries a count yet, \"Enter at least one counted quantity before posting\", and refused 400 if the count is already posted. Lines still uncounted at posting stay uncounted: they are in `line_count` and not in `counted_line_count`, and get_stock_variance_by_supplier reports them as `uncounted_line_count`, so an evidence gap inside a posted count is visible rather than read as zero variance.\n\nThere is no unpost. A posted count cannot be edited or deleted, and a correction is a new count.\n\nScope: `stocktakes:write`, which includes `stocktakes:read`.",
        "operationId": "post_stocktake",
        "parameters": [
          {
            "description": "The open count to close.",
            "in": "path",
            "name": "stocktake_id",
            "required": true,
            "schema": {
              "examples": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "counted_line_count": 1840,
                    "counted_on": "2026-06-30",
                    "counted_total": 41065,
                    "id": "2",
                    "line_count": 1840,
                    "location_names": [
                      "Collect",
                      "London 3PL"
                    ],
                    "name": "Quarter-end full count",
                    "posted_at": "2026-06-30T18:12:00+00:00",
                    "status": "posted",
                    "system_total": 41220,
                    "variance_total": -155,
                    "variance_value_total": -3720.0
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PostStocktakePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The count as posted, with `posted_at` stamped and its figures frozen.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "Nothing has been counted, \"Enter at least one counted quantity before posting\", or the count has already been posted.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_7dfc0aae5a4f468b8b76cc28347fd6f7",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot write Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_75ab3192788148748932c83257b1c7ef",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key holds Stocktakes for reading only, or not at all; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No count with this id in this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Close a count, freezing its figures and recording the adjustment",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:write"
        ]
      }
    },
    "/api/v1/stocktakes/{stocktake_id}/table": {
      "get": {
        "description": "One page of the lines in a count. Each row is one variant at one location: `system_quantity` (the level snapshotted when the count was created), `counted_quantity`, `variance` and `variance_value`, with `sku`, `product_title`, `variant_title`, `location_name` and `unit_cost` for display.\n\n`counted_quantity`, `variance` and `variance_value` are null on a line nobody has counted yet. That null is not a zero: a line counted at exactly system stock carries `variance: 0`, and the two are opposite statements about a shelf. `line_scope` narrows the page: `differences` returns only counted lines whose count differs from system (so it excludes the matched lines AND the uncounted ones), and `uncounted` returns only the lines nobody has counted, which is the work left to do on a count part way through. `variance_only=true` is the older spelling of `line_scope=differences` and still works.\n\n`unit_cost` is null where no cost is on file, and `variance_value` is null with it, a variance in units is a stock problem and a variance in money is a supplier conversation, so an unpriced line is never valued at zero.\n\n`search` matches product title and SKU. `sort_args` takes `product_title`, `sku`, `location_name`, `system_quantity`, `counted_quantity`, `variance` or `variance_value`, prefixed `-` for descending. `limit` defaults to 25. For variance measured across posted counts and attributed to a supplier rather than listed line by line, use get_stock_variance_by_supplier.\n\nScope: `stocktakes:read`.",
        "operationId": "get_stocktake_lines_table",
        "parameters": [
          {
            "description": "The count whose lines to read.",
            "in": "path",
            "name": "stocktake_id",
            "required": true,
            "schema": {
              "examples": [
                1
              ],
              "type": "integer"
            }
          },
          {
            "description": "Match against product title and SKU.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "CREW-NAVY"
              ],
              "type": "string"
            }
          },
          {
            "description": "How many rows to return. Defaults to 25; the platform cap is 10,000.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 25,
              "examples": [
                25
              ],
              "maximum": 10000,
              "type": "integer"
            }
          },
          {
            "description": "How many rows to skip. Page against `filtered_max_size`, not `max_size`.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "examples": [
                0
              ],
              "type": "integer"
            }
          },
          {
            "description": "Which lines to return. `all` is every line in the count. `differences` returns only counted lines whose count differs from system stock, leaving out both the matched lines and the uncounted ones. `uncounted` returns only the lines nobody has counted yet, which is the work left.\n",
            "in": "query",
            "name": "line_scope",
            "required": false,
            "schema": {
              "default": "all",
              "enum": [
                "all",
                "differences",
                "uncounted"
              ],
              "examples": [
                "uncounted"
              ],
              "type": "string"
            }
          },
          {
            "deprecated": true,
            "description": "The older spelling of `line_scope=differences`, honoured where `line_scope` is not given. Prefer `line_scope`.\n",
            "in": "query",
            "name": "variance_only",
            "required": false,
            "schema": {
              "default": false,
              "examples": [
                true
              ],
              "type": "boolean"
            }
          },
          {
            "description": "Sort fields, comma-separated, `-` for descending. One of product_title, sku, location_name, system_quantity, counted_quantity, variance, variance_value.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "-variance"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 24,
                    "max_size": 24,
                    "offset": 0,
                    "rows": [
                      {
                        "counted_quantity": 37,
                        "id": "10",
                        "location_id": "loc1",
                        "location_name": "Collect",
                        "product_title": "Merino Crew",
                        "sku": "CREW-NAVY-M",
                        "system_quantity": 40,
                        "unit_cost": 24.0,
                        "variance": -3,
                        "variance_value": -72.0,
                        "variant_id": "v1",
                        "variant_image": null,
                        "variant_title": "Navy / M"
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "",
                    "service": "stocktakes",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetStocktakeLinesTablePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One page of the count's lines, system against counted.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_ca92377e8f0647cc992059e86f2b69a4",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Stocktakes.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_2150204bb71949b8be086e324353b4b4",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Stocktakes; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No count with this id in this organisation.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The lines of one count, with the difference",
        "tags": [
          "stocktakes"
        ],
        "x-tightly-scopes": [
          "stocktakes:read"
        ]
      }
    },
    "/api/v1/variants/custom-fields": {
      "get": {
        "description": "A page of custom field definitions: what this organisation has added to its variants beyond the catalogue's own columns. Each carries `id`, `name`, `field_type` (`string`, `decimal`, `integer`, `boolean` or `date`), `source`, `description`, `is_active`, `is_editable`, `is_value_editable`, `access_type`, and its timestamps.\n\nRead this before reading or writing a variant's `custom_fields` map: the map is keyed on the field's `id`, and `field_type` is the type a value must parse as.\n\n`source` says where the field came from: `tightly` for one declared here, otherwise the connector that brought it. The two counts beside the page are that split, over the active fields: `total_active_tightly_specific` and `total_active_integration`. `is_value_editable` is computed rather than stored: true when `source` is `tightly`, and otherwise true only when `access_type` is `read_write`.\n\n`label` is what a face prints and `name` is the key values are stored under. `bound_to` names the connection filling the field, and while it is set a write to its values is refused; `used_in` counts the families and saved filters that read it.\n\nPaging is `offset` and `limit`, limit at most 100 and 25 by default; `max_size` is every field matching the filters and `size` is this page. `search` matches name and description. `filter_args` is a JSON array of `{key, operation, value}` over `is_active`, `is_editable`, `field_type`, `name` and `source`.\n\nScope: `products:read`.",
        "operationId": "list_custom_fields",
        "parameters": [
          {
            "description": "Definitions to skip before this page.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Definitions in this page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 25,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Free text over name and description.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "fabric"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of {key, operation, value}. is_active and is_editable take eq and ne on a boolean; field_type and source take eq, ne and in; name takes eq, ilike and in.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"is_active\",\"operation\":\"eq\",\"value\":true},{\"key\":\"source\",\"operation\":\"eq\",\"value\":\"tightly\"}]"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "custom_fields": [
                      {
                        "access_type": "read_write",
                        "bound_to": null,
                        "created_at": "2026-03-02T10:00:00+00:00",
                        "description": "The mill's stated composition.",
                        "field_type": "string",
                        "grain": "product",
                        "id": "123",
                        "is_active": true,
                        "is_editable": true,
                        "is_value_editable": true,
                        "label": "Composition",
                        "name": "composition",
                        "source": "tightly",
                        "updated_at": "2026-08-14T08:21:00+00:00",
                        "used_in": {
                          "families": 2,
                          "saved_filters": 0
                        }
                      },
                      {
                        "access_type": "read_only",
                        "bound_to": {
                          "connection_id": "66b1a0f4c2e91d0007a3b512",
                          "field_key": "product.custom_field.fabric",
                          "source_path": "metafields.custom.fabric"
                        },
                        "created_at": "2026-01-19T12:04:00+00:00",
                        "description": "tightly:grain=product; label=Fabric",
                        "field_type": "string",
                        "grain": "product",
                        "id": "141",
                        "is_active": true,
                        "is_editable": false,
                        "is_value_editable": false,
                        "label": "Fabric",
                        "name": "fabric",
                        "source": "shopify",
                        "updated_at": "2026-01-19T12:04:00+00:00",
                        "used_in": {
                          "families": 0,
                          "saved_filters": 1
                        }
                      }
                    ],
                    "max_size": 15,
                    "offset": 0,
                    "size": 2,
                    "total_active_integration": 10,
                    "total_active_tightly_specific": 5
                  },
                  "message": {
                    "desc": "OK",
                    "service": "variant",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "custom_fields": {
                          "items": {
                            "$ref": "#/components/schemas/CustomField"
                          },
                          "type": "array"
                        },
                        "max_size": {
                          "description": "Definitions matching the filters.",
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "size": {
                          "description": "Definitions in this page.",
                          "type": "integer"
                        },
                        "total_active_integration": {
                          "description": "Active fields a connector brought.",
                          "type": "integer"
                        },
                        "total_active_tightly_specific": {
                          "description": "Active fields declared in Tightly.",
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of definitions, with the active split and the size of the matching set.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "offset or limit is outside its range, or filter_args is not valid JSON.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The custom fields this organisation keeps on its variants",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/custom-fields/{custom_field_id}": {
      "get": {
        "description": "One custom field definition by its id: `name`, `field_type`, `source`, `description`, `is_active`, `is_editable`, `is_value_editable`, `access_type` and its timestamps.\n\nThe id is the one a variant's `custom_fields` map is keyed on, so this is the read that turns a key in that map into a name and a type.\n\n`is_value_editable` is computed rather than stored: true when `source` is `tightly`, and otherwise true only when `access_type` is `read_write`. A field brought by a connector as `read_only` cannot take a value from here, whatever `is_editable` says about the definition.\n\nA field id that does not exist in this organisation is refused 404. For every definition at once, read the list.\n\nScope: `products:read`.",
        "operationId": "get_custom_field",
        "parameters": [
          {
            "description": "The definition this reads, as a variant's custom_fields map keys it.",
            "in": "path",
            "name": "custom_field_id",
            "required": true,
            "schema": {
              "examples": [
                "123"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "custom_field": {
                      "access_type": "read_write",
                      "created_at": "2026-03-02T10:00:00+00:00",
                      "description": "The mill's stated composition.",
                      "field_type": "string",
                      "id": "123",
                      "is_active": true,
                      "is_editable": true,
                      "is_value_editable": true,
                      "name": "Fabric composition",
                      "source": "tightly",
                      "updated_at": "2026-08-14T08:21:00+00:00"
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "variant",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "custom_field": {
                          "$ref": "#/components/schemas/CustomField"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The definition.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No custom field in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One custom field definition",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/events": {
      "get": {
        "description": "The change log of the catalogue: one entry per recorded change on any variant, each with `id`, `variant_id`, `event_type`, `event_date`, `previous_value`, `new_value`, a typed `metadata` object, and the `product_name`, `variant_name` and `sku` the change happened to. `created_at`, `created_by_user_id` and `location_id` are null where the change came from a sync rather than a person or a place.\n\n`event_type` today is one of price_change, cost_change, stock_change, stockout, restock, status_change, supplier_change, lead_time_change, moq_change, velocity_shift, tag_change, managed_change, po_created, variant_created, variant_image_change, forecast_override (a planner edited this variant's demand plan) and forecast_event_declared (a sales-velocity event covering it was declared, changed or cancelled). The list is open, tolerate a type you do not know rather than failing on it, and takes a comma-separated list to narrow to several at once.\n\n`start_date` and `end_date` are ISO dates and bound the range. `end_date` defaults to today; `start_date` unset reads from the beginning of what is kept.\n\nPaging is `offset` and `limit`, limit at most 100 and 8 by default. `total_count` is the whole matching set, so page on that.\n\nIt is the poll to run on a schedule: ask for the types you care about since your last run and apply what comes back, rather than diffing the whole catalogue. For one variant's own timeline, read that variant's events.\n\nScope: `products:read`.",
        "operationId": "list_variant_events",
        "parameters": [
          {
            "description": "Comma-separated event types to filter by. Omit to return every type.\n",
            "in": "query",
            "name": "event_type",
            "required": false,
            "schema": {
              "examples": [
                "price_change,stockout"
              ],
              "type": "string"
            }
          },
          {
            "description": "Start of the range (ISO 8601 date). Unset reads from the beginning of what is kept.",
            "in": "query",
            "name": "start_date",
            "required": false,
            "schema": {
              "examples": [
                "2026-08-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End of the range (ISO 8601 date). Defaults to today.",
            "in": "query",
            "name": "end_date",
            "required": false,
            "schema": {
              "examples": [
                "2026-09-04"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Events in this page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "examples": [
                50
              ],
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Events to skip before this page.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "events": [
                      {
                        "created_at": "2026-08-19T09:14:00+00:00",
                        "created_by_user_id": null,
                        "event_date": "2026-08-19",
                        "event_type": "price_change",
                        "id": "evt_9f21c40b",
                        "location_id": null,
                        "metadata": {
                          "currency": "USD"
                        },
                        "new_value": "129.00",
                        "previous_value": "119.00",
                        "product_name": "Alpine Wool Overshirt",
                        "sku": "AWO-CHR-S",
                        "variant_id": "42318902722609",
                        "variant_name": "Charcoal / S"
                      },
                      {
                        "created_at": "2026-08-21T02:10:00+00:00",
                        "created_by_user_id": null,
                        "event_date": "2026-08-21",
                        "event_type": "stockout",
                        "id": "evt_9f21c512",
                        "location_id": "61240442",
                        "metadata": {
                          "location_changes": [
                            {
                              "location_id": "61240442",
                              "new": 0,
                              "previous": 18
                            }
                          ]
                        },
                        "new_value": "0",
                        "previous_value": "18",
                        "product_name": "Alpine Wool Overshirt",
                        "sku": "AWO-SND-S",
                        "variant_id": "42318902788145",
                        "variant_name": "Sand / S"
                      }
                    ],
                    "total_count": 1284
                  },
                  "message": {
                    "desc": "SKU events retrieved",
                    "service": "variant",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetOrgSKUEventsPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of catalogue changes, with the size of the whole matching set.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A date could not be read, or offset or limit is outside its range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What changed on the catalogue, newest first",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/table": {
      "get": {
        "description": "A page of variants with the fields a catalogue sync needs per SKU: `variant_id` and its `product_id`, product and variant title, `sku`, vendor, category, description, image, `product_status`, `gtin`, `unit_cost`, `sell_price`, `currency`, `sales_channels`, `selected_options`, the physical fields (`weight`, `length`, `width`, `height`, `uom`, `weight_unit`), `hs_code`, `country_of_origin`, and `last_updated_at` and `last_updated_by`.\n\nUnlike the products table these are plain numbers, not `{min, max}` pairs: a variant has one cost and one price.\n\nPaging is `offset` and `limit`, limit at most 10,000 and 8 by default. `variants_count` and `size` count this page, `filtered_max_size` the filtered set and `max_size` the catalogue. Page on `filtered_max_size`.\n\nNarrow with `filter_args`: product_id, vendor, category, product_status, sales_channel_id, country_of_origin and collection_id take `eq` and `in`; unit_cost and sell_price take `gte` and `lte`. `search` matches title, SKU and vendor. Sort with `sort_args`: comma-separated columns, `-` for descending, bare or `+` for ascending. `variant_id`, `sku` and `variant_title` are sortable here as well as the product fields.\n\n`export=true` answers a download URL in `data.url` instead of rows.\n\nScope: `products:read`.",
        "operationId": "list_variants",
        "parameters": [
          {
            "description": "Rows to skip before this page.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Rows in this page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "examples": [
                50
              ],
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Free text over variant title, SKU, product title and vendor.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "AWO-CHR"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of {key, operation, value}. product_id, vendor, category, product_status, sales_channel_id, country_of_origin and collection_id take eq and in; unit_cost and sell_price take gte and lte. collection_id is a collection as the store syncs it, not a curated collection, so a curated collection's id matches nothing here. It matches on the variant's own product, and a product can sit in several collections, so in answers every variant of every product in any of the ids given.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"vendor\",\"operation\":\"eq\",\"value\":\"Veja\"}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated sort columns, `-` for descending and `+` or nothing for ascending. Sortable: product_id, variant_id, product_title, variant_title, sku, vendor, category, product_status, unit_cost, sell_price, last_updated_at, last_updated_by, weight, length, width, height, description, gtin, currency, uom, weight_unit, hs_code, country_of_origin, sales_channels.\n",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "sku"
              ],
              "type": "string"
            }
          },
          {
            "description": "Answer a download URL for the filtered set instead of a page of rows.",
            "in": "query",
            "name": "export",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 3288,
                    "max_size": 14204,
                    "offset": 0,
                    "rows": [
                      {
                        "category": "Outerwear",
                        "country_of_origin": "PT",
                        "currency": "USD",
                        "description": "A brushed wool overshirt cut for layering.",
                        "gtin": "05012345678900",
                        "height": null,
                        "hs_code": "620331",
                        "image_url": "https://cdn.tightly.io/p/7412095483953.jpg",
                        "last_updated_at": "2026-09-01T11:42:00+00:00",
                        "last_updated_by": "ana.ruiz@example.com",
                        "length": null,
                        "product_id": "7412095483953",
                        "product_status": "ACTIVE",
                        "product_title": "Alpine Wool Overshirt",
                        "sales_channels": [
                          {
                            "id": "61240442",
                            "name": "Online Store"
                          }
                        ],
                        "selected_options": {
                          "Colour": "Charcoal",
                          "Size": "S"
                        },
                        "sell_price": 129.0,
                        "sku": "AWO-CHR-S",
                        "unit_cost": 34.0,
                        "uom": "each",
                        "variant_id": "42318902722609",
                        "variant_title": "Charcoal / S",
                        "vendor": "Veja",
                        "weight": 0.62,
                        "weight_unit": "kg",
                        "width": null
                      }
                    ],
                    "size": 1,
                    "variants_count": 1
                  },
                  "message": {
                    "desc": "OK",
                    "service": "inventory",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetPimVariantsTablePayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of variants, with the page's counts and the filtered set's size.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "offset or limit is outside its range, or filter_args is not valid JSON.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "A page of variants, one row per SKU",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/{variant_id}": {
      "get": {
        "description": "One variant as the catalogue holds it, and what is true of it right now: `sku`, `barcode`, `gtin`, `lifecycle` and its alias `status`, description and images, vendor, category, `sell_price`, `unit_cost` and `unit_cost_currency`, the physical fields, `hs_code`, `country_of_origin`, `selected_options`, `custom_fields`, `launch_date`, and the `product_id` and `product_name` it belongs to.\n\n`in_stock`, `incoming_stock` and `inventory_value` are the stock figures, narrowed to one warehouse when `location_id` is sent and totalled across every warehouse when it is not. `inventory_value` is at cost, the variant's or else its product's, and it is null when neither carries one. `health_cover` is per location, `{location, cover, health}`, and `performance_category_by_location` carries the performance category and capital quadrant per location.\n\n`suppliers` is who can supply it, `production_type` says whether it is bought, made or a raw material, and `prepack` carries the full prepack when it is part of one. `last_stockout_date` is null where it has never gone out of stock.\n\nA variant id that does not exist in this organisation is refused 404.\n\nScope: `products:read`.",
        "operationId": "get_variant",
        "parameters": [
          {
            "description": "The variant this reads.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "examples": [
                "42318902722609"
              ],
              "type": "string"
            }
          },
          {
            "description": "Narrow in_stock, incoming_stock and inventory_value to one warehouse.",
            "in": "query",
            "name": "location_id",
            "required": false,
            "schema": {
              "examples": [
                "61240442"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "barcode": "5012345678900",
                    "category": "Outerwear",
                    "category_id": "cat_outerwear",
                    "country_of_origin": "PT",
                    "custom_fields": {
                      "Fabric composition": "80% wool, 20% polyamide"
                    },
                    "description": "A brushed wool overshirt cut for layering.",
                    "gtin": "05012345678900",
                    "health_cover": [
                      {
                        "cover": 38,
                        "health": "healthy",
                        "location": "Rotterdam DC"
                      }
                    ],
                    "hs_code": "620331",
                    "image_url": "https://cdn.tightly.io/p/7412095483953.jpg",
                    "in_stock": 214,
                    "incoming_stock": 240,
                    "inventory_value": 7276.0,
                    "is_managed": true,
                    "last_stockout_date": null,
                    "launch_date": "2026-02-14",
                    "lifecycle": "seasonal",
                    "performance_category_by_location": [
                      {
                        "capital_quadrant": "Grow",
                        "location": "Rotterdam DC",
                        "performance_category": "Best sellers"
                      }
                    ],
                    "product_id": "7412095483953",
                    "product_name": "Alpine Wool Overshirt",
                    "production_type": "buy_only",
                    "published_status": "ACTIVE",
                    "selected_options": {
                      "Colour": "Charcoal",
                      "Size": "S"
                    },
                    "sell_price": 129.0,
                    "shopify_tags": [
                      "aw26"
                    ],
                    "sku": "AWO-CHR-S",
                    "status": "seasonal",
                    "suppliers": [
                      {
                        "is_default": true,
                        "supplier_id": "sup_1180",
                        "supplier_name": "Atelier Norte"
                      }
                    ],
                    "unit_cost": 34.0,
                    "unit_cost_currency": "USD",
                    "uom": "each",
                    "variant_id": "42318902722609",
                    "variant_name": "Charcoal / S",
                    "variant_title": "Charcoal / S",
                    "vendor": "Veja",
                    "weight": 0.62,
                    "weight_unit": "kg"
                  },
                  "message": {
                    "desc": "OK",
                    "service": "variant",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetVariantPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The variant, its stock and its suppliers.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No variant in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One variant with its stock and its suppliers",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/{variant_id}/drawer-overview": {
      "get": {
        "description": "One row per warehouse the variant is held in: `location_id` and `location_name`, `stock_on_hand`, `stock_value`, `incoming_stock`, `weeks_of_cover`, `velocity` (units a day), `health` and `capital_quadrant`.\n\n`weeks_of_cover`, `velocity`, `health` and `capital_quadrant` are null where there is not enough demand history to compute them. Null is \"not known yet\", never zero.\n\nBeside the locations it carries `is_seasonal`, and the variant's `successors` and `predecessors`, each `{variant_id, product_title, variant_title}`, what replaces this variant and what it replaced. Both are `[]` where no successor relationship is recorded.\n\nThis is the per-warehouse view of one variant. For the whole catalogue's stock across warehouses, read the stock resource's table.\n\nScope: `products:read`.",
        "operationId": "get_variant_stock_by_location",
        "parameters": [
          {
            "description": "The variant this reads.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "examples": [
                "42318902722609"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "is_seasonal": true,
                    "locations": [
                      {
                        "capital_quadrant": "Grow",
                        "health": "healthy",
                        "incoming_stock": 240,
                        "location_id": "61240442",
                        "location_name": "Rotterdam DC",
                        "stock_on_hand": 214,
                        "stock_value": 7276.0,
                        "velocity": 5.6,
                        "weeks_of_cover": 5.4
                      },
                      {
                        "capital_quadrant": null,
                        "health": "critical",
                        "incoming_stock": 0,
                        "location_id": "61240443",
                        "location_name": "New Jersey DC",
                        "stock_on_hand": 0,
                        "stock_value": 0.0,
                        "velocity": null,
                        "weeks_of_cover": null
                      }
                    ],
                    "predecessors": [
                      {
                        "product_title": "Alpine Wool Overshirt (AW25)",
                        "variant_id": "41180022331904",
                        "variant_title": "Charcoal / S"
                      }
                    ],
                    "successors": []
                  },
                  "message": {
                    "desc": "OK",
                    "service": "variant",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetVariantDrawerOverviewPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "One row per warehouse, with the variant's successors and predecessors.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No variant in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One variant's stock and cover, warehouse by warehouse",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/{variant_id}/events": {
      "get": {
        "description": "Every recorded change to one variant: `id`, `event_type`, `event_date`, `previous_value`, `new_value`, a typed `metadata` object, `location_id`, `created_at` and `created_by_user_id`. It carries no product or SKU fields, because they are the ones you name in the request.\n\n`event_type` today is one of price_change, cost_change, stock_change, stockout, restock, status_change, supplier_change, lead_time_change, moq_change, velocity_shift, tag_change, managed_change, po_created, variant_created, variant_image_change, forecast_override (a planner edited the demand plan) and forecast_event_declared (a sales-velocity event was declared, changed or cancelled). The list is open, tolerate a type you do not know, and takes a comma-separated list.\n\n`location_id` narrows stock events to one warehouse, matched against the per-location changes in `metadata`.\n\n`start_date` and `end_date` bound the range as ISO dates; `end_date` defaults to today. Paging is `offset` and `limit`, limit at most 100 and 8 by default. Both counts come back: `total_count` is every event this variant has, `filtered_count` is how many the range and type filters matched, and the page is cut from `filtered_count`. The `offset` and `limit` actually used are echoed back.\n\nFor the same log across every variant, read the catalogue's events.\n\nScope: `products:read`.",
        "operationId": "get_variant_events",
        "parameters": [
          {
            "description": "The variant whose timeline this reads.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "examples": [
                "42318902722609"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated event types to filter by. Omit to return every type.",
            "in": "query",
            "name": "event_type",
            "required": false,
            "schema": {
              "examples": [
                "price_change,stockout"
              ],
              "type": "string"
            }
          },
          {
            "description": "Start of the range (ISO 8601 date).",
            "in": "query",
            "name": "start_date",
            "required": false,
            "schema": {
              "examples": [
                "2026-06-01"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "End of the range (ISO 8601 date). Defaults to today.",
            "in": "query",
            "name": "end_date",
            "required": false,
            "schema": {
              "examples": [
                "2026-09-04"
              ],
              "format": "date",
              "type": "string"
            }
          },
          {
            "description": "Narrow stock events to one warehouse, matched against metadata.location_changes.",
            "in": "query",
            "name": "location_id",
            "required": false,
            "schema": {
              "examples": [
                "61240442"
              ],
              "type": "string"
            }
          },
          {
            "description": "Events in this page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 8,
              "examples": [
                50
              ],
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "Events to skip before this page.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "events": [
                      {
                        "created_at": "2026-08-19T09:14:00+00:00",
                        "created_by_user_id": null,
                        "event_date": "2026-08-19",
                        "event_type": "price_change",
                        "id": "evt_9f21c40b",
                        "location_id": null,
                        "metadata": {
                          "currency": "USD"
                        },
                        "new_value": "129.00",
                        "previous_value": "119.00"
                      },
                      {
                        "created_at": "2026-07-02T16:38:00+00:00",
                        "created_by_user_id": "usr_4471",
                        "event_date": "2026-07-02",
                        "event_type": "moq_change",
                        "id": "evt_9e04a771",
                        "location_id": null,
                        "metadata": {
                          "supplier_id": "sup_1180"
                        },
                        "new_value": "120",
                        "previous_value": "100"
                      }
                    ],
                    "filtered_count": 2,
                    "limit": 8,
                    "offset": 0,
                    "total_count": 34
                  },
                  "message": {
                    "desc": "SKU events retrieved",
                    "service": "variant",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/GetSKUEventsPayload"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of this variant's changes, with both the total and the filtered count.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "A date could not be read, or offset or limit is outside its range.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No variant in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One variant's own timeline of changes, newest first",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/{variant_id}/incoming-pos": {
      "get": {
        "description": "The open orders carrying one variant, with the quantities counted for that variant alone.\n\nEach order carries `id`, `name`, `status` (`FullyConfirmed` or `FullyShipped`), `expected_delivery_date`, `supplier_id` and `supplier_name`, `location_id` and `location_name`, `line_items` and `deliveries` for this variant, and `ordered_quantity` and `delivered_quantity` summed over this variant's lines. An order that also carries other variants is here, but its quantities are not: what is served is this variant's share.\n\nA variant with nothing inbound answers `purchase_orders: []`, which is the empty case and not an error.\n\nFor the whole order, with every line on it, read the purchase orders resource; for the same question across a product, read the product's incoming purchase orders.\n\nScope: `products:read`.",
        "operationId": "get_variant_incoming_purchase_orders",
        "parameters": [
          {
            "description": "The variant whose inbound orders this reads.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "examples": [
                "42318902722609"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "purchase_orders": [
                      {
                        "delivered_quantity": 0,
                        "deliveries": null,
                        "expected_delivery_date": "2026-10-12",
                        "id": "48120",
                        "line_items": [
                          {
                            "quantity": 240,
                            "variant_id": "42318902722609",
                            "variant_title": "Charcoal / S"
                          }
                        ],
                        "location_id": "61240442",
                        "location_name": "Rotterdam DC",
                        "name": "PO-00048120",
                        "ordered_quantity": 240,
                        "status": "FullyConfirmed",
                        "supplier_id": "sup_1180",
                        "supplier_name": "Atelier Norte"
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "variant",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "purchase_orders": {
                          "description": "One entry per open order carrying this variant.",
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The open orders carrying this variant, with its own ordered and delivered quantities.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "404": {
            "description": "No variant in this organisation carries that id.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What is still on order for one variant",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/variants/{variant_id}/price-sensitivity": {
      "get": {
        "description": "What past price changes did to this variant's sales velocity, as a tier with the evidence behind it.\n\n`tier` is `HIGH`, `MODERATE`, `LOW`, `INCONSISTENT`, `STILL_LEARNING` or null. `confidence`, `n_events` (the qualifying price changes counted), `sign_agreement`, `shrunk_effect_up` and `shrunk_effect_down` (the effect of a rise and of a cut, as signed fractions) and `data_through` (the last event analysed) are the figures under it. `worth_considering` and `summary` are those figures written as sentences.\n\nA variant with no classification yet answers every field null with `n_events: 0`. That is the honest answer, not an error, and not a tier of zero. An id no variant has answers the same shape rather than a refusal, so this read cannot tell you an id is wrong. Check it against a variant read.\n\n`basis` and the fields beside it say why a tier is what it is: `periods_observed`, `price_variation` against `required_price_variation`, `expected_error`, and a `reference_elasticity` with its `reference_basis` and `reference_source` where the variant's own history is too thin. A `tier` of `STILL_LEARNING` with a `price_variation` well under `required_price_variation` means the price has not moved enough to measure, not that the reading is pending.\n\n`engine` names the run behind the tier: `state` is `not_run`, `current`, `borrowed` or `insufficient`, the figure's state and never a run's, with `computed_at` (the run's, never your clock), `inputs_through`, `stale_after`, `basis` and `run_id`.\n\nScope: `products:read`.",
        "operationId": "get_variant_price_sensitivity",
        "parameters": [
          {
            "description": "The variant this reads.",
            "in": "path",
            "name": "variant_id",
            "required": true,
            "schema": {
              "examples": [
                "42318902722609"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "basis": "measured",
                    "confidence": "medium",
                    "data_through": "2026-08-19",
                    "evidence": "Seven qualifying price changes over 42 weeks.",
                    "expected_error": 0.04,
                    "n_events": 7,
                    "periods_observed": 42,
                    "price_variation": 0.11,
                    "reference_basis": null,
                    "reference_elasticity": null,
                    "reference_source": null,
                    "required_price_variation": 0.08,
                    "shrunk_effect_down": 0.06,
                    "shrunk_effect_up": -0.11,
                    "sign_agreement": 0.86,
                    "summary": "Sales dropped ~11% when price increased. Sales rose ~6% when price decreased.",
                    "tier": "MODERATE",
                    "worth_considering": "Price changes modestly affect sales (~11% velocity shift). Medium confidence. Last event analyzed: August 2026."
                  },
                  "message": {
                    "desc": "Price sensitivity classification retrieved",
                    "service": "variant",
                    "severity": "SUCCESS"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PriceSensitivityClassification"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The tier, the figures under it, and the sentences they make.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, because telling a caller which is which maps the surface for them.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "scope_missing",
                  "message": {
                    "code": "scope_missing",
                    "desc": "This key cannot read Products.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#scope_missing",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc and, on a keyed refusal, code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `scope_missing` when the key does not hold Products; `ip_not_allowed` when the caller's address is outside the key's allowlist. `plan_excludes` when the organisation's plan does not include the public API itself, which is sold with Essentials and is asked of every key minted under that gate. A key minted before the gate existed is never refused by it, whatever the plan holds. Products is ungated, so that is the only plan reason served here.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "How this variant's sales have moved when its price moved",
        "tags": [
          "variants"
        ],
        "x-tightly-scopes": [
          "products:read"
        ]
      }
    },
    "/api/v1/wholesale/edi/documents": {
      "get": {
        "description": "Every EDI document this organisation has exchanged with its accounts: purchase orders (850) in, acknowledgements (855), shipping notices (856) and invoices (810) out, and the inventory (846) and sell-out (852) reports that arrive on the same connection. Each row says which account it belongs to, which order it became or answered, what state it reached, and the sentence behind that state where it has one, so a file that was refused or an acknowledgement a network would not take is readable here rather than in the network's own console. `network` says which lane a document came through, and it is also the filter: this log holds every lane an order can arrive on, so ask for `network=sps_commerce` for the EDI lane alone. Read only: documents are made by the acts that make them, never posted here.\n\nScope: `accounts:read`.",
        "operationId": "list_edi_documents",
        "parameters": [
          {
            "description": "One account's documents only.",
            "in": "query",
            "name": "trading_partner_id",
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "One kind of document only, by its X12 number.",
            "in": "query",
            "name": "document_type",
            "schema": {
              "enum": [
                "850",
                "855",
                "856",
                "810",
                "846",
                "852"
              ],
              "type": "string"
            }
          },
          {
            "description": "Documents that arrived (inbound) or that Tightly sent (outbound).",
            "in": "query",
            "name": "direction",
            "schema": {
              "enum": [
                "inbound",
                "outbound"
              ],
              "type": "string"
            }
          },
          {
            "description": "Documents in this state only.",
            "in": "query",
            "name": "state",
            "schema": {
              "enum": [
                "received",
                "parsed",
                "applied",
                "refused",
                "queued",
                "sent",
                "failed"
              ],
              "type": "string"
            }
          },
          {
            "description": "The lane the document came through. `sps_commerce` is the EDI lane; the other four are the paper lanes that share this table. Omit it for every lane.",
            "in": "query",
            "name": "network",
            "schema": {
              "enum": [
                "sps_commerce",
                "email",
                "upload",
                "link",
                "portal"
              ],
              "type": "string"
            }
          },
          {
            "description": "Rows to skip before the page starts.",
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "type": "integer"
            }
          },
          {
            "description": "Rows on the page.",
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 10000,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 2,
                    "max_size": 50,
                    "offset": 0,
                    "rows": [
                      {
                        "account": {
                          "name": "Coastline Department Stores",
                          "trading_partner_id": "tp_coastline"
                        },
                        "control_number": "CDS-90114",
                        "direction": "outbound",
                        "document_id": 42,
                        "document_type": "855",
                        "file_path": "in/855-CDS-90114.xml",
                        "network": "sps_commerce",
                        "order": {
                          "name": "ORD-00000412",
                          "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                        },
                        "received_at": null,
                        "sent_at": "2026-09-05T17:30:00+00:00",
                        "sentence": null,
                        "state": "sent"
                      },
                      {
                        "account": {
                          "name": "Coastline Department Stores",
                          "trading_partner_id": "tp_coastline"
                        },
                        "control_number": "CDS-90114",
                        "direction": "inbound",
                        "document_id": 41,
                        "document_type": "850",
                        "file_path": "out/PO/PO584615-1-v7.7-BulkImport.xml",
                        "network": "sps_commerce",
                        "order": {
                          "name": "ORD-00000412",
                          "order_id": "ord_01JQ8ZK7C4N2R9V6T3M0X5A1BD"
                        },
                        "received_at": "2026-09-05T06:10:00+00:00",
                        "sent_at": null,
                        "sentence": null,
                        "state": "applied"
                      }
                    ],
                    "size": 2
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                }
              }
            },
            "description": "The documents, newest first.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes`: Accounts is sold with Tightly Connect on Essentials+ and this organisation's plan does not include it; `scope_missing`: the key does not hold Accounts; `account_mismatch`: a key issued to one account named another.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "list edi documents",
        "tags": [
          "accounts"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    },
    "/api/v1/wholesale/retailers": {
      "get": {
        "description": "Every trading account, with the identity a sell-out write and an order-book read both name an account from: its id, the account's own buyer reference, its name, its sales channel, transit time, when its data last synced, its standing terms (tier, fill-rate target, cancellation term and notice days), the next estimated order date, the trading figures read off its own reports, and `portal_seats`, the account's side of the buyer's portal: how many buyers hold a live seat, how many invitations are still unopened, and when any of them last opened it.\n\n`retailer_ids` narrows to named accounts and `variant_id` narrows the trading figures to one variant.\n\nAbsence is never coerced. `account_tier` null means untiered and `fill_rate_target_pct` null means no promise is published; an untiered account read as the lowest tier would be deprioritised in a shortage by a policy nobody wrote. `cancellation_term` null is silence rather than `none`: read as \"no term\", the account's whole book would count as unwalkable and open to buy would be overstated by exactly the value that can still cancel, so `cancellation_term_note` carries the absence instead.\n\nUse this to resolve an account id before posting sell-out. The writes on this prefix configure the integration rather than describe an account, and are not public.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`. A key issued to one account sees that account alone here.\n\nScope: `accounts:read`.",
        "operationId": "list_accounts",
        "parameters": [
          {
            "description": "Restrict to these accounts, comma-separated.",
            "in": "query",
            "name": "retailer_ids",
            "required": false,
            "schema": {
              "examples": [
                "tp_00417,tp_00902"
              ],
              "type": "string"
            }
          },
          {
            "description": "Only accounts that carry this variant, which accounts stock it.",
            "in": "query",
            "name": "variant_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "retailers": [
                      {
                        "account_tier": 1,
                        "buyer_id": "SLF-4471",
                        "cancellation_notice_days": 30,
                        "cancellation_term": "window",
                        "cancellation_term_note": null,
                        "fill_rate_target_pct": 97.0,
                        "id": "tp_00417",
                        "last_data_synced_at": "2026-09-03T02:14:00+00:00",
                        "name": "Selfridges",
                        "next_estimated_sale_order_date": "2026-09-21",
                        "next_estimated_sale_order_in_weeks": 2,
                        "portal_seats": {
                          "accepted": 2,
                          "invited": 1,
                          "last_opened_at": "2026-09-03T16:40:00+00:00"
                        },
                        "sales_channel_id": "61240442",
                        "sales_velocity_per_week": 184.5,
                        "sell_through_rate": 0.86,
                        "total_skus": 412,
                        "transit_time_days": 4
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "retailers": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`retailers`, one row per account, with identity, channel, terms, the cadence of its orders, the trading figures read off its own reports and, where the seat store answered, `portal_seats` with `accepted`, `invited` and `last_opened_at`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Accounts is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Accounts; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "Every trading account, with its terms and how it is trading",
        "tags": [
          "wholesale"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    },
    "/api/v1/wholesale/retailers/{partner_id}/contacts": {
      "get": {
        "description": "The brand's contacts at one account and the email domains that make a thread theirs: the account's name, its recorded domains, and one entry per contact with name, email, phone, role, department, contact type, last interaction date and whether they are the main contact.\n\nBoth in one read because they answer one question, which is how this account is reached.\n\n`domains` null is not an account without domains: it means nobody has recorded one, which `domains_reason` says, and until one is recorded no inbound thread can be matched to this account at all. Recording a domain is a write on this prefix and is not public.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `accounts:read`.",
        "operationId": "get_account_contacts",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in apparel.",
            "in": "path",
            "name": "partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "contacts": [
                      {
                        "contact_type": "buyer",
                        "department": "Womenswear",
                        "email": "morgan@retailer.example",
                        "id": 4471,
                        "is_main": true,
                        "last_interaction_date": "2026-08-28",
                        "name": "Morgan Example",
                        "phone": null,
                        "role": "Buyer"
                      }
                    ],
                    "domains": [
                      "retailer.example"
                    ],
                    "domains_reason": null,
                    "name": "Selfridges",
                    "trading_partner_id": "tp_00417"
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "contacts": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "domains": {
                          "description": "Null where nobody has recorded one, not an account with no domains.",
                          "items": {
                            "type": "string"
                          },
                          "type": [
                            "array",
                            "null"
                          ]
                        },
                        "domains_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "trading_partner_id": {
                          "type": "string"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The account, its recorded email domains (or null with the reason there are none), and every contact with role, department, type and when we last heard from them.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Accounts is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Accounts; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in the path, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The brand's contacts at one account, and the email domains that make a thread theirs",
        "tags": [
          "wholesale"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    },
    "/api/v1/wholesale/retailers/{partner_id}/overview": {
      "get": {
        "description": "One account's stock position: the estimated stock on hand for the current week, the timeline behind it, the forward projection, and the last and next delivery either side of today.\n\n`partner_id` is the account. `variant_id` or `product_id` scopes the read to one variant or to one product's variants.\n\n`replenishment_date` and `replenishment_qty` are served only when the read is variant-scoped and that variant is replenishment-recommended for this account. Otherwise they are null, which is the absence of a recommendation rather than a recommendation of nothing.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `accounts:read`.",
        "operationId": "get_account_overview",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in apparel.",
            "in": "path",
            "name": "partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Scope the overview to one variant.",
            "in": "query",
            "name": "variant_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Scope the overview to every variant of one product.",
            "in": "query",
            "name": "product_id",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "current_stock": {
                      "end_date": "2026-09-06",
                      "estimated_stock_on_hand": 302,
                      "start_date": "2026-08-31"
                    },
                    "forecasted_stock_timeline": [
                      {
                        "date": "2026-09-07",
                        "on_hand": 218
                      }
                    ],
                    "last_delivery": {
                      "date": "2026-08-11",
                      "quantity": 600
                    },
                    "next_delivery": {
                      "date": "2026-09-22",
                      "quantity": 800
                    },
                    "replenishment_date": null,
                    "replenishment_qty": null,
                    "stock_timeline": [
                      {
                        "date": "2026-08-24",
                        "on_hand": 411
                      },
                      {
                        "date": "2026-08-31",
                        "on_hand": 302
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "current_stock": {
                          "description": "start_date, end_date and estimated_stock_on_hand for the week being read.",
                          "type": "object"
                        },
                        "forecasted_stock_timeline": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "last_delivery": {
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "next_delivery": {
                          "type": [
                            "object",
                            "null"
                          ]
                        },
                        "replenishment_date": {
                          "format": "date",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "replenishment_qty": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "stock_timeline": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The current stock estimate with the week it covers, the stock timeline, the forecast, the last and next deliveries, and, variant-scoped only, the replenishment recommendation.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Accounts is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Accounts; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in the path, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One account's stock position",
        "tags": [
          "wholesale"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    },
    "/api/v1/wholesale/retailers/{partner_id}/products": {
      "get": {
        "description": "A page of one account's products: the brand's unit cost and both prices beside the account's own on-hand quantity, incoming units, weeks of cover and sell-through rate, with the variant's identity, supplier and velocity figures.\n\n`partner_id` is the account. The request shape is the one every other table here takes: `limit`, `offset`, `search`, `filter_args` and `sort_args`, with `filtered_max_size` for the size of the filtered set.\n\n`category` is an alias of `product_type`, and `health` is the organisation's own inventory health (`critical`, `caution`, `healthy`) taken worst-of-locations rather than the account's.\n\nCall get_account_products_filters for the values and ranges a filter can name instead of guessing them from a page of rows.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `accounts:read`.",
        "operationId": "get_account_products",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in apparel.",
            "in": "path",
            "name": "partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Rows per page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "type": "integer"
            }
          },
          {
            "description": "Rows to skip.",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "default": 0,
              "type": "integer"
            }
          },
          {
            "description": "Matches product title, variant title, SKU, product id or variant id.",
            "in": "query",
            "name": "search",
            "required": false,
            "schema": {
              "examples": [
                "hoodie"
              ],
              "type": "string"
            }
          },
          {
            "description": "A JSON array of `{key, operation, value}`. Range keys (`gte`, `lte`, `eq`): retailer_weeks_of_cover, retailer_on_hand, retailer_incoming_units, sell_in_price, sell_out_price, unit_cost, sales_velocity. Categorical keys (`in`, `nin`, `eq`): production_type, supplier_id, default_supplier_id, category, health.\n",
            "in": "query",
            "name": "filter_args",
            "required": false,
            "schema": {
              "examples": [
                "[{\"key\":\"retailer_weeks_of_cover\",\"operation\":\"lte\",\"value\":2}]"
              ],
              "type": "string"
            }
          },
          {
            "description": "Comma-separated columns; `-` for descending.",
            "in": "query",
            "name": "sort_args",
            "required": false,
            "schema": {
              "examples": [
                "-unit_cost"
              ],
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "filtered_max_size": 412,
                    "offset": 0,
                    "rows": [
                      {
                        "category": "Knitwear",
                        "default_supplier_name": "Porto Knits",
                        "health": "caution",
                        "product_id": "4410092",
                        "product_image": "https://cdn.example.com/terry-crew.jpg",
                        "product_title": "Terry Crew",
                        "product_type": "Knitwear",
                        "production_type": "buy_only",
                        "retailer_incoming_units": 120,
                        "retailer_on_hand": 42,
                        "retailer_weeks_of_cover": 2.0,
                        "sales_velocity_30_days": 3.6,
                        "sales_velocity_7_days": 4.1,
                        "sales_velocity_90_days": 3.2,
                        "sell_in_price": 29.5,
                        "sell_out_price": 79.0,
                        "sell_out_velocity_per_week": 21.0,
                        "sell_through_rate": 0.86,
                        "sku": "TB-CREW-BLK-M",
                        "supplier_details": [],
                        "supplier_id": "sup_0031",
                        "unit_cost": 12.4,
                        "variant_id": "44100920011",
                        "variant_title": "Black / M"
                      }
                    ],
                    "size": 1
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "filtered_max_size": {
                          "type": "integer"
                        },
                        "offset": {
                          "type": "integer"
                        },
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        },
                        "size": {
                          "type": "integer"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "A page of rows with `offset`, `size` and `filtered_max_size`, so a caller can page without counting twice.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Accounts is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Accounts; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in the path, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "One account's products, with what they hold and sell",
        "tags": [
          "wholesale"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    },
    "/api/v1/wholesale/retailers/{partner_id}/products/filters": {
      "get": {
        "description": "The values and ranges the account products table can be filtered on: a flat map keyed by filter key, carrying the distinct categorical values this account's variants actually carry and a `{min, max}` pair for each numeric range.\n\n`partner_id` is the account. Built for the controls that drive get_account_products, so a caller filters on values that exist rather than on values it guessed.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `accounts:read`.",
        "operationId": "get_account_products_filters",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in apparel.",
            "in": "path",
            "name": "partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "category": [
                      "Knitwear",
                      "Outerwear"
                    ],
                    "production_type": [
                      "buy_only",
                      "manufacturable"
                    ],
                    "retailer_incoming_units": {
                      "max": 2400,
                      "min": 0
                    },
                    "retailer_on_hand": {
                      "max": 1840,
                      "min": 0
                    },
                    "retailer_weeks_of_cover": {
                      "max": 41.0,
                      "min": 0.0
                    },
                    "sales_velocity": {
                      "max": 18.4,
                      "min": 0.0
                    },
                    "sell_in_price": {
                      "max": 190.0,
                      "min": 12.0
                    },
                    "sell_out_price": {
                      "max": 420.0,
                      "min": 29.0
                    },
                    "supplier": [
                      {
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits"
                      }
                    ],
                    "unit_cost": {
                      "max": 88.0,
                      "min": 4.2
                    }
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`category`, `production_type` and `supplier` as lists; `unit_cost`, `sell_in_price`, `sell_out_price`, `sales_velocity`, `retailer_on_hand`, `retailer_incoming_units` and `retailer_weeks_of_cover` as `{min, max}`.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Accounts is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Accounts; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in the path, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "The values and ranges the account products table can be filtered on",
        "tags": [
          "wholesale"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    },
    "/api/v1/wholesale/retailers/{partner_id}/replenishment-recommendations": {
      "get": {
        "description": "What this account should be sent next, highest composite score first: one row per variant with the recommended quantity, the impact level, the replenishment date, the destination location and the default supplier, and `production_type` saying which act follows, a basket for a bought item or a manufacturing order for a made one.\n\n`partner_id` is the account and `limit` bounds the rows.\n\nA recommendation is advice, not an order. Placing it is create_purchase_order or convert_basket_to_purchase_orders; nothing here reserves stock or contacts anyone.\n\nSold with Essentials+: without Tightly Connect the answer is 403 `plan_excludes`.\n\nScope: `accounts:read`.",
        "operationId": "get_account_replenishment_recommendations",
        "parameters": [
          {
            "description": "The account. An id, never a name. Duplicate account names are ordinary in apparel.",
            "in": "path",
            "name": "partner_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "How many rows to return. Clamped to 1 to 100.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 10,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/TightlyVersion"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "rows": [
                      {
                        "composite_score": 0.82,
                        "id": 90114,
                        "impact_level": "high",
                        "location_id": "loc_0004",
                        "location_name": "London DC",
                        "product_id": "4410092",
                        "product_image": "https://cdn.example.com/terry-crew.jpg",
                        "product_title": "Terry Crew",
                        "production_type": "buy_only",
                        "recommended_quantity": 240,
                        "replenishment_date": "2026-09-21",
                        "sku": "TB-CREW-BLK-M",
                        "supplier_id": "sup_0031",
                        "supplier_name": "Porto Knits",
                        "variant_id": "44100920011",
                        "variant_title": "Black / M"
                      }
                    ]
                  },
                  "message": {
                    "desc": "OK",
                    "service": "wholesale",
                    "severity": "INFO"
                  }
                },
                "schema": {
                  "properties": {
                    "data": {
                      "properties": {
                        "rows": {
                          "items": {
                            "type": "object"
                          },
                          "type": "array"
                        }
                      },
                      "type": "object"
                    },
                    "message": {
                      "description": "severity, service and desc.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "`rows`, ranked by composite score, each with the quantity, the date and where it ships from.\n",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "400": {
            "description": "`limit` is not an integer.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "401": {
            "content": {
              "application/json": {
                "example": {
                  "code": "key_invalid",
                  "message": {
                    "code": "key_invalid",
                    "desc": "The API key is not valid.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#key_invalid",
                    "request_id": "req_8f3c1d0a6b2e4a519c772e0a4b6d1f83",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "403": {
            "content": {
              "application/json": {
                "example": {
                  "code": "plan_excludes",
                  "message": {
                    "code": "plan_excludes",
                    "desc": "This organisation's plan does not include Tightly Connect. It is sold with Essentials+.",
                    "doc_url": "https://app.tightly.io/docs/api/guides/errors#plan_excludes",
                    "request_id": "req_6d2b7e1490af4c338b0e51d9c7a2e4b6",
                    "service": "UNKNOWN",
                    "severity": "ERROR"
                  }
                },
                "schema": {
                  "properties": {
                    "code": {
                      "description": "The same stable code as message.code, at the envelope's top level.",
                      "type": "string"
                    },
                    "message": {
                      "description": "severity, service, desc. A keyed refusal also carries code, doc_url and request_id.",
                      "type": "object"
                    }
                  },
                  "type": "object"
                }
              }
            },
            "description": "The key may not reach this operation. `plan_excludes` when Accounts is sold with Essentials+ and this organisation's plan does not include it; `scope_missing` when the key does not hold Accounts; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist; `account_mismatch` when a key issued to one account names another in the path, and the sentence names both accounts.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-Tightly-Region": {
                "$ref": "#/components/headers/X-Tightly-Region"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/DatabaseUnavailable"
          }
        },
        "security": [
          {
            "ApiKey": []
          }
        ],
        "summary": "What this account should be sent next, ranked by impact",
        "tags": [
          "wholesale"
        ],
        "x-tightly-scopes": [
          "accounts:read"
        ]
      }
    }
  },
  "security": [
    {
      "ApiKey": []
    }
  ],
  "servers": [
    {
      "url": "https://api.app.tightly.io"
    }
  ]
}
