HTML body
Read and write the body of an html-kind Surface. Three separate fields (html, css, js) stored independently, rendered together inside a sandboxed iframe served from render.trydock.ai. For block-level html inside doc bodies, see the Doc body API instead and emit a ```html fenced block.
Generated from src/lib/api-paths/html.ts. The body schema (UpdateHtmlSchema) is defined inline in that file.
get/api/workspaces/{slug}/html
Read an HTML surface body
Returns the resolved html surface's body (html, css, js). Empty fields when the surface exists but no body has been written yet. 404 when `?surface_slug=` doesn't match a live html surface.
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
surface_slug | string | no | Surface slug for multi-html workspaces. Omit for the primary html surface (lowest position). |
surface | string | no | Legacy alias for `surface_slug`. Same semantics. Prefer `surface_slug` in new code. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Resolved html body. |
404 | Error | `surface_slug` didn't match any live html surface in this workspace. |
put/api/workspaces/{slug}/html
Replace fields on an HTML surface body
Partial-update friendly. Any of `html` / `css` / `js` can be omitted to leave the column unchanged; pass empty string to clear. HTML is sanitized at write time (see UpdateHtmlSchema). Emits `html.created` on first write to a surface, `html.updated` on subsequent writes — both carry per-field byte counts so webhooks can fan out a meaningful signal. Requires editor role.
Auth: Bearer token (API key or OAuth access token).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
slug | string | yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
surface_slug | string | no | Optional html surface slug. Omit to write to the primary html surface (lowest position). |
html | string | no | Sanitized at write time. Caps: 50 KB per field. Omit to leave the column unchanged; pass empty string to clear. Sanitizer strips <script>, every on* event handler, javascript:/vbscript:/file:/data:text/html URIs, <iframe>, <object>, <embed>, <foreignobject>, <meta>, <base>, <link>, formaction smuggling. Allows inline <style>, ARIA attrs, form markup (rendered but inert), data:image/* URIs. |
css | string | no | CSS source. Cap: 100 KB. Stored verbatim. Omit to leave unchanged; pass empty string to clear. |
js | string | no | JS source. Cap: 100 KB. Stored but not executed at v1. |
Responses
| Status | Body | Description |
|---|---|---|
200 | object | Written. |
400 | Error | Body shape failed validation, OR a field exceeded its byte cap (html 50KB, css 100KB, js 100KB, total envelope 250KB), OR sanitizer rejected the html field entirely. |
404 | Error | `surface_slug` didn't match any live html surface, or the workspace has no html surface yet. |
Frequently asked questions
- How do I read a Dock html surface body via the REST API?
- GET `/api/workspaces/:slug/html`. Returns `{ html, css, js, updatedAt, updatedBy, updatedByPrincipalType, surfaceSlug }`. Empty strings when the surface exists but no body has been written yet. Multi-html workspaces accept `?surface_slug=<slug>` to target a specific tab.
- How do I write to a Dock html surface body via the REST API?
- PUT `/api/workspaces/:slug/html` with any combination of `{ html, css, js, surface_slug? }`. Omitted fields stay unchanged; pass empty string to clear. The html field is sanitized at write time. Editor role required.
- What does the html sanitizer strip and what does it keep?
- Stripped: <script>, every on* event handler (onclick, onload, etc.), javascript:/vbscript:/file:/data:text/html URIs, <iframe>, <object>, <embed>, <foreignobject>, <meta>, <base>, <link>, formaction/formmethod/formtarget smuggling. Allowed: standard HTML5 structural tags, inline <style>, ARIA attributes, form markup (rendered but inert at v1 since the sandbox is scripts-off), data:image/* URIs for inline images. See sanitizeHtmlBlock in src/lib/html-sanitize.ts.
- What are the size caps on an html surface body?
- Per-field: html 50 KB (post-sanitize), css 100 KB, js 100 KB. Total envelope: 250 KB across all three fields. Beyond these, split into multiple html surfaces or wait for the upcoming "site" workspace kind.
- Does Dock execute the JS field?
- Not at v1. The sandbox iframe runs with scripts disabled by default; the js column is stored for forward-compat. An interactive mode (workspace-setting toggle to flip `allow-scripts` on the sandbox) is queued as a follow-up.
- How do I pre-flight an html payload before writing?
- MCP `validate_html(html?, css?, js?)` returns `{ ok, errors, warnings, parsed }` with byte counts per field plus a `htmlDropped` warning when the sanitizer would strip anything from the html field. Pure analysis — never writes.
- Can my AI agent get notified when a Dock html surface changes?
- Yes. Subscribe to `html.created` for first-write signals or `html.updated` for subsequent edits. Both events carry per-field byte counts; `html.updated` also includes before/after deltas so consumers can render "Argus added 1.2 KB of CSS" style activity rows without diffing. Learn more →
- Why is the rendered iframe served from a different origin?
- Defense in depth. Even if a sanitizer slip leaked a <script> tag, executing it on render.trydock.ai (a different origin) means it can't read *.trydock.ai cookies, localStorage, Stripe state, or any other Dock surface. Browser cookie scoping by host enforces the isolation.
- What's the difference between the ``` ```html ``` doc block and an html surface?
- Block lives INSIDE a doc body, capped at 5 blocks * 50 KB per doc — for embedded mockups inside a brief or note. Surface IS its whole tab in a workspace, with three separately-stored fields, its own page, its own MCP tools — for mockups that are the artifact, not an illustration inside another artifact.
Related
- Web → HTML surface — UI guide for the same surfaces (textareas + live preview, more polish coming).
- Doc formats → HTML block — when to use the block-level
```htmlfence instead. - Surfaces API — create an html surface in a workspace.
- Webhooks API — subscribe to
html.createdandhtml.updated.