Create Story
A Story is Riddle's long-form format - quiz meets blog post: a rich text story with images, videos and embeds. Building one via the API works just like a Form: general content blocks plus form fields, with no questions, personalities, or scoring.
When building a story you can add:
- General blocks:
Content,Quote, andAd - The
InteractiveGraphicblock, see below - Form fields: a
FormBuilderblock with many fields, single standaloneFormFieldblocks, or an embeddedFormSelect - One optional result page
- Optional logic
A story has no question blocks - SingleChoice, Order, and the other question types of the other Riddle types are not available here, and so there is no scoring and no leaderboard support either.
Logic
A logic tree is accepted for a Story, but since there are no answer-bearing blocks to branch on, only linear flow control (moving from one block to the next, or ending early) is meaningful here - see logic settings for the general syntax.
Interactive Graphic Block
The InteractiveGraphic block shows an image with clickable hotspots on it - a floor plan, a map, an infographic or a product shot. Every hotspot can open an info pop-up, link to a URL, continue the Riddle or jump to another image of the same block.
A block holds one or more images (images), and every image holds its hotspots (hotspots). Both are arrays of objects (see item formats); neither an image nor a hotspot carries a title - each is described by its own properties below.
Properties
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
type | ✓ | string | Set to InteractiveGraphic | |
images | ✓ | object | The graphic images of this block (at least one); see below - see item formats | |
title | string | The title of the block | ||
description | string | The description of the block | ||
hotspotBackgroundColor | string | Hex color of the hotspot pins | #1a1a64 | |
hotspotTextColor | string | Hex color of the hotspot pins' text | #ffffff | |
isHotspotNumbersEnabled | boolean | Whether the hotspots are numbered 1..n, in the order they appear in hotspots. Areas are numbered too, in the same sequence as the pins | false | |
isHotspotMediaEnabled | boolean | Whether the images inside the hotspot pop-ups are displayed. Giving any hotspot a media switches this on; sending false keeps those images stored but hidden. See feature toggles | true | |
hotspotMediaOrientation | string | How the pop-up media is cropped: Wide, Square, Tall, or Original | Wide |
Note: This block has no media of its own - its first graphic image is what a share preview uses.
Images
Every entry of images is an object:
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
media | ✓ | string|object | The background image the hotspots are placed on. See Use media | |
label | string | Names the image in the Creator and in the hotspot's "jump to image" list. It is never shown to your visitors | ||
hotspots | ✓ | object | The clickable hotspots on this image; see below. At least one is required - an image with none is a picture nobody can interact with. See item formats |
Note: media and at least one hotspots entry are required when you create an image - an image without media renders as an empty canvas, whether it is the block's first image or one a hotspot jumps to, while its hotspots still show. When you edit an existing image the media is already stored, so you only resend it if you want to change it.
Hotspots
Every entry of an image's hotspots is an object. Its action decides what a click does - and which of the other properties are accepted at all: sending one that does not belong to the chosen action is rejected rather than ignored.
| Property | Required | Type | Description | Default |
|---|---|---|---|---|
x | number | Horizontal position of the hotspot's center, in percent of the image (0-100, decimals allowed) | 50 | |
y | number | Vertical position of the hotspot's center, in percent of the image (0-100, decimals allowed) | 50 | |
action | string | showInfo, openUrl, goToNextBlock, noAction, or goToImage:<imageId> | showInfo | |
label | string | Names the hotspot in the Creator; it is never shown to your visitors | ||
title | string | Headline of the info pop-up (showInfo only) | ||
description | string | Text of the info pop-up (showInfo only) | ||
media | string|object | Image of the info pop-up (showInfo only) | ||
ctaLabel | string | Label of the pop-up's call-to-action button (showInfo only) | ||
ctaUrl | URL | Target of the pop-up's call-to-action button (showInfo only) | ||
actionUrl | URL | The URL to open (openUrl only) | ||
displayType | string | dot for a pin, area for a clickable region covering part of the image | dot | |
areaShape | string | circle or rectangle (area only) | circle | |
areaWidth | number | Width of the region, in percent of the image (4-100) (area only) | 16 | |
areaHeight | number | Height of the region, in percent of the image (4-100) (area only) | 16 | |
areaSize | number | Sets both areaWidth and areaHeight at once, in percent of the image (4-100) (area only) | 16 |
Note: An area hotspot has no pin, but it is not invisible - its region is shaded faintly over the image, and it takes part in the numbering like any other hotspot. The whole region reacts to a click, not just its centre.
Note: areaWidth, areaHeight and areaSize accept values up to 100, but the Riddle is rendered with anything above 80 shown as 80 - so 80 is the effective maximum region size.
One hotspot per action
The table above is long because it covers five actions at once. In practice a single hotspot only ever uses the handful of properties that belong to its own action - here is each of them on its own.
showInfo - open a pop-up. The default action, and the only one that uses title, description, media, ctaLabel and ctaUrl. At its smallest it is a pin with a headline and a text:
{
"x": 25.5,
"y": 40,
"title": "Kitchen",
"description": "Where the coffee lives."
}
The same pop-up with an image and a button below it:
{
"x": 25.5,
"y": 40,
"action": "showInfo",
"title": "Kitchen",
"description": "Where the coffee lives.",
"media": "https://httpbin.io/image/png",
"ctaLabel": "Read more",
"ctaUrl": "https://www.riddle.com/help"
}
The button only appears when both ctaLabel and ctaUrl are set, and media is shown only while the block's isHotspotMediaEnabled is on - sending any hotspot a media switches it on for you.
openUrl - leave for a URL. No pop-up, so there is nothing to write into title or description; the target goes into actionUrl, not into ctaUrl:
{
"x": 80,
"y": 12,
"action": "openUrl",
"actionUrl": "https://www.riddle.com"
}
goToImage:<target> - jump to another image of the same block. Name the target by its label or by its id; see connecting images for the rules:
{
"x": 50,
"y": 90,
"label": "Up to the first floor",
"action": "goToImage:First floor"
}
goToNextBlock - continue the Riddle. The click ends the block, exactly as the continue button would:
{
"x": 92,
"y": 92,
"action": "goToNextBlock"
}
noAction - a marker that does nothing. Useful with isHotspotNumbersEnabled to number a feature of the image without offering anything to open:
{
"x": 60,
"y": 30,
"action": "noAction"
}
Areas instead of pins
displayType is independent of the action: every one of the five can be a dot pin or an area covering part of the image. A circular area over the top-left quarter, sized with the shorthand areaSize:
{
"x": 25,
"y": 25,
"action": "showInfo",
"title": "The lobby",
"displayType": "area",
"areaSize": 30
}
A rectangle needs its two sides separately, because areaWidth is a percentage of the image's width and areaHeight one of its height:
{
"x": 50,
"y": 85,
"action": "openUrl",
"actionUrl": "https://www.riddle.com",
"displayType": "area",
"areaShape": "rectangle",
"areaWidth": 60,
"areaHeight": 12.5
}
x/y stay the centre of the region in both cases, so this last hotspot is a banner across the lower part of the image, running from 20% to 80% horizontally and from 78.75% to 91.25% vertically.
How big a hotspot is, and how far apart to place them
The coordinates alone do not tell you how much of the image a hotspot covers, so here are the numbers you need to place them without overlapping:
- A
dotpin is 12 px across, or 20 px whenisHotspotNumbersEnabledis on. That is a fixed size in the player, not a share of the image - so how much of the picture a pin covers depends on how wide your Riddle is rendered, not onx/y. - Its click target is much larger than the pin - roughly 24 px of margin all around - so a click next to a pin still opens it.
- Two
dothotspots that end up closer than 40 px to each other, on both axes, are merged into a single cluster marker the visitor has to click to separate. Keep them about 48 px apart to stay separate: at a 600 px wide Riddle that is roughly 6.7% of the image (about 8% with that margin), half as much at 1200 px, twice as much at 300 px. There is no percentage that holds at every width. areahotspots are never clustered, and overlapping hotspots are never moved apart for you - placing two on the same spot simply stacks them.areaWidthandareaHeightare percentages of the image's width and height respectively. Socirclewith different values - or with equal values on an image that is not square - draws an ellipse, not a circle.- Area sizes are accepted up to
100, but the player renders at most80, so treat 80 as the practical maximum.
Connecting images
A goToImage:<target> action jumps to another image of the same block, which lets you build a drill-down: an overview image whose hotspots open detail images.
You can name the target image in two ways:
- By its
id- either the one you assigned yourself inimages, or the one returned when you read the Riddle back. - By its
label- an exact match on thelabelyou gave that image. This is the easier one when you are creating the images and the connection in the same request, because you do not need to invent or look up any ids: label the images and link"action": "goToImage:Ground floor".
IDs are matched first, and a label only if no image has that ID, so a label that happens to look like a number can never take precedence over a real ID. If two images of the block carry the label you point at, the request is rejected as ambiguous - use the ID, or make the labels unique.
A label is a convenience for writing only: what gets stored, and what you get back when you read the Riddle, is always goToImage:<id>.
Three rules apply:
- A hotspot cannot link to the image it sits on.
- A connection is a single hop: an image that is the target of a connection cannot link on to a third image.
- An image that a hotspot still links to cannot be deleted - see Editing below.
Building this block with an AI agent
This is the one block type whose result you cannot judge from the build configuration. A hotspot's x/y and an area's areaWidth/areaHeight only mean something in relation to the pixels of your image - whether a hotspot actually sits on the door, the product or the city it describes is not visible in the JSON. An agent that only writes JSON is placing coordinates blind, and the hotspots will end up in the wrong places.
So if you let an AI agent build this block, use one that can open a URL in a browser and see the rendered page (vision/screenshots). Then build it in a loop instead of in one shot:
- Create once: send the block with the images and a first estimate of the hotspots.
- Publish: a published Riddle is what the browser shows, so an unpublished edit is invisible.
- Look at it: open the Riddle, look at the screenshot, and check every hotspot against the feature it is meant to mark. Open the pop-ups and click the
goToImage:hotspots too. - Edit only what is off: correct the
x/yor the area size of the individual hotspots that missed, addressed by theirid. - Publish and look again, and repeat until it is right.
Do not delete and re-create the block between rounds: the id of every image and hotspot is what makes step 4 a two-line request instead of a full resend.
Editing
images and each image's hotspots are addressed by their id when you edit a Riddle, so you can change one hotspot without resending the rest - and a goToImage: connection survives an edit that does not mention the image it points at. See Editing Riddles.
Both levels can be reordered with the usual order marker, and neither order is merely cosmetic - the first image is the one the block opens on, and the hotspot numbers follow the order of the hotspots array:
{
"blocks": [
{
"id": 3,
"$imagesOrder": [2, 1],
"images": [
{ "id": 1, "$hotspotsOrder": [2, 1] }
]
}
]
}
Like every order marker, each one has to list every id of the collection it orders, exactly once - a partial list is rejected rather than guessed at.
Deleting an image that a hotspot still links to is rejected, so an edit cannot leave a connection pointing at nothing. The error names the image you deleted and the hotspots that still point at it - repoint or delete those hotspots in the same request. Deleting the image that carries the hotspot is always fine: the hotspot goes away with its own image.
Example
{
"type": "InteractiveGraphic",
"title": "Our office",
"description": "Click around to explore.",
"isHotspotNumbersEnabled": true,
"images": [
{
"label": "Ground floor",
"media": "https://httpbin.io/image/jpeg",
"hotspots": [
{
"label": "The kitchen",
"x": 25.5,
"y": 40,
"action": "showInfo",
"title": "Kitchen",
"description": "Where the coffee lives.",
"media": "https://httpbin.io/image/png",
"ctaLabel": "Read more",
"ctaUrl": "https://www.riddle.com"
},
{
"label": "Our website",
"x": 80,
"y": 12,
"action": "openUrl",
"actionUrl": "https://www.riddle.com",
"displayType": "area",
"areaShape": "rectangle",
"areaWidth": 30,
"areaHeight": 12.5
},
{
"label": "Up to the first floor",
"x": 50,
"y": 90,
"action": "goToImage:First floor"
}
]
},
{
"label": "First floor",
"media": "https://httpbin.io/image/png"
}
]
}
A full Story containing this block is at the end of this page.
Result page
In a story only one result page is allowed which is specified in build.result, and it is entirely optional. Basic results only consist of a title and description property.
Example:
{
"title": "Thanks for reading!",
"description": "We hope you enjoyed the story"
}
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 the block types a story accepts: Content, Quote, InteractiveGraphic and Ad blocks plus a form.
{
"type": "Story",
"build": {
"title": "A day in the life",
"blocks": [
{
"type": "Content",
"title": "It all started on a Monday morning...",
"description": "A story about a very ordinary day",
"media": "https://httpbin.io/image/jpeg",
"mediaOrientation": "Tall"
},
{
"type": "Quote",
"title": "A word of inspiration",
"quoteText": "The only way to do great work is to love what you do.",
"quoteAuthor": "Steve Jobs",
"quoteMedia": "https://httpbin.io/image/png",
"quoteTextColor": "#ffffff",
"quoteBackgroundColor": "#000000"
},
{
"type": "InteractiveGraphic",
"title": "Where it all happened",
"description": "Click around to explore the office.",
"isHotspotNumbersEnabled": true,
"hotspotBackgroundColor": "#1a1a64",
"hotspotTextColor": "#ffffff",
"hotspotMediaOrientation": "Square",
"images": [
{
"label": "Ground floor",
"media": "https://httpbin.io/image/jpeg",
"hotspots": [
{
"label": "The kitchen",
"x": 25.5,
"y": 40,
"action": "showInfo",
"title": "Kitchen",
"description": "Where the coffee lives.",
"media": "https://httpbin.io/image/png",
"ctaLabel": "Read more",
"ctaUrl": "https://www.riddle.com"
},
{
"label": "The reception desk",
"x": 80,
"y": 12,
"action": "openUrl",
"actionUrl": "https://www.riddle.com",
"displayType": "area",
"areaShape": "rectangle",
"areaWidth": 30,
"areaHeight": 12.5
},
{
"label": "Up to the first floor",
"x": 50,
"y": 90,
"action": "goToImage:First floor"
}
]
},
{
"label": "First floor",
"media": "https://httpbin.io/image/jpeg",
"hotspots": [
{
"label": "The meeting room",
"x": 60,
"y": 55,
"action": "showInfo",
"title": "Meeting room",
"description": "Where the Monday morning started."
},
{
"label": "On with the story",
"x": 50,
"y": 92,
"action": "goToNextBlock"
}
]
}
]
},
{
"type": "Ad",
"iframe": {
"url": "https://ads.your-website.com/ad.html",
"height": 250
},
"showNextButtonDelay": 3
},
{
"title": "Want updates on the next story?",
"type": "FormBuilder",
"fields": [
{
"title": "Your email",
"type": "Email",
"isRequired": true,
"requiredMessage": "We need your email to keep you posted."
}
]
}
],
"result": {
"title": "Thanks for reading!",
"description": "We hope you enjoyed the story"
}
}
}
Next steps
Now that you know the basics of building a story, 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
- Define the logic / flow of your Riddle
- 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

