{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://otvs.org/schemas/1.0/ticket.json",
  "title": "OTVS 1.0 Ticket",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "spec",
    "ticketId",
    "issuer",
    "issuedAt",
    "status",
    "event",
    "entitlements"
  ],
  "properties": {
    "spec": {
      "type": "string",
      "pattern": "^otvs/1\\.[0-9]+$",
      "description": "Specification identifier and version."
    },
    "ticketId": {
      "$ref": "#/$defs/uuid"
    },
    "issuer": {
      "type": "string",
      "minLength": 1
    },
    "issuedAt": {
      "$ref": "#/$defs/dateTime"
    },
    "status": {
      "enum": [
        "ACTIVE",
        "BLOCKED",
        "CANCELLED"
      ]
    },
    "statusReason": {
      "type": "string"
    },
    "event": {
      "$ref": "#/$defs/event"
    },
    "product": {
      "$ref": "#/$defs/product"
    },
    "holder": {
      "$ref": "#/$defs/holder"
    },
    "seat": {
      "$ref": "#/$defs/seat"
    },
    "entitlements": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/entitlement"
      }
    },
    "requirements": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/requirement"
      }
    },
    "ext": {
      "$ref": "#/$defs/ext"
    }
  },
  "$defs": {
    "uuid": {
      "type": "string",
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
    },
    "dateTime": {
      "type": "string",
      "format": "date-time",
      "description": "RFC 3339 with an explicit timezone offset."
    },
    "timeHHMM": {
      "type": "string",
      "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$"
    },
    "id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64
    },
    "event": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "eventId",
        "timezone"
      ],
      "properties": {
        "eventId": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "type": "string"
        },
        "timezone": {
          "type": "string",
          "minLength": 1,
          "description": "IANA timezone identifier, e.g. Europe/Warsaw."
        },
        "venueId": {
          "$ref": "#/$defs/id"
        }
      }
    },
    "product": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "productId"
      ],
      "properties": {
        "productId": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "type": "string"
        },
        "category": {
          "type": "string",
          "description": "Informational classification, e.g. SINGLE, PASS, SEASON, INVITATION."
        }
      }
    },
    "holder": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "displayName": {
          "type": "string"
        },
        "identityCheckRequired": {
          "type": "boolean",
          "default": false
        }
      }
    },
    "seat": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "sectionId": {
          "$ref": "#/$defs/id"
        },
        "row": {
          "type": "string"
        },
        "number": {
          "type": "string"
        },
        "label": {
          "type": "string"
        }
      }
    },
    "entitlement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "entitlementId",
        "zones"
      ],
      "properties": {
        "entitlementId": {
          "$ref": "#/$defs/id"
        },
        "name": {
          "type": "string"
        },
        "zones": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "gates": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/id"
          }
        },
        "validity": {
          "$ref": "#/$defs/validity"
        },
        "entryWindows": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/entryWindow"
          }
        },
        "usage": {
          "$ref": "#/$defs/usage"
        },
        "requirements": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/requirement"
          }
        },
        "ext": {
          "$ref": "#/$defs/ext"
        }
      }
    },
    "validity": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "from": {
          "$ref": "#/$defs/dateTime"
        },
        "until": {
          "$ref": "#/$defs/dateTime"
        }
      }
    },
    "entryWindow": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "from",
        "until"
      ],
      "properties": {
        "from": {
          "$ref": "#/$defs/dateTime"
        },
        "until": {
          "$ref": "#/$defs/dateTime"
        },
        "gates": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/id"
          }
        }
      }
    },
    "usage": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "maxUses": {
          "type": "integer",
          "minimum": 1
        },
        "maxUsesPerDay": {
          "type": "integer",
          "minimum": 1
        },
        "dayRollover": {
          "$ref": "#/$defs/timeHHMM",
          "default": "00:00"
        },
        "antiPassback": {
          "type": "boolean",
          "default": true
        }
      }
    },
    "requirement": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "type",
        "critical"
      ],
      "properties": {
        "type": {
          "type": "string",
          "pattern": "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9-]*)+$"
        },
        "critical": {
          "type": "boolean"
        },
        "params": {
          "type": "object"
        }
      }
    },
    "ext": {
      "type": "object",
      "description": "Custom extensions; keys SHOULD be prefixed with a reversed domain, e.g. com.example.tickets.x."
    }
  }
}
