MCP HeartbeatTHE MCP OBSERVATORY
Public workspace

Explore

Evidence for your next connection.

Independent measurements of remote MCP servers. Every observation has a timestamp.

Read-only probes

MCP server · Other

Riddle

Build, publish and analyze quizzes, polls, forms and personality tests. Riddle account required.

Checks passed
13 / 22

1 failed · 8 not measured

Median response
264 ms

Median of successful probes over 7 days

30-day uptime
Not measured

Share of countable probes over 30 days, including daily rollups. Auth challenges are excluded.

Discovered tools
48

Tool definitions returned by this server

Independent monitoring

Health & checks

Status history

Responded

The server completed its last measured handshake. This is an observation, not a guarantee.

Last checked
Check summary by test group
Test areaPassedFailedUnmeasured
ConnectionCan a client connect and discover tools?
500
Reliability & speedDoes it respond consistently and quickly?
004
Access & authenticationWhat is required to use this server?
102
Tool qualityAre the tool definitions usable by an agent?
400
Publisher identityCan the publisher be traced to its source?
312

Unmeasured means there is not enough evidence to decide. How these checks work

Inspect individual checks 22 recorded

Core

  • Pass
    Reachable

    Latest probe returned HTTP 200 in 264 ms.

  • Pass
    Valid handshake

    protocolVersion 2025-06-18, serverInfo.name Riddle Creator.

  • Pass
    Lists tools

    tools/list returned 48 tools.

  • Pass
    Current protocol

    Negotiated 2025-06-18. The bar is 2025-06-18 or newer.

  • Pass
    HTTPS

    The endpoint URL is https.

Performance

  • Not measured
    Fast (p50)

    Median latency 264 ms over 1 samples in 7 days. The bar is 1000 ms. Need at least 5 samples.

  • Not measured
    Consistent (p95)

    95th percentile 264 ms over 1 samples in 7 days. The bar is 3000 ms. Need at least 20 samples.

  • Not measured
    Reliable (uptime)

    1 of 1 attempts returned a tool list in 30 days (100%). The bar is 99%. Need at least 30 samples.

  • Not measured
    Stable tool list

    Fewer than two successful probes in 30 days, so tool-list stability is not measured.

Access

  • Pass
    Open access

    tools/list succeeded with no credential, and the record declares none.

  • Not measured
    Standard auth

    Auth is not required, so the OAuth metadata check does not apply.

  • Not measured
    Free tier cited

    No publisher quote has been checked.

Tools

  • Pass
    Tools described

    Every tool description is at least 20 characters.

  • Pass
    Valid input schemas

    Every inputSchema is a JSON Schema object.

  • Pass
    Parameters documented

    186 of 186 parameters have a description (100%). The bar is 80%.

  • Pass
    Safety hints

    Every tool sets readOnlyHint or destructiveHint.

Publisher

  • Pass
    Verified namespace

    Listed in the Official MCP Registry.

  • Pass
    Website matches publisher

    The website host matches the reverse-domain namespace.

  • Fail
    Source repo linked

    No repository URL is on file.

  • Not measured
    Repo owner matches

    This namespace is not an io.github login, so the owner comparison does not apply.

  • Pass
    Recently updated

    Updated 2026-08-20T13:04:12.494881Z, within 180 days.

  • Not measured
    Open-source licence

    No GitHub repository to ask about a licence.

Start using Riddle

Connect to this server

Choose your client, copy the configuration, then enable the server in your client’s MCP settings.

Run this command in your terminal, then restart Claude Code.

Claude Code

claude mcp add --transport http riddle https://www.riddle.com/creator/api/v3/mcp

Capabilities

Tools 48

integration_list

Lists the Google Sheets and webhook integrations of a project as {items: [{id, type, name, connectedRiddles}]}. "name" is the value a build takes as publish.integrations.googlesheets / .webhook to connect that integration to a Riddle. A Google Sheet can only be connected in the Creator - with none listed, ask the user to add one there. A Riddle with a FileUpload form field cannot be published without a Google Sheets integration. Reads the stored records only - contacts neither Google nor any webhook.

Discovered
media_delete

