Docs

Deploy a single HTML file and give it a real backend — database, files, and AI — with zero config and zero keys in your code. For humans and agents.

Deploy a site

Three ways to ship the same single HTML file — each gives you a live URL at name.myhtml.site.

  1. Dashboard — paste or drop your HTML in the New site box. You'll see a live name.myhtml.site preview and whether the name is free before you deploy.
  2. REST API — one call with your API key:
curl -X POST https://myhtml.io/api/v1/sites \
  -H "Authorization: Bearer mh_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "slug": "pong", "html": "<!doctype html>…" }'

A CLI (npx myhtml-cli) and the MCP server are launching soon. Free sites publish via a quick safety scan (the “slow lane”); paid plans publish instantly. Pick a name that's taken and you'll get a clear error — names are first-come and never overwrite someone else's site.

The mh SDK

When the edge serves your page it injects a short-lived, origin-bound site token and the SDK, so window.mh is just there. The token scopes every call to your site, so there are no API keys in your HTML and nothing for a visitor to steal. The surface:

  • mh.db — a per-site JSON database (shared + per-visitor + key-value).
  • mh.files — file uploads served from your subdomain.
  • mh.ai — call Claude from your page (opt-in; see below).
  • mh.visitor() — the current visitor's anonymous id.
  • mh.realtime — websockets/presence (phase 2).

mh.db — the database

Every site gets its own document database — no setup, no schema, no connection string. A collectionholds JSON documents you create, query, update, and delete. Behind the scenes it's stored as JSONB and scoped to your site; you just call the SDK.

// mh is auto-injected at serve time — no <script> config, no keys.
const scores = mh.db.collection('scores');

// Create a document (returns it with a generated id):
const doc = await scores.create({ player: 'alice', points: 42 });

// List — filter (shallow equality), sort ('-field' = desc), limit, cursor:
const { docs, nextCursor } = await scores.list({
  filter: { player: 'alice' },
  sort: '-points',
  limit: 10,
});

await scores.get(doc.id);                 // one doc
await scores.update(doc.id, { points: 99 }); // shallow-merge patch
await scores.delete(doc.id);

Shared vs. per-visitor

Collections and mh.db.set/get are shared — every visitor of your site reads the same data (great for leaderboards, guestbooks, galleries). mh.db.user is private to each visitor, keyed by an anonymous per-browser id (great for saved progress or preferences).

// Shared — every visitor of this site sees these:
await mh.db.collection('guestbook').create({ name: 'sam' });

// Site key-value sugar (also shared):
await mh.db.set('theme', 'dark');
const theme = await mh.db.get('theme');

// Per-visitor — private to each browser (anonymous visitor id):
await mh.db.user.set('progress', { level: 7 });
const me = await mh.db.user.get('progress');

list() filters are shallow equality ({ status: 'done' }); sort is '-field' for descending. Calls are rate-limited per site and per visitor, and you own all of it — wipe or delete the site anytime from your dashboard.

mh.files — uploads

Upload a File or Blob straight from the page and get back a URL served from your own subdomain at /_f/<key>.

const { url } = await mh.files.upload(file); // → https://<you>.myhtml.site/_f/<key>
await mh.files.list();
await mh.files.delete(path);

mh.ai — AI on a page

Live — enable it per site

mh.ai.chat()lets a page call Claude with no API key in your code — for a roast of a high score, a “make it vegan” button, an NPC, a summariser. It's off by default on every site. You turn it on per site under Settings → AI, and choose who pays:

  • Off — pages can't use AI (the call returns a clear error).
  • On — you pay — visitors use AI on your credits, capped per day so a viral page can't drain your wallet.
  • On — visitor pays — each visitor signs in and uses their own credits.
// Turn AI On for the site first (Dashboard → site → Settings).
const res = await mh.ai.chat([
  { role: 'user', content: 'Roast my high score of 42.' },
]);
console.log(res.content);

Calls run through our metered proxy (a fast, cost-effective model by default), count toward your site's daily cap, and are rate-limited per visitor. “Visitor pays” arrives with visitor sign-in (phase 3), and mh.realtime (websockets + presence) is phase 2.

REST API

Base URL https://myhtml.io/api/v1. Authenticate with a session cookie (dashboard) or an API key from Dashboard → API keys.

curl -X POST https://myhtml.io/api/v1/sites \
  -H "Authorization: Bearer mh_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "slug": "pong", "html": "<!doctype html>…" }'
MethodPathPurpose
POST/sitesCreate + first deploy (errors if name taken)
GET/sitesList your sites
GET/sites/check?slug=Is a name available?
POST/sites/:slug/deployRe-deploy an existing site
PATCH/sites/:slugUpdate settings (AI, visibility)
DELETE/sites/:slugDelete a site (storage purged)
GET/sites/:slug/data/:collectionRead a site's mh.db collection
GET / POST/keysList / create API keys
GET/usageUsage vs plan limits

Agents: the MCP server

Launching soon

The MCP server will wire myhtml into Claude (or any MCP client) in one line — create_site, update_site, list_sites, read_site_data, and more, authenticated with your API key. Until it ships, agents use the REST API above (it covers everything).

claude mcp add --transport http myhtml https://mcp.myhtml.io/mcp

The free-site badge

Free sites carry a small badge that the edge injects automatically. Upgrade to a paid plan to remove it.

made with myhtml— links back to myhtml.io