{"serverId":"mcp-registry/com.neblla/neblla","tools":[{"name":"neblla_archive_app","fixed":{"name":"neblla_archive_app","description":"Archive an app nobody uses: its data is bundled onto the CDN and the active records are cleared from Neblla's database. It stays in the gallery and is restored automatically the next time anyone opens it (first open is slower). Neblla also does this on its own for free apps with no developer or user activity for 30 days; apps with an active subscription are exempt.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":173,"tokensAfter":173},{"name":"neblla_ban_settings","fixed":{"name":"neblla_ban_settings","description":"Read the message a banned user sees in this app, and the one a terminated user sees, or change them with `bannedMessage` / `terminatedMessage` (\"\" puts Neblla's default back). A banned user reads it above a form to write to you; a terminated one has no form, so its message should not point at one.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"bannedMessage":{"type":"string","maxLength":4000,"description":"New message shown to banned users"},"terminatedMessage":{"type":"string","maxLength":4000,"description":"New message shown to terminated users"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":213,"tokensAfter":213},{"name":"neblla_booking","fixed":{"name":"neblla_booking","description":"Bookings (business template), the administration side: action \"createSlot\" { set, start, end?, capacity, title?, repeat? } — repeat is a calendar rule (the usual RFC 5545 RRULE): { freq: \"daily\"|\"weekly\"|\"monthly\"|\"yearly\", interval?, byDay? ([\"MO\",\"WE\"]; monthly \"2TU\" = second Tuesday, \"-1FR\" = last Friday), byMonthDay? (15, -1 = last day), byMonth?, until? (a date: that whole day) or count?, except? (dates left out), timeZone? (IANA: keeps the local hour when the clocks change; left out, the z","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","action","set"],"properties":{"to":{"type":"string"},"end":{"type":"string"},"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"\"pro\" (the published app, default) or \"pre\" (its draft: pre's services and config, on pre's data, its own unless it plays with pro's). Try the business in pre before publishing it. The pre id (_<id>) as appId is the same as env \"pre\""},"min":{"anyOf":[{"type":"integer"},{"type":"null"}]},"set":{"type":"string"},"from":{"type":"string"},"site":{"type":["string","null"]},"slot":{"type":"string"},"step":{"type":"integer"},"text":{"type":"string","description":"chatPost: what the business writes"},"appId":{"type":"string"},"limit":{"type":"integer"},"scope":{"enum":["one","following","all"],"type":"string"},"staff":{"type":["string","null"]},"start":{"type":"string"},"title":{"type":"string"},"action":{"enum":["createSlot","updateSlot","deleteSlot","series","slots","list","cancel","move","cancelSession","times","chat","chatPost"],"type":"string"},"before":{"type":"string"},"reason":{"type":"string"},"refund":{"type":"boolean"},"repeat":{"anyOf":[{"type":"string"},{"type":"object","properties":{"freq":{"enum":["daily","weekly","monthly","yearly"],"type":"string"},"wkst":{"type":"string"},"byDay":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"count":{"type":"integer"},"rrule":{"type":"string"},"until":{"type":"string"},"except":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"weekly":{"type":"boolean","const":true},"byMonth":{"anyOf":[{"type":"integer"},{"type":"array","items":{"type":"integer"}}]},"interval":{"type":"integer"},"timeZone":{"type":"string"},"byMonthDay":{"anyOf":[{"type":"integer"},{"type":"array","items":{"type":"integer"}}]}},"additionalProperties":false}]},"series":{"type":"string"},"toSlot":{"type":"string"},"booking":{"type":"string"},"capacity":{"type":"integer"},"resource":{"type":["string","null"]},"skipBusy":{"type":"boolean"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":745,"tokensAfter":745},{"name":"neblla_broadcast","fixed":{"name":"neblla_broadcast","description":"Write to ALL the users of an app, or to the members of one group (optionally only one role in it): the message lands in each one's inbox as a message from the developer (neblla.inbox; a connected user gets it live, inbox.on('updated') with action \"created\"). It is paid per recipient from the app's daily quota, like any message (msgFixedOut + the bytes of message and data, kind newCommunication). action \"estimate\" { message, data?, group?, role? } says beforehand what it will cost: recipients, cr","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","action"],"properties":{"id":{"type":"string","description":"The send (status, resume)"},"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"\"pro\" (the app's users, default) or \"pre\" (its draft's)"},"data":{"description":"Optional JSON payload, as neblla_send_communication","type":"string"},"role":{"type":"string","description":"With group: only the members with this role"},"appId":{"type":"string","description":"The app public ID"},"group":{"type":"string","description":"Only the members of this group (neblla_group); omit for every user"},"action":{"enum":["estimate","send","status","list","resume"],"type":"string"},"message":{"type":"string","maxLength":5000,"description":"Plain-text message (estimate, send)"}},"additionalProperties":false}},"changes":[{"kind":"schema","detail":"Added \"type\":\"string\" to untyped parameters: data. Change to number/boolean/array where that's what you accept.","verify":true}],"tokensBefore":394,"tokensAfter":398},{"name":"neblla_business","fixed":{"name":"neblla_business","description":"The places and people of a business (C200), the administration side: what its bookings use. kind \"sites\" (a place: { name, timeZone?, address?, hours? }), \"resources\" (a group of identical units: { name, site?, count (how many: 30 tables, 200 spaces; 1), capacity (how many fit in one; 1), hours? }) and \"staff\" (the professionals: { name, email?, sites? ([ids] where they work), hours? }). hours: { timeZone?, week: { MO: [\"09:00-14:00\", \"16:00-20:00\"], … } (a day left out is closed; a professional","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","action"],"properties":{"id":{"type":"string"},"to":{"type":"string"},"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"\"pro\" (the published app, default) or \"pre\" (its draft: pre's services and config, on pre's data, its own unless it plays with pro's). Try the business in pre before publishing it. The pre id (_<id>) as appId is the same as env \"pre\""},"who":{"type":"string"},"date":{"type":"string"},"from":{"type":"string"},"kind":{"enum":["sites","resources","staff"],"type":"string"},"name":{"type":"string"},"note":{"type":"string"},"site":{"type":["string","null"]},"text":{"type":"string"},"user":{"type":"string","description":"The user id in the app (userBookings, userNotes, userNote)"},"appId":{"type":"string"},"count":{"type":"integer"},"email":{"type":["string","null"]},"hours":{"type":"string"},"sites":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"action":{"enum":["list","save","remove","accounts","agenda","note","notes","removeNote","userBookings","userNotes","userNote","removeUserNote"],"type":"string"},"amount":{"type":"integer"},"address":{"type":["string","null"]},"capacity":{"type":"integer"},"timeZone":{"type":["string","null"]}},"additionalProperties":false}},"changes":[{"kind":"schema","detail":"Added \"type\":\"string\" to untyped parameters: hours. Change to number/boolean/array where that's what you accept.","verify":true}],"tokensBefore":526,"tokensAfter":530},{"name":"neblla_create_app","fixed":{"name":"neblla_create_app","description":"Create a new Neblla app. Returns the app ID (appId) to use in subsequent file upload operations. AI BUILDS IT IN MINUTES, NEBLLA KEEPS IT ALIVE FOR YEARS: you write the app, and Neblla is the backend and the back office of their game, app or shop, everything that would otherwise need a server of their own — accounts that work across all of their apps, saved per-user data, payments, content their users publish, AI, and real-time rooms when more than one device is involved. It is NOT only for mult","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["name"],"properties":{"name":{"type":"string","minLength":1,"description":"Display name for the app"},"template":{"type":"string","description":"Legacy alternative to `decisions`: scaffold the canonical files of this page (rooms: the one room example, where the server sets the state and the clients paint it and talk through message; lobby-only; solo-save; home), with no feature pieces and no theme. Prefer `decisions`. Omit both for a blank app."},"acceptDpa":{"type":"boolean","description":"The developer accepts Neblla's terms of use and data processing agreement (DPA), together. Asked once, before their first app (and again when its version changes): the call answers error \"dpa_required\" with the links — show them to the developer and, only if THEY accept, call again with acceptDpa: true. Never accept on their behalf."},"decisions":{"type":"object","description":"The COMPLETE decision set from neblla_validate_requirements (have_enough_context: true → pass its `decisions` verbatim). Scaffolds the matching pattern with the themed UI pieces for each enabled feature AND writes the app config (its neblla.* modules, connections, serverMode, rooms policy, matchmaking mode, AI payer, theme). The recommended way to create any non-blank app.","additionalProperties":{}},"projectId":{"type":"string","description":"The project the app goes to (neblla_projects list). Omitted: the account's Explorer, where apps land by default."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":587,"tokensAfter":587},{"name":"neblla_create_hook","fixed":{"name":"neblla_create_hook","description":"Make an incoming webhook: a URL a third party (Twitch, a payment provider, n8n…) calls with a JSON body, and Neblla hands that body to a room — its server file hears neblla.room.on(\"hook\", ({ name, data }) => …) on the Fogs — or, with to: \"user\", to the user named in the call (?user=<their id in the app>). The URL carries a token shown only now; making it again with the same name gives a new token (the old URL stops working). Up to 20 per app; 64 KB per call; 60 calls a minute per hook. Answers:","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name","to"],"properties":{"to":{"enum":["room","user"],"type":"string","description":"Who gets it: a room (its server file) or the user named in each call"},"name":{"type":"string","description":"Hook name: a letter, then letters, digits, _ or - (≤ 40)"},"room":{"type":"string","description":"The room, when to is \"room\""},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":298,"tokensAfter":298},{"name":"neblla_create_product","fixed":{"name":"neblla_create_product","description":"Create a purchasable product for a Neblla app. type must be \"one_time\" (single purchase, fixed price), \"consumable\" (quantity selector, price per unit) or \"subscription\" (renews every month or year by itself: price per period, interval, optional trialDays; the user manages it with neblla.shop.manage). name should be a multi-language object e.g. { en: \"Gold Coins\", es: \"Monedas de Oro\" }. price (one_time, subscription) and pricePerUnit (consumable) are in cents (e.g. 499 = $4.99). A product is a ","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name","type"],"properties":{"max":{"type":"number","description":"Maximum quantity for consumable"},"min":{"type":"number","description":"Minimum quantity for consumable"},"name":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{"type":"string"}}],"description":"Product name, string or { en: \"...\", es: \"...\" }"},"step":{"type":"number","description":"Quantity step for consumable selector"},"type":{"enum":["one_time","consumable","subscription"],"type":"string"},"appId":{"type":"string","description":"The app public ID"},"image":{"type":"string","description":"Product image URL"},"price":{"type":"number","description":"Price in cents — required for one_time, and per period for subscription"},"currency":{"type":"string","pattern":"^[a-zA-Z]{3}$","description":"ISO 4217 code Stripe charges in, e.g. \"usd\" (default eur). Prices are in its smallest unit: cents for most, whole units for zero-decimal ones (jpy, krw…), thousandths ending in 0 for bhd, jod, kwd, omr, tnd; a price under Stripe's minimum charge in that currency (0.50 EUR/USD…) is refused"},"interval":{"enum":["month","year"],"type":"string","description":"subscription: how often it renews"},"published":{"type":"boolean","description":"Buyable right away (default true). Without an active payout account it waits, hidden, and publishes by itself once the account is active (connectUrl opens the onboarding)."},"trialDays":{"type":"integer","maximum":730,"minimum":0,"description":"subscription: free trial days (optional)"},"defaultLang":{"enum":["en","es","fr","de","it","pt","ja","ko","zh","ar"],"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{"type":"string"}}],"description":"Description, string or per-language object"},"pricePerUnit":{"type":"number","description":"Price per unit in cents — required for consumable"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":714,"tokensAfter":714},{"name":"neblla_create_room","fixed":{"name":"neblla_create_room","description":"Create a predefined room (it exists before anyone joins). Name: A-Z 0-9 _ - (stored uppercase); LOBBY is reserved. `permanent: true` keeps it (and its data) when it empties. `ttlInactivity` gives it its own inactivity time (days, 0 = never deleted); without it the app's value for public rooms applies.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Room name"},"appId":{"type":"string","description":"The app public ID"},"permanent":{"type":"boolean","description":"Keep the room when empty"},"moderators":{"type":"array","items":{"type":"string"},"description":"Per-app user ids that moderate this room"},"ttlInactivity":{"type":"integer","maximum":3650,"minimum":0,"description":"Days without activity before Neblla deletes this room (0 = never). Overrides the app's per-type value (rooms.public/private.ttlInactivity: 30 / 90 by default) for this room only."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":294,"tokensAfter":294},{"name":"neblla_delete_account","fixed":{"name":"neblla_delete_account","description":"This is the DEVELOPER own Neblla account, not an end user of their app — to sanction a user use neblla_set_ban_status instead. For safety the MCP never deletes accounts directly. Returns a URL for the user to visit and confirm deletion from the Neblla dashboard. Deleting it deletes every app of theirs as neblla_delete_app does (users, saves, purchases, subscriptions, domains) and their Neblla billing in Stripe; their own Stripe account and Play listings stay theirs to close. Make sure they know ","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","properties":{}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":181,"tokensAfter":181},{"name":"neblla_delete_api_secret","fixed":{"name":"neblla_delete_api_secret","description":"Delete an API secret; the app's calls to it answer secret_not_found from then on.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Secret name"},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":112,"tokensAfter":112},{"name":"neblla_delete_app","fixed":{"name":"neblla_delete_app","description":"PERMANENTLY delete an app and all its files. This is the most destructive call in this server and it cannot be undone: the files, the version history, the room data, the community entries and the users' saved data all go. Only call it when the developer has asked for that specific app to be deleted, in as many words. Before you do: get their code out (neblla_versions migrate, or neblla_git) and remember paying customers lose their entitlements, so settle any refunds in Stripe first. Neblla itsel","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":210,"tokensAfter":210},{"name":"neblla_delete_automation","fixed":{"name":"neblla_delete_automation","description":"Delete an automation.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Automation name"},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":97,"tokensAfter":97},{"name":"neblla_delete_communication","fixed":{"name":"neblla_delete_communication","description":"Permanently remove a communication from a user's thread. The app receives `communicationUpdated` with `action: \"deleted\"` in real time, so its UI can drop the message.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","commId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"commId":{"type":"string","description":"Communication id to delete"},"userId":{"type":"string","description":"User's mongo ObjectId hex string"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":166,"tokensAfter":166},{"name":"neblla_delete_community_entry","fixed":{"name":"neblla_delete_community_entry","description":"Withdraw an entry of your app's community for good: the entry, its payload, its reviews and favorites are deleted and its bytes freed (entries hanging from it stay, without their parent). This is not moderation — no reason, no record, nothing counts against the author. To take down a user's entry for breaking the rules, use neblla_moderate_community_entry (remove) instead.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","entryId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"entryId":{"type":"string","description":"The entry id"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":197,"tokensAfter":197},{"name":"neblla_delete_file","fixed":{"name":"neblla_delete_file","description":"Delete a single file from an app (in an app linked to its own repo, a commit on pre's branch that drops it).","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","path"],"properties":{"path":{"type":"string","description":"File path to delete, e.g. \"old-page.html\""},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":127,"tokensAfter":127},{"name":"neblla_delete_hook","fixed":{"name":"neblla_delete_hook","description":"Delete an incoming webhook; its URL answers hook_not_found from then on.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Hook name"},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":107,"tokensAfter":107},{"name":"neblla_delete_product","fixed":{"name":"neblla_delete_product","description":"Permanently delete a product from a Neblla app.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["productId"],"properties":{"productId":{"type":"string","description":"The product ID returned by neblla_create_product"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":96,"tokensAfter":96},{"name":"neblla_delete_room","fixed":{"name":"neblla_delete_room","description":"Delete a persistent room and its stored data. If people are inside it right now the session continues and the room disappears when it empties. LOBBY cannot be deleted. Irreversible — confirm with the developer first.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Room name"},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":147,"tokensAfter":147},{"name":"neblla_delete_room_message","fixed":{"name":"neblla_delete_room_message","description":"Delete (soft) one chat message from a room, as the developer. Users see it removed.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","messageId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"messageId":{"type":"string","description":"Message id from neblla_get_room_history"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":124,"tokensAfter":124},{"name":"neblla_export_users","fixed":{"name":"neblla_export_users","description":"Export the users of an app WITH everything the app holds about them: the full record (saved data, public info, communications, stats, language, the email if it was shared) and their purchase balances. For a developer taking their app data with them. Paginated by cursor: pass the `next` you got back as `after` until it is null. The cross-app Neblla account id is not included — only the per-app `_id`.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"after":{"type":"string","description":"The `next` cursor from the previous page"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","default":200,"maximum":500,"minimum":1}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":280,"tokensAfter":280},{"name":"neblla_get_activity_log","fixed":{"name":"neblla_get_activity_log","description":"The app's activity log, the last 90 days, newest first: who changed what and when — the developer from the panel, their team, and you through this MCP (via \"mcp\", which the panel shows as \"from Claude\"): settings, publishing, files, the team and who opens pre, products, the app's people, moderation, the back office screens. Lines { id, at, by (an email), who: owner | team | moderator, access? (the paper of a team member: partner | support | viewer | screens | staff), via: panel | mcp | app, area","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"by":{"type":"string","description":"Only lines by this email"},"via":{"enum":["panel","mcp","app"],"type":"string","description":"Only lines from here: panel, mcp (you) or app (the moderators)"},"area":{"enum":["settings","publish","files","team","products","people","moderation","panels"],"type":"string","description":"Only this area"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":200,"minimum":1,"description":"Lines per page (default 50)"},"before":{"type":"string","description":"The `next` of the previous page (or an ISO date: lines before it)"},"moderation":{"type":"boolean","description":"false: without the moderators' acts from the moderation log"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":394,"tokensAfter":394},{"name":"neblla_get_app","fixed":{"name":"neblla_get_app","description":"Everything about ONE app: its full config and state (features, and `modules`: the neblla.* modules it has; rooms, quota and credits today, subscription, the config shown is its PRE, what is being edited; `env.pro.promotedAt` says when pro last changed, and neblla_promote \"diff\" what differs; gallery, community, ai…), every URL it answers at (by id and slug on each Neblla host and its Cloudflare deploy), the custom domains registered (the allowed origins added with addDomain — Neblla does not ser","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":210,"tokensAfter":210},{"name":"neblla_get_app_url","fixed":{"name":"neblla_get_app_url","description":"Returns the canonical public URL for a Neblla app in the gallery. ALWAYS call this after neblla_create_app — never construct the URL yourself. Two links: `pre` is the private link where the developer tries what is being worked on (give it to the developer only), and `url` is what the users open, at <slug or id>.neblla.app (or the app's own domain); it serves the app once neblla_promote has sent pre to pro (`promoted` says whether that has happened). The slug can be renamed with neblla_set_slug. ","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":210,"tokensAfter":210},{"name":"neblla_get_community_config","fixed":{"name":"neblla_get_community_config","description":"The app's resolved community configuration (mode, who publishes, limits, categories, content types, report threshold, recidivism, prescreen, downvotes) plus how much of its community storage cap is used, and `platformHold` ({ at, reason }) when Neblla has put the whole community on hold.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":154,"tokensAfter":154},{"name":"neblla_get_community_entry","fixed":{"name":"neblla_get_community_entry","description":"Fetch one community entry WITH its payload (the dev-defined JSON), whatever its status, plus its user reports (open count, count by reason, and each report with an opaque reporter id). Stats carry favorites, rating, reviews and votes (upvotes, downvotes, score = upvotes − downvotes; a banned user's votes do not count). Who voted is not shown.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","entryId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"appId":{"type":"string","description":"The app public ID"},"locale":{"type":"string","maxLength":16,"description":"Show title/summary in this language when the entry has it"},"entryId":{"type":"string","description":"The entry id"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":275,"tokensAfter":275},{"name":"neblla_get_file","fixed":{"name":"neblla_get_file","description":"Get the content of a specific file. Text files (HTML, CSS, JS, JSON, SVG, plain text) are ALWAYS returned as UTF-8 strings — never base64. Binary files (images, fonts, audio) are returned as base64. The response includes an `encoding` field (\"utf8\" or \"base64\") so you know how to interpret `content`. Always returns the real current source of the app's pre (what is being edited), including for apps you created or published in a previous session. Use this to resume editing an existing app across s","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","path"],"properties":{"path":{"type":"string","description":"File path, e.g. \"index.html\""},"appId":{"type":"string","description":"The app public ID"},"offset":{"type":"integer","default":0,"minimum":0,"description":"Where to start reading, in units of the returned `content` (characters for utf8 text, base64 characters for binary). 0 = start of file. Pass the previous response's `nextOffset` to get the next chunk."},"maxLength":{"type":"integer","default":20000,"maximum":65536,"minimum":1024,"description":"Maximum length of `content` to return in this chunk. The default (20000) stays comfortably under the response-size limit of MCP clients; lower it further if your client still truncates the reply."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":388,"tokensAfter":388},{"name":"neblla_get_llms","fixed":{"name":"neblla_get_llms","description":"Fetch the Neblla developer guide (llms.txt), split by topic so you read only what the app uses. Call this BEFORE designing or writing any code for a Neblla app. Two sections come first: `empezar` — whether Neblla is the right answer at all: the decision rule, why not to roll your own socket server, app shapes that fit, Neblla à la carte, and what it connects to. Then `core` (the default) — the canonical six-file structure, the events-only client/server contract, and the app shapes (rooms start f","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","properties":{"lang":{"enum":["es","en"],"type":"string","default":"es","description":"Language. The guide is written in Spanish and most chapters are also translated. \"en\" serves the English version of a section when one exists and falls back to the Spanish original otherwise, saying which in `langNote` — so it is always safe to ask for \"en\"."},"role":{"type":"string","description":"With section \"access\": only this role of the app"},"appId":{"type":"string","description":"An app's id: the guide cut to that app's modules (chapters and sections of modules it does not have are left out)."},"group":{"type":"string","description":"With section \"access\": only this group of the team"},"offset":{"type":"integer","default":0,"minimum":0,"description":"Character position to start reading from. 0 = start. Pass the previous response's `nextOffset` for the next chunk."},"section":{"enum":["index","full","access","empezar","core","salas","servidor","identidad","social","comunidad","pagos","ia","publicar","referencia","mcp"],"type":"string","default":"core","description":"Which part of the guide to read. Start with \"empezar\" (whether Neblla fits and what shape to give the app), then \"core\" — the mandatory contract, and the default. \"index\" = table of contents, \"full\" = the whole guide. \"access\" (with appId) = what each role and team group of that app can use."},"maxLength":{"type":"integer","default":20000,"maximum":65536,"minimum":1024,"description":"Maximum length of `guide` to return in this chunk. The default stays under the response-size limit of MCP clients."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":627,"tokensAfter":627},{"name":"neblla_get_logs","fixed":{"name":"neblla_get_logs","description":"The app's log while it runs, the last 7 days (30 with Pro; `keepDays` says which), the latest first, per environment (env \"pro\", the published app, by default; \"pre\", the draft): what to read first when something fails. Kinds: \"error\" — what the app's own code threw and nobody caught: on a page (source \"client\": client.js or another of its files; an uncaught promise too) or in a server file's handler on the room's server peer (source \"server\": lobby.js, gathering.js, room.js), with `file`, `line","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","description":"pro (the published app, default) or pre (the draft)"},"area":{"enum":["quota","data","ai","score"],"type":"string","description":"Only the refusals of this area"},"kind":{"enum":["error","refused","webhook","limit"],"type":"string","description":"Only this kind"},"room":{"type":"string","description":"Only what happened in this room"},"after":{"type":"string","description":"The `now` of an earlier answer (or an ISO date): only what happened since"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":200,"minimum":1,"description":"Lines per page (default 50)"},"before":{"type":"string","description":"The `next` of the previous page (or an ISO date)"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":407,"tokensAfter":407},{"name":"neblla_get_moderation_log","fixed":{"name":"neblla_get_moderation_log","description":"Read the app's moderation log, newest first: who named or removed a moderator (of the app or of a room), each ban, every room message, inbox message or community entry someone other than its author took down. Each line: { kind, by (\"dev:<email>\" or a moderator's per-app user id), target, room?, detail?, at }. `on` says whether the app keeps the log; it is off by default (neblla_update_app moderationLog: true).","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"kind":{"enum":["moderator_add","moderator_remove","room_moderators","ban","flag","message_delete","inbox_delete","community_moderate"],"type":"string","description":"Only lines of this kind"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":500,"minimum":1,"description":"Lines per page (default 100)"},"offset":{"type":"integer","minimum":0}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":280,"tokensAfter":280},{"name":"neblla_get_room_history","fixed":{"name":"neblla_get_room_history","description":"Read a room's chat history (newest first, paginated) for moderation. Returns the users' messages in clear: only use it when the developer asks to review a room.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Room name"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":200,"minimum":1,"description":"Messages per page (default 50)"},"before":{"type":"string","description":"Pagination cursor: the id/date of the oldest message already seen"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":191,"tokensAfter":191},{"name":"neblla_get_stats","fixed":{"name":"neblla_get_stats","description":"The app's statistics (the panel's Statistics tab), kept only while `playerStats` is on (off by default; turn it on with neblla_update_app playerStats: true, in the act — confirm with the developer, it costs quota). The last `days` UTC days (30 by default), today included, of signed-in players: `cards` { activeToday, activeYesterday, activeThisWeek, activeLastWeek, weekTrend (% vs the week before), dailyActiveAvg7, dailyActiveTrend, newPlayers, newTrend (% vs the period before), visits, avgMinute","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"days":{"type":"integer","maximum":90,"minimum":7,"description":"How many days (default 30)"},"appId":{"type":"string","description":"The app public ID"},"format":{"enum":["json","csv"],"type":"string","description":"csv: the days as CSV"},"timeZone":{"type":"string","description":"IANA time zone for the classes' times (default UTC; the days are UTC)"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":292,"tokensAfter":292},{"name":"neblla_get_user","fixed":{"name":"neblla_get_user","description":"Fetch the full record of a single end user — their saved data, public info, purchases, and communications thread. Use the `_id` from neblla_list_users / neblla_search_users_by_email as `userId`.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"appId":{"type":"string","description":"The app public ID"},"userId":{"type":"string","description":"The user's mongo ObjectId hex string (the `_id` returned by neblla_list_users)"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":215,"tokensAfter":215},{"name":"neblla_list_api_secrets","fixed":{"name":"neblla_list_api_secrets","description":"The app's API secrets: name, baseUrl, headers, query, limits and fromClient. Never the values.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":99,"tokensAfter":99},{"name":"neblla_list_apps","fixed":{"name":"neblla_list_apps","description":"List the apps owned by the authenticated developer, one short line each: id, name, slug, plan (its project's Pro, or free), projectId (the project it is in, neblla_projects), archived, files (how many the developer edits), updatedAt and pageUrl. For an app's configuration and state, call neblla_get_app with its id. `shared`: the apps of other developers whose team they are on (neblla_team), with their groups (`groups`; `access` is the first ready-made one: partner, support, viewer, screens or st","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","properties":{}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":179,"tokensAfter":179},{"name":"neblla_list_automations","fixed":{"name":"neblla_list_automations","description":"The app’s automations with their schedule, action, next run and the result of the last one.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":98,"tokensAfter":98},{"name":"neblla_list_communications","fixed":{"name":"neblla_list_communications","description":"List communications across ALL users of an app, filtered and sorted. The most useful tool for triaging an inbox: show only unread, only messages from users (not the dev), only flagged-important, or only those mentioning a given email. Returns flat items with userId so you can follow up via neblla_send_communication on each thread. An item with `contact: true` is someone without an account who wrote through a contact form (Neblla's form on the developer's web): the answer reaches them by email.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"skip":{"type":"integer","default":0,"minimum":0},"sort":{"enum":["date_desc","date_asc","email_asc","email_desc"],"type":"string","default":"date_desc"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","default":50,"maximum":200,"minimum":1},"fromFilter":{"enum":["all","user","dev"],"type":"string","default":"all","description":"\"user\" = sent by end-user, \"dev\" = sent by you, \"all\" = both"},"readFilter":{"enum":["all","read","unread"],"type":"string","default":"all"},"emailFilter":{"type":"string","description":"Case-insensitive email substring filter"},"importantOnly":{"type":"boolean","default":false}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":432,"tokensAfter":432},{"name":"neblla_list_community_entries","fixed":{"name":"neblla_list_community_entries","description":"List the community entries (user-published content, neblla.community) of an app from the backoffice, any status. Use it to triage the moderation queue: status \"pending\" is what awaits approval in review mode. Returns indexed metadata only (title, summary, category, tags, contentType, owner, status, moderation record, and stats: favorites, rating, reviews, uses, upvotes, downvotes, score); fetch a payload with neblla_get_community_entry. Paginate with `cursor` = the previous `next`.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"q":{"type":"string","description":"Text search over title / summary / tags"},"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"sort":{"enum":["gravity","newest","oldest","votes"],"type":"string","description":"Pending entries come most serious first by default (each carries `priority`: notice > serious > reports > prescreen > review); newest/oldest for plain date order; votes: most voted first (stats.score = votes for minus votes against)"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","default":20,"maximum":50,"minimum":1},"cursor":{"type":"string","description":"Opaque pagination cursor from a previous call"},"status":{"enum":["published","pending","rejected","removed","suspended","private"],"type":"string","description":"Filter by status (omit for all): published, pending (awaits approval), rejected, removed, suspended (a formal notice is open), private (only its author sees it)"},"ownerId":{"type":"string","description":"Filter by author. This is the SAME per-app user id that neblla_list_users and neblla_search_users_by_email return (entry.owner.id is that id), so you can go straight from a user to everything they published — no name matching needed."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":572,"tokensAfter":572},{"name":"neblla_list_erasure_requests","fixed":{"name":"neblla_list_erasure_requests","description":"The users who asked, from inside your app (neblla.auth.requestErasure), that you erase the data they gave it. Erase one with neblla_erase_user (its userObjectId); that closes the request. Pending ones by default.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"status":{"enum":["pending","done","all"],"type":"string","description":"Which requests (default pending)"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":163,"tokensAfter":163},{"name":"neblla_list_files","fixed":{"name":"neblla_list_files","description":"List the files of an app's pre (what is being edited; neblla_promote \"diff\" says how it differs from what the users have). Returns path, mimeType, size, and timestamps for each file.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":122,"tokensAfter":122},{"name":"neblla_list_fog_cases","fixed":{"name":"neblla_list_fog_cases","description":"The app’s Fog cases, newest first: when the Fogs running a room’s server file computed different states, Neblla compared them, the majority won, and the odd one’s user was flagged. Each case: room, seq, status (flagged | undecided | reassigned | annulled), fogs (userId, appUserId, leader, verdict: majority | odd | missing | undecided), diff (only the paths that differ), flagged. Undecided: no majority (two Fogs, or three different states) — nobody was flagged.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":200,"minimum":1},"status":{"enum":["flagged","undecided","reassigned","annulled"],"type":"string"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":239,"tokensAfter":239},{"name":"neblla_list_hooks","fixed":{"name":"neblla_list_hooks","description":"The app’s incoming webhooks: name, to, room. Never their tokens (make one again for a new URL).","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":97,"tokensAfter":97},{"name":"neblla_list_moderators","fixed":{"name":"neblla_list_moderators","description":"List the app-level moderators (per-app user ids). App moderators manage rooms of any type and moderate the community. They are the members of the app's reserved group `moderators`, which lives in its data: a pre on pro's data has pro's, a pre with its own data its own (empty at first; env \"pre\" to see it).","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","description":"Whose moderators: \"pro\" (the users', default) or \"pre\" (its own unless it plays with pro's data)"},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":203,"tokensAfter":203},{"name":"neblla_list_products","fixed":{"name":"neblla_list_products","description":"List all products for a Neblla app.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":82,"tokensAfter":82},{"name":"neblla_list_reviews","fixed":{"name":"neblla_list_reviews","description":"List the gallery reviews of an app (a review id — pass it as reviewId to neblla_reply_review, rating 1–5, text, language, author display name, the developer's reply) plus the average, count and 1–5 distribution. Newest first.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":500,"minimum":1,"description":"Max reviews returned (default 100); aggregates always cover all"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":171,"tokensAfter":171},{"name":"neblla_list_rooms","fixed":{"name":"neblla_list_rooms","description":"List the persistent / predefined rooms of an app with their type, owners, whitelist, moderators, data, message count, last activity, their own inactivity days (`ttlInactivity`, null = the app's per-type value), how many players follow each one (`followers`: they get a notice when someone writes while they are away) and how many users are in each one right now.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":172,"tokensAfter":172},{"name":"neblla_list_users","fixed":{"name":"neblla_list_users","description":"List end users of an app — the people who connected through the SDK, NOT the developer's own account. Use this as the entry point for the backoffice flow: it returns a paginated list with each user's mongo `_id` (needed for every other backoffice tool), email, name, language, and stats. Pass `withCommunicationsOnly: true` to get only users who have communication threads — useful when triaging support.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"skip":{"type":"integer","default":0,"minimum":0},"sort":{"enum":["date_desc","date_asc","email_asc","email_desc"],"type":"string","default":"date_desc"},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","default":50,"maximum":100,"minimum":1},"emailFilter":{"type":"string","description":"Substring match against the user's email (case-insensitive). Ignored if withCommunicationsOnly is true."},"withCommunicationsOnly":{"type":"boolean","default":false,"description":"Only return users with a non-empty communications array"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":378,"tokensAfter":378},{"name":"neblla_list_webhook_deliveries","fixed":{"name":"neblla_list_webhook_deliveries","description":"The app’s last outgoing webhook deliveries (neblla_update_app({ webhooks })), newest first: event, secret, path, at, ok, and the endpoint’s status or the error.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","maximum":50,"minimum":1}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":134,"tokensAfter":134},{"name":"neblla_move_file","fixed":{"name":"neblla_move_file","description":"Rename or move a file within an app. oldPath is the current path, newPath is the destination. In an app linked to its own repo, one commit on pre's branch.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","oldPath","newPath"],"properties":{"appId":{"type":"string","description":"The app public ID"},"newPath":{"type":"string","description":"New file path, e.g. \"new-name.html\" or \"subfolder/file.html\""},"oldPath":{"type":"string","description":"Current file path, e.g. \"old-name.html\""}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":174,"tokensAfter":174},{"name":"neblla_payments_account","fixed":{"name":"neblla_payments_account","description":"The developer's payout account (Stripe Connect), needed to sell products inside their apps. One account per developer, for all their apps, unless a project has its own (neblla_projects \"use_own_stripe\"): pass projectId to act on that project's account instead; every action below works the same on it. \"status\": account none | pending | active | restricted, with chargesEnabled / payoutsEnabled, and what is MISSING in words, one line per thing (missing: [{ key, words, overdue }], errors Stripe foun","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["action"],"properties":{"action":{"enum":["status","onboarding_link","dashboard_link","set_descriptor","disconnect"],"type":"string"},"prefix":{"type":"string","description":"set_descriptor: the bank statement prefix, 2-10 Latin letters or digits"},"confirm":{"type":"boolean","description":"Required true for \"disconnect\"."},"projectId":{"type":"string","description":"A project with its own Stripe (C220): its payout account. Omitted, or a project using the account's: the developer's."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":326,"tokensAfter":326},{"name":"neblla_publish_app","fixed":{"name":"neblla_publish_app","description":"Publish a Neblla app to the public gallery. BEFORE calling this tool you MUST follow these steps with the user: 1) Ask for the app title and a short description (1-3 sentences) in the user's language. 2) Ask for the gallery section: \"multiplayer\" (games/apps where users interact in real time), \"singleplayer\" (games/tools for one person at a time), or \"app\" (everything else — utilities, tools, sites). 3) Ask for tags that describe the app (e.g. \"puzzle\", \"multiplayer\", \"drawing\"). 4) Ask what dev","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","section","titles","descriptions"],"properties":{"tags":{"type":"array","items":{"type":"string"},"description":"Tags describing the app"},"appId":{"type":"string","description":"The app public ID"},"bestOn":{"enum":["mobile","desktop"],"type":"string","description":"Device the app is best suited for. Omit for both mobile and desktop."},"titles":{"type":"object","description":"Title per language code, e.g. { en: \"My App\", es: \"Mi App\" }. At least one entry required.","additionalProperties":{"type":"string"}},"section":{"enum":["singleplayer","multiplayer","app"],"type":"string","description":"Gallery section: \"multiplayer\" (real-time/social), \"singleplayer\" (one person at a time), or \"app\" (utilities/tools/sites)"},"portrait":{"type":"string","maxLength":262144,"description":"Cover image. Accepts an http(s) URL, a data: URI, or raw base64 (JPEG/PNG/GIF/BMP/TIFF). Auto-scaled to 400x400 JPEG, stored as portrait.jpg, and set as cover_image."},"defaultLang":{"enum":["en","es","fr","de","it","pt","ja","ko","zh","ar"],"type":"string","default":"en"},"descriptions":{"type":"object","description":"Description per language code (max 500 chars each). At least one entry required.","additionalProperties":{"type":"string"}}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":538,"tokensAfter":538},{"name":"neblla_publish_community_entry","fixed":{"name":"neblla_publish_community_entry","description":"Publish a community entry AS YOURSELF — owned by your own user in the app (the app user whose email is your developer email; it is created on first use if you never opened the app). Use it to seed a community with starter content. It skips the review queue (you are a moderator of your app) and obeys the same rules as neblla.community.publish: community must be enabled, contentType/category must be allowed, payload ≤ maxPayloadBytes, no \"$\"/\".\" keys.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","title","contentType"],"properties":{"i18n":{"type":"object","description":"Title/summary in other languages: { \"fr\": { \"title\": \"…\", \"summary\": \"…\" } } (up to 16). Search covers every language; readers asking for a locale get it","additionalProperties":{"type":"object","properties":{"title":{"type":"string","maxLength":120},"summary":{"type":"string","maxLength":500}},"additionalProperties":false}},"lang":{"type":"string","maxLength":8,"description":"Language of title/summary, e.g. \"es\""},"tags":{"type":"array","items":{"type":"string","maxLength":32},"maxItems":20},"appId":{"type":"string","description":"The app public ID"},"title":{"type":"string","maxLength":120},"payload":{"description":"The dev-defined JSON the app stores for this entry","type":"string"},"summary":{"type":"string","maxLength":500},"category":{"type":"string","maxLength":40},"parentId":{"type":"string","description":"Id of the entry this one derives from"},"contentType":{"type":"string","maxLength":64,"description":"One of the app's community.contentTypes (any when the list is empty)"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"schema","detail":"Added \"type\":\"string\" to untyped parameters: payload. Change to number/boolean/array where that's what you accept.","verify":true},{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":478,"tokensAfter":482},{"name":"neblla_publish_store","fixed":{"name":"neblla_publish_store","description":"The developer’s app as its OWN app in the stores, with THEIR developer account (Android now; iOS after). store \"google\": action \"set\" { packageName (e.g. com.studio.game; fixed once published), serviceAccountJson (a Play service account with release rights, kept sealed), track? (internal|alpha|beta|production), certs? (the app signing SHA-256 from Play Console → App integrity: the app then opens full screen) }; \"build\" builds the Android bundle from the app’s page (<slug or id>.neblla.app or its","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","store","action"],"properties":{"appId":{"type":"string","description":"The app public ID"},"certs":{"type":"array","items":{"type":"string"},"description":"SHA-256 fingerprints (AA:BB:…) of the app signing certificate"},"store":{"enum":["google","apple"],"type":"string"},"track":{"enum":["internal","alpha","beta","production"],"type":"string"},"action":{"enum":["set","build","status","remove"],"type":"string"},"packageName":{"type":"string"},"serviceAccountJson":{"type":"string","description":"The Play service account key, as the JSON text Google gives"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":348,"tokensAfter":348},{"name":"neblla_run_automation","fixed":{"name":"neblla_run_automation","description":"Run an automation now, to try it (its schedule does not change).","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Automation name"},"appId":{"type":"string","description":"The app public ID"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":108,"tokensAfter":108},{"name":"neblla_run_panel","fixed":{"name":"neblla_run_panel","description":"See a panel as the back office shows it: { appId, name, inputs? } → { title, columns, filters, actions, rows }.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string"},"appId":{"type":"string"},"inputs":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":116,"tokensAfter":116},{"name":"neblla_search_users_by_email","fixed":{"name":"neblla_search_users_by_email","description":"Find users of an app by a partial email match (min 2 characters). Returns the matching user docs with email, communications, data, stats, and language. Use neblla_list_users for full pagination instead when no email is known.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","emailPart"],"properties":{"env":{"enum":["pro","pre"],"type":"string","default":"pro","description":"Whose data: \"pro\" (the users', default) or \"pre\" (the app's pre, its own users and data unless it plays with pro's)"},"skip":{"type":"integer","default":0,"minimum":0},"appId":{"type":"string","description":"The app public ID"},"limit":{"type":"integer","default":50,"maximum":50,"minimum":1},"emailPart":{"type":"string","minLength":2,"description":"At least 2 characters of the email to match"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":255,"tokensAfter":255},{"name":"neblla_send_communication","fixed":{"name":"neblla_send_communication","description":"Send a message from the developer to a specific end user. The user receives it live via the SDK's `communicationUpdated` event if connected, and on next reconnect otherwise. Optionally attach a `data` JSON payload (rendered by the app), or a calendar/appointment proposal via dateFrom/dateTo. Set `thread` to the id of an earlier communication to group replies. To someone without an account (`contact: true` in neblla_list_communications) the message goes by email instead, from Neblla with Reply-To","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","message"],"properties":{"data":{"description":"Optional JSON payload attached to the communication","type":"string"},"appId":{"type":"string","description":"The app public ID"},"dateTo":{"type":"string","description":"Optional calendar/appointment proposal end (ISO 8601)"},"thread":{"type":"string","description":"Id of the parent communication to thread under"},"userId":{"type":"string","description":"Target user's mongo ObjectId hex string"},"message":{"type":"string","minLength":1,"description":"Plain-text message body"},"dateFrom":{"type":"string","description":"Optional calendar/appointment proposal start (ISO 8601)"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"schema","detail":"Added \"type\":\"string\" to untyped parameters: data. Change to number/boolean/array where that's what you accept.","verify":true},{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":365,"tokensAfter":369},{"name":"neblla_set_api_secret","fixed":{"name":"neblla_set_api_secret","description":"Create or change an API secret: an external API the app reaches with neblla.api.call(name, path, body) without a server of yours. Neblla fills the secret in where {secret} appears in `headers` or `query`, and calls only under `baseUrl` (https, a public host). The value is written and never read back (neblla_list_api_secrets shows everything else); on a change, leave `value` out to keep it. From the server file (room.js…) it always works; from a user's client only with fromClient: true. Limits: l","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Secret name: a letter, then letters, digits or _ (≤ 40), e.g. CLOUDINARY"},"appId":{"type":"string","description":"The app public ID"},"query":{"type":"object","description":"Query parameters sent on every call, e.g. { \"key\": \"{secret}\" }","additionalProperties":{"type":"string"}},"value":{"type":"string","description":"The secret itself (≤ 8 KB). Required on create; leave out on a change to keep it"},"limits":{"type":"object","properties":{"app":{"type":"object","properties":{"day":{"$ref":"#/properties/limits/properties/perUser/properties/minute"},"month":{"$ref":"#/properties/limits/properties/perUser/properties/minute"}},"additionalProperties":false},"perUser":{"type":"object","properties":{"day":{"$ref":"#/properties/limits/properties/perUser/properties/minute"},"hour":{"$ref":"#/properties/limits/properties/perUser/properties/minute"},"week":{"$ref":"#/properties/limits/properties/perUser/properties/minute"},"month":{"$ref":"#/properties/limits/properties/perUser/properties/minute"},"minute":{"type":"integer","minimum":0},"sixHours":{"$ref":"#/properties/limits/properties/perUser/properties/minute"}},"additionalProperties":false},"loggedInOnly":{"type":"boolean"}},"description":"Call limits (absent or 0 = none)","additionalProperties":false},"baseUrl":{"type":"string","description":"The only place it may call, e.g. https://api.cloudinary.com/v1_1/demo"},"headers":{"type":"object","description":"Headers sent on every call, e.g. { \"Authorization\": \"Bearer {secret}\" }","additionalProperties":{"type":"string"}},"fromClient":{"type":"boolean","description":"Let users' clients call it (default false: only the server file)"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":668,"tokensAfter":668},{"name":"neblla_set_automation","fixed":{"name":"neblla_set_automation","description":"Create or replace an automation: a scheduled action on the app (open a room, hand a room data, call one of your API secrets). For flows across the tools the developer already uses, n8n, Make or Zapier connect through the admin API, incoming webhooks and outgoing webhooks. when: { at: ISO date } once; { every: minutes } (5 – 10080); or { daily: \"HH:MM\", days?: [0–6, 0 = Sunday], tz?: \"Europe/Madrid\" (UTC by default) }. action: { kind: \"openRoom\", room, config?, openForMinutes? (60) } books the ro","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name","when"],"properties":{"day":{"enum":["today","yesterday"],"type":"string","description":"game.dailyTournament with board: the UTC day ranked (today by default)"},"set":{"type":"string"},"top":{"type":"integer"},"days":{"type":"integer"},"name":{"type":"string","description":"Automation name: a letter, then letters, digits, _ or - (≤ 40)"},"path":{"type":"string"},"room":{"type":"string","description":"Template: a live room whose server file hears it as a hook"},"when":{"type":"object","properties":{"at":{"type":"string"},"tz":{"type":"string"},"days":{"type":"array","items":{"type":"integer"}},"daily":{"type":"string"},"every":{"type":"integer"}},"description":"When it runs","additionalProperties":false},"appId":{"type":"string","description":"The app public ID"},"board":{"type":"string","description":"game.dailyTournament: a ranking of neblla.scores (instead of table and field)"},"field":{"type":"string"},"hours":{"type":"integer"},"limit":{"type":"integer"},"table":{"type":"string"},"action":{"type":"object","required":["kind"],"properties":{"body":{},"data":{},"kind":{"enum":["openRoom","message","callApi"],"type":"string"},"path":{"type":"string"},"room":{"type":"string"},"config":{},"method":{"type":"string"},"secret":{"type":"string"},"openForMinutes":{"type":"integer"}},"description":"What it does (or use template)","additionalProperties":false},"secret":{"type":"string","description":"Template: the API secret its results go to"},"enabled":{"type":"boolean","description":"false keeps it without running (default true)"},"template":{"enum":["booking.reminder","community.digest","game.dailyTournament"],"type":"string","description":"A ready-made template instead of action"},"hoursAhead":{"type":"integer"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":677,"tokensAfter":677},{"name":"neblla_set_ban_status","fixed":{"name":"neblla_set_ban_status","description":"Set a user's ban state in this app: \"none\" (lift), \"banned\" (the app shows the ban message and blocks play; Neblla refuses everything but their messages to you), \"terminated\" (blocks them for good: their device is sealed so the app no longer opens there, and they are disconnected), \"community_muted\" (can play, and can still REPORT entries, but cannot publish, rate or review). Takes effect live. WARNING: \"terminated\" seals the user's device, and lifting it with \"none\" does not unseal a device tha","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","status"],"properties":{"appId":{"type":"string","description":"The app public ID"},"status":{"enum":["none","banned","terminated","community_muted"],"type":"string","description":"New ban state"},"userId":{"type":"string","description":"The user's `_id` from neblla_list_users"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":274,"tokensAfter":274},{"name":"neblla_set_communication_important","fixed":{"name":"neblla_set_communication_important","description":"Toggle the \"important\" flag on a communication. This is dev-only metadata and is NOT broadcast to the end user — use it to bubble messages to the top of your own triage view.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","commId"],"properties":{"appId":{"type":"string","description":"The app public ID"},"commId":{"type":"string","description":"Communication id"},"userId":{"type":"string","description":"User's mongo ObjectId hex string"},"important":{"type":"boolean","default":true}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":180,"tokensAfter":180},{"name":"neblla_set_communication_read","fixed":{"name":"neblla_set_communication_read","description":"Mark a specific communication as read or unread (from the developer's side). The end user is notified live via a `communicationRead` socket event if connected.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","commId"],"properties":{"read":{"type":"boolean","default":true},"appId":{"type":"string","description":"The app public ID"},"commId":{"type":"string","description":"Communication id (the ISO timestamp returned when the message was created)"},"userId":{"type":"string","description":"User's mongo ObjectId hex string"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":189,"tokensAfter":189},{"name":"neblla_set_moderator","fixed":{"name":"neblla_set_moderator","description":"Make a user an app moderator (`moderator: true`) or remove them (`false`): it adds them to, or takes them out of, the app's reserved group `moderators`. `userId` is the per-app user id (from neblla_list_users with the same env), not an email. The group lives in the app's data: with env \"pre\" and a pre with its own data, it names a moderator of pre (the developer adds themselves to moderate there); a pre on pro's data shares pro's.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","moderator"],"properties":{"env":{"enum":["pro","pre"],"type":"string","description":"Whose moderators: \"pro\" (the users', default) or \"pre\" (its own unless it plays with pro's data)"},"appId":{"type":"string","description":"The app public ID"},"userId":{"type":"string","description":"Per-app user id"},"moderator":{"type":"boolean","description":"true = add, false = remove"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":281,"tokensAfter":281},{"name":"neblla_set_panel","fixed":{"name":"neblla_set_panel","description":"Create, replace or (panel: null) remove a back office panel: a screen for the developer and their team. panel: { title, source: { table } | { booking: set } | { credits: kind }, columns: [\"field\" | { field, label }] (player.name, start, title… too), where: { field: value | { gte: \"$today\", lt: \"$tomorrow\" } } (dates: $now, $today, $tomorrow, $weekStart, $monthStart, $nextMonthStart), filters: [{ field, op?, label?, type? }] the person fills in, orderBy, limit (≤ 500), actions: [{ label, call: \"b","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"name":{"type":"string","description":"Panel name: letters, digits, _ or - (≤ 40)"},"appId":{"type":"string"},"panel":{"description":"The panel, or null to remove it","type":"string"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"schema","detail":"Added \"type\":\"string\" to untyped parameters: panel. Change to number/boolean/array where that's what you accept.","verify":true},{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":244,"tokensAfter":248},{"name":"neblla_set_slug","fixed":{"name":"neblla_set_slug","description":"Check, set or clear the app's public slug (the readable name in <slug or id>.neblla.app). `checkOnly: true` only tells whether it is valid and free; when another app of YOURS holds it (its slug or an old one) it says so (`reason: \"yours\"`, `heldBy`). `slug: null` removes it. An old slug keeps leading to the app (it stays the app's; nobody else can take it), and the users' sessions follow. `move: true` takes a slug from another app of yours (e.g. moving a game to a new app): the other app loses i","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","slug"],"properties":{"move":{"type":"boolean","description":"Take it from another app of yours that holds it (its slug or an old one)"},"slug":{"type":["string","null"],"description":"Wanted slug (lowercase letters, digits, dashes), or null to clear"},"appId":{"type":"string","description":"The app public ID"},"checkOnly":{"type":"boolean","description":"Only check availability, change nothing"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":300,"tokensAfter":300},{"name":"neblla_table_query","fixed":{"name":"neblla_table_query","description":"Read an app table across users: filters on declared fields, order, page, or just a count. e.g. { table: \"reservas\", where: { clase: \"yoga-martes\", estado: \"confirmada\" }, count: true }, or { table: \"puntos\", orderBy: \"-score\", limit: 10 }. Rows come with id, owner (the user id in the app), createdAt, updatedAt.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","table"],"properties":{"appId":{"type":"string","description":"The app public ID"},"count":{"type":"boolean","description":"Only how many rows match"},"limit":{"type":"integer","description":"Up to 1000 (default 100)"},"owner":{"type":"string","description":"Only this user’s rows"},"table":{"type":"string"},"where":{"type":"object","description":"{ field: value } or { field: { eq, ne, gt, gte, lt, lte, in } }, on declared fields","additionalProperties":{}},"offset":{"type":"integer"},"orderBy":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"\"field\" or \"-field\" (descending); createdAt also"}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":318,"tokensAfter":318},{"name":"neblla_update_app","fixed":{"name":"neblla_update_app","description":"Update an app's configuration. Pass only the fields you want to change. Use addDomain/removeDomain to mutate the allowed-origins list atomically without overwriting other entries. `compiled: true` lets a compiled app (Capacitor, Ionic, Tauri, Electron) use the app: it takes their origins (capacitor://localhost, https://localhost, null…) without a domain, and its new guests are limited per address. A page on localhost opens only the app's PRE, with its local key (neblla_pre_access). `turnstile`: ","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId"],"properties":{"ai":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"null"}],"description":"AI config, replaced whole: { provider: \"claude\" (default) | \"openai\" | \"deepseek\", model (the provider's: opus-latest | sonnet-latest | haiku-latest, gpt-5 | gpt-5-mini | gpt-5-nano, deepseek-chat | deepseek-reasoner; with your key it is a ceiling, a user may ask for a cheaper one of the same provider), maxOutputTokens, userTokenMode (\"off\": your key for the provider, saved in your Neblla account; \"browser\": the user's key, called from the page, not for DeepSeek; \"server\": the user's key through Neblla), presets, limitsEnabled, tokensPerDay } — pass null to disable"},"fog":{"anyOf":[{"type":"object","properties":{"window":{"type":"integer","maximum":5000,"minimum":50,"description":"ms a message stays provisional before it is final (default 1000; an action game wants ~200)"},"latencyWatch":{"type":"boolean","description":"Flag impossible send times (little quota)"}},"additionalProperties":false},{"type":"null"}],"description":"Fog settings; only the fields passed change, null resets them"},"p2p":{"anyOf":[{"type":"object","properties":{"turn":{"type":"boolean","description":"true = offer Neblla's TURN relay when direct P2P fails; relayed bytes spend quota (default false)"},"allow":{"type":"boolean","description":"false = no WebRTC at all (default true)"}},"additionalProperties":false},{"type":"null"}],"description":"P2P settings: { allow, turn }. The SDK has no audio or video."},"auth":{"anyOf":[{"anyOf":[{"type":"object","required":["issuer"],"properties":{"issuer":{"type":"string"},"jwksUrl":{"type":"string"},"audience":{"type":"string"}},"additionalProperties":false},{"type":"array","items":{"type":"object","required":["issuer"],"properties":{"issuer":{"type":"string"},"jwksUrl":{"type":"string"},"audience":{"type":"string"}},"additionalProperties":false}}]},{"type":"null"}],"description":"Your own login without a server (BYOAuth): your auth provider (Auth0, Clerk, Supabase Auth, Firebase, Cognito… any issuer of JWTs) whose token the front passes to neblla.auth.login({ jwt }); its `sub` is the externalId of the user. { issuer (https), jwksUrl? (else OpenID discovery), audience? }, or a list of up to 5; null clears it"},"keys":{"type":"object","description":"Public values the app reads with neblla.api.key(name): text, number, yes/no or JSON. Merged into the current keys; null removes one. Never a secret: anyone who opens the app can read them.","additionalProperties":{}},"name":{"type":"string","description":"New display name"},"appId":{"type":"string","description":"The app public ID"},"inbox":{"anyOf":[{"type":"boolean"},{"type":"object","properties":{},"additionalProperties":true},{"type":"null"}],"description":"Inbox: true/false, or { maxPerUser } to lower the per-user cap within the plan ceiling; the inbox itself is the inbox module, features.inbox"},"roles":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","additionalProperties":{"type":"string"}}},{"type":"null"}],"description":"Roles for groups (neblla_group): each role's rights over each table, \"read\", \"write\" or \"read write\". e.g. { equipo: { tareas: \"read write\" }, cliente: { tareas: \"read\" } }. A row saved with { table, group } is read and written by the group's members as their role says. The back office team (neblla_team) can take a role too. Replaces the whole set; null clears them"},"rooms":{"type":"object","properties":{},"description":"Rooms policy object (same shape the app returns in `rooms`)","additionalProperties":true},"saves":{"anyOf":[{"type":"object","properties":{"maxBytesPerUser":{"type":"integer","minimum":0,"description":"Most bytes one user can keep with db.save() (10240 by default; 0 = no limit). An anti-abuse limit, so nobody tampering with the client fills your store with their own things."}},"additionalProperties":false},{"type":"null"}],"description":"User saves; null goes back to the defaults"},"theme":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"null"}],"description":"Theme: { palette, typography, layout }"},"trade":{"anyOf":[{"type":"object","properties":{"days":{"type":"integer"},"with":{"enum":["friends","all"],"type":"string"},"tables":{"type":"array","items":{"type":"string"}},"products":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},{"type":"null"}],"description":"What users may trade between them (C170): neblla.db.offer({ to, give, get }) → the other accepts with neblla.db.acceptOffer(id), and everything moves at once or nothing does. { tables: [tables whose rows change owner], products: [product ids whose units (one-time or consumable, never a subscription) pass from one user to the other], with: \"friends\" (default: only Neblla friends) | \"all\", days: how long an offer lasts (1–30, 3 by default) }. A real-money product is tradable only if you list it; the payment stays with the buyer (sales and refunds do not move). The log: neblla_trades. Replaces the whole object; null: nothing is tradable"},"groups":{"anyOf":[{"type":"object","properties":{"invitable":{"type":"array","items":{"type":"string"}},"maxMembers":{"type":"integer"},"creatorRole":{"type":"string"},"clientCreate":{"type":"boolean"}},"additionalProperties":false},{"type":"null"}],"description":"Groups the users make themselves (neblla.groups.create / invite / join / leave): clientCreate on, the role the creator gets (creatorRole, who invites), maxMembers, and the roles an invitation may give (invitable). null: no groups from the client"},"limits":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"tables":{"type":"object","additionalProperties":{"type":"object","required":["maxRowsPerPlayer"],"properties":{"maxRowsPerPlayer":{"type":"integer"}},"additionalProperties":false}},"playerQuota":{"type":"integer","maximum":100,"minimum":0},"maxBytesPerUser":{"type":"integer"}},"additionalProperties":false}},{"type":"null"}],"description":"What a purchase raises, per product id: { premium: { maxBytesPerUser, tables: { notas: { maxRowsPerPlayer } }, playerQuota } }. playerQuota: the % of the app's daily quota a buyer may spend, instead of the app's playerQuota (0 = no cap). A user who owns the product (bought, or a live subscription) gets them; without it, the app's own. Owning several, the most generous wins; 0 = no limit"},"scores":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"max":{"type":"number","description":"The most a score can be; over it, refused (over_max)"},"min":{"type":"number","description":"The least a score can be; under it, refused (below_min)"},"seed":{"enum":["run","daily"],"type":"string","description":"run (default): a seed per game; daily: the same seed for every player all day (UTC), e.g. the word of a daily puzzle"},"check":{"anyOf":[{"type":"object","required":["secret"],"properties":{"path":{"type":"string"},"secret":{"type":"string"}},"additionalProperties":false},{"type":"null"}],"description":"Your own validation: Neblla POSTs { event: \"score\", board, player, value, data, run: { id, startedAt, seconds, seed, day } } to that callApi secret (neblla_set_api_secret) + path, and your code answers { ok: true } or { ok: false, reason }; no answer: check_unavailable and the game can be sent again"},"order":{"enum":["asc","desc"],"type":"string","description":"desc (default): more is better (points); asc: less is better (tries, milliseconds)"},"integer":{"type":"boolean","description":"Only whole numbers (else invalid_value)"},"maxSeconds":{"type":"number","description":"How long a game can be sent after it started (86400 by default, up to 7 days; later: run_expired)"},"minSeconds":{"type":"number","description":"The shortest a real game can last; a score sent sooner after start is refused (too_soon)"},"runsPerDay":{"type":"integer","description":"Games a user may start per day and board (1–1000; none = no limit)"},"maxPerSecond":{"type":"number","description":"The most points per second of game (else too_fast)"}},"additionalProperties":false}},{"type":"null"}],"description":"Rankings of a game for one, checked by Neblla before a score enters (neblla.scores.start(board) → run; neblla.scores.submit(run, value, { data }) → { ok, best, place } or { ok: false, reason }; neblla.scores.top(board, { period: \"all\"|\"day\" })). { board: { order, min, max, integer, minSeconds, maxSeconds, maxPerSecond, seed, runsPerDay, check } }; names a-z0-9_, up to 20. What is refused does not enter, goes to the app log (Build › Errors and refusals) and, if only a cheat explains it, marks the user. Replaces the whole set; null clears it. A board left out is deleted, and its scores with it (the draft's at once, the published app's when this reaches it with neblla_promote); to rename one and keep its scores, say so in scoreRenames"},"server":{"type":"object","properties":{"fallback":{"anyOf":[{"enum":["fog"],"type":"string"},{"type":"null"}]}},"description":"Server (\"own\"): { fallback: \"fog\" } (the default) lets Fog take the room with the same room.js when the developer's server is gone, and gives it back when the server returns; { fallback: null } makes the room wait for it","additionalProperties":false},"tables":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","required":["fields"],"properties":{"index":{"type":"array","items":{"type":"string"}},"fields":{"type":"object","additionalProperties":{"enum":["string","number","integer","boolean","date"],"type":"string"}},"server":{"type":"string"},"maxRowsPerPlayer":{"type":"integer"}},"additionalProperties":false}},{"type":"null"}],"description":"The app tables: rows each tied to one user (neblla.db.save(data, { table }) / neblla.db.load({ table, where })). { name: { fields: { field: type }, index: [fields], maxRowsPerPlayer? (500), server?: \"write\" (a real server, own or cloud, and the admin API may write rows for a user; a Fog never) } }; names a-z0-9_, types string|number|integer|boolean|date. Replaces the whole set; a field added later becomes a new column (none is dropped). Queries across players: neblla_table_query, room.js, Automations. null clears them"},"pattern":{"type":"string","description":"App pattern id"},"visible":{"anyOf":[{"type":"object","properties":{"data":{"type":"object","additionalProperties":{"enum":["all","friends"],"type":"string"}},"tables":{"type":"object","additionalProperties":{"enum":["all","friends"],"type":"string"}}},"additionalProperties":false},{"type":"null"}],"description":"What other users may see of a user's saves (C169), read with neblla.db.dataOf(user) and neblla.db.loadOf(user, { table, where… }): { data: { <path of db.data>: \"all\" | \"friends\" }, tables: { <table>: \"all\" | \"friends\" } }. Paths are top-level keys or dotted (\"perfil.nombre\", up to 5 levels, 50 paths); \"all\" is any user of the app, a guest too; \"friends\" is the user's Neblla friends (and the user). Nothing that is not marked is ever shown. Replaces the whole object; null: nothing is visible"},"business":{"anyOf":[{"type":"object","properties":{"booking":{"type":"object","additionalProperties":{"type":"object","properties":{"pay":{"enum":["booking"],"type":"string"},"cost":{"type":"integer"},"product":{"type":["string","null"]},"creditKind":{"type":"string"},"cancelHours":{"type":"number"}},"additionalProperties":true}},"credits":{"type":"object","additionalProperties":{"type":"object","properties":{"products":{"type":"object","additionalProperties":{"type":"integer"}}},"additionalProperties":false}},"standing":{"type":"object","additionalProperties":{"type":"object","required":["set","series"],"properties":{"set":{"type":"string"},"series":{"type":"string"}},"additionalProperties":false}}},"additionalProperties":true},{"type":"null"}],"description":"Business templates: booking sets { <set>: { cancelHours (deadline to cancel with a refund), cost (credits per booking, 0 = free), creditKind } or, paid each time at booking, { cancelHours, pay: \"booking\", product: <one-time productId> } (the place is held 30 minutes while the user pays; cancelling in time refunds the money in Stripe), plus what the service needs: name, minutes, min, max (places), confirmHours, remindHours (1–336: a reminder in each client's inbox that many hours before, once, in their language and with their name; remindText: your own words, with {name} {service} {day} {time} {staff} {site} {for}), resources: [ids], staff: true | [ids], steps: [{ minutes, resources, staff? }], book: \"times\" for free times instead of sessions, chat: true (each session gets a private chat of whoever is booked in it; the business writes in it with neblla_booking chatPost) } and credit kinds { <kind>: { products: { <productId>: credits per unit } } } (a purchase of that product tops the balance up), and standing places { <productId>: { set, series } } (a «plaza fija»: buying that one-time product books its buyer in every case of that repetition up to the end of the month; a subscription, in every case of each paid period; series ids from neblla_booking series { set }). Slots: neblla_booking. Replaces the whole object; null clears it"},"compiled":{"type":"boolean","description":"A compiled app (Capacitor, Ionic, Tauri, Electron): take its origins without a domain"},"features":{"type":"object","properties":{},"description":"The app's neblla.* modules to switch, as booleans: auth and db (always on), message, state, room, rooms (what multiplayer means), social, shop, inbox, ai, api, anticheat, groups, community, business, tools. Only the modules you pass change; turning one on brings what it needs (message, state, rooms and anticheat need room) and turning one off takes what needs it. Whatever is off is not in the SDK the app downloads, the server refuses it and the guide (neblla_get_llms with appId) leaves it out. An old switch name (multiplayer, friends, purchases, communications, login) is refused with unknown_module.","additionalProperties":true},"webhooks":{"anyOf":[{"type":"array","items":{"type":"object","required":["event","secret"],"properties":{"path":{"type":"string","description":"Path under the secret’s base URL (default /)"},"event":{"enum":["purchase","refund","register","erasure_request","subscription_end","ban","flag","community_report","booking"],"type":"string"},"secret":{"type":"string","description":"An API secret (neblla_set_api_secret, the one neblla.api.call uses): its base URL, headers and limits"}},"additionalProperties":false}},{"type":"null"}],"description":"Outgoing webhooks: Neblla POSTs { event, appId, at, data } to the secret’s URL when the event happens (users by their id in this app). Replaces the whole list; null clears it. Deliveries: neblla_list_webhook_deliveries"},"addDomain":{"type":"string","description":"Append a domain entry (URL or origin) to the allowed list. A wildcard allows one subdomain level: \"*.platform.com\" (for platforms that serve each game from its own subdomain); \"*\" or \"*.com\" are refused (invalid_domain)."},"community":{"type":"object","properties":{},"description":"Community config (user-published entries): { mode, whoPublishes, maxPayloadBytes, categories, contentTypes, maxEntriesPerUser (0 = no limit, the default), reportThreshold (3 by default), rules, prescreen: { mode: off|flag|hold, images }, downvotes (true: users may also vote against; false by default), fields: { name: string|number|integer|boolean|date } and index: [names] — your own indexed fields: publish({ fields }), list({ where: { f: { gte, lt… } }, orderBy: \"-f\" }) }. Merged over the stored config.","additionalProperties":true},"purchases":{"anyOf":[{"type":"object","properties":{"emailCheckout":{"type":"boolean"}},"additionalProperties":false},{"type":"null"}],"description":"emailCheckout (on by default): a guest buys with just the email Stripe asks for — a light account on that device, no password; on another device neblla.shop.restore(email) mails a link that brings the purchases; signing in later with a Neblla account of that confirmed email takes them into it. false: only signed-in users buy, and neblla.shop.buy() from a guest opens the sign-in in the checkout window and goes on to the payment once they are in"},"turnstile":{"anyOf":[{"anyOf":[{"type":"boolean"},{"type":"object","required":["siteKey"],"properties":{"siteKey":{"type":"string"},"secretKey":{"type":"string"}},"additionalProperties":false}]},{"type":"null"}],"description":"Turnstile before a new guest: true (Neblla's widget), { siteKey, secretKey } (the developer's own), false/null (off)"},"connection":{"anyOf":[{"type":"object","properties":{"clock":{"anyOf":[{"type":"object","required":["max"],"properties":{"max":{"type":"integer","maximum":86400,"minimum":10}},"additionalProperties":false},{"type":"boolean","const":false}],"description":"The common clock (neblla.room.now()): after the first samples it is measured every 1 s, 1.4 s, 2 s, 2.8 s… (×√2) up to max seconds (3600 by default). A fast game may lower it (e.g. 120); false measures it only when the session starts (a chat can trust the device clock)"},"presence":{"type":"object","required":["every"],"properties":{"every":{"type":"integer","maximum":600,"minimum":5},"timeout":{"type":"integer","maximum":3600}},"description":"\"Are you still there?\": the SDK sends a heartbeat every `every` seconds and after `timeout` without one Neblla cuts the connection and keeps the player's place for 3 minutes (12 s if they were the room's server, who hands over at once); only then do they leave the room (4 × every by default, at least 2 ×). Defaults: an app with the room module 15/45, any other 120/600. A hidden page always gets 180 s","additionalProperties":false}},"additionalProperties":false},{"type":"null"}],"description":"How often devices keep in touch with Neblla; null goes back to the defaults"},"serverMode":{"enum":["js","own","cloud"],"type":"string","description":"Where server/*.js runs: js on a user's device (Fog); own on the developer's server (npx @neblla/server, see neblla_server_key); cloud in Neblla's cloud (a box per app that sleeps when idle; saving a server/ file redeploys it; billed by awake time and messages)"},"connections":{"enum":["all","p2p","server","none"],"type":"string","description":"How devices talk. p2p (what a new app is born with; an app from before with no value reads as all): direct, then the TURN relay if p2p.turn is on; a device that reaches nobody is left out (the user is told, the app gets room.on(\"leftOut\"), the panel counts them). all: the same, and Neblla's server forwards messages as the last resort (each copy costs quota). server: only through Neblla, never WebRTC. none: neither (and the messages of a room are not kept in its history)."},"matchmaking":{"anyOf":[{"type":"object","properties":{},"additionalProperties":true},{"type":"null"}],"description":"No-code matchmaking config: { mode: \"nocode\"|\"custom\", rule: \"count\"|\"timer\"|\"count_then_timer\"|\"button\", minPlayers, maxPlayers (0=unlimited), waitSeconds, allowPresenterStart (true by default: the presenter may also start the room with neblla.room.start(); false in nocode: only gathering.js does, the presenter gets not_authorized) }. Fields you leave out keep their saved value. In \"nocode\" mode (the default) this regenerates server/gathering.js from the rules; in \"custom\" mode you own that file — set it before writing gathering.js by hand."},"playerQuota":{"anyOf":[{"type":"integer","maximum":100,"minimum":0},{"type":"null"}],"description":"The most one user may spend of the app's daily quota, in % (20 by default; 0 = no cap; null = back to the default), so one user alone cannot use it up. Past it, that user's events are refused with the error player_quota_exceeded (neblla.app.on('error')) until 00:00 UTC; the rest keep playing. The room's server is never capped."},"playerStats":{"type":["boolean","null"],"description":"Your players' visit statistics, off by default: true keeps, for each player, when they first came, when they last came and how many times, in a table of the app's store (nb_player_stats, id = the player's user.id; with BYODB in your own database, created on the first visit). It is yours: Neblla keeps its own measurement apart and never reads it. false or null: off, nothing more is written (what was written stays)"},"customDomain":{"type":["string","null"],"description":"Your own domain for the app Neblla hosts (e.g. \"mijuego.com\"): the answer gives the CNAME to point it at; Neblla gets the certificate and serves the app there, as at <slug or id>.neblla.app. Asking again reports the status (pending → active). null removes it"},"keepFogCases":{"type":["boolean","null"],"description":"With BYODB the Fogs' cases are yours (table nb_fog_cases in your database) and are deleted on their own 30 days after they were opened. true: they stay until you delete them. false or null: deleted at 30 days (the default). Without BYODB it does nothing: Neblla keeps them 30 days"},"removeDomain":{"type":"string","description":"Remove a domain entry exactly matching this string"},"savedReplies":{"anyOf":[{"type":"array","items":{"type":"object","required":["title","text"],"properties":{"id":{"type":"string"},"text":{"type":"string"},"title":{"type":"string"}},"additionalProperties":false}},{"type":"null"}],"description":"The inbox's saved replies (the developer picks one when answering a user): [{ id?, title (≤ 80), text (≤ 5000) }], ≤ 50, the whole list; keep the ids of the ones that stay. The app's own: it applies at once to the published app and its pre, nothing to promote. null clears them"},"scoreRenames":{"type":"object","description":"With scores: the boards renamed in this call, { oldName: newName } (the old one gone from scores, the new one in it). Their scores move to the new name: the draft's at once, the published app's when this reaches it (neblla_promote). Without it, a new name is a new empty board and the old one is deleted with its scores","additionalProperties":{"type":"string"}},"erasureWindow":{"type":"boolean","description":"neblla.auth.requestErasure opens a Neblla window where the user confirms (default true); false if you handle erasure requests your own way"},"moderationLog":{"type":["boolean","null"],"description":"The moderation log, off by default: true writes down every moderation act (who named or removed a moderator, each ban, every message or community entry someone other than its author took down), who did it and when; read it with neblla_get_moderation_log or in the back office. Each line costs quota and fills the app's store (with BYODB it goes to your own database). false or null: off, nothing more is written"},"confirmRoomsChange":{"type":"boolean","description":"Set true to apply a rooms change that discards saved rooms data (only after the developer agreed)"},"permissionJustifications":{"type":"object","properties":{},"description":"Why the app asks for each SSO scope, in one sentence the USER reads on the consent screen: { email, friends, discoverable }. Keyed by scope; any other key is stored but never shown. NOT browser permissions (microphone/camera/etc. are not read from here).","additionalProperties":true}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":6668,"tokensAfter":6668},{"name":"neblla_update_community_entry","fixed":{"name":"neblla_update_community_entry","description":"Edit any entry of your app's community — yours or a user's — as the developer: title, summary, category, tags, lang, i18n (replaces the whole map; null clears it) and/or payload (replaced whole). Only the fields you pass change. Editing a user's entry records that you did and tells them. Use it to keep a community fed from elsewhere in sync.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","entryId"],"properties":{"i18n":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"title":{"type":"string","maxLength":120},"summary":{"type":"string","maxLength":500}},"additionalProperties":false}},{"type":"null"}],"description":"Title/summary in other languages; replaces the whole map, null clears it"},"lang":{"type":"string","maxLength":8},"tags":{"type":"array","items":{"type":"string","maxLength":32},"maxItems":20},"appId":{"type":"string","description":"The app public ID"},"title":{"type":"string","maxLength":120},"entryId":{"type":"string","description":"The entry id"},"payload":{"description":"Replaces the stored payload whole","type":"string"},"summary":{"type":"string","maxLength":500},"category":{"type":"string","maxLength":40}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"schema","detail":"Added \"type\":\"string\" to untyped parameters: payload. Change to number/boolean/array where that's what you accept.","verify":true},{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":361,"tokensAfter":366},{"name":"neblla_update_product","fixed":{"name":"neblla_update_product","description":"Update an existing product. Pass only the fields to change. Use this to adjust price, rename, or republish a product without losing its productId — changing the id would break checkout flows already wired into the app.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["productId"],"properties":{"max":{"type":"number","description":"Maximum quantity for consumable"},"min":{"type":"number","description":"Minimum quantity for consumable"},"name":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{"type":"string"}}],"description":"Product name, string or { en: \"...\", es: \"...\" }"},"step":{"type":"number","description":"Quantity step for consumable selector"},"image":{"type":"string","description":"Product image URL"},"price":{"type":"number","description":"Price in cents (one_time; per period for subscription — applies to new subscribers)"},"currency":{"type":"string","pattern":"^[a-zA-Z]{3}$","description":"ISO 4217 code Stripe charges in, e.g. \"usd\"; the price is checked again in it"},"interval":{"enum":["month","year"],"type":"string","description":"subscription: how often it renews (new subscribers)"},"productId":{"type":"string","description":"The product ID returned by neblla_create_product"},"published":{"type":"boolean","description":"Whether the product is buyable (without an active payout account it waits, as in neblla_create_product)"},"trialDays":{"type":"integer","maximum":730,"minimum":0,"description":"subscription: free trial days (new subscribers)"},"defaultLang":{"enum":["en","es","fr","de","it","pt","ja","ko","zh","ar"],"type":"string"},"description":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{"type":"string"}}],"description":"Description, string or per-language object"},"pricePerUnit":{"type":"number","description":"Price per unit in cents (consumable)"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":548,"tokensAfter":548},{"name":"neblla_update_room","fixed":{"name":"neblla_update_room","description":"Change a room: replace its shared `data` (users in the room receive it live), its `moderators` list and/or its own inactivity time (`ttlInactivity` in days, 0 = never deleted, null = back to the app's per-type value). Replacing data overwrites the live game state of that room — confirm with the developer first.","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","name"],"properties":{"data":{"type":"object","properties":{},"description":"New room data (replaces the whole object)","additionalProperties":true},"name":{"type":"string","description":"Room name"},"appId":{"type":"string","description":"The app public ID"},"moderators":{"type":"array","items":{"type":"string"},"description":"New moderators list (per-app user ids)"},"ttlInactivity":{"anyOf":[{"type":"integer","maximum":3650,"minimum":0,"description":"Days without activity before Neblla deletes this room (0 = never). Overrides the app's per-type value (rooms.public/private.ttlInactivity: 30 / 90 by default) for this room only."},{"type":"null"}],"description":"Days without activity before Neblla deletes this room (0 = never). Overrides the app's per-type value (rooms.public/private.ttlInactivity: 30 / 90 by default) for this room only."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":374,"tokensAfter":374},{"name":"neblla_update_user_data","fixed":{"name":"neblla_update_user_data","description":"REPLACES (does not merge) the `data` object for a specific end user (the same object the SDK's neblla.db.save() writes and neblla.db.data shows). There is no merge option here, unlike the SDK which offers shallow/deep/crdt: whatever you pass becomes the whole document, so read it first with neblla_get_user, modify, and write it back — and be aware the user may be saving at the same moment. Use sparingly: this overwrites the user's saved state and the change is broadcast live. Useful for support ","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","userId","data"],"properties":{"data":{"type":"object","description":"The replacement data object (must be a plain object, not array/null)","additionalProperties":{}},"appId":{"type":"string","description":"The app public ID"},"userId":{"type":"string","description":"User's mongo ObjectId hex string"}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":275,"tokensAfter":275},{"name":"neblla_upload_file","fixed":{"name":"neblla_upload_file","description":"Upload or update a file for an app. Mind the per-app file cap before a bulk import: 30 files on free, 150 on Pro, 25 MB per file. Pass text files (HTML, CSS, JS, TS, JSON, SVG) as raw UTF-8 text with encoding=\"utf8\" (the default) — do NOT base64-encode them, even if they contain quotes, backticks, ${...} or newlines; send the source verbatim. For binary files (images, fonts, audio, video) you have TWO options: (a) pass a public URL via `contentUrl` — Neblla downloads the bytes server-side (recom","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","required":["appId","path"],"properties":{"path":{"type":"string","description":"File path within the app. Canonical: \"index.html\", \"main.css\", \"client.js\", \"server/room.js\". Subfolders allowed for assets."},"appId":{"type":"string","description":"The app public ID"},"content":{"type":"string","description":"File content. For text files: the raw source as-is (NOT base64). For binary files: a base64 string. Required UNLESS `contentUrl` is set."},"message":{"type":"string","maxLength":120,"description":"A short note about this change. It names this version in the app history (neblla_versions), so the history reads like a changelog instead of \"upload: 3 file(s)\". A burst of uploads is one version: pass the same note on each."},"encoding":{"enum":["utf8","base64"],"type":"string","default":"utf8","description":"Content encoding for `content`. Ignored when `contentUrl` is used. Use \"utf8\" for all text files (HTML/CSS/JS/TS/JSON/SVG); \"base64\" only for binary assets."},"contentUrl":{"type":"string","description":"Public URL of the file to upload. Neblla downloads it server-side (no need to base64-encode in this conversation). Use this for any binary asset (images, fonts, audio, video) when you have a URL. Mutually exclusive with `content`."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":537,"tokensAfter":537},{"name":"neblla_validate_requirements","fixed":{"name":"neblla_validate_requirements","description":"Walk the SAME decision map the Neblla dashboard wizard uses, before creating an app. STRONGLY RECOMMENDED before neblla_create_app on any new-app request (skip it only for edits/fixes to existing code). It front-loads every structural decision so the app is born configured right. \n\nHow to use it: pass the `decisions` you can already answer from the user's request. The tool returns every still-pending decision, each with `how_to_infer` (how to answer it YOURSELF from context) and `ask_user` (whet","inputSchema":{"type":"object","$schema":"http://json-schema.org/draft-07/schema#","properties":{"decisions":{"type":"object","default":{},"description":"Decisions answered so far, keyed by decision id. Example: { kind: \"game\", players: \"multi\", modules: [\"social\", \"anticheat\"], palette: { primary: \"#0F7BD7\", secondary: \"#15C2B0\", alert: \"#FF8552\" }, typography: \"techy\" }. Include everything the user stated plus everything you inferred.","additionalProperties":{}}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":299,"tokensAfter":299}],"toolsChanged":73,"tokensSaved":0,"todo":[{"tool":"neblla_alerts","params":["action"]},{"tool":"neblla_auth_secret","params":["action"]},{"tool":"neblla_backups","params":["action"]},{"tool":"neblla_billing","params":["action"]},{"tool":"neblla_booking","params":["to","end","min","set","from","site","slot","step","appId","limit","scope","staff","start","title","action","before","reason","refund","repeat","series","toSlot","booking","capacity","resource","skipBusy"]},{"tool":"neblla_broadcast","params":["action"]},{"tool":"neblla_business","params":["id","to","who","date","from","kind","name","note","site","text","appId","count","email","hours","sites","action","amount","address","capacity","timeZone"]},{"tool":"neblla_byodb","params":["action"]},{"tool":"neblla_byos","params":["action","provider"]},{"tool":"neblla_community_cases","params":["limit","action"]},{"tool":"neblla_create_product","params":["type","defaultLang"]},{"tool":"neblla_credits","params":["kind","appId","delta","action"]},{"tool":"neblla_decide_fog_case","params":["action"]},{"tool":"neblla_developer_email","params":["action"]},{"tool":"neblla_developer_notices","params":["action"]},{"tool":"neblla_export_users","params":["limit"]},{"tool":"neblla_get_moderation_log","params":["offset"]},{"tool":"neblla_git","params":["action"]},{"tool":"neblla_group","params":["name","role","user","appId","group","action"]},{"tool":"neblla_list_communications","params":["skip","sort","limit","readFilter","importantOnly"]},{"tool":"neblla_list_community_entries","params":["limit"]},{"tool":"neblla_list_fog_cases","params":["limit","status"]},{"tool":"neblla_list_users","params":["skip","sort","limit"]},{"tool":"neblla_list_webhook_deliveries","params":["limit"]},{"tool":"neblla_media","params":["alt","text","limit","title","action","offset"]},{"tool":"neblla_moderate_community_entry","params":["action","reason"]},{"tool":"neblla_payments_account","params":["action"]},{"tool":"neblla_pre_access","params":["action"]},{"tool":"neblla_projects","params":["action"]},{"tool":"neblla_promote","params":["action"]},{"tool":"neblla_publish_app","params":["defaultLang"]},{"tool":"neblla_publish_community_entry","params":["tags","title","summary","category"]},{"tool":"neblla_publish_store","params":["store","track","action","packageName"]},{"tool":"neblla_retheme_app","params":["typography"]},{"tool":"neblla_run_panel","params":["name","appId","inputs"]},{"tool":"neblla_sales","params":["skip","limit","action","status","productId"]},{"tool":"neblla_scores","params":["board","limit","action","period"]},{"tool":"neblla_search_users_by_email","params":["skip","limit"]},{"tool":"neblla_server_key","params":["action"]},{"tool":"neblla_set_automation","params":["set","top","days","path","field","hours","limit","table","hoursAhead"]},{"tool":"neblla_set_communication_important","params":["important"]},{"tool":"neblla_set_communication_read","params":["read"]},{"tool":"neblla_set_panel","params":["appId"]},{"tool":"neblla_table_query","params":["table","offset"]},{"tool":"neblla_table_row","params":["id","data","appId","table","action"]},{"tool":"neblla_tax","params":["type","appId","runId","state","action","address","country","taxCode","automatic","taxBehavior","registrationId"]},{"tool":"neblla_team","params":["role","appId","email","action","panels"]},{"tool":"neblla_trades","params":["appId","action","offset","status"]},{"tool":"neblla_update_community_entry","params":["lang","tags","title","summary","category"]},{"tool":"neblla_update_product","params":["defaultLang"]},{"tool":"neblla_versions","params":["action"]}],"note":"Deterministic fixes. Changes marked verify need a human look before shipping (renames, inferred annotations, inferred types, removed text). Parameter descriptions in `todo` need to be written by someone who knows the API."}