{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.ningboware.com/ai/rfq.schema.json",
  "title": "RFQ list (request for quotation), version 1",
  "description": "The machine copy (rfq.json) attached to an RFQ-list email sent from ningboware.com or zhonghejapan.com: buyer, item lines (catalog model code, color/spec, quantity, logo, packaging, note) and the sender checks. Prices are never part of it. Model codes are the codes of the published catalogs (https://www.ningboware.com/ai/products.json); a reader should still look each one up before using it. Fields are only ever added, never renamed or removed.",
  "type": "object",
  "required": [
    "schema",
    "version",
    "site",
    "submittedAt",
    "channel",
    "language",
    "buyer",
    "items",
    "checks"
  ],
  "properties": {
    "schema": {
      "type": "string",
      "const": "https://www.ningboware.com/ai/rfq.schema.json"
    },
    "version": {
      "type": "integer",
      "const": 1
    },
    "site": {
      "type": "string",
      "enum": [
        "ningboware.com",
        "zhonghejapan.com"
      ]
    },
    "submittedAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$",
      "description": "When the website received the list, UTC."
    },
    "channel": {
      "type": "string",
      "enum": [
        "browser",
        "program"
      ],
      "description": "browser: sent from the site's own page; program: a script or AI agent posting without the site's Origin header."
    },
    "language": {
      "type": "string",
      "pattern": "^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
      "description": "The buyer's language (ISO 639-1), used for the buyer's copy."
    },
    "buyer": {
      "type": "object",
      "required": [
        "name",
        "email"
      ],
      "properties": {
        "company": {
          "type": "string",
          "maxLength": 120
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 80
        },
        "email": {
          "type": "string",
          "minLength": 3,
          "maxLength": 160
        },
        "market": {
          "type": "string",
          "maxLength": 80,
          "description": "Destination country or market (the market tag group)."
        }
      }
    },
    "items": {
      "type": "array",
      "minItems": 1,
      "maxItems": 30,
      "items": {
        "type": "object",
        "required": [
          "model",
          "qty"
        ],
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60,
            "description": "Catalog model code, in the catalog's spelling."
          },
          "color": {
            "type": "string",
            "maxLength": 40,
            "description": "Color or spec (the SKU tag group). Free text, one line."
          },
          "qty": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000000
          },
          "logo": {
            "type": "string",
            "enum": [
              "",
              "yes",
              "no"
            ],
            "description": "The surface/printing tag group: the buyer's logo or not; empty when not chosen."
          },
          "packaging": {
            "type": "string",
            "enum": [
              "",
              "standard",
              "color-box",
              "custom"
            ],
            "description": "The packaging tag group; empty when not chosen."
          },
          "note": {
            "type": "string",
            "maxLength": 200
          }
        }
      }
    },
    "message": {
      "type": "string",
      "maxLength": 5000
    },
    "heardFrom": {
      "type": "string",
      "maxLength": 120
    },
    "source": {
      "type": "object",
      "properties": {
        "pageUrl": {
          "type": "string"
        },
        "referrer": {
          "type": "string"
        },
        "landingPage": {
          "type": "string"
        },
        "utm": {
          "type": "object",
          "properties": {
            "source": {
              "type": "string"
            },
            "medium": {
              "type": "string"
            },
            "campaign": {
              "type": "string"
            },
            "term": {
              "type": "string"
            },
            "content": {
              "type": "string"
            }
          }
        }
      }
    },
    "checks": {
      "type": "object",
      "required": [
        "turnstile",
        "sentBy",
        "attention"
      ],
      "properties": {
        "turnstile": {
          "type": "string",
          "enum": [
            "pass",
            "fail",
            "none"
          ],
          "description": "Cloudflare Turnstile result: pass (verified), fail (configured but the token was missing or failed), none (not configured, or a program sending without a token). A signal only; it never stops an inquiry."
        },
        "sentBy": {
          "type": "string",
          "enum": [
            "browser",
            "program"
          ]
        },
        "attention": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "What the sales team should know about the submission; empty when nothing."
        }
      }
    }
  }
}
