App SDKan app that already knows the space.
Space Apps run inside Wakato with the window.wakato SDK: their own records, the Space's real tables and contacts, mail, the device, AI models and payments, each behind a capability.
A Space App is a small web app that lives in a Space: a check-in board, a
quote builder, a content planner, a game. It runs in a sandboxed iframe on the
web and inside the phone app, and it reaches Wakato only through one object,
window.wakato, injected by the host. Every method returns a Promise.
The sandbox blocks fetch, XHR and WebSockets, and silently ignores alert,
confirm and prompt. Everything the app needs goes through the SDK, and the
app draws its own dialogs.
How apps are built
Apps are written by agents over MCP. Connect a coding agent with a Space
key (see Use with Claude Code) holding the apps
scope, and it works through these tools:
get_app_sdk_referenceandintrospect_capabilities: the full SDK text and the capability keys and models this Space allows. Read them first.create_appfor the first draft, thenedit_app_codefor every change (find-and-replace patches, never the whole document again). Saves are checked for syntax and runtime errors, and capabilities the code uses are declared for you.test_app: runs the app headless against sample records and reports a pass or fail, with optional click and expect steps.publish_app: goes live and records a version.rollback_appandget_app_logsare there when something breaks.
The App Builder in the Space is a manual editor for people. There is no AI inside it; the authoring happens through your agent.
Apps are Preact + HTM (or Phaser for games), styled with Tailwind, and must work at phone width (about 390px).
The standalone rule
An app's core job must work for a person clicking buttons while every agent's
computer is off. If the app generates text, images, speech or video, the app
calls wakato.gateway.* itself. Work that genuinely needs a machine (ffmpeg,
long browser automation) is declared in app_schema.agent_features and queued
with wakato.agent.requestWork, so any agent can pick it up.
The app's own records
wakato.data.* is the app's private store: JSON records grouped by model name.
const open = await wakato.data.query("orders", {
filters: { status: ["open", "late"], total: { gt: 50 } },
order_by: "created_at",
ascending: false,
limit: 20,
count: true, // adds a non-enumerable `total`
});
const rec = await wakato.data.insert("orders", { customer: "Ana", total: 120, status: "open" });
await wakato.data.update("orders", rec.id, { status: "shipped" });
const stop = wakato.data.onChange(({ event, model, record }) => redraw());Totals are computed on the server: wakato.data.aggregate(model, { group_by, metrics: ["count", "sum:total"] }). Never sum a query result past 200 rows.
The Space's own things
An app is a view over the Space's real data, never a copy. These doors act as the person using the app, with exactly their permissions.
const { space, my_role } = await wakato.space.get(); // timezone included
const { members } = await wakato.space.members();
const { tables } = await wakato.tables.list();
const leads = tables.find((t) => t.plural_label === "Leads");
const { rows } = await wakato.tables.query(leads.id, {
filters: { status: "new" },
sort: [{ column_key: "created_at", direction: "desc" }],
limit: 20,
});
wakato.tables.onChange(leads.id, ({ event, row }) => applyChange(event, row));
await wakato.contacts.create({ first_name: "Ana", email: "ana@example.com", tags: ["Waitlist"] });
await wakato.mail.send({ to: "ana@example.com", subject: "Thanks", text: "We got your request." });
await wakato.notify.send({ title: "New lead", body: "Ana joined the waitlist", to: "admins" });Tables are addressed by id; read tables.columns(id) before writing, since
a row's fields are the column keys. wakato.tables.myAccess(id) tells the app
what this person may do so it draws only those buttons.
Beyond these, the SDK covers tasks, documents, forms, bookings, meetings,
calendar, the shop and its orders, jobs, the inbox, Wakato Pass, the books,
social, SMS and calls, search and the activity trail. get_app_sdk_reference
lists every method.
The device
Each device door is asked of the person first (a consent bar, the camera or
file picker, then the system prompt). A refusal rejects with declined, which
is a normal answer.
try {
const pos = await wakato.device.getLocation({ accuracy: "precise" });
const { now } = await wakato.app.now(); // the server's clock, not the phone's
await wakato.tables.insert(checkins.id, { lat: pos.lat, lng: pos.lng, at: now });
} catch (e) {
showBanner("No location shared");
}Also scanCode, capturePhoto, pickFile, share, vibrate and
watchLocation. Call them from a button, never on load.
AI models
Calls are charged to the Space wallet and attributed to the app.
const { content } = await wakato.gateway.llm({
messages: [{ role: "user", content: "Three subject lines for a spring sale" }],
});
const { job_id } = await wakato.gateway.run(videoEndpointKey, { prompt });
const job = await wakato.gateway.waitForJob(job_id);gateway.listEnabledEndpoints() says which models the Space allows.
Money, the outside world and paper
payments.createLink/createInvoicehand people a pay page on the shop run from the Space. An app never takes a card itself.http.fetch(url, opts)goes out through Wakato and may carry the Space's secrets as${secret:NAME}, filled in at the edge and never shown to the app. Private addresses are refused; redirects are not followed.render.pdf({ html })turns markup into a PDF in the Space's assets.
The agent, in the app
wakato.agent.chat.*is the person's own line with the Space's agent:open,send,messages,onMessage,ask.wakato.agent.bot.send(text)talks to the agent's External Reach on its website channel: instant, no machine to wake.wakato.agent.requestWork(feature, inputs)queues a declared machine task.
Capabilities and consent
Every door belongs to a capability, such as tables.read, mail.send,
device.location or gateway.llm. An app built by an agent is deny-by-default:
it holds only what app_schema.required_capabilities declares, and the owner
can take any capability back in the app's details. A refused call says which
capability is missing.
Running on a clock
Declare schedules and Wakato runs a named pipeline on that clock, as the Space owner, with nobody looking:
"schedules": {
"daily_summary": { "cron": "0 18 * * 1-5", "timezone": "Europe/Paris", "pipeline": "summary" }
}Up to 20 schedules; a bad cron is refused at save. Space automations also hear
the app's records through the app_record_created, app_record_updated and
app_record_deleted triggers.
Webhooks into an app
const { url } = await wakato.app.webhookUrl("stripe");Give that URL to another system. Each POST becomes one record of model
_webhook_stripe, which the app reads with data.query or watches with
data.onChange (capability webhooks.receive, up to 256 KB per delivery and
120 a minute).
Try it safely
Someone who can change the app can switch on Try it safely in the app's
header. Reads stay real; anything that writes, sends, spends or leaves Wakato
answers { would: true } and does nothing. Only people who may edit the app
can use it, checked on the server.
A guide for agents
An app can carry a plain-text guide for agents (up to 20,000 characters):
what it is for, what each model holds, where to start, what never to do. Set it
with agent_guide on create_app / update_app, or in the app's details under
For agents. get_app returns it first to any agent that opens the app, so
"work in the Quotes app" is enough.
Next
- The whole reference: call
get_app_sdk_referencefrom any MCP client. - The app tools: /developers/tools/apps.
- The REST routes: /developers/reference/apps.
Events and webhooks
Hear what happens in a Space as it happens: signed webhooks to your own URL, a feed you pull by cursor, or a line in an agent's chat.
Storefront SDK
Design the product page and even the checkout form your own way, bound live to a Wakato listing, while the payment always runs on Wakato.