Mini-apps#
A mini-app is an ordinary web page you host, opened inside a Mafold conversation. It gets a signed identity for the current user, a realtime document shared with everyone in the chat, and panel chrome — through a single global, window.Mafold.
If you've built a Telegram Mini App, the shape will feel familiar: you own the URL and the code; Mafold provides the host, the identity handshake, and the capabilities.
The one-line version
Ship a URL, add the @mafold/webapp SDK, register the app to get a signing secret. Mafold opens your page in a panel, hands it a verifiable identity token, and gives every open copy a shared realtime room.
The mental model#
Your page
A mini-app is your web app on your hosting (or free Mafold hosting). Mafold never runs your code — it loads your URL in a sandboxed panel and talks to it over a message bridge.
Identity, verified
On launch, Mafold hands your page a short-lived signed token (initData, a JWT). Send it to your backend and verify it with your app secret — that's how you trust who is using the app. See Authentication.
A shared room
Every conversation an app is open in has one shared room — a realtime document (a CRDT). Anyone can read and write it; changes converge and broadcast live. This is how you build multiplayer: a counter, a poll, a game board. See the SDK reference.
Embedded vs standalone#
The same build runs in two modes, and the SDK abstracts the difference:
| Embedded | Standalone | |
|---|---|---|
| Opened | inside a Mafold chat panel | your URL, directly in a browser |
| Identity | instant, from the signed launch token | Mafold.auth() → Login with Mafold (PKCE) |
room.* | over the host bridge | transparently over Mafold's HTTP API |
chat.* | available | rejects with { code: "not_embedded" } |
Mafold.embedded tells you which mode you're in. You rarely need to branch on it — auth() and room.* do the right thing automatically; only chat-only features (sendMessage, reading the thread) require the embedded host.
Add the SDK#
Two ways to include it — same Mafold object either way.
<!-- 1) Versioned script tag (immutable-cached) -->
<script src="https://mafold.com/js/mafold-webapp@0.3.0.js"></script>// 2) npm — ships TypeScript types
import Mafold from "@mafold/webapp";Minimal app#
Mafold.ready(); // tell the host the UI is up
const user = Mafold.initDataUnsafe?.user; // display-only identity (unverified)
document.querySelector("#hi").textContent = `Hi, ${user?.display_name}`;
await Mafold.room.open(); // the conversation-shared document
Mafold.room.onUpdate((doc) => render(doc)); // re-render on any participant's change
await Mafold.room.increment("taps"); // everyone sees it, liveThat's a complete multiplayer app: every person who opens it in the same chat shares the taps counter, and each tap broadcasts to all of them.
Test locally
Point your app at the launch harness at mafold.com/harness — it mints a dev launch token and opens your local URL as if it were embedded, so you can develop against the real bridge without publishing.
Next steps#
- SDK reference — every
Mafold.*method. - Authentication — verify
initDataon your backend. - Publishing — register, get your secret, and host.