## Read first — everything a straightforward document needs **Publish before you polish** — make the FIRST call a SKELETON: the real title, the theme and template you picked, the document's section headings, stubbed. Its response carries the `id` and the url `https://artifactbin.dev/a/`: hand it over at once and say it is live and still filling in. It is — an edit reaches an open reader in seconds, so they watch the sections land. **The reading path never precedes the first publish**; each section then lands as one targeted `edit_artifact`. ```bash curl -X POST https://artifactbin.dev/api/artifacts \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"title":"…","markup":"…","theme":"industry","template":"deck"}' ``` Every write answers `markup_changed`: true = storing rewrote it (formatting, a hoisted ``) and the canonical `markup` rides back — edit against that. A 400 names exactly what to fix. **`title` is what a browser tab and link previews show** — always set it; the on-page heading is not it. **Every `/api` call, `GET` included, sends `Authorization: Bearer `.** **Editing a published document** — send the CHANGE, not the whole file: `GET https://artifactbin.dev/api/artifacts/` returns the current `markup` and an `edit_id`; pass it back with the exact text to swap: ```bash curl -X POST https://artifactbin.dev/api/artifacts//edits \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"edit_id":"…","old_string":"exact text once in the document","new_string":"replacement"}' ``` `old_string` must appear EXACTLY ONCE. Prefer it to replacing the whole document: smaller, and a human may be reading the page live. **markup** is JSX treated as data: ordinary HTML tags for everything including prose (`h1 h2 p ul li blockquote table figure img`, inline `svg`) plus the component kit (`Card`, `Tabs`, `Badge`, `Grid`/`GridItem`, `SlideDeck`/`Slide`, `Icon`, and the data embeds `Question`, `DataTable`, `Number`), styled ONLY with Tailwind utilities via `className` — inline `style=` is rejected. There is no markdown. **Guess rather than look up.** An unknown HTML tag is refused with a 400 carrying the allowed set (`allowed_html_tags`), an unknown component the registry: a wrong guess costs one cheap round trip. One exception: `script iframe object embed base meta link form frame frameset applet noscript` are refused with NO list — never guess them (`
` and `