Create Minigame
When building a minigame you can add:
- A
SlotMachineblock - A
WheelSpinnerblock (also called Wheel of fortune) - A
Sudokublock - A
Minesweeperblock - A
Crosswordsblock - Form fields:
FormBuilder, standaloneFormFieldblocks, or an embeddedFormSelect - General blocks:
Content,Quote, andAd - One result page
Every minigame block's title is required and has to carry text: "", a whitespace-only string and markup that renders to nothing are all rejected, because the game would be published with a blank heading above it.
Adding a SlotMachine
Properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to SlotMachine | |
title | ✓ | string | The title of the slot machine | |
description | string | The description of the slot machine | ||
media | string|object | Media to display as part of the block. See Use media | ||
startBalance | integer | The starting balance of the user; 1-1000. 0 (or less) is a machine that cannot be played, and the ceiling keeps the balance inside the small badge the player sees | 10 | |
winningProbability | string | Set to Low, Medium or High | Medium | |
winTitle | string | Title of the message shown once the player lands a winning combination | ||
winDescription | string | Description of that win message | ||
winMedia | string|object | Media of that win message | ||
hasCustomSymbolsEnabled | boolean | Whether custom reel symbols are rendered instead of the default emoji | false | |
customSymbols | object | Up to 6 custom reel symbols; see below. Every entry carries its own keys instead of a title - see item formats |
The slot machine has no mediaOrientation - its media is always shown as-is.
Note: Like WheelSpinner below, a SlotMachine block has no score property - a spin only produces a win/loss outcome (whether the reels land on the winning combination), not a rankable score.
Custom reel symbols
The reels always carry exactly these six symbols, and customSymbols replaces the emoji of one of them with an image of your own. There is no way to add a seventh symbol or a symbol under a different key. customSymbols only accepts objects (see item formats) - each entry needs both a symbolKey and a media.
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
symbolKey | ✓ | string | Which symbol to replace: :recycle:, :cherries:, :lemon:, :grapes:, :bell:, or :dollar: | |
media | ✓ | string|object | The image to use; must be an image (no video/social media) |
Rules:
- At most 6 entries, and every
symbolKeymay only appear once - a duplicate key is rejected. - Supplying at least one custom symbol automatically sets
hasCustomSymbolsEnabledtotrue, even if you explicitly sendfalse- otherwise the custom symbols would never be rendered.
Example
{
"title": "Spin the slot machine",
"description": "Try your luck!",
"type": "SlotMachine",
"startBalance": 1000,
"winningProbability": "High",
"winTitle": "Jackpot!",
"winDescription": "You won a 20% voucher.",
"customSymbols": [
{
"symbolKey": ":cherries:",
"media": "https://httpbin.io/image/jpeg"
}
]
}
Adding a WheelSpinner
Properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to WheelSpinner | |
title | ✓ | string | The title of the wheel spinner | |
items | ✓ | object | The items on the wheel, 2-30; see WheelSpinnerItem properties below - see item formats | |
description | string | The description of the wheel spinner | ||
media | string|object | Media to display as part of the block | ||
isMediaVisible | boolean | Whether the items' own media renders on the wheel segments | false | |
areEmojisEnabled | boolean | Whether emoji are rendered on the wheel segments | false | |
isCopyToClipboardButtonEnabled | boolean | Whether the result offers a "copy to clipboard" button for the awarded item, e.g. a discount code | true |
The wheel spinner has no mediaOrientation. Its items are objects described by the properties below rather than by a title (see item formats).
Note: A WheelSpinner block and its items do not support scoring - neither has a score property (unlike Sudoku below, which does). This is also why a minigame consisting of nothing but a WheelSpinner cannot be connected to a Leaderboard: a wheel spin only produces a win/loss/free-spin outcome, not a rankable score. See Create and connect to Leaderboard.
WheelSpinnerItem properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to either Win, Loss, or FreeSpin | |
title | ✓ | string | The title of the item | |
percent | ✓ | integer | The percentage of the wheel this item takes up; 0-100 | |
award | required if type is Win | string | The award the user gets, e.g. a voucher code | |
backgroundColor | string | The background color of the item | rgba(255, 255, 255, 1) | |
textColor | string | The text color of the item | rgba(0, 0, 0, 1) | |
media | string|object | Media to display on this wheel segment |
Note: No wheel item has a score property either - a Win/Loss/FreeSpin item only carries its award/percentage/appearance, never a score.
Note: The percent values across all items must sum to exactly 100, otherwise a validation error is thrown. Because the API always states the percentages explicitly, the Creator's "custom percentages" option is switched on automatically for wheels built via the API.
Note: Giving any item a media automatically switches isMediaVisible on for the whole block. Send isMediaVisible: false without item media if you want a wheel without images.
Example
{
"title": "Spin the wheel",
"description": "Try your luck!",
"type": "WheelSpinner",
"items": [
{
"title": "Win 100$",
"type": "Win",
"percent": 50,
"award": "100$"
},
{
"title": "Lose 50$",
"type": "Loss",
"percent": 30,
"award": "-50$"
},
{
"title": "Free spin",
"type": "FreeSpin",
"percent": 20,
"award": "Free spin"
}
]
}
Adding a Sudoku
Properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to Sudoku | |
title | ✓ | string | The title of the sudoku block | |
description | string | The description of the sudoku block | ||
media | string|object | Media to display as part of the block. Adding it automatically switches its display on | ||
mediaOrientation | string | How it is cropped: Settings, Wide, Square, Tall, or Original | Settings | |
difficulty | integer | The difficulty level of the sudoku; 0-100. A knob, not the percentage of cells removed - see the note below | 50 | |
areRulesVisible | boolean | Whether the rules are visible to the user | true | |
isConflictHighlightingAllowed | boolean | Whether conflict highlighting is allowed | false | |
isGuessCheckingAllowed | boolean | Whether guess checking is allowed | false | |
refreshInterval | string | The interval in which the sudoku automatically refreshes; set to a value of: None, Hourly, Daily, Weekly, or Monthly | None | |
score | integer | Points awarded for solving the puzzle | 1 | |
successMessage | string | Message shown once the puzzle is solved. Setting a non-empty message switches the success message on; an empty string switches it off | ||
isSuccessMessageEnabled | boolean | Whether the success message is shown. Sending false keeps the message stored but hidden. See feature toggles | derived from successMessage | |
successMessageMedia | string|object | Media of that success message | ||
isSuccessMessageMediaEnabled | boolean | Whether the success message's own media is displayed | false |
The puzzle itself is generated for you from difficulty - you cannot supply a grid or a solution.
Note: difficulty is a 0-100 knob, not a share of the grid. The generator blanks floor(difficulty * 0.3) + 31 of the 81 cells, so the scale runs from 31 blanks (50 givens) at 0 to 61 blanks (20 givens) at 100 - between 38% and 75% of the cells removed, never less than 38%. It moves one cell per ~3.3 points, so 0 and 1 produce the same puzzle. A cell is only blanked while the puzzle still has a unique solution, so the result can fall a cell or two short of that number, never above it. difficulty: 40, for example, gives 38 givens - not the 49 a "40% removed" reading would suggest.
Example
{
"title": "Sudoku Challenge",
"description": "Solve the puzzle!",
"type": "Sudoku",
"difficulty": 70,
"areRulesVisible": true,
"isConflictHighlightingAllowed": true,
"isGuessCheckingAllowed": true,
"refreshInterval": "Weekly"
}
Adding a Minesweeper
Properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to Minesweeper | |
title | ✓ | string | The title of the minesweeper block | |
description | string | The description of the minesweeper block | ||
media | string|object | Media to display as part of the block. Adding it automatically switches its display on | ||
mediaOrientation | string | How it is cropped: Settings, Wide, Square, Tall, or Original | Settings | |
rows | integer | Height of the grid; 5-18 | 9 | |
cols | integer | Width of the grid; 5-18 | 9 | |
mines | integer | How many mines are hidden in the grid; 1-20 whatever the board size is | 10 | |
gameMode | string | Set to Normal, Streak, or None | Streak | |
areRulesVisible | boolean | Whether the rules are visible to the user | true |
The board is laid out at play time from rows, cols and mines - there is nothing to configure per cell, and the smallest grid (5x5) always has room for the maximum of 20 mines.
Note: the mine cap does not scale with the board - it is 20 on every grid size, matching the Creator's own mine input, so a board built here stays editable there. Mine density therefore falls as the board grows: 20 mines in 25 cells on the smallest grid, but 20 in 324 cells (about 6%) on an 18x18, well below the 15-20% a conventional Minesweeper uses. Large boards play sparse; choose a smaller grid if you want a dense one. An 18-column board is also wider than a default 540px embed and scrolls sideways inside it rather than being visible all at once.
Note: Unlike Sudoku and Crosswords, a Minesweeper block has no success message and no score property.
Example
{
"title": "Clear the minefield",
"description": "Avoid the mines!",
"type": "Minesweeper",
"rows": 12,
"cols": 12,
"mines": 18,
"gameMode": "Normal",
"areRulesVisible": true
}
Adding a Crosswords
Properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to Crosswords | |
title | ✓ | string | The title of the crossword block | |
words | ✓ | object | The words and their clues, at least 1; see CrosswordWord properties below - see item formats | |
description | string | The description of the crossword block | ||
media | string|object | Media to display as part of the block. Adding it automatically switches its display on | ||
gridSize | integer | The most room the layout may use, and therefore the longest word it can take; 5-15. Not the grid you get: the puzzle is laid out in the smallest grid its words still interlock in (never smaller than 5), and that is the size stored and read back. Leave it out and the words are simply fitted into the smallest grid that takes them | fitted to the words | |
areRulesVisible | boolean | Whether the rules are visible to the user | false | |
isHighlightingCurrentWordEnabled | boolean | Whether the word the player is currently on is highlighted | true | |
isGuessCheckingAllowed | boolean | Whether the player may check their current guess | true | |
isAutoCheckingAllowed | boolean | Whether each entry is checked as it is typed | true | |
isAnswerRevealingAllowed | boolean | Whether the player may reveal an answer | true | |
highlightedWordColor | string | Color of the currently active word | #ffe082 | |
highlightedCellColor | string | Color of the single active cell | #90caf9 | |
score | integer | Points awarded for solving the puzzle | 1 | |
successMessage | string | Message shown once the puzzle is solved. Setting a non-empty message switches the success message on; an empty string switches it off | ||
isSuccessMessageEnabled | boolean | Whether the success message is shown. Sending false keeps the message stored but hidden. See feature toggles | derived from successMessage | |
successMessageMedia | string|object | Media of that success message | ||
isSuccessMessageMediaEnabled | boolean | Whether the success message's own media is displayed | false |
The crossword block has no mediaOrientation.
CrosswordWord properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
text | ✓ | string | The answer, 2 letters up to gridSize letters long | |
clue | ✓ | string | The clue shown for that answer, plain text (HTML is rejected - the clue list prints what it is given, so the tags themselves would show up in the puzzle), at most 200 characters. It is genuinely required: an empty or whitespace-only clue is rejected like a missing one, because the word would be drawn as a numbered entry of the grid with nothing in the clue list to answer it by | |
direction | string | Set to across or down to pin the word to that direction. There is no default to fall back to: leave the key out (or send null) and the direction is part of what the layout decides - whichever one interlocks the word best, except for the very first word of the list, which is laid down across through the centre of the grid to start the puzzle off. An empty string is not a way to say "no direction" and is rejected | decided by the layout |
A word entry has exactly these three keys - any other key is rejected. The same answer cannot appear twice, however differently cased or padded and however different the two clues are: the puzzle has one place for those letters, so the second clue would be numbered like the first with no entry of its own to answer.
Note: You supply the words and their clues; where they sit is generated for you. The grid, each word's starting cell, its direction and number, and the clue list are all laid out server-side so that the words interlock on matching letters. This is the one deliberate difference from the Creator, where you drag the words around the grid yourself: a build configuration cannot know which cells are still free, and a crossword whose words do not interlock is not a crossword. The layout is deterministic, so the same word list always produces the same puzzle.
Note: Interlocking is a requirement of the build, not a best effort. A word that does not fit into the grid, and a word that fits but shares no letter with the others and would end up lying beside the puzzle rather than crossing into it, both fail the build with an error naming that word - so a successful build always gives you one connected puzzle, never a crossword for some of the words plus a loose word list for the rest. If you hit it, raise gridSize to give the layout more room - or leave it out entirely, which offers the layout the largest grid there is - or replace the word that shares no letters with the others.
Note: The grid size is generated too, not just the layout inside it. gridSize is a ceiling on the room the layout may use: once the words interlock, they are laid out again in the smallest grid they still interlock in, so a puzzle is never stored inside a border of empty cells - which the player would see as a grid far bigger than the crossword in it. A three-word puzzle asking for "gridSize": 15 therefore comes back at 6x6, and reading the Riddle back gives you that 6, not the 15 you sent. Two consequences worth knowing: asking for a bigger grid is not a way to space a puzzle out, and leaving gridSize out is the normal thing to do - the words are then fitted automatically instead of you having to guess a size that takes them.
Note: Because the whole puzzle is re-generated from the word list, words is the one collection that does not merge by id and cannot be reordered - an update that sends words replaces the puzzle wholesale, so always send the complete list you want, not just the word you are changing. For the same reason "$create": true and "$delete": true are rejected on a word entry: with no merge to apply them to, they would leave the puzzle consisting of that one marked word.
Example
{
"title": "Capitals crossword",
"description": "Fill in the grid!",
"type": "Crosswords",
"gridSize": 9,
"score": 10,
"successMessage": "Well done - you solved it!",
"words": [
{ "text": "PARIS", "clue": "Capital of France", "direction": "across" },
{ "text": "ROME", "clue": "Capital of Italy" },
{ "text": "OSLO", "clue": "Capital of Norway" }
]
}
Result page
In a minigame only one result page is allowed which is specified in build.result. Basic results have a title and description property:
{
"title": "Thank you!",
"description": "Thanks for playing"
}
If you want to create complex result pages with texts, images, ... click here to learn how to build advanced result pages.
Full example
Example with all three minigame blocks + options:
{
"type": "Minigame",
"build": {
"title": "Minigame example (Builder API)",
"blocks": [
{
"title": "Spin the slot machine",
"description": "Try your luck!",
"type": "SlotMachine",
"media": "https://httpbin.io/image/jpeg",
"startBalance": 1000,
"winningProbability": "High",
"winTitle": "Jackpot!",
"winDescription": "You won a 20% voucher.",
"winMedia": "https://httpbin.io/image/png",
"customSymbols": [
{
"symbolKey": ":cherries:",
"media": "https://httpbin.io/image/webp"
},
{
"symbolKey": ":bell:",
"media": "https://httpbin.io/image/svg"
}
]
},
{
"title": "Spin the wheel",
"description": "Try your luck!",
"type": "WheelSpinner",
"isCopyToClipboardButtonEnabled": true,
"areEmojisEnabled": false,
"isMediaVisible": false,
"items": [
{
"title": "Win 100$",
"type": "Win",
"percent": 50,
"award": "100$",
"backgroundColor": "rgba(89, 225, 171, 1)",
"textColor": "rgba(0, 0, 0, 1)"
},
{
"title": "Lose 50$",
"type": "Loss",
"percent": 30,
"backgroundColor": "rgba(0, 0, 0, 1)",
"textColor": "rgba(255, 255, 255, 1)"
},
{
"title": "Free spin",
"type": "FreeSpin",
"percent": 20
}
]
},
{
"title": "Sudoku Challenge",
"description": "Solve the puzzle!",
"type": "Sudoku",
"difficulty": 70,
"areRulesVisible": true,
"isConflictHighlightingAllowed": true,
"isGuessCheckingAllowed": true,
"refreshInterval": "Weekly",
"score": 5,
"successMessage": "Well done - you solved it!"
},
{
"title": "Clear the minefield",
"description": "Avoid the mines!",
"type": "Minesweeper",
"rows": 12,
"cols": 12,
"mines": 18,
"gameMode": "Normal"
},
{
"title": "Capitals crossword",
"type": "Crosswords",
"gridSize": 9,
"score": 10,
"successMessage": "Well done - you solved it!",
"words": [
{ "text": "PARIS", "clue": "Capital of France", "direction": "across" },
{ "text": "ROME", "clue": "Capital of Italy" },
{ "text": "OSLO", "clue": "Capital of Norway" }
]
},
{
"title": "Where should we send your prize?",
"type": "FormBuilder",
"fields": [
{ "title": "Your name", "type": "Name" },
{
"title": "Your email",
"type": "Email",
"isRequired": true,
"requiredMessage": "We need your email to send you the prize."
}
]
}
],
"result": {
"title": "Thanks for playing!",
"description": "We hope you had fun."
}
}
}
Next steps
Now that you know the basics of building a minigame, you might want to learn how to:
- Add form fields to your Riddle
- Use the Ad block to monetize your Riddle or add Content blocks
- Define the publish settings: Enable DOI, email automation, add integrations, and more
- Define the preset and palette you want to use
- Build an advanced result page
- Add media, such as images, videos, and social media content
- Serve this Riddle through a Placeholder, so you can swap what an embed shows without changing the embed code

