# 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](./overview.md#the-guarantee) and [the trust model](./trust-model.md). > **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](./sdk-changes.md#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?](#tenant)); opening or posting in a room someone shared needs neither. ```html ``` 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](https://witbitz-spaces.pages.dev/sdk-example.html) (`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](https://witbitz-spaces.pages.dev/confidential-example.html) | An advisor you can tell what you would never paste into an ordinary chatbot, beside a live panel that calls [`space.sealedProof()`](./sdk-keys.md#certificates) and shows the server holds only a sealed envelope, under a key it never receives. | | [The Ops Desk](https://witbitz-spaces.pages.dev/approvals-example.html) | A private assistant that drafts real actions — refunds, emails — but never runs one itself: each is a card you [approve](./sdk-approvals.md), and only then does it run, on your device, from your handler. `privateLane({ actions })`, `on('proposal')` and `approve()`. | ## The pages {#path} Start with [Rooms and messages](./sdk-rooms.md) and [The assistant](./sdk-assistant.md); the rest you need when your app needs it. | Page | What it covers | |---|---| | [Rooms and messages](./sdk-rooms.md) | Create and open a room, read and send messages, replies, reactions, attachments, the room's name, live updates | | [The assistant](./sdk-assistant.md) | Its name, instructions and model, when it answers, a turn you can hold, the assistant as a function, tools, voice | | [Approvals](./sdk-approvals.md) | Proposals from end to end: what is parked, who decides, what runs on approval | | [The room as a store](./sdk-store.md) | Your app's rows in the room, schemas, conditional writes | | [People](./sdk-people.md) | Sign-in, gated rooms, admins and owners, direct chats, changing members, @usernames | | [Private and outside agents](./sdk-agents.md) | Each member's private assistant, assistants that ask each other, outside agents over A2A and MCP, AG-UI | | [Cloud computers](./sdk-computers.md) | A coding agent on the person's own computer in the cloud: make one, open it, its sessions, approvals and files | | [Embed a Space](./embed.md) | A room inside your site with one script tag, isolated in its own origin; your page's functions as its tools | | [Accounts and connections](./sdk-accounts.md) | The backup and recovery code, one account on every device, device protection, connections, the Siri inbox | | [Where the keys live](./sdk-keys.md) | The confidential tier and its proofs, split-key rooms, vault mode | | [Your server](./sdk-builder.md) | `WitbitzAdmin`: keys, origins, rooms, customers and billing | | [Reference](./sdk-reference.md) | Every call, the configuration, the error codes, the limits | | [HTTP API](./api-reference.md) | The `/v1` endpoints under the SDK, for when you want the wire | | [Changes](./sdk-changes.md) | What changed between versions, newest first | ## Load it ### Versions {#versions} Load a **version** in production: `https://witbitz-spaces.pages.dev/sdk//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`](https://witbitz-spaces.pages.dev/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](./sdk-changes.md#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 {#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](./sdk-rooms.md#live) and [one account on every device](./sdk-accounts.md#account-index) | `connect-src wss://code-relay.witbitz.chat` | | [Voice input](./sdk-assistant.md#dictation) | `connect-src wss://inference.tinfoil.sh` | | [Reading aloud](./sdk-assistant.md#speak) | `connect-src https://inference.tinfoil.sh` and `media-src blob:` | ### Invite links {#invite-links} A link reads `/space?room=#mk=` — `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=` (a shared-key room), `#us=&spc=` (a [split-key](./sdk-keys.md#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: ```js 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? {#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](./sdk-builder.md#restricted-keys) `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: ```sh 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](./vault-spaces.md#keeping-it)). The origin rules: [Your page origins](./sdk-builder.md#origins).