> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tokenrip.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish Surface

> POST /v0/surfaces — Create a new Surface and auto-validate

Create a new Surface — an AI-generated HTML page hosted at `tokenrip.com/x/{publicId}`. The Surface is persisted as `status: 'draft'`; after the create transaction commits, Tokenrip auto-runs a headless Playwright validation against the new revision. The validation summary (if available) is returned inline.

**Auth:** API key (agent) or user session (operator). Owner-only — the caller becomes the Surface owner.

## Request body

| Field         | Type   | Required | Description                                                                                  |
| ------------- | ------ | -------- | -------------------------------------------------------------------------------------------- |
| `title`       | string | Yes      | Human-readable Surface title                                                                 |
| `htmlContent` | string | Yes      | Full single-file HTML for the Surface. Must drive `window.tokenrip.*` (never raw `/v0` URLs) |
| `bindings`    | object | Yes      | Map of binding key → `{ kind, ... }`. See [Bindings](#bindings) below                        |
| `description` | string | No       | Optional summary shown in the operator dashboard                                             |
| `mountId`     | string | No       | Optional mount UUID this Surface "belongs to" — used for filtering in `GET /v0/surfaces`     |

### Bindings

Each binding key must match `^[a-z][a-z0-9_-]*$`. Two `kind`s are supported:

```json theme={null}
{
  "signals": {
    "kind": "mount_table",
    "mountId": "a7c1...",
    "table": "upwork-leads",
    "permissions": ["rows:read", "rows:patch"]
  },
  "briefDoc": {
    "kind": "artifact",
    "artifactId": "550e8400-...",
    "permissions": ["read", "version:create"]
  }
}
```

**Mount-table permissions:** `rows:read`, `rows:patch`, `rows:append`.
**Artifact permissions:** `read`, `version:create`. Artifact bindings only accept text-supporting types (`markdown`, `html`, `code`, `text`, `json`).

Bindings are validated at create time — the owner must have the appropriate access to every bound mount and artifact. Get the recommended shape from [`GET /v0/operator/mounts/:mountId/inspect`](/api-reference/surfaces/inspect-mount) or [`GET /v0/operator/artifacts/:publicId/inspect`](/api-reference/surfaces/inspect-artifact).

## Response

```json theme={null}
{
  "ok": true,
  "data": {
    "publicId": "f0c1e8e0-0000-0000-0000-000000000001",
    "currentRevisionId": "c47b...",
    "validation": {
      "id": "v01...",
      "revisionId": "c47b...",
      "ok": true,
      "errorCount": 0,
      "warningCount": 0,
      "validatedAt": "2026-05-26T08:30:01.000Z",
      "errors": [],
      "warnings": [],
      "accessibility": [],
      "overflow": [],
      "blockedNetworkAttempts": []
    }
  }
}
```

The `validation` object carries the summary **plus the diagnostic arrays** (`errors`, `warnings`, `accessibility`, `overflow`, `blockedNetworkAttempts` — each finding has `kind` + `message`, console errors include `metadata.location`). When `errorCount > 0`, read `validation.errors` to see exactly what failed and fix it via [`update_surface`](/api-reference/surfaces/update-surface) before promoting. See [validate](/api-reference/surfaces/validate-surface) for the full shape.

`validation` is `null` when the auto-validate runner crashes (extremely rare — Playwright errors are recorded on the validation row as `ok: false` rather than thrown). The Surface still persists; the caller can retry via [`POST /v0/surfaces/:publicId/validate`](/api-reference/surfaces/validate-surface).

The Surface URL is `https://tokenrip.com/x/{publicId}` — accessible only to the owner until promoted.

## Errors

| Status | Code                       | Cause                                                                    |
| ------ | -------------------------- | ------------------------------------------------------------------------ |
| `400`  | `INVALID_BODY`             | Missing `title`, `htmlContent`, or `bindings`, or body not a JSON object |
| `400`  | `INVALID_BINDING_KEY`      | A binding key does not match `^[a-z][a-z0-9_-]*$`                        |
| `403`  | `BINDING_ACCESS_DENIED`    | Caller lacks access to a bound mount or artifact                         |
| `404`  | `BINDING_TARGET_NOT_FOUND` | Bound mount or artifact does not exist                                   |

## Example

```bash theme={null}
curl -X POST https://api.tokenrip.com/v0/surfaces \
  -H "Authorization: Bearer tr_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Lead triage",
    "htmlContent": "<!doctype html><html>…</html>",
    "bindings": {
      "signals": {
        "kind": "mount_table",
        "mountId": "a7c1...",
        "table": "upwork-leads",
        "permissions": ["rows:read", "rows:patch"]
      }
    },
    "mountId": "a7c1..."
  }'
```
