Building a Placeholder
A Placeholder is a Riddle with no content of its own. It holds a routing rule that is read every time the embed is requested, deciding which other Riddle to show in its place. Embed the Placeholder once, and swap what visitors see by changing the rule.
Note: The Placeholder feature has to be enabled for your account - otherwise building one is rejected with Placeholder riddles are not enabled.
Build configuration
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
title | ✓ | string | The title of the Placeholder | |
riddleId | string | The fallback: the UUID of the Riddle to show when no condition matches | ||
conditions | array | The routing rules, evaluated before the fallback; up to 100. See below | ||
preset | object | The preset settings | ||
publish | object | The publish settings |
A Placeholder has no blocks, no result/results, no logic and no leaderboards - there is nothing in it to answer, and its conditions pick a target Riddle rather than branch inside one.
The fallback is optional and stays meaningful when left out: a Placeholder with no fallback and no matching condition resolves to nothing at all. In an edit, "riddleId": null clears it.
Note: An edit may not take away the last routing target. A Placeholder routes through exactly two things - the fallback riddleId and the conditions list - so an edit whose result would have neither is rejected with "This edit would leave the Placeholder with no fallback "riddleId" and no "conditions" at all, which makes it unable to display any Riddle." and nothing is written. That covers both ways of getting there: clearing the fallback of a Placeholder that has no conditions, and sending "conditions": [] on one that has no fallback. Keep at least one of the two - clear the fallback and send the conditions that replace it in the same edit.
This guards only that transition, not the state itself:
- Creating a Placeholder with neither target is still allowed - that is the empty shell the Creator's own "new Placeholder" flow starts from, and how one is created before its targets are known.
- A Placeholder that already has no target stays editable, so renaming one is never blocked by a target it never had.
- Expired conditions count as targets. A condition whose window has passed is kept on the Placeholder (see The stored order is not your order), so removing the fallback of a Placeholder whose only conditions are expired is accepted - whether an expired window resolves to anything at request time is decided when the embed is requested.
Any target - the fallback and every condition's riddleId - has to be an existing Riddle of your own account or team. A Placeholder cannot route to itself, and not to another Placeholder: chaining is not supported.
{
"type": "Placeholder",
"build": {
"title": "Homepage slot",
"riddleId": "abcdef12"
}
}
Conditions
Each condition targets either one specific Riddle or a tag, and may be limited to a time window. They are checked in order and the first match wins; if none matches, the fallback riddleId is used.
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
riddleId | either | string | The UUID of the Riddle to show | |
tag | either | string|integer | A tag name or tag ID - the Riddle to show is picked from the Riddles carrying that tag | |
tagMode | with tag | string | lastPublished (the most recently published Riddle with that tag) or random | |
randomRefreshIntervalSeconds | integer | How often a random pick is re-drawn, in seconds. Only allowed with "tagMode": "random" | ||
from | string | Start of a date window, e.g. 2026-09-01 00:00:00 | ||
to | string | End of that date window. Omit it for an open-ended one | ||
dailyFrom | string | Start of a daily time-of-day window, as HH:MM:SS | ||
dailyTo | string | End of that daily window |
Exactly one target per condition: sending riddleId and tag is rejected, and so is sending neither. tagMode is required for a tag condition and rejected on a riddleId one.
A tag has to already exist in the Riddle's own scope (its team's tags, or your personal ones). A tag that cannot be found is an error - a condition never creates one, so a typo is reported instead of silently defining an empty tag.
Time windows
A condition carries either a date window (from, optionally to) or a daily window (dailyFrom and dailyTo, always together) - mixing the two on one condition is rejected. A condition with no window always matches whenever it is reached, which is how you express "everything else lands here".
{
"type": "Placeholder",
"build": {
"title": "Homepage slot",
"riddleId": "abcdef12",
"conditions": [
{
"riddleId": "12abcdef",
"from": "2026-12-24 00:00:00",
"to": "2026-12-26 23:59:59"
},
{
"tag": "evergreen",
"tagMode": "random",
"randomRefreshIntervalSeconds": 3600,
"dailyFrom": "18:00:00",
"dailyTo": "23:59:59"
},
{
"tag": "daily-quiz",
"tagMode": "lastPublished"
}
]
}
}
In spoken form: over Christmas show one specific Riddle; on every other day between 18:00 and midnight show a random one tagged evergreen; at all other times show the most recently published Riddle tagged daily-quiz; and if none of that resolves to a Riddle, show the fallback abcdef12.
The condition without a window is written last on purpose: it matches whenever it is reached, so anything after it would never be evaluated.
The stored order is not your order
Conditions are normalized on every build and edit, exactly as they are in the Creator: the list is re-sorted by its own priority rules and overlapping windows are trimmed. Reading the Placeholder back with GET /riddle-builder/{UUID} can therefore return the conditions in a different order, or with narrower windows, than you sent. Sending that result back is stable - it just is not literal.
Because a stored condition has no ID of its own, conditions is replaced as a whole by an edit: resend every condition you want to keep. For the same reason the list cannot be reordered with a marker - the order you send is the input to the normalization above. Sending "conditions": [] therefore removes all of them, which is only accepted while the Placeholder keeps a fallback riddleId - see the build configuration above.
Reading a Placeholder back
A target that has meanwhile been deleted, or turned into a Placeholder itself, can no longer be expressed. Such a condition - or the fallback - is left out of the returned build config and reported in warnings instead, naming exactly which one and why.
Full example
A Placeholder with all three kinds of routing in one build config: a specific Riddle over Christmas, a random one tagged evergreen in the evenings, the most recently published one tagged daily-quiz at all other times, and abcdef12 as the fallback.
{
"type": "Placeholder",
"project": 123,
"publish": true,
"build": {
"title": "Homepage slot",
"riddleId": "abcdef12",
"conditions": [
{
"riddleId": "12abcdef",
"from": "2026-12-24 00:00:00",
"to": "2026-12-26 23:59:59"
},
{
"tag": "evergreen",
"tagMode": "random",
"randomRefreshIntervalSeconds": 3600,
"dailyFrom": "18:00:00",
"dailyTo": "23:59:59"
},
{
"tag": "daily-quiz",
"tagMode": "lastPublished"
}
]
}
}
Next steps
Now that you know how a Placeholder routes, you might want to learn how to:
- Build the Riddles it routes to: quiz, poll, personality, form, minigame, story, or predictor
- Tag those Riddles, so a condition can pick them by tag
- Re-route the Placeholder later by editing its conditions
- Get the embed code you place once and never touch again
- Define the preset and palette you want to use
- Define the publish settings
- Handle exceptions

