{"name":"com.riddle/creator","slug":"riddle-creator","title":"Riddle","description":"Build, publish and analyze quizzes, polls, forms and personality tests. Riddle account required.","url":"https://mcp.market/server/riddle-creator","rating":null,"grade":"C","score":60,"certified":false,"status":"active","category":"other","tags":[],"presence":{"score":8,"stars":null,"forks":null,"downloads_week":null,"last_push_at":null,"license":null},"uptime":{"percent":100,"checks":20,"ok":20,"last_checked_at":"2026-09-24T06:41:09.861Z","last_ok_at":"2026-09-24T06:41:09.861Z","latency_ms":118},"claimed":false,"transport":"remote","callable_via_gateway":true,"default_price_micros":0,"repository":null,"website":"https://www.riddle.com/help/api/mcp","version":"1.0.2","remotes":[{"type":"streamable-http","url":"https://www.riddle.com/creator/api/v3/mcp"}],"packages":[],"tools":[{"name":"media_delete","description":"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}.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"mediaId":{"type":"integer","description":"The media library id of the file to delete - the \"mediaId\" the upload response returned, or the id shown for the file in the Creator's media library."}},"required":["mediaId"],"additionalProperties":false}},{"name":"media_upload_link","description":"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). Returns {uploadUrl, expiresAt (UTC), singleUse, maxBytes, allowedTypes, 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}: \"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\" (fixed, so the user can review what an agent uploaded in one place), counts against their storage, and media_delete removes it again.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"palette_customize","description":"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. And only Riddles created by the riddle_builder_* tools or the Riddle AI can be restyled - one the user built by hand in the Creator is rejected, so check context.origin.apiManageable on riddle_get (or \"origin\" on riddle_list) rather than finding out from the error. 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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"riddleUUID":{"type":"string","description":"The UUID of the Riddle you want to restyle. Only Riddles created via the Riddle Builder tools or generated by the Riddle AI can be restyled."},"values":{"type":"object","description":"Map of palette path => new value, e.g. {\"bgColor\": \"#ffffff\", \"buttonColor\": \"rgba(0,0,0,0.8)\", \"font.name\": \"Roboto\", \"riddleBorderRadius\": 12, \"isImageInBgDisplayed\": true}. Every path must be one of the paths listed in riddle://reference/palette/fields.","additionalProperties":true},"paletteUUID":{"type":["null","string"],"description":"The palette to change. Omit to change the currently selected palette. Pass a built-in id (\"default:timeless\") to start from that palette.","default":null},"newPaletteName":{"type":["null","string"],"description":"Create a new palette with this name (copied from paletteUUID / the selected palette) and apply the values to the copy, leaving the original untouched.","default":null},"select":{"type":["null","boolean"],"description":"Select the palette afterwards so the Riddle actually renders with it. Newly created palettes are always selected.","default":null}},"required":["riddleUUID","values"],"additionalProperties":false}},{"name":"palette_get","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"riddleUUID":{"type":"string","description":"The UUID of the Riddle whose palettes you want"},"paletteUUID":{"type":["null","string"],"description":"Return only this palette instead of all of them. Accepts a palette UUID or a built-in palette id like \"default:timeless\".","default":null},"includeBuiltInPalettes":{"type":["null","boolean"],"description":"Include the full values of all built-in palettes. Defaults to false, in which case only their ids and names are listed.","default":false},"omit":{"type":"array","description":"Leaves parts of the response out - the way to keep this call small. \"paletteValues\" lists every palette as {uuid, name} instead of with its ~30 values (then read the one you want with paletteUUID); \"builtInPalettes\", \"customizedValues\" and \"hints\" drop those keys entirely. Omit the parameter for the full response. Whatever you leave out is echoed back under \"omittedFields\", so a missing key never means the Riddle has none of it.","default":null,"items":{"type":"string","enum":["paletteValues","builtInPalettes","customizedValues","hints"]},"uniqueItems":true}},"required":["riddleUUID"],"additionalProperties":false}},{"name":"ping","description":"A simple tool that returns \"Pong\". Can be used to test connectivity and authentication to the MCP.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{},"additionalProperties":false}},{"name":"project_get","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"projectId":{"type":"integer","description":"The ID of the project to look up"}},"required":["projectId"],"additionalProperties":false}},{"name":"project_get_settings","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"projectId":{"type":"integer","description":"The ID of the project whose default Riddle settings to return"}},"required":["projectId"],"additionalProperties":false}},{"name":"project_list","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"page":{"type":["null","integer"],"description":"Page number to fetch (1-indexed). Defaults to 1. Zero, negative or non-numeric is rejected, not clamped.","default":null},"pageSize":{"type":["null","integer"],"description":"How many projects to return per page (max 100). Defaults to 25. Same validation as page; over 100 is rejected, not clamped.","default":null}},"additionalProperties":false}},{"name":"question_bank_create","description":"Creates a new, empty question bank for the given Riddle type (\"Quiz\" or \"Poll\"). Add items with question_bank_item(action: \"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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"riddleType":{"type":"string","description":"The Riddle type this bank's items are shaped for - \"Quiz\" or \"Poll\". Fixed for the lifetime of the bank; every item added later must match it."},"title":{"type":["null","string"],"description":"The bank's title. Defaults to \"New question bank\" when omitted.","default":null},"projectId":{"type":["null","integer"],"description":"The project (team) ID to create the bank in, as returned by project_list. Omit for the project the API key is scoped to (your personal space on a user API key).","default":null}},"required":["riddleType"],"additionalProperties":false}},{"name":"question_bank_delete","description":"Deletes a question bank or ONE of its items - \"target\" says which, and nothing else deletes either. target \"bank\" removes the bank and every item it holds (its tags cleaned up exactly as question_bank_tag(action: \"remove\") does), and is 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. target \"item\" takes questionBankItemId 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_manage(action: \"publish\") purges it, and question_bank_discard_changes brings it back until then. Both are permanent for the caller: there is no trash and no restore. Rejected for a public template id - a template is copied with question_bank_manage(action: \"duplicate\"), never deleted.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The ID of the question bank to delete, or of the bank the item belongs to."},"target":{"type":"string","description":"What to delete: \"bank\" (the bank itself and every item in it) or \"item\" (one question of it).","enum":["bank","item"]},"questionBankItemId":{"type":["null","integer"],"description":"The ID of the item to delete, as returned by question_bank_get_items or question_bank_item(action: \"add\"). Required for target \"item\", rejected for \"bank\".","default":null}},"required":["questionBankId","target"],"additionalProperties":false}},{"name":"question_bank_discard_changes","description":"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_manage(action: \"publish\") whenever the draft is what should survive. Rejected for a public template id.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The ID of the question bank to discard the unpublished changes of."}},"required":["questionBankId"],"additionalProperties":false}},{"name":"question_bank_get","description":"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_manage(action: \"publish\").","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The ID of the question bank to retrieve, as returned by question_bank_create/question_bank_list - or of a built-in template from question_bank_list(scope: \"templates\")."}},"required":["questionBankId"],"additionalProperties":false}},{"name":"question_bank_get_items","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The bank to list items of - your own, or a template id from question_bank_list(scope: \"templates\")."},"search":{"type":["null","string"],"description":"Filter items by their column values (e.g. a question title or a choice).","default":null},"blockType":{"type":["null","string"],"description":"Filter to one block type (e.g. \"SingleChoice\").","default":null},"category":{"type":["null","string"],"description":"Filter to one category.","default":null},"difficultyRange":{"type":["array","null"],"description":"Filter to a difficulty range, as [min, max] (e.g. [1, 5]).","default":null},"published":{"type":["null","boolean"],"description":"Read the bank's PUBLISHED items instead of its draft (default false) - a different set of items, and the pool a live Riddle's QuestionBank block actually draws from. Counting what one block criterion can draw is then a single call: its blockType/category/difficultyRange plus pageSize: 1, then read \"total\" - what that number does and does not include is in riddle://reference/concepts/question-bank-items.","default":null},"page":{"type":["null","integer"],"description":"Page number, starting at 1 (default: 1).","default":null},"pageSize":{"type":["null","integer"],"description":"Items per page (default: 250, max: 500).","default":null}},"required":["questionBankId"],"additionalProperties":false}},{"name":"question_bank_item","description":"Adds or replaces ONE question/item of a question bank, by \"action\" - deleting one is question_bank_delete. 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). \"update\" replaces the item's content entirely, so blockType, category, difficulty and columns must all be sent even when unchanged, and blockType is immutable: a different value is rejected rather than retyping the item (delete it and add a new one instead). Both change the bank's DRAFT - question_bank_manage(action: \"publish\") is what makes a change something a QuestionBank block draws, and 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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The ID of the question bank the item belongs to."},"action":{"type":"string","description":"What to do: \"add\" a new item (takes blockType, category, difficulty, columns) or \"update\" an existing one (the same, plus questionBankItemId).","enum":["add","update"]},"questionBankItemId":{"type":["null","integer"],"description":"The ID of the item to update, as returned by question_bank_item(action: \"add\")/question_bank_get_items. Required for \"update\", rejected for \"add\".","default":null},"blockType":{"type":["null","string"],"description":"The block type this question is shaped for, e.g. \"SingleChoice\". riddle://reference/question-bank/block-type-columns lists the ones valid for this bank's riddleType. Immutable: on \"update\" send the item's current one back unchanged (question_bank_get_items returns it), a different value is rejected.","default":null},"category":{"type":["null","string"],"description":"The item's category - free text, used to filter what a QuestionBank block draws (see the \"questionBankCriteria\" of the QuestionBank block type).","default":null},"difficulty":{"type":["null","integer"],"description":"The item's difficulty, 1-10 - also used to filter what a QuestionBank block draws.","default":null},"columns":{"type":"object","description":"The question content, as {columnName: [values]} - never a list, and a column holding a single value still takes a list. Which names are valid depends on the bank's riddleType AND the blockType, so take them from riddle://reference/question-bank/block-type-columns instead of guessing (an unknown one is rejected, naming it and the valid ones). On \"update\" this replaces the content entirely rather than merging: send every column, and hand question_bank_get_items output straight back with its {\"id\", \"value\"} objects intact - keeping those ids is what edits the stored values instead of replacing them with a fresh set.","default":null,"additionalProperties":{"type":"array","description":"The values of this column, in order - either plain strings/numbers, or the {\"id\": .., \"value\": ..} objects question_bank_get_items returns. Position in this array is what defines the order; the \"id\"s carry no order of their own.","items":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"object","properties":{"id":{"type":["integer","null"],"description":"The value's ID as returned by question_bank_get_items for this item; omit or null to have one assigned, and an ID the item does not have is rejected. It is an opaque handle for addressing THIS value on an update and nothing else - never sort or index by it (riddle://reference/concepts/question-bank-items). A column's order is the order of this array alone."},"value":{"type":["string","number"]}},"required":["value"],"additionalProperties":false}]}}}},"required":["questionBankId","action"],"additionalProperties":false}},{"name":"question_bank_list","description":"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_manage(action: \"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"scope":{"type":"string","description":"What to list: \"own\" (default) for the question banks of a project, \"templates\" for the built-in starter banks anyone can duplicate.","default":null,"enum":["own","templates"]},"riddleType":{"type":["null","string"],"description":"Filter by Riddle type (\"Quiz\" or \"Poll\"). Omit to list banks of every type. Applies to both scopes.","default":null},"tags":{"type":["array","null"],"description":"Filter by tag IDs (array of integers), as returned by riddle_tag_list. Omit to ignore tags. Scope \"own\" only.","default":null},"search":{"type":["null","string"],"description":"Search term to filter banks by title. Scope \"own\" only.","default":null},"sortBy":{"type":["null","string"],"description":"Sort field: \"createdAt\" or \"modifiedAt\". Defaults to \"createdAt\". Scope \"own\" only.","default":null},"sortOrder":{"type":["null","string"],"description":"Sort direction: \"ASC\" or \"DESC\". Defaults to \"DESC\". Scope \"own\" only.","default":null},"page":{"type":["null","integer"],"description":"Page number (default: 1). Scope \"own\" only.","default":null},"pageSize":{"type":["null","integer"],"description":"Items per page (default: 12, max: 50). Scope \"own\" only.","default":null},"projectId":{"type":["null","integer"],"description":"The project (team) ID whose banks you want, as returned by project_list. Omit for the banks of the project the API key is scoped to (your personal ones on a user API key). Scope \"own\" only.","default":null}},"additionalProperties":false}},{"name":"question_bank_manage","description":"Everything that acts on a question bank as a whole, by \"action\" - the items themselves are question_bank_item, and the two calls that only destroy are question_bank_delete and question_bank_discard_changes. A public template id is accepted for \"duplicate\" only and rejected by every other action. \"rename\" sets the title and \"updateNotes\" replaces the plain-text maintainer notes (never shown to participants): both are metadata rather than DRAFT content, so they take effect immediately AND permanently, are unaffected by publishing or discarding and never count as an unpublished change. \"publish\" makes every item's draft content its published content - what a QuestionBank block actually draws at view time - and is when a pending item delete is finally purged; a bank with unpublished changes still works in a block, it just draws its last published state. \"duplicate\" copies the bank and all of its items into a new independent bank (editing one never affects the other) in projectId or the key's project, and is the way to change a public template.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The ID of the question bank to act on."},"action":{"type":"string","description":"What to do with the bank: \"rename\" (takes title), \"updateNotes\" (takes notes), \"duplicate\" (takes title and optionally projectId) or \"publish\" (no further parameters).","enum":["rename","updateNotes","duplicate","publish"]},"title":{"type":["null","string"],"description":"The new title for \"rename\", or the title of the copy for \"duplicate\". Required for both, rejected for every other action.","default":null},"notes":{"type":["null","string"],"description":"Plain-text notes for this bank, e.g. sourcing/curation instructions - replaces any existing notes. Required for \"updateNotes\", rejected for every other action.","default":null},"projectId":{"type":["null","integer"],"description":"The project (team) ID the copy should land in, as returned by project_list. Only for \"duplicate\", where omitting it uses the project the API key is scoped to.","default":null}},"required":["questionBankId","action"],"additionalProperties":false}},{"name":"question_bank_tag","description":"Adds a tag to a question bank or removes one from it, by tagName or tagId from riddle_tag_list - exactly one of the two, as riddle_tag 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. \"add\" reuses the project's tag of that name or creates it, and re-tagging changes nothing. \"remove\" never creates anything - a tag the bank does not carry is an error - and 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 adds or removes the tag on THIS bank; riddle_tag_delete removes the label from the project itself. Rejected for a public template id.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"questionBankId":{"type":"integer","description":"The ID of the question bank to tag or untag."},"action":{"type":"string","description":"\"add\" the tag to the bank, or \"remove\" it from the bank.","enum":["add","remove"]},"tagName":{"type":["null","string"],"description":"The name of the tag. On \"add\" the project's tag of that name is reused, or created if there is none yet; on \"remove\" nothing is ever created and a name the bank does not carry is an error. Pass either this or tagId.","default":null},"tagId":{"type":["null","integer"],"description":"The ID of an existing tag, as returned by riddle_tag_list. Pass either this or tagName.","default":null}},"required":["questionBankId","action"],"oneOf":[{"required":["tagName"],"properties":{"tagName":{"not":{"type":"null"}},"tagId":{"const":null}}},{"required":["tagId"],"properties":{"tagId":{"not":{"type":"null"}},"tagName":{"const":null}}}],"additionalProperties":false}},{"name":"reference_get","description":"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.\n\n\"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"topics":{"type":"array","description":"The documents to read, as their \"riddle://reference/...\" URIs (a document's short resource name, e.g. \"block-types\", works too). What each one holds:\n- riddle://reference/index: every document this server has, with its exact size. Read this first when you do not know which of the others you need - it is by far the smallest, and the one place the member names of the {...} families below are listed.\n- riddle://reference/getting-started: what this server is for, the authentication model, the guided prompts, and the addresses of the documents that hold the rules. Start here when unsure which tool to use.\n- riddle://reference/prompts/{prompt} (one per guided prompt, e.g. riddle://reference/prompts/build_LeadGenQuiz): the playbook for a whole goal - what to ask the user first, the tool calls in order, what to verify, the traps. Read one when the user states a GOAL rather than an operation.\n- riddle://reference/response-format: the envelope every Riddle-returning tool answers with, plus the list, bulk and error shapes.\n- riddle://reference/riddle-builder/riddle-types: all 9 Riddle types with their required/optional build fields and result structure.\n- riddle://reference/riddle-builder/block-types: every question and content block type - the reference for the \"blocks\" of a riddle_builder_* call. Returned scoped only, and worth scoping twice (see \"blockTypes\").\n- riddle://reference/riddle-builder/form-field-types and .../result-blocks: the 18 form field types of the FormBuilder block, and the 12 result page block types with their format and styling options.\n- riddle://reference/concepts/{concept} (one per subject, e.g. riddle://reference/concepts/merge-semantics): how an edit merges, the \"preset\" and \"publish\" objects, branching logic, defaults, bulk calls, move restrictions, limits, troubleshooting.\n- riddle://reference/block-defaults/{blockType}, riddle://reference/riddle-defaults/{riddleType}, riddle://reference/form-field-defaults, riddle://reference/publish-defaults: what a read-back leaves out for still being at its default, and what that default is. Read riddle://reference/concepts/defaults once for how to use them.\n- riddle://reference/palette/fields, .../built-in-palettes, .../fonts: every palette value palette_customize accepts and where it shows up, the built-in palettes to start from, the available font families.\n- riddle://reference/question-bank/overview: what a question bank is, its draft/publish model, and how its items relate to a QuestionBank block - read before the first question_bank_create.","items":{"anyOf":[{"type":"string","enum":["riddle://reference/index","riddle://reference/getting-started","riddle://reference/response-format","riddle://reference/riddle-builder/riddle-types","riddle://reference/riddle-builder/block-types","riddle://reference/riddle-builder/form-field-types","riddle://reference/riddle-builder/result-blocks","riddle://reference/palette/fields","riddle://reference/palette/built-in-palettes","riddle://reference/palette/fonts","riddle://reference/publish-defaults","riddle://reference/form-field-defaults","riddle://reference/question-bank/overview","riddle://reference/question-bank/block-type-columns"]},{"type":"string","pattern":"^riddle://reference/(block-defaults|riddle-defaults|concepts|prompts)/[^/]+$"},{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9-]*$"}]},"minItems":1,"maxItems":3,"uniqueItems":true},"riddleType":{"type":"array","default":null,"description":"Narrow riddle-types/block-types to these Riddle type(s) - on block-types that drops every other type's question blocks while keeping the shared conventions (commonBlockProperties, the general Content/Ad/Quote blocks). REQUIRED on block-types unless \"blockTypes\" is given instead, and only the WIDE scope of it. Ignored on a topic with no per-type split, and not applicable to the block-defaults/riddle-defaults families - read the entity's own address there. Omit for every type.","items":{"type":"string","enum":["Quiz","Poll","Personality","Form","Predictor","Minigame","Leaderboard","Story","Placeholder"]},"uniqueItems":true},"blockTypes":{"type":"array","default":null,"description":"The block type name(s) you are actually going to build (e.g. \"SingleChoice\", \"WheelSpinner\"), narrowing block-types on top of whatever \"riddleType\" kept - the normal way to read that document, not an optimization for later: pass the two or three the Riddle needs, and come back for another. The names are deliberately not enumerated here (dozens of them, on a schema every agent reads every turn); an unknown one, or a real one outside the Riddle type(s) you filtered to, is rejected with the names that are valid for your situation. Omit for every block type.","items":{"type":"string"},"uniqueItems":true},"fieldTypes":{"type":"array","default":null,"description":"Narrow form-field-types and form-field-defaults to these form field type name(s) (e.g. \"Dropdown\", \"Privacy\"); the property sets a kept type refers to (propertySets/commonProperties) and the \"fields\" usage notes always come with it. Not enumerated here for the same reason \"blockTypes\" is not - an unknown one is rejected with the full list. Ignored on every other topic. Omit for every field type.","items":{"type":"string"},"uniqueItems":true},"includeAvailableTopics":{"type":"boolean","default":null,"description":"Whether to carry the full \"availableTopics\" catalogue. Omit it - the first reference_get of a session gets it, later ones get a pointer instead of repeating ~2.5 KB you already have. true gets it again (a fresh conversation on an existing session), false never pays for it."}},"required":["topics"],"additionalProperties":false}},{"name":"riddle_builder_create","description":"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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"type":{"type":"string","description":"Which kind of Riddle to build - this decides which \"build\" fields and which block types exist. Quiz: questions with right and wrong answers, scored, one result page per score range (title + blocks). Poll: opinion questions, nothing right or wrong, one shared result page (title + blocks). Personality: answers score towards personalities, the winner is the result (title + blocks + personalities). Form: lead-collecting fields only, no questions (title + blocks). Predictor: predictions of real-world outcomes, scored once the actual result is entered (title + blocks). Minigame: SlotMachine, WheelSpinner, Sudoku, Minesweeper or Crosswords (title + blocks). Story: linear content pages, no answers of any kind (title + blocks). Leaderboard: a standalone ranking other published Riddles connect to, no blocks (title). Placeholder: no content of its own, routes to another Riddle by prioritized, time-windowed rules (title). Per-type shapes: riddle://reference/riddle-builder/riddle-types.","enum":["Quiz","Poll","Personality","Form","Predictor","Minigame","Story","Leaderboard","Placeholder"]},"build":{"type":"object","description":"The build configuration of the Riddle type named in \"type\".This is the raw build configuration in the engine's own key names - the exact same shape riddle_get returns under \"build\", riddle_builder_update takes, and riddle_builder_validate dry-runs, so a read-back can be fed straight back in - riddle_get's \"build\" KEY, never the outer envelope around it, whose \"uuid\" and siblings are rejected as unsupported properties. Unknown keys are rejected rather than ignored. WHICH fields exist is decided by \"type\": each one below opens with the types it belongs to, and a field the chosen type does not have is rejected. Only \"title\" exists on all nine.","properties":{"title":{"type":"string","description":"The Riddle title."},"blocks":{"type":"array","description":"The Riddle's blocks - required by every type except Leaderboard and Placeholder, which have none. A block is {\"type\": <one of the block types listed below for the \"type\" you are building>, \"title\": \"...\"} plus that block type's own content, and the content differs sharply per block type - unknown keys are rejected rather than ignored. A CHOICE block's answer options go in \"items\" - not \"answers\" - each one an object: {\"title\": \"Paris\", \"isCorrect\": true} on a Quiz, {\"title\": \"Paris\", \"scores\": [3, 0]} on a Personality Test. The blocks that take typed-in answers instead name their own key - a Quiz TextEntry takes \"answers\", a Quiz GuessIt \"acceptableAnswers\" - and some blocks (a Poll NetPromoterScore or TextEntry) have no options at all; the per-block-type reference below is what says which. A FormBuilder's inputs go in \"fields\" instead. Every remaining property, value range and trap per block type is in riddle://reference/riddle-builder/block-types; read it with reference_get scoped to your \"riddleType\" AND the \"blockTypes\" you intend to build before writing them. The block types each Riddle type allows: >> QUIZ: SingleChoice / MultipleChoice / TextEntry / Order / Flashcard / TypeRush / GuessIt / TapAndFind / SpotTheDifference / QuestionBank - the last two place their answers as hotspots on an image, in percentages of it. >> POLL: SingleChoice / MultipleChoice / Matrix / Order / Upvote / NetPromoterScore / RateIt / Swiper / Reaction / TextEntry / TierList / ThisOrThat / QuestionBank. >> PERSONALITY: SingleChoice / MultipleChoice - every answer item's \"scores\" array must have one integer per personality, in personality order. >> FORM: FormBuilder, whose \"fields\" are the 18 types in riddle://reference/riddle-builder/form-field-types. >> PREDICTOR: PickTheWinner / GuessTheScore. >> MINIGAME: SlotMachine / WheelSpinner / Sudoku / Minesweeper / Crosswords. >> STORY: Content / Ad / Quote / InteractiveGraphic / FormBuilder / FormField / FormSelect - a Story has no answers of any kind, so build a Poll or a Quiz instead if you need them. >> QUIZ / PREDICTOR / MINIGAME / STORY additionally take the lead-collecting blocks FormBuilder, FormField and FormSelect in the same array; on a Quiz, a Predictor or a Minigame a Name and an Email field are what make it Leaderboard-connectable (a Story scores nothing, so it collects leads but never feeds a Leaderboard). A QuestionBank block draws its questions from an existing bank (\"questionBankId\" plus a non-empty \"questionBankBlocks\"; riddle://reference/question-bank/overview)."},"personalities":{"type":"array","description":"Personality only, and required there. Array of personality objects (min 2), each with \"title\" (string). Optional: \"description\", \"media\", \"minScore\"/\"maxScore\" (0-100, the score window in which this personality wins), the CTA button (\"ctaButtonText\"/\"ctaButtonURL\" - either one shows it - plus \"isIncludeCTAButtonEnabled\") and the runner-ups (\"otherResults\", \"areOtherResultsEnabled\", \"areOtherResultsMediaEnabled\"). Their exact shapes: riddle://reference/riddle-builder/riddle-types under types.Personality."},"results":{"type":"array","description":"Quiz only - every other type with a result page has the singular \"result\" instead. Array of result objects, each {\"title\": \"...\", \"minPercentage\": 0, \"maxPercentage\": 50} (0-100) with optional \"description\" - or \"minPercentage\"/\"maxPercentage\" plus an advanced \"blocks\" array (Text / ResultScore / Share / AnsweredBlocks / Button) for full control over the page. Send \"redirectUrl\" instead of \"blocks\" to redirect the respondent rather than show a page. Block properties and redirect support: riddle://reference/riddle-builder/result-blocks."},"result":{"type":"object","description":"The single result page - Poll, Form, Predictor, Minigame, Story and Personality. A Quiz has \"results\" (plural, one page per score range) instead, and a Leaderboard and a Placeholder have no result page at all. Single result object: \"title\" plus optional \"description\", or an advanced \"blocks\" array (PopularChoices, AnsweredBlocks, Text, Media, ...) for full control over the page. Send \"redirectUrl\" instead to redirect rather than show a page. Both: riddle://reference/riddle-builder/result-blocks. >> FORM also: Supports dynamic %variables naming a form field by TYPE and position - \"%name1\", \"%email1\", the first field of a type also as \"%name\" - never by its label (\"%Your name\" is rejected). riddle://reference/concepts/form-fields-usage (\"variables\"). >> PERSONALITY instead: Optional result page configuration. Takes the full result-builder block set plus the Personality-only WinningPersonality, OtherPersonalities and Attributes blocks - riddle://reference/riddle-builder/result-blocks."},"scoring":{"type":"object","description":"Predictor only. Scoring rules of the Predictor, a nested object. Keys (ints >= 0): \"correct\" (exactly right), \"tendency\" (right winner, wrong score), \"difference\" (right goal difference), \"wrong\". Defaults 30 / 10 / 20 / 0."},"riddleConnections":{"type":"array","items":{"type":"string"},"description":"Leaderboard only. Riddle UUIDs (max 10) to connect to the Leaderboard - quizzes, predictors and minigames (except WheelSpinner-only ones), each already published; connecting a draft fails. On an edit this REPLACES the whole list rather than appending, so resend every UUID you want to keep."},"riddleId":{"type":["string","null"],"description":"Placeholder only. The fallback target: the UUID of the Riddle to show when no condition matches. Must be an existing, non-Placeholder, published Riddle, and not this Placeholder itself. Having none is meaningful - the Placeholder then resolves to nothing. On an EDIT, omitting it leaves it as it is; \"riddleId\": null clears it, and is rejected if that would leave the Placeholder with no fallback and no conditions at all - as is \"conditions\": [] while this is empty. riddle://reference/riddle-builder/riddle-types with riddleType [\"Placeholder\"]."},"conditions":{"type":"array","description":"Placeholder only. Prioritized (first match wins), time-windowed routing rules evaluated before the fallback \"riddleId\"; at most 100. Each targets exactly one of \"riddleId\" (UUID of an existing, non-Placeholder, published Riddle) or \"tag\" (name or numeric id of an EXISTING tag in this Riddle's scope - a typo'd name is rejected, never auto-created), never both; a \"tag\" also requires \"tagMode\": \"lastPublished\" or \"random\" (with optional \"randomRefreshIntervalSeconds\", \"random\" only). ONE kind of time window per condition - a date range (\"from\" required, \"to\" optional) or a daily one (\"dailyFrom\" plus \"dailyTo\") - with neither it always matches. No other key is accepted. This REPLACES the whole list, and the stored list is re-sorted into tiers with overlapping windows trimmed, so a read-back is not what you sent. Normalization, and why resending a read-back can delete a condition: riddle://reference/riddle-builder/riddle-types with riddleType [\"Placeholder\"]."},"logic":{"type":"object","description":"Quiz / Poll / Form / Story only. Optional branching logic tree over the blocks. A node is {\"blockId\": int, \"branchingType\": \"linear\"|\"answer\"|\"condition\"|\"score\"}. For \"linear\" add \"next\": {<child node>}; for \"answer\" add \"rules\": [{\"answers\": [\"Answer text\"], \"blockId\": int, \"next\": {...}}]; for \"condition\" add \"condition\": {\"field\": string, \"operator\": string, \"value\"?: mixed} plus \"ifTrue\"/\"ifFalse\": {<child node>} (riddle://reference/concepts/logic lists the \"field\"/\"operator\" values); for \"score\" add \"scoreType\": \"total\"|\"block\" plus \"ranges\": [{\"min\": int, \"max\": int, \"blockId\": int, \"next\": {...}}]. A bare {\"blockId\": X} shows X and ends the flow; to merge paths and keep going you must repeat the shared tail under each branch. Omit for a default linear flow. >> QUIZ: A Quiz containing a Flashcard block is linear-only: one \"answer\"/\"condition\"/\"score\" node anywhere in the tree is then rejected for the whole Riddle. >> STORY: A Story has no answers to branch on, so only \"linear\" nodes are meaningful here."},"leaderboard":{"type":"object","description":"Quiz / Predictor / Minigame only. Leaderboard connection. Keys: connections (leaderboard UUIDs to attach this Riddle to), identifier, nickname. identifier/nickname name a form field of this Riddle by its label (\"Your email\", case-insensitive) or by the key riddle_get reports (\"email1\"); an ambiguous label is rejected, pass the key then."},"preset":{"type":"object","description":"Riddle-level design and behaviour settings (design, open/close scheduling, timers, progress, live score, result visibility, vote integrity, page chrome, ~20 UX flags); only the keys you send are applied, and a key only exists for the types that have it. A Leaderboard's own display settings live here too. Exact names: riddle://reference/concepts/preset."},"publish":{"type":"object","description":"Every type except Leaderboard. Publish CONFIGURATION stored on the Riddle - an object, not this tool's separate top-level \"publish\" boolean, which publishes right away. Covers showcase/QR code, lead verification, email automation, dataLayer, tracking, integrations, embedSettings. Keys: riddle://reference/concepts/publish."}},"required":["title"]},"publish":{"type":["null","boolean"],"default":null,"description":"Whether to publish the Riddle right after creation; default false (draft). Distinct from the \"publish\" field INSIDE the build config, which is the stored publish configuration."},"queue":{"type":["null","boolean"],"default":null,"description":"Whether to queue the creation asynchronously."},"project":{"type":["null","integer"],"default":null,"description":"The project ID; pass NULL for personal project; omit to use the currently selected project."},"templateId":{"type":["null","integer"],"default":null,"description":"Only when this build config came from a template you adapted (riddle_template_get): that template's id. Always pass it when you have one - the new Riddle then starts out on the template's whole DESIGN, including what no build config can express, with your build config applied on top. The content is yours either way. Details: riddle://reference/concepts/unknown-properties."},"omit":{"type":"array","default":null,"description":"Sections of the returned envelope to leave out; omit the parameter for the whole envelope. \"uuid\"/\"type\"/\"modifiedAt\" are always returned, and whatever you leave out is echoed back under \"omittedFields\", so a missing key never means the Riddle has none of it. Details: riddle://reference/concepts/warnings.","items":{"type":"string","enum":["build","warnings","nextBlockId","published","context"]},"uniqueItems":true}},"required":["type","build"],"additionalProperties":false}},{"name":"riddle_builder_update","description":"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. Only Riddles created by the riddle_builder_* tools or by the Riddle AI can be edited here - one built manually in the Creator can hold content this build config cannot express, and is rejected; check context.origin.apiManageable on riddle_get (\"origin\" on riddle_list) beforehand. Returns the compact build-configuration envelope of riddle_get (riddle://reference/response-format).","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":"string","description":"The UUID of the Riddle to edit, as returned by riddle_list or riddle_get (e.g. \"6FA740EW\") - the Riddle itself, never a block id or a project id."},"build":{"type":"object","description":"The changes to apply, as a partial build configuration in the engine's own key names - the same shape riddle_get returns under \"build\" and riddle_builder_create takes. Only the fields you send are touched; send at least one. A field only exists for the Riddle types that have it, and unknown keys are rejected rather than ignored.","properties":{"title":{"type":"string","description":"New Riddle title; omit to keep the current one."},"blocks":{"type":"array","description":"The blocks to change, add or remove - only the ones you touch, everything else stays as it is. EDIT: the block's \"id\" (riddle_get reports it) plus only the properties that change - {\"id\": 3, \"title\": \"New question title\"}. ADD: \"$create\": true instead of an \"id\", plus everything a new block needs in the shape riddle_builder_create takes - {\"$create\": true, \"type\": \"SingleChoice\", \"title\": \"New question\", \"items\": [...]}. REMOVE: \"id\" plus \"$delete\": true. A block cannot change its \"type\"; an unknown \"id\" is rejected naming the ids that exist. A collection inside a block merges by \"id\" the same way - {\"id\": 3, \"items\": [{\"id\": 2, \"title\": \"Bonn\"}]} renames one answer and leaves the others alone. Which collections merge, which are replaced wholesale, and which deletes are refused for still being referenced: riddle://reference/concepts/merge-semantics (markers: riddle://reference/concepts/editing)."},"$blocksOrder":{"type":"array","items":{"type":"integer"},"description":"The new order of the Riddle's blocks, as the complete list of their IDs - [3, 1, 2] puts block 3 first. Complete means every ID the Riddle has AFTER this edit, exactly once; a partial list is rejected. It runs last, so a deleted block must NOT be listed and an added one MUST be - give the \"$create\" entry an explicit \"id\" from riddle_get's \"nextBlockId\". The same marker orders a collection inside a block (\"$itemsOrder\", \"$fieldsOrder\"). Rules: riddle://reference/concepts/merge-semantics."},"personalities":{"type":"array","description":"Personality Test only. The personalities to change, add or remove, merged by \"id\" exactly like \"blocks\" - \"id\" plus the changed properties, \"$create\": true plus a full new personality, or \"id\" plus \"$delete\": true. There is no \"$personalitiesOrder\": the order is not editable, and the order you send carries no meaning. The minimum of 2 is checked against the merge result. Answer \"scores\" follow a personality by identity, not position - a new one starts at 0 on every existing answer item, so resend the affected blocks' \"items\" with their full \"scores\" arrays in the same call."},"result":{"type":"object","description":"New single result page (Poll / Form / Predictor / Personality / Minigame / Story), same shape as in riddle_builder_create. Replaces the current one as a whole - a result page is neither editable property-by-property nor mergeable by \"id\", so send it complete. Omit to keep it untouched."},"results":{"type":"array","description":"New result pages (Quiz), same shape as in riddle_builder_create. Replaces ALL current pages with what you send, in the order you send it - pages are neither editable per entry nor reorderable, so read them with riddle_get first and resend every page you want to keep, including its \"id\", which is honored to keep the page stable. Omit to keep them all untouched. riddle://reference/concepts/result-pages."},"scoring":{"type":"object","description":"Predictor only. New scoring rules, same shape as in riddle_builder_create - only the keys you send are changed, so {\"correct\": 50} leaves \"tendency\"/\"difference\"/\"wrong\" as they were. Omit to keep them."},"logic":{"type":"object","description":"New branching logic tree, same shape as in riddle_builder_create. Replaces the current logic; omit to keep it. Deleting a block the stored CUSTOM logic still references is rejected unless the same call sends a replacement tree without it, or \"logic\": {\"$reset\": true} to fall back to the default linear flow (\"$reset\" with any other key is rejected). Node shapes: riddle://reference/concepts/logic."},"preset":{"type":"object","description":"Design and riddle-level behaviour settings to change, same shape as in riddle_builder_create; only the keys you send are applied. A Leaderboard's podium colours (\"color1st\"/\"color2nd\"/\"color3rd\") and \"isEmailVerificationEnabled\" live here too."},"publish":{"type":"object","description":"Publish-configuration settings to change, same shape as in riddle_builder_create; only the keys you send are applied. Distinct from this tool's top-level \"publish\" boolean, which publishes right away."},"leaderboard":{"type":"object","description":"Quiz/Predictor/Minigame only. New leaderboard connection: \"connections\" (leaderboard UUIDs), \"identifier\", \"nickname\", as in riddle_builder_create. Omit to leave the Riddle's connections exactly as they are - sending this only ever appends a connection, so there is nothing to merge per key."},"riddleConnections":{"type":"array","items":{"type":"string"},"description":"Leaderboard only. The complete list of connected Riddle UUIDs - it REPLACES the stored set rather than appending, so read the Riddle with riddle_get first and resend every UUID you want to keep. Max 10; each target must already be published and have Name and Email form fields."},"riddleId":{"type":["string","null"],"description":"Placeholder only. The fallback target: the UUID of the Riddle to show when no condition matches. Must be an existing, non-Placeholder, published Riddle, and not this Placeholder itself. Having none is meaningful - the Placeholder then resolves to nothing. On an EDIT, omitting it leaves it as it is; \"riddleId\": null clears it, and is rejected if that would leave the Placeholder with no fallback and no conditions at all - as is \"conditions\": [] while this is empty. riddle://reference/riddle-builder/riddle-types with riddleType [\"Placeholder\"]."},"conditions":{"type":"array","description":"Placeholder only. Prioritized (first match wins), time-windowed routing rules evaluated before the fallback \"riddleId\"; at most 100. Each targets exactly one of \"riddleId\" (UUID of an existing, non-Placeholder, published Riddle) or \"tag\" (name or numeric id of an EXISTING tag in this Riddle's scope - a typo'd name is rejected, never auto-created), never both; a \"tag\" also requires \"tagMode\": \"lastPublished\" or \"random\" (with optional \"randomRefreshIntervalSeconds\", \"random\" only). ONE kind of time window per condition - a date range (\"from\" required, \"to\" optional) or a daily one (\"dailyFrom\" plus \"dailyTo\") - with neither it always matches. No other key is accepted. This REPLACES the whole list, and the stored list is re-sorted into tiers with overlapping windows trimmed, so a read-back is not what you sent. Normalization, and why resending a read-back can delete a condition: riddle://reference/riddle-builder/riddle-types with riddleType [\"Placeholder\"]."}}},"publish":{"type":["null","boolean"],"default":null,"description":"Whether to publish the Riddle after the edit; default false, which leaves the changes in the draft. It rides along with an edit, it is not one: publish: true with an empty \"build\" is rejected. To publish what is already in the draft, call riddle_publish."},"omit":{"type":"array","default":null,"description":"Sections of the returned envelope to leave out; omit the parameter for the whole envelope. \"uuid\"/\"type\"/\"modifiedAt\" are always returned, and whatever you leave out is echoed back under \"omittedFields\", so a missing key never means the Riddle has none of it. Details: riddle://reference/concepts/warnings.","items":{"type":"string","enum":["build","warnings","nextBlockId","published","context"]},"uniqueItems":true}},"required":["UUID","build"],"additionalProperties":false}},{"name":"riddle_builder_validate","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"builds":{"type":"array","description":"The build configs to dry-run, 1 to 20 entries, creates and edits mixable: {type, build}\nfor a would-be creation or {UUID, build} for an edit, exactly one of \"type\"/\"UUID\" per\nentry. \"type\" is one of Quiz / Poll / Personality / Form / Predictor / Leaderboard /\nMinigame / Story / Placeholder, and \"build\" is exactly what riddle_builder_create or\nriddle_builder_update takes, so a config moves between them unchanged. An edit entry hits\nthe same origin gate as riddle_builder_update - rejected unless the Riddle is\napiManageable (context.origin.apiManageable on riddle_get).","default":[]},"project":{"type":["null","integer"],"default":null,"description":"The project to dry-run the CREATING entries in - riddle_builder_create's own parameter, and it must be the project you intend to create in: the scratch Riddle is built inside it, and anything project-scoped the build REFERENCES (a Form behind FormSelect, a tag, a project ad slot) is only resolvable from there. Without it such a build is evaluated in the personal project and rejected with \"You are not authorized to access Form <UUID>\" - a scope problem wearing a permissions error, and a false negative for a build a real create would accept. NULL means the personal project, omitted the currently selected one. Ignored by editing entries ({UUID, build}), always evaluated in their own Riddle's project."}},"additionalProperties":false}},{"name":"riddle_delete","description":"Deletes one or many Riddles. Only Riddles created through the riddle_builder_* tools or generated by the Riddle AI can be deleted - the ones a user built by hand in the Creator cannot, so check \"origin.apiManageable\" (riddle_get's context.origin, or riddle_list's \"origin\") beforehand rather than learning it from the error. 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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":["null","string"],"description":"The UUID of the single Riddle you want to delete. Pass either this or UUIDs. Only Riddles created via the Riddle Builder tools or generated by the Riddle AI can be deleted.","default":null},"UUIDs":{"type":["array","null"],"description":"Several Riddles to delete at once, as Riddle UUID strings, e.g. [\"6FA740EW\", \"OllsevHa\"] (max 100). Pass either this or UUID. Same restriction as UUID: each one must have been created via the Riddle Builder or generated by the Riddle AI.","default":null,"items":{"type":"string","pattern":"^\\s*[A-Za-z0-9]{4,32}\\s*$"},"minItems":1,"maxItems":100},"dryRun":{"type":"boolean","description":"true reports whether each Riddle could be deleted and deletes nothing. Works for one UUID and for UUIDs.","default":false}},"oneOf":[{"required":["UUID"],"properties":{"UUID":{"not":{"type":"null"}},"UUIDs":{"const":null}}},{"required":["UUIDs"],"properties":{"UUIDs":{"not":{"type":"null"}},"UUID":{"const":null}}}],"additionalProperties":false}},{"name":"riddle_get","description":"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 riddle_delete/riddle_builder_update/palette_customize work on this Riddle), 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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":["null","string"],"description":"The UUID of the single Riddle you want to read. Pass either this or UUIDs.","default":null},"UUIDs":{"type":["array","null"],"description":"Several Riddles to read at once, as Riddle UUID strings, e.g. [\"6FA740EW\", \"OllsevHa\"]\n(max 20 - lower than the other bulk tools, since every entry is a full envelope rather\nthan compact state). Either this or UUID; rejected together with includeRiddleData.\nEntries are read in the order given and, once the response would exceed the inline size\nbudget, the remaining ones come back as compact state with \"truncated\": true instead -\nso put the Riddles you need in full first. See riddle://reference/concepts/bulk.","default":null,"items":{"type":"string","pattern":"^\\s*[A-Za-z0-9]{4,32}\\s*$"},"minItems":1,"maxItems":20},"includeRiddleData":{"type":"boolean","description":"Additionally returns the full stored Riddle payload (content, settings, preset\nmerge/diff, ...) under \"riddle\" - tens of KB, only for inspecting raw stored\ndata. Not allowed together with UUIDs.","default":false},"omit":{"type":"array","description":"Leaves whole sections out: \"build\", \"warnings\", \"nextBlockId\", \"published\", \"context\"\n(identity fields always stay). The one omission that materially shrinks a response is\n\"published\" on a Riddle with unpublished changes - a second full build configuration.\nWhatever you leave out is echoed in \"omittedFields\", so a missing key never reads as\n\"this Riddle has none of that\". Details: riddle://reference/concepts/warnings.","default":null,"items":{"type":"string","enum":["build","warnings","nextBlockId","published","context"]},"uniqueItems":true},"omittedDefaults":{"type":"boolean","default":null,"description":"Whether every block should additionally carry its \"omittedDefaults\" map: the properties it left at that block type's default, with the value each is at. Off by default - measured, those maps are 85-90% of a read-back, and riddle://reference/block-defaults/<block type> states the same defaults without a Riddle in hand. Ask for them only to learn what THIS Riddle left at its default, and read them, never resend them."}},"oneOf":[{"required":["UUID"],"properties":{"UUID":{"not":{"type":"null"}},"UUIDs":{"const":null}}},{"required":["UUIDs"],"properties":{"UUIDs":{"not":{"type":"null"}},"UUID":{"const":null}}}],"additionalProperties":false}},{"name":"riddle_get_embed_code","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":"string","description":"The UUID of the Riddle you want to retrieve the HTML embed code for"}},"required":["UUID"],"additionalProperties":false}},{"name":"riddle_list","description":"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 personal project. 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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"scope":{"type":"string","description":"What to list: \"project\" (default) for the Riddles of a single project, \"account\" for every Riddle of the account at once.","default":null,"enum":["project","account"]},"projectId":{"type":["null","integer"],"description":"Filter by project ID; omit or null for the authenticated user's personal project. Scope \"project\" only.","default":null},"type":{"type":["array","null"],"description":"Filter by Riddle type (array of strings). Valid values: \"Quiz\", \"Poll\", \"Form\", \"Personality\", \"Predictor\", \"Minigame\", \"Leaderboard\", \"Placeholder\", \"Story\". Omit to include all types.","default":null},"notType":{"type":["array","null"],"description":"Exclude specific Riddle types (array of strings, same valid values as type). Omit to exclude nothing. Scope \"project\" only - the account list has no exclusion filter.","default":null},"tags":{"type":["array","null"],"description":"Filter by tag IDs (array of integers). Omit to ignore tags.","default":null},"status":{"type":["null","string"],"description":"Filter by status: \"published\", \"modified\", or \"draft\". Omit to include all statuses.","default":null},"search":{"type":["null","string"],"description":"Search term to filter Riddles by title","default":null},"origin":{"type":["null","string"],"description":"Filter by how the Riddle was created: \"api\" (Riddle Builder API or Riddle AI, so\nriddle_delete/riddle_builder_update/palette_customize work on it) or \"manual\" (built by\nhand in the Creator, where those three are rejected). Omit for both. A filter value\nonly - the \"origin\" returned per Riddle is {builder, aiGenerated, apiManageable}.","default":null},"sortBy":{"type":["null","string"],"description":"Sort field: \"created\", \"published\", or \"modified\"","default":null},"sortOrder":{"type":["null","string"],"description":"Sort direction: \"ASC\" or \"DESC\"","default":null},"page":{"type":["null","integer"],"description":"Page number, 1-indexed, 12 Riddles per page (default: 1). Zero or negative is rejected with a VALIDATION_ERROR rather than clamped.","default":null}},"additionalProperties":false}},{"name":"riddle_move","description":"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 (the move check - a mode of this tool, not a tool of its own): 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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"projectId":{"type":["null","integer","string"],"default":null,"description":"Where to move the Riddles: the project (team) ID to move them into, as returned by project_list, or the string \"personal\" for your personal project. There is no default destination - omit this and nothing is moved, the call reports the read-only movability check instead.","anyOf":[{"type":"integer","minimum":1,"description":"A project (team) ID from project_list."},{"type":"string","const":"personal","description":"Move the Riddles into the personal project."}]},"UUID":{"type":["null","string"],"description":"The UUID of the single Riddle you want to move or check. Pass either this or UUIDs.","default":null},"UUIDs":{"type":["array","null"],"description":"Several Riddles to move or check at once, as Riddle UUID strings, e.g. [\"6FA740EW\", \"OllsevHa\"] (max 100). Pass either this or UUID. For a move, this must be the complete set of Riddles that have to move together.","default":null,"items":{"type":"string","pattern":"^\\s*[A-Za-z0-9]{4,32}\\s*$"},"minItems":1,"maxItems":100},"dryRun":{"type":"boolean","description":"true runs the movability check against the given projectId and changes nothing. Pointless without projectId, which already only checks.","default":false}},"oneOf":[{"required":["UUID"],"properties":{"UUID":{"not":{"type":"null"}},"UUIDs":{"const":null}}},{"required":["UUIDs"],"properties":{"UUIDs":{"not":{"type":"null"}},"UUID":{"const":null}}}],"additionalProperties":false}},{"name":"riddle_publish","description":"Publishes one or many Riddles, or takes them off the web again with publish: false - unpublishing is this tool, there is no separate unpublish tool. A published Riddle is live at https://www.riddle.com/view/[RIDDLEID]. 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 and never giving up early, so a Riddle that cannot be (un)published is reported as that entry's \"error\" while 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 always allowed 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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":["null","string"],"description":"The UUID of the single Riddle you want to publish or unpublish. Pass either this or UUIDs.","default":null},"UUIDs":{"type":["array","null"],"description":"Several Riddles to publish or unpublish at once, as Riddle UUID strings, e.g. [\"6FA740EW\", \"OllsevHa\"] (max 100). Pass either this or UUID.","default":null,"items":{"type":"string","pattern":"^\\s*[A-Za-z0-9]{4,32}\\s*$"},"minItems":1,"maxItems":100},"publish":{"type":"boolean","description":"true (the default) publishes the Riddles, false takes them off the web again.","default":true},"omit":{"type":"array","description":"Same \"omit\" parameter as riddle_get's, applied to the single-UUID response envelope (see riddle_get for the full description). Has no effect on the bulk (UUIDs) response, which never carries a build configuration to begin with.","default":null,"items":{"type":"string","enum":["build","warnings","nextBlockId","published","context"]},"uniqueItems":true}},"oneOf":[{"required":["UUID"],"properties":{"UUID":{"not":{"type":"null"}},"UUIDs":{"const":null}}},{"required":["UUIDs"],"properties":{"UUIDs":{"not":{"type":"null"}},"UUID":{"const":null}}}],"additionalProperties":false}},{"name":"riddle_rename","description":"Renames a Riddle with the specified UUID. Returns only the confirmation of the change (uuid, title, modified state), not the entire Riddle.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":"string","description":"The UUID of the Riddle you want to rename"},"title":{"type":"string","description":"The new title for the Riddle"}},"required":["UUID","title"],"additionalProperties":false}},{"name":"riddle_tag","description":"Puts a tag on Riddles or takes it off again, by \"action\" - ONE assignment per Riddle, never the tag itself (riddle_tag_list reads the labels of a project, riddle_tag_delete removes one). \"add\" 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 needs no publish. \"remove\" takes the tag off again 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, unless a question bank still uses it - so a tag rarely has to be deleted by hand. 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. Both actions return 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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"action":{"type":"string","description":"What to do with the tag on those Riddles: \"add\" it or \"remove\" it. Each takes UUID or UUIDs and tagName or tagId.","enum":["add","remove"]},"UUID":{"type":["null","string"],"description":"The UUID of the single Riddle to tag or untag. Pass either this or UUIDs.","default":null},"UUIDs":{"type":["array","null"],"description":"Several Riddles to tag or untag at once, as Riddle UUID strings, e.g. [\"6FA740EW\", \"OllsevHa\"] (max 100). Pass either this or UUID.","default":null,"items":{"type":"string","pattern":"^\\s*[A-Za-z0-9]{4,32}\\s*$"},"minItems":1,"maxItems":100},"tagName":{"type":["null","string"],"description":"The name of the tag. On \"add\" the Riddle\\'s project gets the tag of that name, created if it has none yet; on \"remove\" nothing is ever created and a name the project does not have is an error. Pass either this or tagId.","default":null},"tagId":{"type":["null","integer"],"description":"The ID of an existing tag, as returned by riddle_tag_list. Pass either this or tagName.","default":null}},"required":["action"],"allOf":[{"oneOf":[{"required":["UUID"],"properties":{"UUID":{"not":{"type":"null"}},"UUIDs":{"const":null}}},{"required":["UUIDs"],"properties":{"UUIDs":{"not":{"type":"null"}},"UUID":{"const":null}}}]},{"oneOf":[{"required":["tagName"],"properties":{"tagName":{"not":{"type":"null"}},"tagId":{"const":null}}},{"required":["tagId"],"properties":{"tagId":{"not":{"type":"null"}},"tagName":{"const":null}}}]}],"additionalProperties":false}},{"name":"riddle_tag_delete","description":"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 action \"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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"tagId":{"type":"integer","description":"The ID of the tag to delete, as returned by riddle_tag_list. The only way to name it: a tag is deleted by id, not by name."},"projectId":{"type":["null","integer"],"description":"The project (team) ID the tag belongs to, as returned by project_list. Omit for the project the API key is scoped to, or your personal tags on a user API key.","default":null}},"required":["tagId"],"additionalProperties":false}},{"name":"riddle_tag_list","description":"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 is 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(action: \"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"projectId":{"type":["null","integer"],"description":"The project (team) ID, as returned by project_list. Omit for the project the API key is scoped to, or your personal tags on a user API key.","default":null}},"additionalProperties":false}},{"name":"riddle_template_create","description":"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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"UUID":{"type":"string","description":"The UUID of the Riddle to store as a template, e.g. \"6FA740EW\"."},"title":{"type":["null","string"],"description":"Title for the template. Omit to keep the Riddle's own title.","default":null},"isQuickCreate":{"type":"boolean","description":"Whether the template also shows up in the Creator's \"quick create\" list, i.e. among the starting points offered when creating a new Riddle. Defaults to false.","default":false}},"required":["UUID"],"additionalProperties":false}},{"name":"riddle_template_get","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"id":{"type":"integer","description":"The ID of the template, as returned by riddle_template_list or riddle_template_public_list."},"includeBuildConfig":{"type":"boolean","description":"Whether to read the template as a build configuration. Defaults to true, which is the point of this tool; false returns only the template's metadata, a far smaller response.","default":true},"omittedDefaults":{"type":["null","boolean"],"description":"Whether every block additionally carries its \"omittedDefaults\" map - the properties it left out for still being at that block type's default, with the value each is at. Off by default: those maps measure 85-90% of the response, and riddle://reference/riddle-defaults/<riddle type> plus riddle://reference/block-defaults/<block type> state the same defaults without a template in hand. Pass true only to learn what THIS template left at its default - to read, never to resend.","default":null}},"required":["id"],"additionalProperties":false}},{"name":"riddle_template_list","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"type":{"type":["null","string"],"description":"Only return templates of this Riddle type: \"Quiz\", \"Poll\", \"Form\", \"Personality\", \"Predictor\", \"Minigame\", \"Leaderboard\", \"Story\" or \"Placeholder\". Omit to get all of them.","default":null},"projectId":{"type":["null","integer"],"description":"The project (team) ID whose templates you want, as returned by project_list. Omit to use the project the API key is scoped to (your personal templates for a user API key).","default":null}},"additionalProperties":false}},{"name":"riddle_template_public_list","description":"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).","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"type":{"type":["null","string"],"description":"Only return templates of this Riddle type: \"Quiz\", \"Poll\", \"Form\", \"Personality\", \"Predictor\", \"Minigame\", \"Leaderboard\", \"Story\" or \"Placeholder\". Omit to get all of them.","default":null},"category":{"type":["null","string"],"description":"Only return templates of this category (the tag public templates are grouped under), a snake_case slug such as \"audience_research\" or \"feedback_surveys\" - not a display name. Omit to get every category, which is also how you find out which ones exist.","default":null}},"additionalProperties":false}},{"name":"riddle_template_use","description":"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. 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.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"id":{"type":"integer","description":"The ID of the template to create a Riddle from, as returned by riddle_template_list or riddle_template_public_list."},"title":{"type":["null","string"],"description":"Title for the new Riddle. Omit to keep the template's own title.","default":null},"projectId":{"type":["null","integer"],"description":"The project (team) ID to create the Riddle in, as returned by project_list. Omit to use the project the API key is scoped to (your personal project for a user API key).","default":null},"omit":{"type":"array","description":"Same \"omit\" parameter as riddle_get's, applied to the returned envelope (see riddle_get for the full description): sections of it to leave out, \"published\" being the one that still moves a copy of a template materially. The per-block default maps are not in this response at all - they are opt-in through riddle_get/riddle_template_get and this tool has no such parameter.","default":null,"items":{"type":"string","enum":["build","warnings","nextBlockId","published","context"]},"uniqueItems":true}},"required":["id"],"additionalProperties":false}},{"name":"stats_fetch","description":"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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"view":{"type":"string","description":"How deeply to measure: \"totals\" (one aggregate for the period), \"timeseries\" (that aggregate per consecutive interval, requires dateFrom and dateTo) or \"breakdown\" (the per-block detail, or the per-Riddle summary for namespace \"user\").","enum":["totals","timeseries","breakdown"]},"namespace":{"type":"string","description":"Entity type to fetch stats for.","enum":["project","user","riddle"]},"entityId":{"type":["null","string"],"description":"ID of the entity: a project ID (integer as string) for \"project\", a user ID (integer as string) for \"user\", a Riddle UUID for \"riddle\". Get them from project_list, whoami (\"user\", not the string \"user\") and riddle_list respectively. Required for every view except the account-wide summary (view \"breakdown\" with namespace \"user\"), which measures the API key's own account and rejects it.","default":null},"dateFrom":{"type":["null","string"],"description":"Start date (inclusive), format: YYYY-MM-DD, e.g. \"2026-01-01\". Required for \"timeseries\". On \"totals\" and a Riddle/project \"breakdown\", omit both dates for alltime and pass only one for an open-ended range; the account-wide summary and the project breakdown instead default to one CALENDAR month before today - the same default the Creator's dashboards use, so 29 to 32 days depending on the month, not exactly 30. Send it explicitly when the exact span matters.","default":null},"dateTo":{"type":["null","string"],"description":"End date (inclusive), format: YYYY-MM-DD, e.g. \"2026-01-31\". Same rules as dateFrom; where a default applies it is today.","default":null},"projectIds":{"type":["array","null"],"description":"Narrow the account-wide summary to these project IDs (array of integers, from project_list). Omit to cover the whole account, including the personal project. Passing a project the user cannot view stats of is an error, not a silently skipped project. That view only.","default":null},"page":{"type":["null","integer"],"description":"Page number of the account-wide summary, 1-indexed (default: 1). Zero or negative is rejected with a VALIDATION_ERROR, not clamped to page 1 - the same contract as riddle_list/project_list. That view only.","default":null},"sortBy":{"type":"string","description":"Metric the account-wide summary's Riddles are ranked by before the page is cut: \"views\" (default), \"starts\", \"submissions\" or \"timeActive\". Sorting always spans the whole account (or the projects given), so page 1 is genuinely the top of the account. That view only.","default":null,"enum":["views","starts","submissions","timeActive"]}},"required":["view","namespace"],"additionalProperties":false}},{"name":"whoami","description":"Reports the current API key: the authenticated user (\"userEmail\"/\"userName\", the latter null if never set), the project/team on a project-scoped key, 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.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{},"additionalProperties":false}}],"scan":{"score":60,"grade":"C","scanned_at":"2026-09-22T11:51:50.855Z","report":{"scannerVersion":"0.1.9","scannedAt":"2026-09-22T11:51:50.799Z","components":{"code":{"score":-1,"max":25,"notes":["remote-only server, no package to scan"]},"reliability":{"score":20,"max":20,"notes":["remote reachable in 1550ms"]},"poisoning":{"score":15,"max":15,"notes":["38 tool descriptions checked"]},"auth":{"score":3,"max":15,"notes":["open endpoint exposes 12 write-action tools with no auth"]},"maintenance":{"score":3,"max":15,"notes":["no repository listed"]},"identity":{"score":4,"max":10,"notes":["verified namespace with website, no repo"]}},"findings":[{"id":"auth.open-write","severity":"high","component":"auth","title":"Write-action tools reachable without authentication"},{"id":"maint.no-repo","severity":"low","component":"maintenance","title":"No source repository listed"}],"inputs":{"probes":[{"url":"https://www.riddle.com/creator/api/v3/mcp","reachable":true,"authRequired":false,"latencyMs":1550,"serverInfo":{"name":"Riddle Creator MCP","version":"1.0"}}],"packages":[],"repo":{"found":false},"icon":{"url":"https://cdn.riddle.com/website/favicon.png","source":"registry","width":196,"height":196},"presence":{"stars":null,"forks":null,"downloadsWeek":null,"license":null,"lastPushAt":null,"score":8}}}},"grade_history":[],"reviews":[]}