{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/altoviz-com/json-schema/altoviz-com-product-schema.json",
  "title": "Product",
  "description": "Product",
  "x-generated": "2026-11-01",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/altoviz-com-openapi.json#/components/schemas/Product",
  "type": "object",
  "properties": {
    "active": {
      "type": "boolean",
      "description": "Indicates whether the product is active"
    },
    "defaultQuantity": {
      "type": "number",
      "description": "Default quantity",
      "format": "double"
    },
    "description": {
      "type": [
        "string",
        "null"
      ],
      "description": "Description"
    },
    "family": {
      "$ref": "#/$defs/ProductFamily"
    },
    "id": {
      "type": "integer",
      "description": "Product ID",
      "format": "int32",
      "readOnly": true
    },
    "imageUrl": {
      "type": [
        "string",
        "null"
      ],
      "description": "Gets the product image URL",
      "readOnly": true
    },
    "internalId": {
      "type": [
        "string",
        "null"
      ],
      "description": "ID used in your system"
    },
    "internalNotes": {
      "type": [
        "string",
        "null"
      ],
      "description": "Internal notes"
    },
    "isUnitPriceTaxIncluded": {
      "type": "boolean",
      "description": "<b>Deprecated</b> — always false on read, because `unitPrice` is now always the\n            tax-excluded price. There is nothing left for it to select between.\n            \nOn write it is still honoured: setting it to true means the price you are sending is\n            tax-included, exactly as before. Send `taxIncludedPrice` instead.",
      "deprecated": true
    },
    "margin": {
      "type": "number",
      "description": "Margin per unit, excluding tax: tax-excluded price less purchase price.\n\nDerived by the server on every save; a value sent here is ignored.",
      "format": "double"
    },
    "marginRate": {
      "type": "number",
      "description": "Margin rate: the margin as a percentage of the sale price (FR « taux de marque »).\n\nDerived by the server on every save; a value sent here is ignored.",
      "format": "double"
    },
    "markupRate": {
      "type": "number",
      "description": "Markup rate: the margin as a percentage of the purchase price (FR « taux de marge »).\n\nDerived by the server on every save; a value sent here is ignored.",
      "format": "double"
    },
    "metadata": {
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": {
        "type": [
          "string",
          "null"
        ]
      },
      "description": "Metadata: free-form key/value pairs carried with the product, for an integration to keep\nits own identifiers and flags against it.\n\nSent as a PATCH, not a replacement: keys you send are added or updated, keys you leave out\nkeep whatever they held, and a key is removed only when you send it as `null`. Omitting\nthe property entirely leaves the whole bag untouched, and `\"\"` stores an empty value\nrather than removing the key."
    },
    "name": {
      "type": [
        "string",
        "null"
      ],
      "description": "Name"
    },
    "number": {
      "type": [
        "string",
        "null"
      ],
      "description": "Number"
    },
    "purchasePrice": {
      "type": "number",
      "description": "Purchase price",
      "format": "double"
    },
    "taxExcludedPrice": {
      "type": "number",
      "description": "Sale price excluding tax.",
      "format": "double"
    },
    "taxIncludedPrice": {
      "type": "number",
      "description": "Sale price including tax.",
      "format": "double"
    },
    "type": {
      "$ref": "#/$defs/ProductType"
    },
    "unit": {
      "$ref": "#/$defs/Unit"
    },
    "unitPrice": {
      "type": "number",
      "description": "<b>Deprecated</b> — use `taxExcludedPrice`. Unit price, excluding tax: the same\n            value as `taxExcludedPrice`.\n            \nStill populated, and still read on write when neither new price field is sent, so no\n            existing integration has to change today.\nIt used to carry whichever half the product was edited in — the tax-included price when\n            `isUnitPriceTaxIncluded` was true — because only one price was stored. Both are stored\n            now, so this always reports the tax-excluded one and `isUnitPriceTaxIncluded` is always\n            false: a client that reads the flag keeps computing the same result.",
      "format": "double",
      "deprecated": true
    },
    "vat": {
      "$ref": "#/$defs/Vat"
    }
  },
  "additionalProperties": false,
  "$defs": {
    "ProductFamily": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "description": "ID",
          "format": "int32",
          "readOnly": true
        },
        "label": {
          "type": [
            "string",
            "null"
          ],
          "description": "Label"
        },
        "number": {
          "type": [
            "string",
            "null"
          ],
          "description": "Number"
        }
      },
      "additionalProperties": false,
      "description": "Product family"
    },
    "ProductType": {
      "enum": [
        "Product",
        "Service",
        "Text"
      ],
      "type": "string"
    },
    "Unit": {
      "type": "object",
      "properties": {
        "code": {
          "type": [
            "string",
            "null"
          ],
          "description": "Code"
        },
        "conversion": {
          "type": "number",
          "description": "Conversion",
          "format": "double",
          "readOnly": true
        },
        "decimals": {
          "type": [
            "number",
            "null"
          ],
          "description": "Decimals",
          "format": "double",
          "readOnly": true
        },
        "id": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Unit Id",
          "format": "int32",
          "readOnly": true
        },
        "name": {
          "type": [
            "string",
            "null"
          ],
          "description": "Name",
          "readOnly": true
        },
        "type": {
          "$ref": "#/$defs/UnitType"
        }
      },
      "additionalProperties": false,
      "description": "Unit"
    },
    "UnitType": {
      "enum": [
        "Time",
        "Weight",
        "Area",
        "Volume",
        "Dimension",
        "Other"
      ],
      "type": "string"
    },
    "Vat": {
      "type": "object",
      "properties": {
        "default": {
          "type": "boolean",
          "description": "Indicatess if this is the default VAT for the region",
          "readOnly": true
        },
        "id": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Vat ID",
          "format": "int32",
          "readOnly": true
        },
        "label": {
          "type": [
            "string",
            "null"
          ],
          "description": "Label",
          "readOnly": true
        },
        "rate": {
          "type": [
            "number",
            "null"
          ],
          "description": "Rate",
          "format": "double"
        },
        "region": {
          "$ref": "#/$defs/VatRegion"
        }
      },
      "additionalProperties": false,
      "description": "Vat"
    },
    "VatRegion": {
      "enum": [
        "FR",
        "EU",
        "IE",
        "DOM",
        "Corse",
        "Monaco"
      ],
      "type": "string"
    }
  }
}