<!-- Core API · Docs · clayjs · https://clayjs.com/docs/api · generated from the page; every page has one at its path plus .md -->

[Docs](https://clayjs.com/docs) · Core API

# window.clay

Everything on window.clay: saving, reading and writing data, edit mode, and what each plugin adds.

| Member | What it does |
| --- | --- |
| `clay.ready` | A Promise that resolves once clayjs has booted (core loaded, plugins attached). In inline scripts, `await clay.ready` before touching anything else. `clay:ready` fires on `document` at the same moment. |
| `clay.save()` | Snapshot the page and save it. Skips when nothing changed. Returns `Promise<{ok, msg, msgType, code, etag}>`; check `ok`. `msgType` is `success`, `error`, `skipped` (nothing was sent), or `unknown` (the request timed out, so the write may or may not have landed). A host may answer with its own severity, such as `warning`, and that rides through untouched. Never rejects. |
| `clay.save.force()` | Save even when nothing appears to have changed. |
| `clay.save.flush({keepalive, timeoutMs})` | Save now and resolve only when the host has accepted the bytes the page holds at that moment: `Promise<{state, etag}>`, where `state` is `view`, `clean` (the file already holds these bytes, so nothing was sent) or `saved`. Rejects with an `Error` whose `state` is `failed`, `conflict` or `blocked`, the last meaning the bytes are on the host but the page holds work a save cannot write (see `clay.registerUnsavedState`), which only a person can resolve; a refusal is never resolved by overwriting, which stays a person's decision (`clay.save.overwrite()`). In the first seconds after load it waits for the page to settle, so load-time rendering is not written as an edit, and `timeoutMs` bounds the whole call. |
| `clay.hasUnsavedChanges()` | Is there work here that no save has written? Synchronous, and the same question the close warning asks. Answers `false` in view mode. |
| `clay.readData(rules?)` | Read the saved file as `{data, etag}`. Omitted rules use the page's `api` rules tag. Rejects when the host does not announce `data-read`. |
| `clay.writeData(values, {rules, ifMatch})` | Conditionally write JSON into the saved file and return `{data, etag}`. Requires an editable clean page, a connected sync plugin, a trustworthy ETag, and host support. |
| `clay.getHTML()` | The exact HTML string a save would send, after all cleanup. |
| `clay.addDocumentTransform(fn)` | Register a callback that receives the cloned document before serialization. The live page is never touched. |
| `clay.onSnapshot(fn)` | Like `addDocumentTransform`, but runs for every snapshot, including live-sync broadcasts. |
| `clay.isEditMode` | Whether this session may edit (see [edit mode](https://clayjs.com/docs/edit-mode)). |
| `clay.isOwner` | Whether the platform's owner cookie is set. Unlike `isEditMode`, the URL and global overrides don't affect it. |
| `clay.siteVisibility` | How the host serves this site: `"private"`, `"public"`, or `null` when the host says nothing. Read once, at boot. |
| `clay.toggleEditMode()` | Flip between edit and view mode (reloads the page). |
| `clay.cacheBust(el)` | Re-download one resource by stamping `?v=` onto its `href` or `src`. [Details](https://clayjs.com/advanced#save-attrs). |
| `clay.Mutation` | The shared mutation hub. See [Advanced](https://clayjs.com/advanced#libraries). |
| `clay.morph(oldEl, newEl)` | The DOM morphing engine: morphs `oldEl` in place to match `newEl` (sync plugin). |
| `clay.conflicts` | Edits of yours that live sync replaced and you have not decided on: `list()`, `acknowledge(ids, {reason})`, `claim(ids, {owner})`, `recoveryOf(id)` (sync plugin). The lost edit notice draws from it. |
| `clay.registerUnsavedState({id, isPending})` | For an editor whose work can be ahead of the page (its model not yet written into the DOM): `isPending()` is called fresh whenever the close warning checks, and the warning fires while it returns true. Returns `{changed(), dispose()}`; call `changed()` when the answer may have changed. |
| `clay.undo` | The undo singleton: `clay.undo.undo()` / `clay.undo.redo()` (undo plugin). |
| `clay.cms` | The content panel: `clay.cms.open()` (cms plugin). |
| `clay.RichClay` | The rich-text editor class behind `editable` (richclay plugin, default on). |
| `clay.quickcrop(file, opts)` | The crop modal: resolves `{blob, dataURL, width, height}`, or `null` if cancelled (quickcrop plugin). See [Plugins](https://clayjs.com/plugins#quickcrop). |
| `clay.upload` | Pick a file and get it into the page (upload plugin). |
| `clay.me` | Who is editing: `{id, name, initials, color}` or null (people plugin). |
| `clay.people` | Names in this page: `get`, `list`, `available`, `add` (people plugin). |
| `clay.author` | Stamp an element with your id, asking your name once with no host (people plugin). |
| `clay.wire` | A per-file control channel to a process on your own machine: `send`, `cancel`, `get`, `list`, `isBusy`, `on` (wire plugin). See [Plugins](https://clayjs.com/plugins#wire). |
| `clay.aiEdit` | The AI comment box: `clay.aiEdit.init()` (ai-edit plugin). It boots itself, and does nothing unless the host lists a ready `ai-edit` wire helper. See [Plugins](https://clayjs.com/plugins#ai-edit). |

In view mode the edit-only members are absent: `window.clay` keeps `ready`, `toggleEditMode`, `isEditMode`, `isOwner`, `Mutation`, `region`, `readData`, and `writeData` (plus `morph`/`cms` when those plugins load), and `clay.hasUnsavedChanges()`, which answers `false` there because a page that cannot save has nothing unsaved. `writeData` refuses without edit mode. Feature-detect with `'save' in clay`.

Three tiers, and only the first two are a promise:

| Tier | What it is | Promise |
| --- | --- | --- |
| `clay.*` from `clay.js` | The everyday surface, above | Stable |
| `clay.*` from a satellite | Opt-in, one script tag each: `clay-ui`, `clay-utils`, `clay-dom`, `clay-events`, `clay-options`, `clay-internals`, `clay-data` | Stable |
| Anything else under `src/` | Reachable by direct import, because `src/` ships to npm and the CDN | **May change in any release** |

The contract starts at 1.0.0: no name in the first two tiers changes without a major version. Everything in the third tier works today and is welcome to be used, but it is not a promise.
