{
  "$id": "https://distribot.world/distribot/wtb/schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Distribot WTB demand post",
  "description": "A want-to-buy demand post. One POST creates the demand; the buyer confirms it from their inbox; the operator reviews it onto the public board. The budget block is the buyer's own willing-to-pay declaration and is required (RWD-LRD-0077).",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "product_type",
    "deliverable",
    "budget",
    "contact_email"
  ],
  "properties": {
    "product_type": {
      "title": "What kind of product",
      "type": "string",
      "enum": [
        "download",
        "license",
        "api_key",
        "saas_access"
      ],
      "description": "The manifest taxonomy type the demand targets."
    },
    "deliverable": {
      "title": "What you want built or delivered",
      "type": "string",
      "minLength": 10,
      "maxLength": 400,
      "description": "The demand headline the board renders."
    },
    "scope": {
      "title": "Scope details",
      "type": "string",
      "maxLength": 2000,
      "description": "Optional constraints, integrations, volumes."
    },
    "budget": {
      "title": "Willing-to-pay budget",
      "type": "object",
      "additionalProperties": false,
      "required": [
        "currency",
        "negotiable"
      ],
      "description": "Exactly one budget shape: a single amount_cents OR a min_cents/max_cents range (max_cents >= min_cents). All amounts are integer minor units.",
      "properties": {
        "currency": {
          "title": "Currency",
          "type": "string",
          "pattern": "^[A-Za-z]{3}$",
          "description": "ISO-4217 alpha code, e.g. EUR."
        },
        "negotiable": {
          "title": "Negotiable",
          "type": "boolean",
          "description": "Whether the budget is open to negotiation."
        },
        "amount_cents": {
          "title": "Budget amount (minor units)",
          "type": "integer",
          "minimum": 1,
          "maximum": 100000000
        },
        "min_cents": {
          "title": "Budget range minimum (minor units)",
          "type": "integer",
          "minimum": 1,
          "maximum": 100000000
        },
        "max_cents": {
          "title": "Budget range maximum (minor units)",
          "type": "integer",
          "minimum": 1,
          "maximum": 100000000
        }
      },
      "oneOf": [
        {
          "required": [
            "amount_cents"
          ],
          "not": {
            "anyOf": [
              {
                "required": [
                  "min_cents"
                ]
              },
              {
                "required": [
                  "max_cents"
                ]
              }
            ]
          }
        },
        {
          "required": [
            "min_cents",
            "max_cents"
          ],
          "not": {
            "required": [
              "amount_cents"
            ]
          }
        }
      ]
    },
    "delivery_window_days": {
      "title": "Delivery window (days)",
      "type": "integer",
      "minimum": 1,
      "maximum": 365,
      "description": "How soon the deliverable is needed."
    },
    "acceptance": {
      "title": "Acceptance criteria / revisions",
      "type": "string",
      "maxLength": 1000
    },
    "license_expectation": {
      "title": "License expectation",
      "type": "string",
      "maxLength": 200
    },
    "refund_expectation": {
      "title": "Refund expectation",
      "type": "string",
      "maxLength": 500
    },
    "settlement": {
      "title": "Settlement mechanics",
      "type": "string",
      "maxLength": 500,
      "description": "How the buyer expects to pay (escrow, invoice, ...)."
    },
    "expires_in_days": {
      "title": "Post lifetime after publication (days)",
      "type": "integer",
      "minimum": 7,
      "maximum": 90,
      "default": 30
    },
    "contact_email": {
      "title": "Your email (confirm + reply channel)",
      "type": "string",
      "pattern": "^[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}$",
      "maxLength": 254,
      "description": "Receives the confirm link; stays private \u2014 the board never renders it (RWD-LRD-0078 reply channel)."
    }
  }
}