MCP
The Model Context Protocol (MCP) is a standard that lets AI models and agents call external tools and APIs. Riddle's MCP exposes our API to AI agents, so any action you can do via the API – such as listing, building, editing, and analyzing Riddles – can be triggered from an AI chat.
How to use
When using the Riddle MCP, you have two options for authentication:
- Preferred: Via public MCP URL in Claude (using the CIMD (Client ID Metadata Document) protocol)
- Via MCP key embedded in the URL
Via public MCP URL in Claude
- To add the Riddle MCP to your Claude account, navigate to Settings > Connectors > Add custom connector.
- To add the Riddle MCP to your Claude organization, navigate to Organization settings > Connectors > Add > Custom > Web connector.
In both cases the following dialogue will show:

Enter the following:
- Name: Riddle MCP
- URL:
https://www.riddle.com/creator/api/v3/mcp
After clicking on add, you are almost ready to use the MCP in your Claude chats: Click on connect afterwards to link your Riddle account with Claude and if necessary, you'll have to log in. Once you're done with this, you can try asking Claude to list the latest, published Riddles from your account to test the connection. You can also use the whoami tool to verify your account and project access.
Via MCP key
If you wish to use the MCP with an MCP key, create one on the API keys page (see the Authentication guide for details). Please make sure to pick a MCP key – regular API keys will not work and will return an error. Personal MCP keys are best suited: they give you access both to your personal project and to all projects in which you have the corresponding AI permissions. If you can only create normal API keys, your user role is missing the required permissions – ask a project administrator to grant them or to issue an MCP key for you.
Once you have your key, append it to the MCP URL as a path segment: https://www.riddle.com/creator/api/v3/mcp/[MCP_KEY]
Replace [MCP_KEY] with your actual key, then add this URL as a custom connector in Claude (same steps as above).
Please note that accessing the MCP via a configured MCP key is inferior to the CIMD option. It requires you to embed your key in the URL, and it is less flexible: you need to create a new MCP key for every Riddle account you want to access. With CIMD, each user authorizes with their own Riddle account instead, so no account has to be pre-configured in the URL.
Available Tools, Resources & Prompts
Note: You can use standard MCP functionality to list tools and resources available to the model; this will include more verbose information, e.g. API parameters. Below is a short summary, but make sure to fetch the MCP's current capabilities to get the most up-to-date information.
Tools (38)
The 38 tools fall into eleven categories:
| Category | Tools |
|---|---|
| Documentation | 1 |
| Authentication & projects | 5 |
| Riddle management | 6 |
| Riddle Builder | 3 |
| Design & palettes | 2 |
| Media | 2 |
| Tags | 3 |
| Templates | 5 |
| Question banks | 9 |
| Embedding & sharing | 1 |
| Statistics | 1 |
The same grouping is readable from the server itself, with an explanation of what each category is for, at riddle://reference/concepts/tools-by-category.
Documentation (1)
| Tool | Description |
|---|---|
| reference_get | Reads the documentation of the MCP server itself: the block types, form field types, result blocks, palette values, response shapes, per-entity defaults and the concept documents the other tools expect. Every riddle://reference/... URI mentioned in a tool description, a tool response or an error message is a topic of this tool, and the content is identical to reading that URI as an MCP resource, so a client that cannot read resources can read them through this tool instead. A topic can be given as the full URI or as its short name (block-types, block-defaults-Flashcard). Start with riddle://reference/index, which lists every address with its exact size. At most 3 topics per call, and riddleType / blockTypes / fieldTypes narrow the large documents before they are sent; see their sizes. |
Authentication & projects (5)
Test connectivity, check the context of your API key, and browse projects.
| Tool | Description |
|---|---|
| ping | Connectivity and authentication test – simply returns "Pong". |
| whoami | Returns information about the current API key: the authenticated user (userEmail, userName), the project/team on a project-scoped key, and the account's plan under subscription. The plan is informational – every tool works on every plan, free trial included. Mind which id is which: the response's own id is the API key row, the user id (what stats_fetch wants as entityId for namespace user) is user, and team is the project id on a project key. |
| project_list | Paginated list of the projects this token has access to (25 per page, 100 max), as {items, page, pageSize, total, hasMore}. If it is a project API token, only the project linked to the token is returned. Returns a trimmed summary (id, name, image) per project. |
| project_get | Fetches a single project by its project ID – name, image and your permissions on it. |
| project_get_settings | Returns the default Riddle settings of a project - the settings every new Riddle in it starts from, as defaultRiddleSettings (the draft the project owner edits) and publishedDefaultRiddleSettings (what Riddles actually inherit). A large, deeply nested payload - use project_get for a project's name, image and permissions. |
Riddle management (6)
Find, read, and manage existing Riddles. riddle_publish, riddle_delete, riddle_move and riddle_get each take one Riddle (UUID) or many (UUIDs) – see bulk calls.
| Tool | Description |
|---|---|
| riddle_list | Paginated list of Riddles as {items, page, pageSize, total, hasMore} – filterable by type, status, tags, origin (API created vs. created by hand), and search term; sortable. scope: "project" (the default) lists one project, named with projectId or the personal project when omitted; scope: "account" lists the entire account instead – the personal project plus every team project the user can access, in one list. The account scope has no projectId and no notType, and rejects them rather than ignoring them. |
| riddle_get | Fetches a single Riddle by its UUID as a build configuration: {uuid, type, modifiedAt, build, nextBlockId, warnings, published, context}. build is the same builder config the builder tools accept, so a Riddle can be read, edited and rebuilt; anything it cannot express is reported in warnings. nextBlockId is the block ID a new block may claim in an edit. published describes the live version: null when never published, {"status": "identical", "isLive": true} when the live Riddle is exactly what build describes, and {"status": "differs", "isLive": ..., "build": ..., "warnings": ...} (spelled out in full) when there are unpublished changes. status compares content, isLive says whether the Riddle is online at all, and the two are independent: unpublishing does not clear published back to null (the last published content stays stored), so an unpublished Riddle answers {"status": "identical", "isLive": false}. context adds title, features, origin, published state, the Riddle this one was copied from (duplicated), the public viewUrl, project and preset information. Reads up to 20 Riddles in one call with UUIDs, and omit leaves parts of the response out, the only way to make it smaller. |
| riddle_rename | Renames a Riddle (new title by UUID). Returns only the confirmation of the change, not the whole Riddle. |
| riddle_publish | Publishes one Riddle (UUID) or up to 100 at once (UUIDs) – each becomes available via its public URL – and takes them offline again with publish: false. There is no separate unpublish tool. For a single Riddle the answer is the same envelope riddle_get returns, with the new state under context.published, and omit shrinks it exactly as it does there; for many it is the bulk envelope {bulk, operation, summary, results} with one entry per Riddle. A bulk (un)publish is not atomic and never gives up early: a Riddle that cannot be (un)published is reported as that entry's error while all others still go through. A refused publish carries validationErrors, one entry per reason; non-blocking findings arrive as validationWarnings on a failed and on a successful publish. A live Leaderboard with active Riddle connections cannot be unpublished – use riddle_delete on it instead, which is always allowed and cleans the connections up. |
| riddle_delete | Deletes one Riddle (UUID) or up to 100 at once (UUIDs). Only Riddles created via the builder, created from a template with riddle_template_use, or generated by the Riddle AI can be deleted – context.origin.apiManageable on riddle_get tells you upfront whether a Riddle qualifies. A bulk delete is not atomic and never gives up early. Order does not matter: a Riddle that another item of the same batch still references (a Leaderboard's connected Quiz, a FormSelect's Form) is retried once automatically after the rest of the batch has run, so only a failure that survives the retry is reported as an error. dryRun: true deletes nothing and reports what would happen per Riddle instead – {dryRun, deletable, summary, items, addUUIDs}, naming what blocks each one (a Riddle another Riddle uses, one in the template gallery, one connected to a Zap). |
| riddle_move | Moves up to 100 Riddles into another project, or into the personal project with projectId: "personal". All-or-nothing: Riddles that reference each other - a Quiz and its Leaderboard, an embedded Form - have to move together, and addUUIDs names the ones to add. Needs the transfer permission in both projects. Tags are re-pointed to the destination scope; a tag left behind in the source scope is removed once nothing - no Riddle, no question bank - uses it there any more. The Riddle keeps its own design: a move does not switch it to the destination project's default preset. Leave projectId out (or pass dryRun: true alongside it) and nothing is moved: the call runs the very same validation and answers {canMoveAll, movable, blocked, addUUIDs, canMoveToPersonal, projects} instead. That check is a mode of this tool, not a tool of its own. |
Riddle Builder (3)
Create and edit Riddles with structured parameters (blocks, results, logic, presets, publish settings).
| Tool | Description |
|---|---|
| riddle_builder_create | Builds a new Riddle of any of the nine types – type says which kind (Quiz, Poll, Form, Personality, Predictor, Minigame, Leaderboard, Story, Placeholder) and decides which build fields and which block types exist; build carries the content, in exactly the shape riddle_get returns under build. There is no longer one tool per type. Optional: publish to go live right away, queue for asynchronous processing, project to target a project, templateId when the configuration came from a template, and omit to shrink the answer. Per-type shapes: riddle://reference/riddle-builder/riddle-types. |
| riddle_builder_update | Edits an existing Riddle of any type as a merge, not a rebuild: only what you send changes. Blocks are addressed by their ID and can be added ("$create": true), removed ("$delete": true) or reordered ($blocksOrder); title, result/results, logic, a Personality Test's personalities, a Leaderboard's riddleConnections and a Placeholder's conditions are replaced as a whole when sent and kept when omitted, while preset and publish merge per key, so only the settings you send change. Only Riddles created with the builder or generated by the Riddle AI can be edited - check context.origin.apiManageable from riddle_get first, which is also where the block IDs are. Returns the edited Riddle in the same envelope riddle_get returns. See Editing Riddles. |
| riddle_builder_validate | Dry-runs creates and/or edits – 1 to 20 per call, mixed freely – without creating or changing anything: each entry of builds ({type, build} for a creation, {UUID, build} for an edit) is validated exactly as the real call would validate it, against a scratch Riddle that is discarded before the answer. Nothing is persisted, published or queued. Media URLs are still checked for reachability with a live request (the file is never downloaded), so an unreachable URL is rejected here too. The answer is {validate, valid, summary, items} with one entry per item, in the order sent – one item's outcome never affects another's. Pass project whenever a build references anything project-scoped (a Form behind FormSelect, a tag, a project ad slot), set to the project you will actually create in. Needed plan: Business or Enterprise. |
Design & palettes (2)
Read and change the design (colors, fonts, buttons, background) of a Riddle.
| Tool | Description |
|---|---|
| palette_get | Reads the palettes of a Riddle: every palette it can use (including the ones inherited from the account or project default preset) with all values, which palette is selected, which values this Riddle overrides, and the built-in palettes that can be used as a starting point. |
| palette_customize | Changes the palette of a single Riddle via a path ⇒ value map, e.g. {"bgColor": "#ffffff", "font.name": "Roboto"}. An inherited palette is never changed for other Riddles - the values are stored as an override on this Riddle, and built-in palettes are duplicated into a Riddle-owned copy automatically. A palette change does not update the Riddle's modifiedAt, matching the Riddle Creator, which does not stamp modifiedAt for a design change either. The new design is only visible on the embedded Riddle after publishing it again. Like riddle_delete, this only works on Riddles created via the builder or generated by the Riddle AI. |
Media (2)
Get your own images, videos and audio into a Riddle – and out again. Read Working with media before the first call: an upload takes two steps, and the step you perform yourself needs outbound network access to *.riddle.com.
| Tool | Description |
|---|---|
| media_upload_link | Creates a single-use link for uploading one local file into the account's media library – an MCP call carries JSON, not a file, so the server hands out a URL and you POST the file to it yourself. Returns {uploadUrl, expiresAt, singleUse, maxBytes, allowedTypes, usage, requiresNetworkAccessTo}; usage is the ready-to-run curl command. The link lives 5 minutes, dies on first use whether or not it succeeded, and 20 are handed out per account per 5 minutes. The upload answers with {mediaId, type, width, height, size, folderId}. |
| media_delete | Deletes a file from the media library permanently, to clean up after yourself, and frees the storage it counted against. The file has to be unused: one that any Riddle still shows is refused with a message naming those Riddles – remove it there first (riddle_builder_update, then riddle_publish so the change is live). "Unused" is a property of the file across the whole account, not of one Riddle. Not reversible, and the id is not reused. |
Tags (3)
Organize Riddles with tags. A tag belongs to a project, not to a Riddle, and Riddle tags and question bank tags are one set per project – the same tag applies to both and has the same id in both.
| Tool | Description |
|---|---|
| riddle_tag_list | The tags of a project as {items: [{id, name, count, bankCount}]}, unpaginated – and where the tag IDs the riddle_list filter accepts come from. count counts Riddles and bankCount question banks, so count: 0 alone never means unused: a tag is free of carriers only when both are 0. |
| riddle_tag | Puts a tag on Riddles or takes it off again, by action ("add" / "remove") – one Riddle (UUID) or up to 100 (UUIDs). add takes tagName (created in each Riddle's own project if it has none of that name, so one call can span projects) or tagId (which exists in one project and therefore works only for that project's Riddles). remove creates nothing – a tag the Riddle does not have is an error for that Riddle – and untagging the last carrier deletes the tag itself, unless a question bank still uses it. Changes one assignment, never the label itself, and needs no publish. |
| riddle_tag_delete | Deletes the tag itself from a project – the label, for good – and no Riddle, no question bank and no assignment. Rarely needed, since untagging the last carrier already removes a tag. It detaches nothing on the way, so a tag any Riddle or question bank still carries is rejected with a message naming how many of each. |
Templates (5)
Reuse a Riddle as the starting point for new ones – your project's own templates and Riddle's public ones. A template is a copy taken at one moment: the Riddle it was made from keeps living its own life.
| Tool | Description |
|---|---|
| riddle_template_create | Stores an existing Riddle as a template of its project (or of you, for a personal Riddle), so new Riddles can be created from it. It copies what the Riddle currently has stored, published or not; later changes to the Riddle do not reach the template. Requires the template-create permission. |
| riddle_template_list | The templates of a project (or your personal ones) – the ones your Riddles were made into, not Riddle's public ones. Quick-create templates are included alongside the regular ones. Filterable by Riddle type, returned as {items, total} without the build configuration – not paginated, so total is simply how many there are. |
| riddle_template_public_list | Riddle's public, ready-made templates, grouped by category ({categories, total}) – so calling it without arguments is also how you learn which categories exist. Also filterable by type. An unknown category is rejected with an error naming the categories that are actually allowed, rather than silently returning nothing. |
| riddle_template_get | A single template including its build configuration – the same build shape riddle_get returns and the builder tools accept. This is how a template is adapted rather than copied: edit the returned build and build it with riddle_builder_create under the template's type, passing the template's id as templateId so the new Riddle is still recorded as coming from it (and starts out on the template's whole preset, layout included). The design comes along in preset; preset.paletteValues is included whenever the palette is not an unmodified built-in one. A template whose content has no build-configuration equivalent comes back with an empty build and a warning saying so. |
| riddle_template_use | Creates a new Riddle from a template as an unchanged copy – content, settings and design preset – in one call, and records the lineage: reading it back reports the template's Riddle under context.duplicated. The new Riddle is a draft, so call riddle_publish to make it live, and it counts as programmatically created, so riddle_builder_update, palette_customize and riddle_delete work on it (context.origin.apiManageable). Requires the template-use permission plus Riddle-create permission in the target project. |
Question banks (9)
Manage the reusable question pools a QuestionBank block of a Quiz or Poll draws from. A bank belongs to one project and to one Riddle type, and its items have a draft/published split of their own: a QuestionBank block always draws from a bank's published state, so a bank has to be published before its questions show up in a Riddle. See Question banks.
| Tool | Description |
|---|---|
| question_bank_create | Creates a new, empty bank for a Riddle type (Quiz or Poll) in a project. Its id is what a QuestionBank block references as questionBankId, and a bank can only be referenced from Riddles of its own project and its own type. |
| question_bank_list | Lists question banks. scope: "own" (the default) lists the banks of a project, paginated as {items, page, pageSize, total, hasMore} and filterable by search term and tags. scope: "templates" lists the built-in starter banks anyone can duplicate to begin with real content instead of an empty bank; it takes riddleType and nothing else. A template that was never published reports itemCount: 0 however many questions it holds – judge its real size by draftItemCount. Every write rejects a template id: a template is only ever changed through a copy of it. |
| question_bank_get | A single bank: title, riddleType, tags, notes, categories/blockTypes and whether it has unpublished changes. The items themselves are question_bank_get_items. Works on a template id too, to see what it holds before duplicating. categories comes from every draft item while blockTypeCategoryMap counts only published ones, so the two can legitimately disagree. |
| question_bank_manage | Everything that acts on a bank as a whole, by action: rename, updateNotes, publish and duplicate. Title and notes are metadata outside the draft/publish split, so they take effect immediately and never count as an unpublished change. publish makes every item's draft content its published content – what a QuestionBank block actually draws – and is also the moment a pending delete of a published item is purged for good. duplicate copies a bank (or a public template) with all of its items into a new, independent bank, optionally in another project, and is the way to change a template. |
| question_bank_item | Adds (action: "add") or replaces (action: "update") one question of a bank's draft. There is no universal item shape – read riddle://reference/question-bank/block-type-columns first, which lists the valid blockTypes per Riddle type and the exact columns each expects (a Quiz SingleChoice and a Poll SingleChoice differ). update replaces the item's content entirely, so blockType, category, difficulty and columns must all be sent even when unchanged, and blockType is immutable. |
| question_bank_get_items | The items of a bank – its draft content by default, or its published items with published: true. Filterable by search term, blockType, category or difficulty range: the same filters a QuestionBank block draws with. Paginated as {items, page, pageSize, total, hasMore}. Reads a template id too, which is how to preview its questions before duplicating it. |
| question_bank_discard_changes | Resets every item back to its last published state, undoing pending deletes as well. Irreversible. An item added since the last publish has no published state and is left alone, so this is not "restore the bank as it was published". Title and notes are unaffected. |
| question_bank_delete | Deletes a bank or one of its items – target says which, and nothing else deletes either. target: "bank" removes the bank and every item it holds, and is refused while a Riddle of the bank's own project still references it (counting the draft and, on a published Riddle, the live version too). target: "item" touches nothing else: a never-published item is gone immediately, a published one only leaves the draft and keeps being drawn live until the next publish. |
| question_bank_tag | Adds a tag to a bank or removes one from it, by tagName or tagId from riddle_tag_list – bank tags and Riddle tags are one set per project. add reuses the project's tag of that name or creates it; remove never creates anything, and a tag nothing uses any more – no bank and no Riddle – is deleted from the project. |
Embedding & sharing (1)
| Tool | Description |
|---|---|
| riddle_get_embed_code | Returns the HTML embed code of a Riddle. The code is returned regardless of publish state, but the URL it embeds will not serve the Riddle until it is published. For a scannable link, take the Riddle's public view URL from riddle_get's context.viewUrl and turn that into a QR code yourself. |
Statistics (1)
Views, starts, and submissions – one tool over two axes.
| Tool | Description |
|---|---|
| stats_fetch | Stats of one entity: namespace (project, user or riddle) plus entityId says which, view says how deeply. totals is one aggregate for the period (omit the dates for all-time). timeseries is that aggregate per consecutive interval – {intervalDays, intervalCount, intervals} – ideal for graphs and trend analysis; a range longer than the interval count is grouped into equal intervals whose stats is the interval total, so divide by days rather than reading the shorter final interval as a drop. breakdown depends on the namespace: riddle gives one Riddle down to its blocks, choices and fields (every id being a block id riddle_get exposes), project gives that tree for every Riddle in it, and user gives the account-wide summary instead – one row per Riddle plus totals, ranked by sortBy, paginated, narrowed with projectIds, and the one combination that requires a user API key. Metrics sit in core_metrics under global_stats for a Riddle and under stats for a user/project aggregate. An empty response ({}) means nothing was recorded – report "no stats recorded", not 0. |
Bulk calls and dry-runs
riddle_get (up to 20), riddle_publish, riddle_delete, riddle_move and riddle_tag (up to 100 each) take UUIDs instead of UUID. The answer is then the bulk envelope {bulk, operation, summary, results} with one entry per Riddle – also for a one-element list, so a client can always send the same parameter and get the same shape back.
- Per-Riddle, not atomic:
riddle_publish,riddle_deleteandriddle_tagnever give up early. One Riddle failing is reported as that entry'serrorwhile every other Riddle still goes through. riddle_moveis the exception: it validates the whole set up front and moves nothing if any Riddle is rejected, because Riddles that reference each other can only move together. So pass exactly the set you intend to move – the same Riddle can be blocked on its own and movable inside a larger call.- Ask before you act. The two operations you cannot take back report what they would do:
riddle_movewhenever no destination is named,riddle_deletewithdryRun: true. Both run the same validation the real call runs and change nothing. When something is blocked, act onaddUUIDs, add those UUIDs and check again.
Details: riddle://reference/concepts/bulk, and riddle://reference/concepts/move-restrictions for what makes a move impossible rather than merely denied.
Which writes are gated
Three writes only work on Riddles the API itself created: riddle_builder_update, riddle_delete and palette_customize. All three test the same flag – context.origin.apiManageable on riddle_get, origin on riddle_list – which is true for a Riddle built by riddle_builder_create or riddle_template_use, or generated by the Riddle AI, and false for one a user built by hand in the Creator. It is a permanent property of that Riddle: no API key, admin included, lifts it, and the only route to the same result is rebuilding the content through riddle_builder_create.
Every other write works on any Riddle the key may access, hand-built ones included: riddle_rename, riddle_publish (both directions), riddle_move, riddle_tag and riddle_template_create – as does everything under Media and Question banks, which are not Riddles at all. So check the flag before an edit, a delete or a restyle (riddle_list with origin: "api" returns exactly the Riddles all three work on, origin: "manual" the ones they do not), and do not check it before renaming, tagging, publishing or moving.
Resources (73 documents)
Every reference document has an address of the form riddle://reference/..., and each address is readable two ways with identical content: as an MCP resource (resources/read), and through the reference_get tool (topics: ["riddle://reference/..."], up to 3 addresses per call). Most clients do not expose resources to the model, which is what the tool is for.
The catalogue has three parts: an index, thirteen single documents, and three families that generate one address per entity.
Start at the index
| Resource | URI | Size | Description |
|---|---|---|---|
| reference-index | riddle://reference/index | 4 KB / ~1k tokens | Every address of this server with a one-line summary and its exact size in bytes, the generated per-entity addresses included. |
Read it first when you do not know which document you need. It is by far the smallest, its sizes are the same numbers resources/list advertises, and they are measured on the JSON a read actually returns, so a plan made from the index is a plan that holds.
Single documents (13)
One written document each, at a fixed address.
| Resource | URI | Size | Description |
|---|---|---|---|
| getting-started | riddle://reference/getting-started | 6 KB / ~1.4k tokens | What the server is for, the two API key types, the guided prompts, and the addresses of the concept documents. Start here when you do not know which tool to reach for |
| response-format | riddle://reference/response-format | 4 KB / ~1k tokens | The {uuid, type, modifiedAt, build, nextBlockId, warnings, published, context} envelope every Riddle-returning tool answers with, the {items, page, pageSize, total, hasMore} shape every listing answers with, and the bulk and error envelopes |
| riddle-types | riddle://reference/riddle-builder/riddle-types | 15 KB / ~3.9k tokens | All 9 Riddle types with their required and optional build fields, result structure and scoring settings |
| block-types | riddle://reference/riddle-builder/block-types | 48 KB / ~12k tokens | Every question and content block type per Riddle type. The reference for writing blocks |
| form-field-types | riddle://reference/riddle-builder/form-field-types | 7 KB / ~1.7k tokens | All 18 form field types of the FormBuilder block |
| result-blocks | riddle://reference/riddle-builder/result-blocks | 12 KB / ~3k tokens | All 12 result page block types with their properties, format options and styling |
| palette-fields | riddle://reference/palette/fields | 15 KB / ~3.8k tokens | Every palette value palette_customize accepts, where it is used and which CSS variable it renders into |
| palette-built-in | riddle://reference/palette/built-in-palettes | 22 KB / ~5.6k tokens | The built-in palettes with all of their values, as starting points for a custom one |
| palette-fonts | riddle://reference/palette/fonts | 6 KB / ~1.4k tokens | The font families available as font.name, plus self-hosted and Google/Typekit fonts |
| publish-defaults | riddle://reference/publish-defaults | 3 KB / ~0.8k tokens | The resolved default of every publish.<name> setting a read-back leaves out. One address, because they are the same for every Riddle type |
| form-field-defaults | riddle://reference/form-field-defaults | 22 KB / ~5.7k tokens | What every form field type accepts and defaults to, keyed by field type. fieldTypes narrows it to the ones you are building |
| question-bank-overview | riddle://reference/question-bank/overview | 4 KB / ~1.1k tokens | What a question bank is, its draft/publish model, and the filters a QuestionBank block draws with |
| question-bank-block-type-columns | riddle://reference/question-bank/block-type-columns | see the index | Every blockType a bank of a given Riddle type accepts, with the exact columns shape question_bank_item expects for it. There is no universal item shape - read this before the first question_bank_item(action: "add") |
Families (3)
A family is a URI template. Replace the {variable} with a member name to get an address, for example riddle://reference/block-defaults/SingleChoice. Every member name and its exact size is listed in the index.
| Family | URI template | Addresses | Total size | Description |
|---|---|---|---|---|
| block-defaults | riddle://reference/block-defaults/{blockType} | 31 | 55 KB | Per block type: the constraint set its build configuration is checked against (types, required properties, enum values, sizes, whether a collection merges by id) and the resolved default of every property a read-back omits. Both generated from the block itself. Members: Ad, Content, Quote, SingleChoice, MultipleChoice, TextEntry, Flashcard, Order, TypeRush, GuessIt, TapAndFind, SpotTheDifference, QuestionBank, FormBuilder, FormField, Upvote, Matrix, NetPromoterScore, Swiper, RateIt, Reaction, TierList, ThisOrThat, GuessTheScore, PickTheWinner, SlotMachine, WheelSpinner, Sudoku, Minesweeper, Crosswords, InteractiveGraphic |
| riddle-defaults | riddle://reference/riddle-defaults/{riddleType} | 9 | 28 KB | Per Riddle type: the constraint set of the Riddle-level properties, plus their defaults and the defaults of that type's preset. Members: the 9 Riddle types |
| concepts | riddle://reference/concepts/{concept} | 19 | 102 KB | One address per subject that is not an entity, so no entity document has to repeat it. Members: tools-by-category, workflows, editing, merge-semantics, defaults, response-envelope, warnings, bulk, common-block-properties, result-pages, form-fields-usage, logic, preset, publish, limits, unknown-properties, move-restrictions, question-bank-items, troubleshooting |
Note: The defaults families replaced the single riddle://reference/riddle-builder/block-defaults document, which held every block type of every Riddle type at once. One block type is now one address of 1 to 4 KB instead of a 53 KB read.
These documents are large - read them selectively
The whole catalogue is on the order of 350 KB, roughly 90k tokens, which is a substantial part of a model's context window before a single Riddle has been built. It is written to be read one address at a time, not loaded wholesale.
- Read the index first. It is ~1k tokens and it names the exact cost of everything else. Guessing which document you need and reading the wrong one costs far more than that.
- Never fetch everything.
reference_getaccepts at most 3 topics per call and rejects a longer list rather than returning a huge response. The limit is deliberately small:block-typesalone is the biggest response this server produces, and a call naming eight substantive topics exceeds what a client accepts inline - the answer is then no answer at all. block-typesis the expensive one at ~12k tokens, followed byform-field-defaultsandpalette-built-in.riddle-typesplusblock-typesis the usual pair before building. Addform-field-typesonly for a Riddle with a form,result-blocksonly when you style a result page.- Read one entity, not a family. For defaults and constraints of the block you are configuring, read that block's own address (
riddle://reference/block-defaults/Matrix, ~1.5 KB), never the whole family. - Let the server slice the big ones.
reference_gettakesriddleType,blockTypesandfieldTypes: asking forblock-typeswithriddleType: ["Quiz"]drops every other type's blocks before the document is sent, while the shared conventions are kept. Filtering is far cheaper than receiving all 9 Riddle types and ignoring 8. These filters do not apply to the two defaults families, where the address already is the filter. - Prefer the cheaper source when there is one. For defaults of a block you have already built,
riddle_get's per-blockomittedDefaultscosts less than any reference read. For palettes,palette_getreturns the Riddle's actual values, sopalette-fieldsis only needed to look up a path you want to write. - Fetch once per session. Content changes only when the server is updated, so re-reading the same address in one conversation is pure token cost. The exception is a
VALIDATION_ERROR, where re-reading the relevant document is exactly the right move.
Guided workflows (12)
The MCP server also ships prompts: ready-made workflows that chain the tools above in the right order and ask you for the few things they cannot know (brand colors, the URL, which project). Pick one in your MCP client instead of describing the whole job yourself.
| Prompt | What it does |
|---|---|
| build_LeadGenQuiz | A lead-generation Quiz: entertaining questions, a form block that collects contact data before the result, optional double opt-in, published and ready to embed. |
| build_LeadForm | A standalone lead form (newsletter, registration, contact): the right field types, GDPR consent, double opt-in and a confirmation email. |
| build_ProductRecommender | A "which one fits me" product or plan recommender as a Personality test - each product is a personality, every result links to its page. |
| build_GatedContentStory | A gated content Story: teaser sequence, form gate in the middle, the asset behind it. |
| build_PredictorWithLeaderboard | A prediction game with a public ranking: a Predictor for the picks, a connected Leaderboard, and the scoring rules. |
| build_QuizContestWithLeaderboard | A quiz contest (e.g. a daily quiz) with a ranked Leaderboard: timed and scored questions, one entry per participant, a live ranking. |
| restyle_ToBrand | Restyles a Riddle to brand colors and fonts and publishes so the change reaches the embed. |
| organize_Library | Cleans up the library: find Riddles by filter, tag them, publish or unpublish the set, move them once the move is verified as possible. |
| localize_Riddle | Localizes a Riddle: read it as a build configuration, create the translated counterpart with the language set, keep the two in sync. |
| setup_DataLayerTracking | Instruments a Riddle for analytics: tracking networks, which events they fire, dataLayer pushes, publish. |
| analyze_CampaignPerformance | Reports on a campaign: collect its Riddles, pull views/starts/submissions plus the daily trend, turn it into a funnel and next steps. |
| optimize_Riddle | Optimizes a Riddle against its own stats: find the drop-offs, propose fixes, apply them as a merge edit and publish. |
Builder capabilities
The riddle_builder_* tools do more than their short descriptions suggest. Beyond the required title and blocks inside build, riddle_builder_create accepts optional fields that unlock the same power available in the Creator:
- Branching logic (
logic): Define a conditional flow over your blocks instead of a fixed linear order – route users to different blocks based on their answers, and merge paths back together. Supported by Quiz, Poll, and Form. - Design presets (
preset): Base the Riddle on an existing design/preset and override settings such as color palette, language, "remember user", and auto-open/auto-close dates. Supported by all types except Leaderboard. - Publish automation (
publish): Configure what happens on publish: double opt-in (DOI), email automation, tracking (pixels, analytics), data layer items, showcase visibility, and third-party integrations. Supported by all types except Leaderboard. Not to be confused with the builder tools' own top-levelpublishboolean, which publishes the Riddle right away. - Leaderboard connections (
leaderboard/riddleConnections): Attach a Quiz, Predictor, or Minigame to one or more leaderboards on creation, or create a Leaderboard that connects to existing Riddles (which must have Name and Email fields). - Publish & queue (
publish,queue): Publish the Riddle immediately after creation instead of leaving it as a draft, and/or process the creation asynchronously. - Project targeting (
project): Create the Riddle in a specific project, in your personal project, or in the currently selected project. - Reading Riddles back (
riddle_get): Every Riddle - not only the ones created via the API - can be read back as exactly this builder config, so an agent can inspect an existing Riddle, change a detail and rebuild it. What a builder config cannot express is listed inwarningsinstead of being silently dropped. - Editing Riddles (
riddle_builder_update): Change an existing Riddle instead of rebuilding it - blocks are addressed by their ID, only what you send changes, and blocks can be added, removed or reordered. Limited to Riddles created with the builder or generated by the Riddle AI (context.origin.apiManageable). See Editing Riddles. - Routing instead of content (
type: "Placeholder"): Build a Placeholder - embed one Riddle and decide by rule, tag or time window which Riddle actually shows. - Questions from a bank (
QuestionBankblock): Instead of authoring questions on the Riddle, let a Quiz or Poll draw them at view time from a question bank - filtered by category and difficulty, and optionally re-drawn on an interval. The bank itself is managed with thequestion_bank_*tools. - Restyling (
palette_get/palette_customize): Read the full design of a Riddle and change individual palette values without touching the design of any other Riddle. - Checking before building (
riddle_builder_validate): Dry-run up to 20 creates and/or edits in one call - the same validation, nothing created or changed - to find out whether a configuration would be accepted, and why not, before spending a real call on it. See Validating build configurations. - Starting from a template (
riddle_template_*): Copy a template as-is withriddle_template_use, or read it withriddle_template_getand build the edited configuration with the builder tool of its type, passingtemplateIdso the new Riddle keeps the template's preset and its lineage.
Read riddle://reference/riddle-builder/riddle-types and block-types for the fields and formats each of these takes, the block's own riddle://reference/block-defaults/{blockType} address for its constraints and defaults, and riddle://reference/concepts/{concept} for the rules that span tools (editing, merging, presets, publishing, logic, limits). Listing the tools via standard MCP functionality gives you their parameter schemas.
Working with media
Everything a Riddle shows has to live on Riddle's CDN, so a build configuration cannot point at a file on your machine – and an MCP call carries JSON, not a multipart body, so the server cannot take the bytes for you either. Three ways to reference a file:
| The file is | How to reference it |
|---|---|
| Somewhere on the public internet | Put the URL in media – Riddle downloads it and re-hosts it |
| Already in your account's media library | Reference it by mediaId – nothing is uploaded, nothing is copied |
| On your machine (or the agent's) | media_upload_link, then upload it yourself – see below |
Full property reference for all three: Use media.
There is no tool that browses the library. The id of a file the account already has is shown in the Creator's media library – ask the user for it rather than uploading the same file twice.
Uploading a local file
1. Mint a link. media_upload_link takes no arguments and returns {uploadUrl, expiresAt, singleUse, maxBytes, allowedTypes, usage, requiresNetworkAccessTo} – usage is the ready-to-run curl command.
2. Upload the file. POST it to uploadUrl as multipart/form-data under the field name file. The link carries its own signature, so no API key and no header are needed:
curl -F 'file=@/path/to/image.png' 'https://www.riddle.com/media-upload/<token>?_sig=<signature>'
The answer is everything needed to use the file, so no read-back call is required: {mediaId, type, width, height, size, folderId}.
3. Reference it by id.
"media": { "type": "Image", "mediaId": 4711, "altTag": "The Eiffel Tower in Paris" }
Never guess a URL for the file. A Riddle CDN URL passed as a plain url media is downloaded and re-hosted as a second, unrelated file – the account pays for the same image twice, and the library file records no usage, which makes an image in a live Riddle look safe to delete when it is not.
Step 2 leaves your environment, so it has to be allowed to reach the host named in requiresNetworkAccessTo – on production www.riddle.com, covered by the allowlist entry *.riddle.com. Use the wildcard rather than a hardcoded name; the host differs per environment. In Claude this is an organization setting only an admin can change. If the request is blocked, the fix is the allowlist, not another attempt: there is no second route in.
The rules a link comes with
| Validity | 5 minutes |
| Uses | One – the link dies on first use, successful or not |
| Links per account | 20 per 5 minutes |
| Files per link | Exactly one |
| Size | At most 10 MB (maxBytes in the answer is authoritative) |
| Types | Images, video and audio (allowedTypes is authoritative) |
So mint a link immediately before each upload rather than a batch up front, upload files one at a time, and never store or share a link – while it lives it is a credential that needs no other authentication.
Not every rejection costs you the link: everything judgeable without spending it – signature, permission, file size and type – is judged first, so an oversized file or a missing form field comes back as a plain error and the link stays usable. Anything that reaches the CDN spends it, success or not, and the answer is always a new link rather than a retry.
| Error | What happened |
|---|---|
This upload link is invalid, expired or already used. | Spent, older than 5 minutes, or never valid. Mint a new one. The three cases are deliberately one message – separating them would tell an unauthenticated caller which tokens exist |
No file received. Send the file as multipart/form-data under the field name "file" | Wrong field name, or the body was not multipart. Link not spent |
The file is N bytes, which exceeds the ... limit of this upload link. | Over maxBytes. Link not spent |
The upload did not complete: ... | The body never arrived in full – usually larger than the server's post limit |
| A media-library or permission error | The account has no media library enabled, or the API key's user may not use the project's library |
Where the file lands, and how to clean up
Uploads go into one fixed place per account – the folder "AI Uploads", tagged "AI Upload" – so the account owner can review everything an agent uploaded in a single view in the Creator. Agents cannot pick a folder or tag. The file is otherwise a normal library file: it belongs to the account (or project), counts against its storage, and any number of Riddles can use it.
media_delete removes a file again by mediaId, and frees the storage. It refuses while any Riddle still shows the file, naming those Riddles – so take it out of the Riddle first (riddle_builder_update, then riddle_publish so the change is live) and delete afterwards. "Unused" is a property of the file across the whole account: any one Riddle using it keeps it alive. The delete is permanent, the id is not reused, and a stored configuration still pointing at the file simply stops resolving – so an agent should ask before calling it.
Keeping the context small
An MCP conversation pays for every token it receives, and a few of these tools can answer with a lot. These are the levers – they matter for you in a chat, and they matter much more for an agent that runs unattended over many calls.
- Leave out what you will not read (
omit). Every tool that answers with a Riddle –riddle_get, allriddle_builder_*tools,riddle_publishin both directions,riddle_template_use– acceptsomitto drop parts of the answer:build,warnings,nextBlockId,published,context, andbuild.omittedDefaults. That last one is the big one: the per-block default maps are most of a read-back, soomit: ["build.omittedDefaults"]alone can shrink the response by an order of magnitude. Only fetching a title?omit: ["build", "published", "warnings", "nextBlockId"]. - Scope the reference documents instead of loading them. The catalogue is ~90k tokens in total. Read
riddle://reference/index(~1k tokens) to see what each address costs, then askreference_getfor the one or two the current step needs (three at most per call), narrowed withriddleType/blockTypes/fieldTypes, and read them once per conversation. For a single block type, read its ownblock-defaultsaddress rather than a big document. See the note above. - One call instead of a loop.
riddle_publish,riddle_delete,riddle_moveandriddle_tagtake a list (UUIDs, up to 100),riddle_getup to 20. One bulk call carries one envelope and one round of tool overhead rather than a hundred. - Edit instead of rebuilding.
riddle_builder_updateis a merge: to fix one question you send that one block, not the whole Riddle. Reading withomit: ["build.omittedDefaults"]and sending back the single block that changed is the cheapest edit loop there is. - Dry-run in batches.
riddle_builder_validatechecks up to 20 configurations in a single call. Twenty real builds that each fail cost twenty error responses – and twenty Riddles to clean up. - Filter at the source.
riddle_list(in either scope),question_bank_listandquestion_bank_get_itemsall take filters, a search term and a page size. Narrowing the query beats fetching a broad list and discarding most of it. - Pick the smaller tool for the question.
project_getfor a project's name and permissions, notproject_get_settings(a deeply nested payload).riddle_get's per-blockomittedDefaultsfor the defaults of a block you already have, not a reference read.palette_getfor a Riddle's actual design, not the palette reference. - Start from a guided workflow. The prompts already chain the tools in the right order, so the model does not have to discover the sequence – and rediscover it after a wrong turn.
Response Format
A tool answers with its payload at the top level. There is no {success, data} envelope and no HTTP code around it: read build, never data.build. Success is the tool result's own isError flag. This is the one place the MCP deliberately differs from the REST API these tools call, which does wrap its responses that way – so a payload copied from the REST documentation has one level more than what an MCP client receives.
Apart from that wrapper, the content follows the REST schema of the corresponding endpoint, and three shapes cover almost everything:
A Riddle comes back as the build-configuration envelope {uuid, type, modifiedAt, build, nextBlockId, warnings, published, context} – from riddle_get, riddle_publish, riddle_template_use and every riddle_builder_* tool. None of them returns the raw stored Riddle, which is far too large for an inline MCP response, and none of them fails on content the build configuration cannot express: that is reported in warnings instead. Types are stable across calls, so a field that is an object is always an object – an empty map is {}, never [].
A listing comes back as {items, page, pageSize, total, hasMore}. items is always present and always an array – empty when nothing matched, never null. The paging fields are present exactly when the listing is paginated (riddle_list, project_list, question_bank_list, question_bank_get_items): page and pageSize as the call actually ran, total how many entries match the filters at all, hasMore whether another page follows. An unpaginated listing (riddle_template_list, riddle_tag_list, question_bank_list with scope: "templates") answers with items alone – the missing hasMore is what says "this is everything". No listing reports a count: the entries in one response are items.length. The one exception is riddle_template_public_list, which groups by category ({categories, total}), since discovering the categories is the point of that call.
A failed call answers with {success: false, error, message} instead of any payload – the one place success survives, because the same object also travels as a failed entry inside a bulk call that did not fail as a whole. error is a stable code to branch on, message is prose for the user. When the failure has more than one thing to say, validationErrors carries one {message, code, ...} entry per individual reason (each with whatever identifies the offending part – a blockId, a resultId, a field name) and validationWarnings the same shape for findings that did not cause the failure. Read validationErrors rather than the top-level message when it is present: it is the complete set of reasons, and the only way to fix them all in one pass. Both lists are absent when empty, never present as []. Branch on code, never on the message text.
Field by field: riddle://reference/concepts/response-envelope, plus /warnings, /publish and /bulk.
Troubleshooting
If you encounter any issues while using the MCP, here are some common troubleshooting steps:
- API authentication failed: Make sure you are using an MCP key (not a regular API key – re-create if in doubt) and that the URL contains the actual key in place of the
[MCP_KEY]placeholder. - Missing API permissions: Check if your user role has the necessary AI permissions to access the desired projects and perform the intended actions. If you do not have permission to modify roles, ask your account administrators to grant you the required permissions.
- An upload cannot reach
*.riddle.com: the upload is a request your own environment makes, so it must be allowed to reach the host inrequiresNetworkAccessTo– in Claude an organization setting only an admin can change. Retrying will not help; see Working with media. - An upload link no longer works: links live 5 minutes and are single-use, even when the upload failed after reaching the CDN. Mint a new one and upload right away.
- A tool is not there / has the wrong name: every tool was renamed to
snake_caseand a number of them were merged in favour of a mode argument. Look the old name up in Renamed and merged tools. If the agent still offers old names, reconnect the MCP so the client re-reads the tool list. - The agent reads
data.buildand finds nothing: MCP answers are flat, with no{success, data}wrapper – see Response Format. - A write is refused with "This Riddle was created in the Creator and can only be changed there" (
RIDDLE_NOT_API_MANAGEABLE): Riddles created in the Creator can be read and updated through the MCP, but only a handful of write tools accept them – see Which writes are gated.
... Still not solved? Please reach out to our support team with details about the issue, either via our support chat or via email, including any error messages received and the steps you have already taken to troubleshoot.
Next steps
Want to continue reading? Read the following API sections to understand the MCP tools in more detail and see example requests and responses for each of them:
- Look at the specification of all available API endpoints: Find out what you can do with the Riddle API.
- Stats API: Use the Riddle Stats API to retrieve stats for any of your Riddles, projects, or for your personal project.
- Builder API: Use the Riddle Builder API to create Riddles programmatically with defined build configurations.
- Generative AI API: Use the Riddle Generative AI API to create Riddles programmatically based on topics and URLs.

