{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://fanpassport.world/schemas/challenge-catalog.schema.json", "title": "Fan Passport Challenge Catalog", "description": "Machine-readable validation schema for data/seeds/challenges.json.", "type": "object", "additionalProperties": false, "required": [ "catalogVersion", "contentType", "tournamentId", "ruleSchemaVersion", "updatedAt", "defaultTiming", "challengeSets", "rewardTables", "ruleEvents", "challenges" ], "properties": { "catalogVersion": { "type": "string", "pattern": "^20[0-9]{2}\\.[0-9]+\\.[0-9]+$" }, "contentType": { "const": "fan-passport.challenge-catalog" }, "tournamentId": { "type": "string", "minLength": 3 }, "ruleSchemaVersion": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" }, "updatedAt": { "type": "string", "format": "date-time" }, "defaultTiming": { "$ref": "#/$defs/timing" }, "challengeSets": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/challengeSet" } }, "rewardTables": { "type": "object", "additionalProperties": true }, "ruleEvents": { "type": "array", "minItems": 1, "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, "uniqueItems": true }, "challenges": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/challenge" } } }, "$defs": { "phase": { "type": "string", "enum": [ "pre_tournament", "group_stage", "knockout_stage", "round_of_32", "round_of_16", "quarter_final", "semi_final", "third_place_playoff", "final", "post_tournament" ] }, "category": { "type": "string", "enum": [ "onboarding", "daily", "watch", "collection", "prediction", "quiz", "sticker", "memory", "live", "social", "knockout", "stadium" ] }, "difficulty": { "type": "string", "enum": ["starter", "easy", "medium", "hard", "elite"] }, "repeatability": { "type": "string", "enum": ["once", "daily", "weekly", "seasonal", "unlimited"] }, "timing": { "type": "object", "additionalProperties": false, "required": ["startAt", "endAt", "timezone"], "properties": { "startAt": { "type": "string", "format": "date-time" }, "endAt": { "type": "string", "format": "date-time" }, "timezone": { "type": "string", "minLength": 1 }, "cadence": { "type": "string", "enum": ["daily", "weekly", "event_window", "manual"] } } }, "challengeSet": { "type": "object", "additionalProperties": false, "required": ["id", "title", "description", "categories", "phase", "unlockStrategy"], "properties": { "id": { "type": "string", "pattern": "^set_[a-z0-9_]+$" }, "title": { "type": "string", "minLength": 3, "maxLength": 80 }, "description": { "type": "string", "minLength": 10, "maxLength": 240 }, "categories": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/category" }, "uniqueItems": true }, "phase": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/phase" }, "uniqueItems": true }, "unlockStrategy": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" } } }, "challenge": { "type": "object", "additionalProperties": false, "required": [ "id", "setId", "title", "description", "category", "difficulty", "repeatability", "phase", "rules", "rewards", "contentRefs", "tags", "riskFlags" ], "properties": { "id": { "type": "string", "pattern": "^chal_[a-z0-9_]+$" }, "setId": { "type": "string", "pattern": "^set_[a-z0-9_]+$" }, "title": { "type": "string", "minLength": 3, "maxLength": 80 }, "description": { "type": "string", "minLength": 10, "maxLength": 260 }, "category": { "$ref": "#/$defs/category" }, "difficulty": { "$ref": "#/$defs/difficulty" }, "repeatability": { "$ref": "#/$defs/repeatability" }, "phase": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/phase" }, "uniqueItems": true }, "timing": { "$ref": "#/$defs/timing" }, "rules": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/rule" } }, "rewards": { "$ref": "#/$defs/rewards" }, "contentRefs": { "$ref": "#/$defs/contentRefs" }, "tags": { "type": "array", "minItems": 1, "items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" }, "uniqueItems": true }, "riskFlags": { "type": "array", "items": { "type": "string", "enum": [ "requires_live_result", "requires_official_feed", "requires_manual_review", "anti_spoiler", "geo_sensitive", "provider_dependent" ] }, "uniqueItems": true } } }, "rule": { "type": "object", "additionalProperties": true, "required": ["type", "event", "target"], "properties": { "type": { "type": "string", "enum": [ "event_count", "dynamic_collection", "streak", "quiz_score", "sticker_collection", "challenge_completion_count", "check_in_collection", "prediction_outcome", "prediction_batch_score", "memory_count", "composite" ] }, "event": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, "target": { "type": "object", "minProperties": 1, "additionalProperties": true }, "scope": { "type": "object", "additionalProperties": true }, "distinctBy": { "type": "string", "minLength": 1 }, "verification": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, "uniqueItems": true }, "predictionTemplateId": { "type": "string", "pattern": "^pred_template_[a-z0-9_]+$" } } }, "rewards": { "type": "object", "additionalProperties": false, "required": ["xp", "coins", "badges", "stickerPacks"], "properties": { "xp": { "type": "integer", "minimum": 0, "maximum": 10000 }, "coins": { "type": "integer", "minimum": 0, "maximum": 10000 }, "badges": { "type": "array", "items": { "type": "string", "pattern": "^badge_[a-z0-9_]+$" }, "uniqueItems": true }, "stickerPacks": { "type": "array", "items": { "$ref": "#/$defs/stickerPackReward" } } } }, "stickerPackReward": { "type": "object", "additionalProperties": false, "required": ["setId", "quantity"], "properties": { "setId": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*-2026$|^welcome-2026$" }, "quantity": { "type": "integer", "minimum": 1, "maximum": 20 }, "minimumRarity": { "type": "string", "enum": ["common", "uncommon", "rare", "epic", "legendary"] } } }, "contentRefs": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } } } }