The SDK — build an app on Witbitz
The SDK is one ES module a static page imports. With it your page creates and opens Spaces, sends and reads messages,
runs the room's assistant, keeps your app's data in the room, signs people in and checks where a room runs — while the
room key, the sealing and the polling stay inside it. Your server, if you have one, uses a second module,
WitbitzAdmin, for your tenant's own settings.
Why your users can trust what you build with it: the overview and the trust model.
Status: public beta. Usable today from any static page. The core — create a room, open it, send, listen — is small and meant to stay stable; around it the surface has grown with the platform (lanes, the store, sign-in, connections, voice, certificates), and any change that can break your code is listed under Changes. It wraps the exact reference client the production Spaces app runs on — nothing is forked or reimplemented.
The whole thing
Creating a room needs your tenant id on the page and the page's origin declared once (Do I need a tenant?); opening or posting in a room someone shared needs neither.
<script type="module">
import { Witbitz } from 'https://witbitz-spaces.pages.dev/sdk/3.10.0/witbitz-sdk.js'
const wb = new Witbitz({ tenant: 't_…' }) // your tenant id
// A private, agent-backed room. The link IS the auth — share it to let someone in.
const space = await wb.createSpace({
agent: { name: 'Helper', instructions: 'You help a small group plan and decide together.' }
})
space.on('message', (m) => console.log(m.from, m.text)) // yours, others', and the agent's — all arrive here
await space.send('Plan us a weekend in Kyoto') // the agent answers on its own turn → via on('message')
console.log('Share this:', space.shareLink)
</script>That page is a complete multiplayer AI app, private to its members: no backend of your own, no key handling, no database.
Try it
Three example apps run live; each one's source is beside it on the same origin.
| Example | What it shows |
|---|---|
A shared assistant room (sdk-example.js, about 40 lines) |
Create or join a room. Open its share link in a second tab to see the same sealed room as another member. |
| The Confidential Room | An advisor you can tell what you would never paste into an ordinary chatbot, beside a live panel that calls space.sealedProof() and shows the server holds only a sealed envelope, under a key it never receives. |
| The Ops Desk | A private assistant that drafts real actions — refunds, emails — but never runs one itself: each is a card you approve, and only then does it run, on your device, from your handler. privateLane({ actions }), on('proposal') and approve(). |
The pages
Start with Rooms and messages and The assistant; the rest you need when your app needs it.
| Page | What it covers |
|---|---|
| Rooms and messages | Create and open a room, read and send messages, replies, reactions, attachments, the room's name, live updates |
| The assistant | Its name, instructions and model, when it answers, a turn you can hold, the assistant as a function, tools, voice |
| Approvals | Proposals from end to end: what is parked, who decides, what runs on approval |
| The room as a store | Your app's rows in the room, schemas, conditional writes |
| People | Sign-in, gated rooms, admins and owners, direct chats, changing members, @usernames |
| Private and outside agents | Each member's private assistant, assistants that ask each other, outside agents over A2A and MCP, AG-UI |
| Cloud computers | A coding agent on the person's own computer in the cloud: make one, open it, its sessions, approvals and files |
| Embed a Space | A room inside your site with one script tag, isolated in its own origin; your page's functions as its tools |
| Accounts and connections | The backup and recovery code, one account on every device, device protection, connections, the Siri inbox |
| Where the keys live | The confidential tier and its proofs, split-key rooms, vault mode |
| Your server | WitbitzAdmin: keys, origins, rooms, customers and billing |
| Reference | Every call, the configuration, the error codes, the limits |
| HTTP API | The /v1 endpoints under the SDK, for when you want the wire |
| Changes | What changed between versions, newest first |
Load it
Versions
Load a version in production: https://witbitz-spaces.pages.dev/sdk/<version>/witbitz-sdk.js (and
witbitz-admin.js, witbitz-sdk.d.ts, witbitz-admin.d.ts beside it; from 2.2.4 also witbitz-admin.mjs, the admin
client in one file for your server). A released version never changes, so our next
release reaches your app only when you move the number. Each version's manifest.json lists a sha384 integrity hash
for every file it contains, if you want the browser to check them too. The versions so far:
/sdk/versions.json.
| Path | What it is |
|---|---|
/sdk/3.10.0/witbitz-sdk.js |
A release: fixed bytes. Use this in production. (/sdk/3.9.0/, /sdk/3.8.1/, /sdk/3.8.0/, /sdk/3.7.0/, /sdk/3.6.1/, /sdk/3.6.0/, /sdk/3.5.0/, /sdk/3.4.0/, /sdk/3.3.0/, /sdk/3.2.0/, /sdk/3.1.0/, /sdk/3.0.0/, /sdk/2.2.4/, /sdk/2.2.3/, /sdk/2.2.2/, /sdk/2.2.1/, /sdk/2.2.0/, /sdk/2.1.2/, /sdk/2.1.1/, /sdk/2.1.0/, /sdk/2.0.0/, /sdk/1.1.0/ and /sdk/1.0.0/ stay as they were.) |
/sdk/latest/witbitz-sdk.js |
Redirects to the newest release. For demos and experiments. |
/witbitz-sdk.js |
The live build of the Spaces app, between releases. Not for production. |
Versions follow semver: a change that can break your code comes in a new major version, listed under Changes
(3.0.0 retired fork() — members change in place). An older release keeps working at its fixed address until what it
depends on is retired, and Changes says when. An npm package is coming.
Your page's CSP
The SDK is a single ES module; importing it from the address above pulls its other files from the same origin. Your page's Content Security Policy must allow:
| For | Add |
|---|---|
| The SDK and the Space API | script-src and connect-src for the SDK's origin and your endpoint |
| Live updates and one account on every device | connect-src wss://code-relay.witbitz.chat |
| Voice input | connect-src wss://inference.tinfoil.sh |
| Reading aloud | connect-src https://inference.tinfoil.sh and media-src blob: |
Invite links
A link reads <viewer>/space?room=<id>#mk=<key> — viewer is new Witbitz({ viewer }) (default: the reference
app), so links open on your page if you set it to your origin and serve /space there. Variants in the fragment:
#s=<share> (a shared-key room), #us=<half>&spc=<commit> (a split-key room), plus &enc=1
(confidential) and &gate=email (email-gated). The fragment never reaches a server. openSpace does not read the
page's URL for you; to check it:
function isSpaceLink(href) {
try {
const u = new URL(href)
const frag = new URLSearchParams(u.hash.slice(1).split('?')[0])
return !!u.searchParams.get('room') && (frag.has('mk') || frag.has('s') || frag.has('us'))
} catch { return false }
}
if (isSpaceLink(location.href)) space = await wb.openSpace(location.href)The reference viewer is the fastest way to see a Space; a production app usually draws its own with
on('message') and send, as the example does.
Do I need a tenant?
| You want to | From | Needs |
|---|---|---|
| Open, read, post in, approve in an existing room | any page | the link — nothing else |
| Create a room | your own origin | new Witbitz({ tenant: 't_…' }) and that origin declared by your tenant (admin.origins.declare, server side). Otherwise the create answers 403 tenant_required. |
| Create a split-key room | your own origin | the above, plus appShare (your half's function) or tenant |
| Builder calls (origins, customers, billing, room deletes) | your server | new WitbitzAdmin({ tenantKey }) (wsk_…, or a restricted key wrk_…) — never in a page |
So "any static page, no backend" holds for everything except creating rooms, which needs a tenant id on the page and a one-time origin declaration — no key on the page, still no backend.
Get a tenant (free; the secret key is shown once — keep it off your page), then declare the origin your page runs on:
curl -sX POST https://api.witbitz.chat/v1/tenants/demo -H 'content-type: application/json' -d '{"name":"My app"}'
# → data.tenant.tenant_id (t_…, for the page) and data.keys.secret (wsk_…, for your terminal or server only)
curl -sX PUT https://api.witbitz.chat/v1/tenant/webhooks -H "authorization: Bearer $WSK" \
-H 'content-type: application/json' -d '{"origins":["https://app.example.com"]}'A demo tenant expires unless you upgrade it, keeping the same id and keys (Keeping it). The origin rules: Your page origins.
Machine-readable source: sdk.md · Generated 2026-10-27T07:05:42Z · build f301388c · every doc in one fetch: llms-full.txt (HTML)