Schema Reference
This is the complete field reference for goal set JSON files. For how these fields affect board generation, see Board Generation. For how to use the editors to build goal sets, see The Editors.
Top-Level Fields
Section titled “Top-Level Fields”| Field | Type | Description |
|---|---|---|
schema_version | 3 | Schema version. Must be 3 (current). |
schema_mode | string | Validation mode: strict, capped, dedicated, or relaxed. See Schema Modes. |
capped_schema_settings | object | Capped mode only. Contains max_size (preset size ID, e.g. "square5"). |
dedicated_schema_settings | object | Dedicated mode only. Contains game_mode (e.g. "bingo") and game_size (e.g. "square5"). |
game_name | string | Name of the game. Max 50 characters. |
set_name | string | Name of the objective set. Max 30 characters. |
tag_names | string[] | List of valid exclusion tags. Max 150 tags, each max 20 characters, snake_case. |
objectives | array | Array of objective objects (see below). |
limits | object | Category limit maps (see below). |
Objective Fields
Section titled “Objective Fields”Required
Section titled “Required”| Field | Type | Constraints | Description |
|---|---|---|---|
goal | string | Max 60 chars. One {{X}} placeholder allowed. | The task text shown on the board. |
range | number[] | Max 12 values, positive integers, ascending order. | Target amounts for {{X}}. Required if goal uses {{X}}, and vice versa. See Using Ranges. |
board_categories | string[] | Max 4. Must exist in limits.board. | Board-wide category labels for this objective. |
line_categories | string[] | Max 4. Must exist in limits.line. | Per-line category labels for this objective. |
icons | string[] | Max 12. Filenames must be .webp. | Icon images for this objective. Cannot be used with emoji. |
progression | string[] | 1-4 values from e, m, l, n. Unique. | Which progression zones this objective can appear in (Early, Mid, Late, Endgame). |
Optional
Section titled “Optional”| Field | Type | Default | Constraints | Description |
|---|---|---|---|---|
individual_limit | integer | 1 | 1-99. Must be ≤ number of range values. | How many times this objective can appear on a single board. |
weighting | integer | 100 | 1-100 | Probability of being included in the generation pool. |
tooltip | string | Max 120 chars. Supports {{X}}. | Help text shown on hover. | |
tag | string | Max 20 chars, snake_case. Must exist in tag_names. | Exclusion tag. Only one objective per tag can appear on a board. | |
forced_positions | integer[] | 1-100, ascending, unique. Dedicated/Relaxed only. | Board positions where this objective must be placed. | |
progressive_ranges | boolean | false | If true, lower amounts appear in earlier board positions and higher amounts in later positions, instead of random selection. | |
overlay_icon | boolean | false | Display the goal text overlaid on the icon. | |
text_color | string | 7-char hex, e.g. #ffffff. | Text colour override for overlay mode. | |
emoji | string | Max 2 emoji, no whitespace. | Displayed instead of icons. Cannot be used with icons. | |
shiny | boolean | false | Permanently marks this objective as shiny. | |
disabled | boolean | false | Excludes this objective from generation without deleting it. |
Limits
Section titled “Limits”Category limits control how many objectives of a given type can appear on the board or in any single line. They’re defined as percentage values and converted to absolute counts during generation.
| Field | Type | Constraints | Description |
|---|---|---|---|
limits.board | object | Max 100 categories. Keys: snake_case, max 30 chars. Values: integer 0-9999. | Board-wide category limits. 0 means “at most 1”, not zero. |
limits.line | object | Max 50 categories. Keys: snake_case, max 30 chars. Values: integer 0-9999. | Per-line category limits. 0 means “at most 1 per line”. |
Category keys used in objective board_categories must exist in limits.board, and keys used in line_categories must exist in limits.line.
Using Ranges
Section titled “Using Ranges”Ranges represent a target amount to achieve. They’re for objectives where the goal is the same but the quantity scales the difficulty. The selected value is displayed on the board as x10, x100, etc.
Good uses of ranges:
- “Collect
{{X}}gold” with range [100, 500, 1000] - “Defeat
{{X}}boss[[es]]” with range [1, 2, 3] - “Score
{{X}}point[[s]]” with range [500, 1000, 5000] - “Reach level
{{X}}” with range [10, 25, 50]
The common thread: the number is something you’re working toward, and a bigger number is harder. This is also why progressive_ranges works the way it does (lower values early, higher values endgame).
Cross-Field Rules
Section titled “Cross-Field Rules”These rules are enforced by validation:
- If
goalcontains{{X}},rangemust have at least one value (and vice versa) - Only one
{{X}}placeholder is allowed per goal individual_limitmust be ≤ the number ofrangevalues (each copy needs a distinct range)emojiandiconsare mutually exclusiveforced_positionsis only available indedicatedandrelaxedschema modes- All
board_categorieskeys must exist inlimits.board - All
line_categorieskeys must exist inlimits.line - All
tagvalues must exist intag_names