Deletes a file from the media library permanently, to clean up after yourself: an upload that turned out wrong, or a file the user no longer wants stored (it frees the storage it counted against). The file must be unused - one any Riddle still shows is refused with a message naming those Riddles, since deleting it would leave a broken image behind in a live Riddle; remove it there 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, not of one Riddle: several Riddles can use it and any one of them keeps it alive, and a file the Creator lists as in use cannot be forced out from here either. Not reversible, no undo, and the id is not reused, so anything still pointing at the file stops resolving - ask the user before calling this. Returns {deleted: true, mediaId, name, type, size}. Without full access only files in the "AI Uploads" folder (media_upload_link's destination): riddle://reference/concepts/access.

Discovered
media_upload_link

Creates a single-use link for uploading ONE media file into the media library. Call it when the user has a LOCAL file for a Riddle: this server cannot receive bytes, so the upload is yours. A link expires after 5 minutes, dies on first use (successful or not), and only 20 are handed out per account per 5 minutes - so create one immediately before each upload, a batch one file at a time rather than the links up front, and never store or share one. Check the file BEFORE minting a link, since a link a rejected file burns is gone: an image, a video or an audio file, at most 10 MB (some environments cap lower - "maxBytes" and "allowedTypes" in the answer are the authoritative pair). Pass the "project" you will build in. Returns {uploadUrl, expiresAt (UTC), singleUse, maxBytes, allowedTypes, project, usage, requiresNetworkAccessTo}. POST the file to "uploadUrl" as multipart/form-data under the field name "file" - "usage" is that command ready to run, e.g. curl -F 'file=@/path/to/image.png' '<uploadUrl>' (the link carries its own signature, so no API key or header). That POST leaves your environment and needs outbound HTTPS to the host in "requiresNetworkAccessTo" (allowlist the wildcard it gives; in Claude only an admin can change that organization setting). If it is blocked or does not resolve, tell the user which host to allow - do not retry or look for another way in. The POST answers with {mediaId, type, width, height, size, folderId, project}: "mediaId" is the ONLY handle - use it as "media": {"type": "Image", "mediaId": <mediaId>} in a block (riddle://reference/riddle-builder/block-types). Never guess a url for it: a CDN url passed as a plain "url" media is re-downloaded as a second, unrelated copy - the account pays twice and this file records no usage, which makes an image in a live Riddle look safe to delete. The file lands in the account's "AI Uploads" folder tagged "AI Upload", counts against their storage, and media_delete removes it again.

Discovered
palette_customize

Changes the palette (colors, fonts, button styles, background) of ONE Riddle, as a path => value map, e.g. {"bgColor": "#ffffff", "font.name": "Roboto"} - the paths are palette_get's or riddle://reference/palette/fields. Never affects another Riddle: a palette inherited from the account/project default preset is not changed for the others, the values are stored as an override on this one. A built-in palette ("default:*") is stored nowhere and is therefore duplicated into a Riddle-owned copy automatically; newPaletteName always works on a copy. Two things to know. The new design only reaches the embedded (live) Riddle after another riddle_publish. A palette write does NOT move modifiedAt/modifiedBy (the Creator does not stamp them for a design change either), so polling those will not notice it: the detector is riddle_get's context.modified.hasChanges, true from the preset side. context.preset.drifted usually moves too but is not reliable alone - it means "diverged from the PARENT preset", so on a Riddle whose context.preset.parentId is null it stays false however much you change; read it only alongside parentId. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
palette_get

Reads the palettes - colors, fonts, button styles, background settings - of a Riddle: every palette it can use (the ones inherited from the account/project default preset included) with all of their values, which one is selected, which values this Riddle overrides, and the built-in palettes to start from. What each value does is riddle://reference/palette/fields. Mind the size: ~30 values per palette and an account preset can contribute palettes that have nothing to do with this Riddle, so the full response runs into thousands of tokens. Cut it with "omit" - omit: ["paletteValues"] lists the palettes by uuid and name only, which is how you find WHICH one you want (paletteUUID then returns that one in full), and "builtInPalettes"/"customizedValues"/"hints" drop those keys. To read just the design in effect, pass the selectedPaletteUuid from such a listing as paletteUUID.

Discovered
ping

A simple tool that returns "Pong". Can be used to test connectivity and authentication to the MCP.

Discovered
project_get

Returns one project by id - id, name, image and the authenticated user's permission matrix for it. The id comes from project_list or from the "team" of riddle_get. The project's default Riddle settings are NOT included; they are a large nested tree with its own tool, project_get_settings.

Discovered
project_get_settings

Returns the default Riddle settings every new Riddle of the project starts from - PUBLISHED and ENABLED only: "publishSettings" (privacy/DOI/OTP, email automation, tracking, data layer, ...) and "embedSettings" (iframe sizing, auto-scroll, ...), each holding only the areas whose "isDefaultEnabled"/"isEnabled" was on in the last published version. Never-published drafts and disabled areas are deliberately left out - this is what applies to Riddles right now, not a way to inspect unpublished or disabled defaults. Internal "_ids" bookkeeping (counters for repeatable items) is stripped, meaning nothing outside the Creator. Both fields are always objects keyed by settings area - a project with nothing enabled gets {}, never an empty list. Read it to know what a project forces onto its Riddles before creating or editing one there; project_get is the cheap call for name, image and permissions.

Discovered
project_list

Lists the projects this token has access to as {items, page, pageSize, total, hasMore} - the paging every listing of this server answers with (riddle://reference/concepts/response-envelope), here 25 per page, 100 max, so an account with many projects can be paged through; a project API token only ever returns its own project. Each entry is a trimmed summary (id, name, image): project_get adds your permissions on one, project_get_settings its default Riddle settings. An invalid page/pageSize (zero, negative, or over 100) is rejected rather than silently clamped.

Discovered
question_bank_create

Creates a new, empty question bank for the given Riddle type ("Quiz" or "Poll"). Add items with question_bank_item_add, then reference the returned "id" as "questionBankId" on a QuestionBank block of a riddle_builder_create call of the matching "type" - a bank created for "Quiz" can only be referenced from a Quiz, never a Poll, and vice versa. A bank lives in one project and can only be referenced from Riddles of that same project, so pass the projectId of the project the Riddle belongs to; omitting it creates the bank in the project the API key is scoped to. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_delete

Deletes a question bank and every item it holds (its tags cleaned up exactly as question_bank_tag_remove does) - one item alone is question_bank_item_delete. Rejected with QUESTION_BANK_INTERDEPENDENCY while a Riddle of the bank's own project still references it - counting the DRAFT and, on a published Riddle, the live version too, so a block removed from a draft does not release the bank until that Riddle is republished; read "deletingABank" in riddle://reference/question-bank/overview before deleting one. Permanent: there is no trash and no restore. Rejected for a public template id - a template is copied with question_bank_duplicate, never deleted. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_discard_changes

Throws away every unpublished change to a question bank's items at once, resetting each of them to its last published content. Irreversible - there is no undo and no copy of the discarded draft. A pending item delete comes back; 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". Only items are affected: the title and the notes are metadata outside the draft/publish split and never change here. Read the two sides before calling - question_bank_get_items reads the draft, the same call with published: true reads exactly the state this resets to - and prefer question_bank_publish whenever the draft is what should survive. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_duplicate

Copies a question bank and all of its items into a new, independent bank (editing one never affects the other) in projectId or the key's project. The source is left untouched. This is the one write a public template id is accepted for, and the way to change a template: duplicate it and edit the copy. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_get

Retrieves one question bank by id: title, riddleType, tags, notes, its categories/blockTypes and whether it has unpublished changes. The items themselves are question_bank_get_items, not included here. Works on a built-in template id from question_bank_list(scope: "templates") to see what it holds before duplicating - a template reads back without owner and tags/notes, belonging to nobody. "categories" and "blockTypeCategoryMap" are different slices and can disagree: "categories" comes from every DRAFT item, while "blockTypeCategoryMap" (categories per blockType) counts only PUBLISHED ones - a category used only by unpublished items is in "categories" but missing for its blockType there until question_bank_publish.

Discovered
question_bank_get_items

Lists the items of a question bank - its DRAFT content by default, or its published items with published: true. Filter by search term, blockType, category or difficultyRange - the same filters a QuestionBank block draws with. A template id from question_bank_list(scope: "templates") reads too - that is how to preview its questions before duplicating it. Paginated {items, page, pageSize, total, hasMore}. The item shape, the "columns" format, the pagination it runs with and what the response leaves out: riddle://reference/concepts/question-bank-items.

Discovered
question_bank_item_add

Adds ONE new question/item to a question bank; nothing existing is changed (question_bank_item_update replaces an item, question_bank_item_delete removes one). There is no universal item shape: read riddle://reference/question-bank/block-type-columns before the first add into a bank you have not populated - it lists the valid blockType values per Riddle type and the "columns" each expects (the blockTypeColumns listing is that document, not a tool). The item lands in the bank's DRAFT - question_bank_publish is what makes it something a QuestionBank block draws. "hasChanges" in the response says whether the bank now has unpublished changes. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_item_delete

Deletes ONE question/item of a question bank and touches nothing else. On a never-published item the delete is immediate and permanent; on a published one it only leaves the DRAFT - a live Riddle keeps drawing that item until question_bank_publish purges it, and question_bank_discard_changes brings it back until then. Permanent once purged: there is no trash and no restore. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_item_update

Replaces the content of ONE existing question/item of a question bank entirely - not a merge: blockType, category, difficulty and columns must all be sent even when unchanged, and a column or value left out is gone from the draft. blockType is immutable: a different value is rejected rather than retyping the item (delete it with question_bank_item_delete and add a new one instead). Read the item with question_bank_get_items first and send it back with its value ids intact. The change lands in the bank's DRAFT - question_bank_publish makes it live, question_bank_discard_changes throws every unpublished change away again. "hasChanges" in the response says whether the bank now has unpublished changes. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_list

Lists question banks. Scope "own" (the default) lists the banks of a project - the same ones the Creator shows; pass a projectId to look into another project, omit it for the project the API key is scoped to (your personal ones on a user API key). Paginated {items, page, pageSize, total, hasMore}. Scope "templates" lists the built-in starter banks instead - pre-filled banks anyone can copy with question_bank_duplicate to get real content immediately instead of starting empty. It takes riddleType and nothing else (a handful of them, nothing to page or search) and answers with id, title, PUBLISHED "itemCount", DRAFT "draftItemCount", which blockTypes those items are (null when that cannot be told yet) and a few example categories out of "categoryCount". A template that was never published reports "itemCount": 0 however many questions it holds - judge its real size by "draftItemCount", the number a duplicate would give you, and read the chosen id with question_bank_get and question_bank_get_items before duplicating. Every write here rejects a template id: a template is only ever changed through a copy of it.

Discovered
question_bank_publish

Makes every item's draft content its published content - what a QuestionBank block of a live Riddle actually draws at view time - so the change reaches participants immediately: every Riddle using the bank is republished and re-draws its questions, even when nothing changed. The previously published content of each item is overwritten, and a pending item delete (question_bank_item_delete on a published item) is purged for good at this point. A bank with unpublished changes still works in a block, it just draws its last published state; question_bank_discard_changes is the opposite step. Read question_bank_get_items with and without published: true first when unsure what will change. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_rename

Sets the title of a question bank. The title is metadata rather than DRAFT content, so it takes effect immediately AND permanently, is unaffected by publishing or discarding and never counts as an unpublished change. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_tag_add

Adds a tag to a question bank, by tagName or tagId from riddle_tag_list - exactly one of the two, as riddle_tag_add does. Question bank tags and Riddle tags are ONE set per project, not two: the same tag applies to banks and Riddles and has the same id in both, which is why the ids come from riddle_tag_list. Reuses the project's tag of that name or creates it, and re-tagging changes nothing. question_bank_tag_remove takes it off again. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_tag_remove

Removes a tag from a question bank, by tagName or tagId from riddle_tag_list - exactly one of the two. Never creates anything: a tag the bank does not carry is an error. A tag nothing uses any more is deleted from the project, where "nothing" includes Riddles: a tag a Riddle still uses survives its last bank. Deleting a tagged bank cleans up the same way. This only ever takes the tag off THIS bank; riddle_tag_delete removes the label from the project itself. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
question_bank_update_notes

Replaces the plain-text maintainer notes of a question bank (never shown to participants) - the previous notes are overwritten entirely, not appended to, so read them with question_bank_get first and send everything that should remain. Notes are metadata rather than DRAFT content: the change takes effect immediately AND permanently, is unaffected by publishing or discarding and never counts as an unpublished change. Rejected for a public template id. Needs full access: riddle://reference/concepts/access.

Discovered
reference_get

Read this server's own documentation: the block types, form field types, result blocks, palette values and response shapes the other tools expect. Every "riddle://reference/..." URI named in a tool description, a response or an error message is a topic of this tool - pass it here to read that document. These are the authoritative parameter reference for the riddle_builder_* and palette_* tools: read the relevant one BEFORE the first call instead of guessing property names, and re-read it when a call fails with a VALIDATION_ERROR. Up to 3 topics per call, and only the ones you need - most are long. Which topics exist, and what each one holds, is the "topics" parameter's own enum and description - not repeated here. "riddleType", "blockTypes" and "fieldTypes" narrow a document to your own material, and are ignored - whole document returned, stated in the response - on one that has no such split. block-types is never returned unscoped: {"riddleType": ["Quiz"]} is still all thirteen of a Quiz's block types (~20 KB), so name the ones you are about to build in "blockTypes" (a Quiz filtered to ["SingleChoice"]: ~5.5 KB, and it says everything about that block the wide read does). Decide the blocks first, then read only those. "fieldTypes" does the same for form-field-types and form-field-defaults, so reading both of them with {"fieldTypes": ["Dropdown"]} is the complete reference for one field type and nothing else. The per-entity families need no filter at all - riddle://reference/block-defaults/SingleChoice IS the filtered read.

Discovered
riddle_builder_create

Builds a new Riddle of any of the nine types from a build configuration: "type" says which kind (see that argument for what each one is and what it needs at a minimum), "build" carries the content. Everything a type accepts is described on the "build" fields themselves, marked with the types it belongs to; the exhaustive per-type shapes are in riddle://reference/riddle-builder/riddle-types, the block types in riddle://reference/riddle-builder/block-types, and the human help pages at https://www.riddle.com/help/api/build-riddles/riddle-types-and-other-blocks/<type>. To change an existing Riddle instead, use riddle_builder_update (a merge, not a rebuild); to check a configuration without creating anything, riddle_builder_validate. Returns the compact build-configuration envelope of riddle_get (riddle://reference/response-format), with 'queued': true added when queue is set. The new Riddle's view URL comes back in it - "context.viewUrl" once published, "context.viewUrlUnavailable.url" (already final, not live yet) before that.

Discovered
riddle_builder_update

Edits an existing Riddle of any type with the same build configuration riddle_builder_create takes - but as a merge, not a rebuild: only the fields you send are touched, an omitted one is left exactly as it is. Blocks are addressed by their "id", added with "$create": true, removed with "$delete": true and reordered with "$blocksOrder"; the same grammar edits a block's "items"/"fields" and a Personality Test's "personalities", while a Placeholder's "conditions" is replaced as a whole (see each field). Read the Riddle with riddle_get first: what it returns under "build" is exactly the shape this takes, block IDs included. Send only what you mean to change. Returns the compact build-configuration envelope of riddle_get (riddle://reference/response-format). Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
riddle_builder_validate

Dry-runs one or many Riddle Builder creates and/or edits without creating or changing anything: per entry of "builds", the same validation the real call would apply, run against a scratch/deep-copied Riddle that is discarded before this returns - never persisted, never published, no event dispatched. A media URL in the build IS still checked for reachability with a live HEAD request (no content fetched or stored), so an unreachable one is rejected here too (INVALID_MEDIA) instead of at flush/publish time. An entry is {type, build} for a would-be creation or {UUID, build} for an edit; the answer is the {validate, valid, summary, items} envelope of riddle://reference/concepts/bulk, one item per entry in the order sent - a single config is items[0]. Use it to see why a config would be rejected before spending a real create/edit on it, and to pre-flight a set of similar Riddles in one call. 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: creating entries are dry-run inside it, and without it they run in the personal project, where such a reference is invisible and comes back as "You are not authorized to access ..." for a build a real create would accept. Catches nothing that only happens once a Riddle is really flushed or published - a database constraint violation, queue-worker behaviour. Per-type build shapes: riddle://reference/riddle-builder/riddle-types.

Discovered
riddle_delete

Deletes one or many Riddles. Pass UUID for one, or UUIDs (max 100) to clean up several at once - e.g. everything riddle_list returned with origin: "api". A bulk delete is not atomic and never gives up early: a Riddle that cannot be deleted is that entry's "error" and every other Riddle is still deleted (riddle://reference/concepts/bulk). A Riddle another item of the same batch still references (a Leaderboard's connected Quiz/Predictor/Minigame, a FormSelect's Form) is retried once after the rest of the batch, so the order of the list does not matter; only a failure that survives the retry is reported. DRY RUN: pass dryRun: true and NOTHING is deleted - the call runs the same validation a real delete runs and answers {dryRun, deletable, summary, items, addUUIDs} per Riddle. Do this first whenever the set was not created by you; then act on "addUUIDs" to delete the referencing Riddles along with their targets. Every field, the batch effect and which blockers no addition to the call can lift: riddle://reference/concepts/bulk. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
riddle_get

Reads one Riddle - or several, with UUIDs - as the compact build-configuration envelope {uuid, type, modifiedAt, build, nextBlockId, warnings, published, context}. "build" is the DRAFT and is exactly what the riddle_builder_* tools accept back; "published" is the live version visitors see (null = never published, {"status": "identical"} = the live version matches the draft, {"status": "differs", "build": ...} = unpublished edits, and "isLive" on either says whether it is on the web right now). Content the build config cannot express never fails the call - it is reported in "warnings" instead. A property still at its block type's default is left out rather than returned: riddle://reference/block-defaults/<block type> and riddle://reference/riddle-defaults/<Riddle type> state what it is, and it must NOT be resent as a property, since many build properties enable a feature by being present at all. "context" holds what sits around the config: title, image, tags, notes, viewUrl, features, origin (whether this key may change this Riddle: "writableByThisKey", see riddle://reference/concepts/access), publish/unpublish/modify state, project and preset identity. Make the response smaller with "omit"; bigger with omittedDefaults or includeRiddleData, both off by default and both large. Field by field: riddle://reference/concepts/response-envelope, plus /warnings, /publish and /bulk.

Discovered
riddle_get_embed_code

Gets the HTML embed code for a Riddle by its UUID. The code is returned regardless of publish state, but the URL it embeds will not serve the Riddle until it is published.

Discovered
riddle_list

Returns a paginated list of Riddles. With the default scope "project" they come from a single project - use projectId to name it, or omit it for the project the API key is scoped to. With scope "account" they come from the entire account instead: the personal project and every team project the user has access to, in one list - the account-wide listing is this scope, not a tool of its own; projectId and notType do not apply there and are rejected rather than ignored. Every other filter works the same in both scopes. Answers with {items, page, pageSize, total, hasMore}, where "total" is how many Riddles match the filters in total and "hasMore" whether another page follows.

Discovered
riddle_move

Moves one or many Riddles into another project or into the personal project - and, given no destination, reports whether it could instead of moving anything. Pass UUID for one Riddle or UUIDs (max 100) for several. CHECK MODE: leave projectId out (or pass dryRun: true alongside one) and NOTHING is changed - the call runs the very same validation a real move runs and answers {canMoveAll, movable, blocked, addUUIDs, canMoveToPersonal, projects}. Do this first whenever the Riddles were not all created by you, or to explain to a user why a move is impossible. MOVE MODE: pass projectId (a project/team ID from project_list, or the string "personal") and the Riddles move, answering {bulk, operation, summary, results} with the new project per Riddle. Unlike the other bulk tools this one is atomic - it validates the whole set first and moves nothing if any Riddle is rejected - because Riddles that reference each other (a Quiz and the Leaderboard it reports to, a Form embedded in another Riddle) 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. When something is blocked, act on the top-level "addUUIDs" (or a blocked entry's "resolveByAddingUUIDs"), add those to UUIDs and check again until canMoveAll is true - connections chain, and blocking travels along them, so never read an empty "missingUUIDs" as "this one is fine" or a blocked Riddle as having a problem of its own. A move drops project-specific ad blocks tied to the old project and updates the published version along with the draft, but it does not adopt the new project's design. Every field of the check answer, why "projects" can be shorter than project_list and when a blocker cannot be resolved at all: riddle://reference/concepts/move-restrictions. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
riddle_preview

Shows the user the current DRAFT of a Riddle as a playable preview - the Riddle as it looks right now, published or not. Call it after riddle_builder_create or riddle_builder_update whenever the user wants to see the result (right after riddle_publish the draft and the live version are identical, so it shows what is live); do not call it on every edit of a multi-step build, once at the end is enough. In a client that renders UI this tool draws the preview widget (playable Riddle, a publish button, a link into the Creator); in any other client use the returned "previewUrl" - it works anonymously and serves the draft for "previewExpiresInSeconds" seconds, after which calling this tool again renews it. Changes nothing about the Riddle; the draft is served from a temporary cache under that anonymous previewUrl. Whether it is live and where: "isPublished", "viewUrl" (null until published - reference_get(topics: ["riddle://reference/concepts/publish"]) explains the draft/live split).

Discovered
riddle_publish

Publishes one or many Riddles, or takes them off the web again with publish: false (there is no unpublish tool). A published Riddle's public URL is the response's "context.viewUrl" - report that, never one spelled from the UUID (hosts differ per environment). With UUID the response is the riddle_get envelope with the new state under "context.published" ("omit" shrinks it the same way); with UUIDs (max 100) it is the bulk envelope, one compact entry per Riddle instead of a build configuration - not atomic: a Riddle that cannot be (un)published is that entry's "error", the rest still go through (riddle://reference/concepts/bulk). PUBLISHING: a refusal is error "RIDDLE_PUBLISH_VALIDATE" carrying "validationErrors", one {message, code} per reason ("MIN_ONE_BLOCK", "MIN_ONE_RESULT", "REDIRECT_RESULT_WITHOUT_URL", "LOGIC_DEAD_ENDS", ...) - fix them all and publish again. Non-blocking findings arrive as "validationWarnings" on a FAILED and on a SUCCESSFUL publish alike (the Riddle is live either way): report them, do not retry. Only a publish produces either list - riddle_builder_validate dry-runs a build configuration, not a publish. UNPUBLISHING: a LIVE Leaderboard with active Riddle connections is refused ("RIDDLE_UNPUBLISH", same list naming what is in the way) - riddle_delete it instead, which is not blocked by the connections and cleans up the connected Riddles. Unpublishing a Riddle that is not live is a no-op that says so in a "message" - read it before reporting a Riddle as taken offline, because "context.published.at" is null whether it was never published or already offline. Codes and the draft-vs-live model: riddle://reference/concepts/publish. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
riddle_rename

Renames a Riddle with the specified UUID. Returns only the confirmation of the change (uuid, title, modified state), not the entire Riddle. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
riddle_tag_add

Puts a tag on Riddles - ONE assignment per Riddle, never the tag itself (riddle_tag_list reads the labels of a project, riddle_tag_remove takes a tag off again, riddle_tag_delete removes a label). Gives one Riddle (UUID) or many (UUIDs, max 100) the tag named by tagName - created in each Riddle's OWN project if it has none of that name, so one call can span projects - or by tagId, which exists in one project and therefore works only for that project's Riddles. Re-tagging changes nothing and no tag needs a publish. There is no projectId here: a tag lives in the project of the Riddle it is put on, which is where the scope comes from. Returns the tag plus all tags of the Riddle, and with UUIDs the {bulk, operation, summary, results} envelope with one entry per Riddle, a failure (no edit permission, a tagId from another project) reported as that entry's "error" without stopping the others. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
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: untagging the last carrier already removes a tag (riddle_tag_remove), so this is for a leftover from before that cleanup existed. 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 - check with riddle_tag_list first, where a tag is free only when count AND bankCount are 0, and untag the carriers before calling this. Permanent, and ids are not reused, so a stored riddle_list filter on the old id stops matching a tag of the same name created later. Needs full access: riddle://reference/concepts/access.

Discovered
riddle_tag_list

Lists the tags of a project as {items: [{id, name, count, bankCount}]}, unpaginated - the labels the Creator shows and riddle_list's "tags" filter selects by, and where the ids that filter takes come from. Riddle tags and question bank tags are ONE set per project (question_bank_tag_add/_remove are the bank side of the same tags, with the same ids), and a tag belongs to a project: "Campaign 2026" in one project is a different tag from the one of that name in another. "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, which is what riddle_tag_delete requires. Read this before riddle_tag_add, which creates a tag it does not find, to tag with the name a project already has rather than a second one beside it.

Discovered
riddle_tag_remove

Takes a tag off Riddles - ONE assignment per Riddle (riddle_tag_add puts it on, riddle_tag_delete removes a label nothing carries). Untags one Riddle (UUID) or many (UUIDs, max 100), by tagName or tagId, and creates nothing: a tag the Riddle or its project does not have is an error for that Riddle. Untagging the LAST carrier deletes the tag itself from the project, unless a question bank still uses it - so a tag rarely has to be deleted by hand, and a tag removed that way is gone for good (re-adding creates a new one with a new id). Returns the tag plus all remaining tags of the Riddle, and with UUIDs the {bulk, operation, summary, results} envelope with one entry per Riddle, a failure reported as that entry's "error" without stopping the others. Only if the Riddle's origin.writableByThisKey is true (riddle_get, riddle_list): riddle://reference/concepts/access.

Discovered
riddle_template_create

Stores an existing Riddle as a template of its project (or of the user, for a personal Riddle), so new Riddles can be created from it with riddle_template_use. The template is a copy taken at this moment - the Riddle keeps living its own life and later changes to it do not reach the template. It copies what the Riddle currently has stored, published or not. Returns the created template: {id, title, type, category, isPublic, isQuickCreate, blocksCount, image, icon, createdAt, riddle}. Requires the template-create permission in the Riddle's project. Without full access only for a Riddle whose origin.writableByThisKey is true: riddle://reference/concepts/access.

Discovered
riddle_template_get

Reads one template, by default INCLUDING its build configuration - the same "build" shape riddle_get returns and riddle_builder_create accepts. This is how a template is ADAPTED rather than copied: edit the returned "build" (wording, questions, blocks) and build it with riddle_builder_create under the template's "type", passing this id as "templateId" so the new Riddle is still recorded as coming from it - and so it starts out on the template's whole preset, layout included, with your build config on top. The design comes along in "preset", which carries the preset settings and the palette; "preset.paletteValues" is included whenever that palette is not an unmodified built-in one, so a custom (or customized) palette rebuilds directly. On an unmodified built-in palette there is no "paletteValues" at all rather than a partial one: "preset.palette" names it in full (e.g. "Forest") and its colors/fonts come from riddle://reference/palette/built-in-palettes, matched by "name". Not changing anything? riddle_template_use is one call and copies the template whole. Returns {id, title, type, category, isPublic, isQuickCreate, blocksCount, image, icon, createdAt, riddle, build, nextBlockId, warnings}; a template whose content has no build-configuration equivalent comes back with an empty "build" and a warning saying so - use riddle_template_use for those.

Discovered
riddle_template_list

Lists the templates of a project (or your personal ones) - the ones made from your own Riddles with riddle_template_create, NOT Riddle's public ones, which riddle_template_public_list returns. Quick-create templates (isQuickCreate: true) are included alongside the regular ones; there is no separate listing for them. "type" filters by Riddle type. Returns {items: [{id, title, type, blocksCount, image, icon, createdAt, riddle, isPublic, isQuickCreate}], total} - not paginated, so "total" is simply how many there are - without the build configuration - read that with riddle_template_get, or hand the id straight to riddle_template_use.

Discovered
riddle_template_public_list

Lists Riddle's public templates - the ready-made ones every account has, as opposed to riddle_template_list's own ones. They are grouped by category (the tag the Creator sorts them by), so calling this without arguments is also how you learn which categories exist; "category" and "type" narrow it. Returns {categories: {<category>: [{id, title, type, blocksCount, image, icon, createdAt, riddle, isPublic}]}, total} - the one listing here that is grouped rather than a flat "items" - and without the build configuration, which is riddle_template_get (or hand the id straight to riddle_template_use).

Discovered
riddle_template_update

Changes a template's title and/or its gallery image and icon. Images: media_upload_link into the template's project, pass the reported "mediaId" (stored by URL - deleting the file breaks the image). Only what you pass changes; the template's content cannot be changed (create a new template from the edited Riddle). Public templates only by Riddle admins. Returns the template like riddle_template_create. Without full access only for a Riddle whose origin.writableByThisKey is true: riddle://reference/concepts/access.

Discovered
riddle_template_use

Creates a new Riddle from a template as an unchanged copy of it - content, settings and design preset - recorded as a copy of the template's Riddle (riddle_get reports it as context.duplicated). Use this whenever the template is what you want; to adapt it first, read it with riddle_template_get and build the edited configuration with riddle_builder_create instead. The new Riddle is a DRAFT - riddle_publish makes it live - except that Riddles the template embeds or that depend on it (a Form, a Leaderboard) are copied and published right away, and so is the new Riddle when one of them depends on it. Returns it in the standard build-configuration envelope, and takes riddle_get's "omit" to leave sections of it out. Requires the template-use permission plus Riddle-create in the target project.

Discovered
stats_fetch

Views/starts/submissions of one entity - "namespace" plus "entityId" says which (a project, a user, or a Riddle by UUID), "view" how deeply. "totals": one aggregate for the period. The response IS the entity's stats document: metrics sit in "core_metrics" under "global_stats" (a Riddle) or under "stats" (a user/project aggregate, or an old Riddle) - read only "stats" and a Riddle with real traffic looks unmeasured. A Riddle also carries one "block_<blockId>_stats" per block, keyed by riddle_get's ids. "timeseries": those numbers per consecutive interval - {intervalDays, intervalCount, intervals}, each {from, to, days, stats} inclusive of both ends. Up to 31 days give one interval per day; a longer range is grouped into at most 31 equal intervals whose "stats" is the interval TOTAL, not a daily number - divide by "days", and never read the shorter final interval as a drop. "breakdown" is per namespace: "riddle" gives one Riddle down to its blocks, choices and fields, every "id" being the stored block id riddle_get's "build" exposes, so a weak question can be fixed directly; "project" gives that tree for every Riddle in it - prefer a short range, or one Riddle, on a big project; "user" gives NOT a deep breakdown but the account-wide summary: one row per Riddle plus the totals over every Riddle in scope, ranked by sortBy, 25 per page ("hasMore" says whether another follows), narrowed with projectIds rather than paged on a big account, requires a USER API key, takes no entityId. Two kinds of "no data": an empty response ({}, always an object, never a list) means nothing was recorded - report "no stats recorded", never 0 - while a missing key inside a POPULATED "core_metrics" is a genuine zero (no "finish" beside a "view" means no submissions). A "breakdown" signals nothing by shape: a Riddle nobody opened still answers with its full tree at 0, so confirm it was published then (riddle_get's "published": null means it never was) before reading zeros as a finding.

Discovered
whoami

Reports the current API key: the authenticated user ("userEmail"/"userName", the latter null if never set), the project/team on a project-scoped key, whether this key has full access ("allowsFullAccess", see riddle://reference/concepts/access), and the account's plan in "subscription" ({plan, period, active, created, termEnd, isFreeTrial, daysLeft} - on a free trial "termEnd" is when the trial ends and "daysLeft" what is left of it, null on a paid plan). The plan is informational: every tool here works on every plan, free trial included, and no call is refused for it. Mind which id is which: the response's own "id" is the API KEY row, the user id (what the stats tools want as "entityId" for namespace "user") is "user", and "team" is the project id on a project key. A Riddle's own "plan" in riddle_get is a different thing - the plan level the features used in THAT Riddle need, not the account's.

Discovered

Recent measurements

Probe history

View status
Latest stored probes. Raw probe rows are kept for 14 days.
CheckedResultResponseTools
Connected264 ms48