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

# Manifest

> The chatblocks.json schema — formats, sizes, widget, binding, permissions.

Every block has a `chatblocks.json` manifest at the root of its file set. The manifest is the contract between your code and the platform: it declares the format, the canvas footprint, what data flows in, and what the iframe is allowed to do.

The full Zod schema lives in `packages/shared/src/manifest.ts`. This page covers the shape; the schema there is the source of truth and what the server validates against.

## Minimal manifest

A barebones `code` block with no widget or data binding:

```json theme={null}
{
  "schemaVersion": 1,
  "format": "code",
  "entry": "src/App.tsx",
  "defaultSize": { "width": 320, "height": 200 }
}
```

That's all that's required. Everything else has sensible defaults.

## Top-level fields

| Field           | Type                  | Required                                   | Notes                                                                                                                        |
| --------------- | --------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| `schemaVersion` | `1`                   | yes                                        | Currently always `1`. Schema bumps go through a coordinated migration.                                                       |
| `format`        | `"code"` \| `"embed"` | no (defaults to `"code"`)                  | See [Formats](#formats).                                                                                                     |
| `entry`         | string                | yes for `code`, must be absent for `embed` | Relative path ending in `.js`, `.jsx`, `.ts`, `.tsx`, or `.html`. No URLs, absolute paths, parent traversal, or backslashes. |
| `defaultSize`   | `{ width, height }`   | yes                                        | Canvas footprint in pixels. Each dimension is an integer in `[50, 2000]`.                                                    |
| `constraints`   | object                | no                                         | Min/max resize bounds. Defaults to 0.6×/2.5× of `defaultSize`, clamped to `[50, 2000]`.                                      |
| `resizable`     | boolean               | no (defaults `true`)                       | Set `false` for fixed-shape blocks like a stamp or a specific-aspect-ratio widget.                                           |
| `permissions`   | array                 | no (defaults `[]`)                         | See [Permissions](#permissions).                                                                                             |
| `network`       | object                | no                                         | `{ allowedOrigins: [] }`. Required when `"network"` permission is declared.                                                  |
| `embed`         | object                | yes for `embed`, must be absent for `code` | See [Formats](#formats).                                                                                                     |
| `widget`        | object                | no                                         | iOS widget projection. See [Widget](/authoring/widget).                                                                      |
| `binding`       | object                | no                                         | Live-data binding. See [Binding](#binding).                                                                                  |

## Formats

Chatblocks has two first-class formats. The bar for adding a new one is a platform invariant the AI generator can't be trusted to uphold on its own — everything else stays `code`.

<Tabs>
  <Tab title="code">
    Vibe-coded block. Your file set is built into an iframe-loaded artifact via Vercel Sandbox. Strict CSP, no cross-origin framing, sandboxed iframe.

    Required: `entry` must be a relative path to a file in your set. Conventional: `src/App.tsx`.

    ```json theme={null}
    {
      "schemaVersion": 1,
      "format": "code",
      "entry": "src/App.tsx",
      "defaultSize": { "width": 320, "height": 200 }
    }
    ```
  </Tab>

  <Tab title="embed">
    First-class live-website embed. The manifest declares a URL; the platform renders a fixed sandboxed iframe shell pointing at it. URL is visible in the block chrome and probed for framability at publish time.

    Required: `embed.url` (HTTPS, public hostname, no credentials). Optional: `embed.sandbox` (`"scripts-only"` default, or `"strict"` for no JS).

    ```json theme={null}
    {
      "schemaVersion": 1,
      "format": "embed",
      "defaultSize": { "width": 600, "height": 400 },
      "embed": {
        "url": "https://example.com/dashboard",
        "sandbox": "scripts-only"
      }
    }
    ```

    Embed blocks can't declare `permissions` or `network.allowedOrigins` — the embedded site manages its own network, and the iframe sandbox is platform-enforced.
  </Tab>
</Tabs>

## Permissions

Declare the capabilities your `code` block needs. Each is opt-in.

| Permission       | Grants                                                                    |
| ---------------- | ------------------------------------------------------------------------- |
| `viewer.profile` | Read the viewer's display name and avatar (when permitted by visibility). |
| `storage.kv`     | Per-block key/value storage scoped to the viewer.                         |
| `assets`         | Upload and serve static assets via Convex storage.                        |
| `events`         | Emit custom events into the audit log.                                    |
| `forms`          | Render `<form>` elements with submission posting back to the platform.    |
| `network`        | Make outbound HTTP requests. Requires `network.allowedOrigins` to be set. |

The `network` permission and `network.allowedOrigins` are coupled — declaring one without the other is a validation error. Origins must be HTTPS, origin-only (no path/query/hash/credentials, no wildcards).

```json theme={null}
{
  "permissions": ["network"],
  "network": {
    "allowedOrigins": ["https://api.example.com"]
  }
}
```

## Binding

Optional. Connects the block to a configured data source so the platform refreshes its widget data on a cron (or push) cadence.

```json theme={null}
{
  "binding": {
    "dataSourceId": "<id-from-/settings/data-sources>",
    "queryConfig": {
      "type": "stripe.mrr",
      "params": {}
    },
    "projection": {
      "fields": {
        "value": { "source": "amount_cents", "format": "currency" },
        "label": { "literal": "MRR" }
      }
    }
  }
}
```

The `queryConfig.type` is one of `KNOWN_QUERY_TYPES` (see `packages/shared/src/binding.ts`):

* `stripe.mrr`, `stripe.balance`, `stripe.recentCharges`
* `postgres.sql`
* `webhook.latest`, `webhook.aggregate`, `webhook.list`
* `mcp.tool`

The `projection` maps raw query output into the widget's `data` shape. Each field is either `{ source, format?, formatOptions? }` (pluck from the query result with optional formatting) or `{ literal }` (constant value). Formats: `currency`, `number`, `percent`, `percent-with-sign`, `datetime`.

Cross-reference: the [Connecting data](/connecting-data/stripe) section walks through each connector and the queries it supports.

## Constraints reference

`constraints.min*` and `constraints.max*` set the block's resize bounds on a canvas. Defaults are derived from `defaultSize`:

* `min*` = `defaultSize.* × 0.6`, clamped to ≥ 50.
* `max*` = `defaultSize.* × 2.5`, clamped to ≤ 2000.

Override explicitly when your block has a tighter aesthetic range:

```json theme={null}
{
  "defaultSize": { "width": 320, "height": 200 },
  "constraints": {
    "minWidth": 280,
    "minHeight": 160,
    "maxWidth": 500,
    "maxHeight": 320
  }
}
```

The validator enforces `min ≤ max` for each axis, and `defaultSize` must lie within `[min, max]`.

## Validation errors

When validation fails, the API returns a list of issues, each with a stable code:

| Code                         | Meaning                                                               |
| ---------------------------- | --------------------------------------------------------------------- |
| `missing_entry`              | `code` format requires `entry`.                                       |
| `invalid_entry`              | `entry` path failed the relative-path / extension check.              |
| `unexpected_entry`           | `embed` format must not declare `entry`.                              |
| `missing_embed`              | `embed` format requires `embed.url`.                                  |
| `invalid_embed_url`          | URL is not HTTPS, has credentials, or has a non-public hostname.      |
| `unexpected_embed`           | `code` format must not declare `embed`.                               |
| `invalid_constraint`         | `min > max` on width or height.                                       |
| `default_size_out_of_bounds` | `defaultSize` falls outside `constraints`.                            |
| `duplicate_permission`       | Same permission listed twice.                                         |
| `missing_network_permission` | `network.allowedOrigins` declared without the `"network"` permission. |
| `missing_network_origins`    | `"network"` permission declared without any origins.                  |
| `invalid_origin`             | An origin failed the HTTPS-origin-only check.                         |
| `unsupported_permissions`    | `embed` format must not declare permissions.                          |
| `unsupported_network`        | `embed` format must not declare `network.allowedOrigins`.             |
| `unrecognized_field`         | Unknown top-level field.                                              |

## What's next

<CardGroup cols={2}>
  <Card title="Widget" icon="window" href="/authoring/widget">
    The iOS widget projection — six templates, five themes, sample-data schema.
  </Card>

  <Card title="Runtime" icon="cube" href="/authoring/runtime">
    What happens at render time — render paths, sandbox, data injection.
  </Card>
</CardGroup>
