---
title: Tools
description: Every tool a connected agent may call, with what each one does and takes.
sidebar:
  order: 15
---

37 tools, all scoped to the websites the token's owner can reach.
Every one takes an optional `tenant_id`, the site's friendly id as `get_site` returns
it, and works on the account's original site when it is left out; it is not repeated
under each tool below. A token that may only read is offered the 19 that ask for
no more, and `tools/list` names exactly what a connection may call.

## Reading

### get_site

**Get site overview.** Scope `site:read`, read-only.

The selected website: its friendly id, name, public address, every
live page with its path and file, the branches ahead of main
(`pending_branches`; list_branches has their preview URLs), and a
preview URL for the live site. Also its languages, its tables (with
how many `posts` are `published` beside their `records`), its
JavaScript files, and its plan: limits, usage, and whether the site
is frozen.

Takes nothing beyond `tenant_id`.

## Notifications

### list_notifications

**Read what the owner has been told.** Scope `site:read`, read-only.

What the owner has been told about this site, newest first: the alerts
on the Notifications screen of their dashboard. Each says what
happened, when (`received_at`), and whether it is still unread
(`unread`). Call it when the owner asks what is wrong with their
site or what has happened on it. `get_site` says how many are unread.

Arguments:

- `kind` (string, one of `form_submission`, `page_degraded`, `plan`, `script_failed`, `site_transfer`): Only the alerts of this kind. Leave it out for every kind.
- `unread` (boolean): true for only the alerts that are still unread.

## Vocabulary

### describe_vocabulary

**Describe the site vocabulary.** Scope `site:read`, read-only.

Everything a site's files may say, generated from the running code
rather than written down, so it is never out of date. Topic language
is the whole of vzk on one card; the rest are the platform's: looks,
behaviors, javascript, fields, meta, layout, sites and guides.

Arguments:

- `topic` (string, one of `looks`, `behaviors`, `javascript`, `fields`, `meta`, `layout`, `sites`, `guides`, `language`): Which part of the vocabulary to describe. Defaults to language.
- `name` (string, one of `apps`, `building`, `claret`, `cobalt`, `copying`, `instructions`, `mineral`, `photographer`, `photographer/blog`, `photographer/blog/[slug]`, `photographer/index`, `photographer/services`, `photographer/work`, `photographer/work/[slug]`, `playbooks/photographer`, `playbooks/plumber`, `plumber`, `plumber/index`, `plumber/prices`, `press`, `publication`, `starter`, `starter/index`, `vizitka`, `vizitka/about`, `vizitka/acceptable-use`, `vizitka/cookies`, `vizitka/data-deletion`, `vizitka/dpa`, `vizitka/index`, `vizitka/legal`, `vizitka/legal-notice`, `vizitka/pricing`, `vizitka/privacy`, `vizitka/subprocessors`, `vizitka/terms`, `vzk/authoring`, `vzk/javascript`, `vzk/languages`, `vzk/pages`, `vzk/styling`, `vzk/tables`, `vzk/tests`): With topic=looks, sites or guides, return only this one.

### check

**Check a change without writing it.** Scope `site:read`, read-only.

Says what write_files would say of a change, without writing it: up to
50 files written, deleted or moved over a branch or
a commit (default main), checked as a commit is. Answers `ok`, each
refusal under `problems` with its file, the compiler's `warnings`,
and what a write would fix and format in a .vzk file, under
`would_apply` and `would_format`.

Arguments:

- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.
- `files` (array of object, required): Each file with exactly one of content, delete or move_to.
  - `path` (string, required): The file, such as pages/about.vzk.
  - `content` (string): The whole new text of the file.
  - `delete` (boolean): true to delete the file.
  - `move_to` (string): A new path for the file, its text unchanged.

## Disk

### list_files

**List the site's files.** Scope `site:read`, read-only.

Every picture on this site's disk, newest first: its name, what kind of
file it is, how big it is, the public URL it is served at, and a
`reference` ready to write in a page. The favicon (the icon browsers
show in a tab) is in the list, marked `kind: "favicon"`; an ordinary
image goes on a page as `<img src={assets["<key>"]}>`, or anywhere
else a path belongs (guide vzk/pages, Pictures).

Takes nothing beyond `tenant_id`.

### get_disk_usage

**Check how much storage the site has left.** Scope `site:read`, read-only.

How much of this site's storage is used and how much is left, in bytes.

Ask before telling the owner to upload something, so "add three photos
to the gallery" does not end with them meeting a refusal you could have
seen coming. `list_files` is what is actually stored.

`picture_metadata` is what the site does with a camera's notes on the
next upload: `archive` and `discard` serve visitors a copy with them
taken off, while `publish` serves the file as it arrived, so the
camera, the moment and, from a phone, the location go out with it. Say
which one is in effect when you upload a photograph for somebody. You
cannot change it; the owner does, on the Disk screen.

Takes nothing beyond `tenant_id`.

### prepare_file_upload

**Prepare a local file upload.** Scope `site:write`, writes.

Prepares an ordinary HTTP upload for a picture that is available as a
local file but cannot be put into an MCP argument. Call this instead of
base64-encoding a local photo or icon. For a picture already at a
public https address, use `upload_file` directly instead.

Arguments:

- `kind` (string, one of `image`, `favicon`, `social_image`, required): What the file is for: image (an ordinary picture, answered with the public URL it is served at); favicon (the site's tab icon — replaces the current one, live at once); social_image (the picture a shared link to this site shows — replaces the current one, live at once, and cropped to 1200×630 from the middle).

### upload_file

**Put a file on the site's disk.** Scope `site:write`, writes.

Puts a file on this site's disk, from exactly one source: a picture the
customer attached in this conversation (`file`), a public https address
(`url`), or base64 bytes (`data`). Only raster images are accepted (PNG,
JPEG, WebP), and the bytes are identified on arrival: a file that is not
really a picture is refused whatever it is named. When the customer has
attached the picture to this conversation, `file` is the source to use:
pass that attachment and the client supplies the address. For a local
file on a machine with a shell, do not base64 it either: call
`prepare_file_upload` and POST it as multipart form data instead.

Arguments:

- `kind` (string, one of `image`, `favicon`, `social_image`, required): What the file is for: image (an ordinary picture, answered with the public URL it is served at); favicon (the site's tab icon — replaces the current one, live at once); social_image (the picture a shared link to this site shows — replaces the current one, live at once, and cropped to 1200×630 from the middle).
- `file` (object): The picture the customer attached to this conversation. When there is an attachment and it is the thing to store, pass it here and the client fills in its address. Never write a download_url of your own — a link given to you as text goes in `url` instead.
  - `download_url` (string, required): A public https address the file can be fetched from.
  - `file_id` (string, required): The client's own id for the file.
  - `mime_type` (string): What the client believes the file is. Advisory only: the bytes are identified on arrival, and it is they that decide.
  - `file_name` (string): What to call the file, when the client knows.
- `url` (string): A public https address to fetch the file from.
- `data` (string): The file's bytes, base64-encoded. A data: URL is fine too.
- `filename` (string): What to call the file. Required with data. With file the client's own name is used when this is left out, and with url the address's own last segment.

## Code

### list_tree

**List the site's files.** Scope `site:read`, read-only.

Every file of the site's code (pages, layouts, components, locales,
scripts, stylesheet, import map) at a branch or a commit, with its
kind and size. Defaults to main. Read the text with read_files.

Arguments:

- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.

### read_files

**Read some of the site's files.** Scope `site:read`, read-only.

The text of up to 20 files at a branch or a commit (default main),
with the commit read, which write_files takes as base. list_tree names
the files; describe_vocabulary topic layout explains them.

Arguments:

- `paths` (array of string, required): File paths from list_tree, such as pages/index.vzk.
- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.

### outline

**Outline the site or one file.** Scope `site:read`, read-only.

The map of a site written in vzk at a branch or a commit (default
main): its files, or with path one file's blocks, each with its
address, lines, size and note. Read one with read_block.
describe_vocabulary topic language teaches vzk.

Arguments:

- `path` (string): A .vzk file, such as pages/index.vzk, to list its blocks. Omit for the site's files.
- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.

### describe

**Describe what the blocks do.** Scope `site:read`, read-only.

What the blocks of a site written in vzk do, in sentences made from
their code alone, at a branch or a commit (default main): with path
every block of one file, with address one block, each with a
person's notes on it. With neither, what each file is and how many
blocks it holds. outline lists the addresses.

Arguments:

- `path` (string): A .vzk file, such as pages/index.vzk, to describe its blocks. Omit for a line on every file.
- `address` (string): One block's address from outline, such as pages/index.vzk#hero. Not with path.
- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.
- `manifest` (boolean): With path or address: also answer the facts under the sentences as data, under facts. Default false.

### read_block

**Read one block of a file.** Scope `site:read`, read-only.

The text of one block of a vzk file, named by its address from
outline (pages/index.vzk#hero), at a branch or a commit (default
main), with the commit read, which write_block takes as base, and
under notes what a person noted on the block.

Arguments:

- `address` (string, required): The block's address from outline: its file, # and its id, such as pages/index.vzk#hero.
- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.

### log

**Show the history of the site's code.** Scope `site:read`, read-only.

The commits on a branch (default main), newest first, with who made
each and a preview URL; with path, only the commits that changed that
file. revert takes a commit id from here.

Arguments:

- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.
- `path` (string): Only the commits that changed this file.
- `limit` (integer): How many commits. Default and most 50.

### diff

**Compare two versions of the site's code.** Scope `site:read`, read-only.

The files that differ between two branches or commits (from defaults
to main), each added, modified or deleted, with its text before and
after. diff to a branch is what merge_branch would put live. Where
the site's tables are files, `tables` says what that would do to
them, and which steps wait for the owner.

Arguments:

- `to` (string, required): The branch or commit to compare with from.
- `from` (string): A branch name from list_branches, or a commit id from log. Defaults to main.
- `paths` (array of string): Only these files.

## Branches

### list_branches

**List the site's branches.** Scope `site:read`, read-only.

Every branch of the site's code with its head commit, how many commits
it is ahead of and behind main, and a preview URL. main is what the
public site serves. Read guide vzk/authoring.

Takes nothing beyond `tenant_id`.

### create_branch

**Create a branch.** Scope `site:write`, writes.

Starts a branch of the site's code, from main or from another branch
or a commit. Write to it with write_files or edit_file; visitors see
nothing until merge_branch. Read guide vzk/authoring.

Arguments:

- `name` (string, required): Lowercase segments joined by slashes, such as agent/opening-hours.
- `from` (string): A branch or a commit id to start from. Defaults to main.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### write_files

**Change files on a branch.** Scope `site:write`, writes.

Writes, deletes or moves up to 50 files on a branch as one
commit, checked first: a file the compiler refuses refuses the whole
commit. A .vzk file is fixed and put in canonical form as it is
written, and the answer says what changed. Never writes main; visitors
see it after merge_branch. Read guide vzk/authoring and
describe_vocabulary topic layout.

Arguments:

- `branch` (string, required): The branch to write, from create_branch. main is never written directly.
- `files` (array of object, required): Each file with exactly one of content, delete or move_to.
  - `path` (string, required): The file, such as pages/about.vzk.
  - `content` (string): The whole new text of the file.
  - `delete` (boolean): true to delete the file.
  - `move_to` (string): A new path for the file, its text unchanged.
- `message` (string): What the change is, in a line. Defaults to naming the files.
- `base` (string): The commit you read the files at. A file changed on the branch since then is refused, so another writer's change is never overwritten.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### edit_file

**Change one passage of a file.** Scope `site:write`, writes.

Changes one passage of one file on a branch and leaves the rest as it
is, as one commit checked like write_files. `find` is copied exactly
from read_files and must occur exactly once, or nothing is written.
A .vzk file is fixed and put in canonical form as it is written, and
the answer says what changed.

Arguments:

- `branch` (string, required): The branch to write, from create_branch. main is never written directly.
- `path` (string, required): The file, such as pages/index.vzk.
- `find` (string, required): Exact text from the file, whitespace included. Must occur once.
- `replace` (string, required): What takes its place, taken literally. An empty string deletes it.
- `message` (string): What the change is, in a line. Defaults to naming the files.
- `base` (string): The commit you read the files at. A file changed on the branch since then is refused, so another writer's change is never overwritten.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### write_block

**Change one block of a file.** Scope `site:write`, writes.

Replaces one block of a vzk file on a branch with new text and leaves
the rest of the file as it is, as one commit checked like
write_files. Read the block with read_block first. A block that
shares a line with other markup is refused: change it with edit_file.
The file is fixed and put in canonical form as it is written, and the
answer says what changed.

Arguments:

- `branch` (string, required): The branch to write, from create_branch. main is never written directly.
- `address` (string, required): The block's address from outline: its file, # and its id, such as pages/index.vzk#hero.
- `content` (string, required): The block's whole new text, taken literally, as read_block answers it.
- `message` (string): What the change is, in a line. Defaults to naming the files.
- `base` (string): The commit you read the files at. A file changed on the branch since then is refused, so another writer's change is never overwritten.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### edit_block

**Move, copy, rename, remove, wrap, extract or add a block.** Scope `site:write`, writes.

Moves (within its file or to another), copies, renames, removes or
wraps one block of a vzk file on a branch by its address from outline,
makes it a component or puts a component back, or adds new markup at a
place, without sending the file. One commit, checked like write_files,
that may change several files. To undo it, revert to the answer's
parent with its paths. guide vzk/authoring shows each operation.

Arguments:

- `branch` (string, required): The branch to write, from create_branch. main is never written directly.
- `operation` (string, one of `delete`, `duplicate`, `rename`, `move`, `extract`, `inline`, `insert`, `wrap`, required): What to do: every operation but insert takes the block's address, move and insert a place, insert and wrap markup, rename an id, extract a name.
- `address` (string): The block's address from outline: its file, # and its id, such as pages/index.vzk#hero. Every operation but insert.
- `markup` (string): insert: the element or elements to add. wrap: one element with nothing in it, such as &lt;div class="band"&gt;&lt;/div&gt;, to put around the block.
- `id` (string): duplicate: the copy's id. Omit it for the next free name. rename: the block's new id.
- `name` (string): extract: the component's name, a capital then letters and digits, such as Hero. It is written to components/&lt;name&gt;.vzk.
- `before` (string): move, insert: the address of the block it goes before, in this file or another.
- `after` (string): move, insert: the address of the block it goes after.
- `into` (string): move, insert: the address of the block it goes inside, at its end.
- `end` (string): move, insert: the path of the file whose markup it goes at the end of.
- `message` (string): What the change is, in a line. Defaults to naming the files.
- `base` (string): The commit you read the files at. A file changed on the branch since then is refused, so another writer's change is never overwritten.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### revert

**Put files back as a commit had them.** Scope `site:write`, writes.

Writes one commit on a branch that puts files (path, or up to 50
paths; without either, every file) back as they were at a commit. A
file that commit did not have is deleted. Visitors see it after
merge_branch. guide vzk/authoring explains undoing a change.

Arguments:

- `branch` (string, required): The branch to write, from create_branch. main is never written directly.
- `to` (string, required): The commit id to go back to, from log or a write's parent.
- `path` (string): Only this file. Omit it and paths to put back every file.
- `paths` (array of string): Only these files, put back together in one commit.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### update_branch

**Bring a branch up to date with main.** Scope `site:write`, writes.

Replays a branch's commits on top of main so merge_branch can put it
live. A file changed on both in the same place refuses the update,
handing back its text at the fork, on main and on the branch; retry
with resolutions. Read guide vzk/authoring.

Arguments:

- `branch` (string, required): The branch to write, from create_branch. main is never written directly.
- `resolutions` (object): For each conflicting file, its whole resolved text, or null to delete it.
- `note` (string): One sentence, in the customer's words, kept with the commit.

### delete_branch

**Delete a branch.** Scope `site:write`, writes, destructive.

Deletes a branch and whatever it holds that was never merged. main
cannot be deleted, and the live site is not touched.

Arguments:

- `name` (string, required): The branch, from list_branches.

## Publishing

### run_tests

**Play a site's scenarios.** Scope `site:read`, read-only.

Plays the scenarios in a branch's or a commit's tests/&lt;name&gt;.test.ts
files (default main) with stand-in visitors and rows of their own;
the site's rows are never touched. Answers each scenario by name,
and for one that fails its line, what did not hold and what the
page showed. merge_branch is refused while one fails. Read guide
vzk/tests.

Arguments:

- `ref` (string): A branch name from list_branches, or a commit id from log. Defaults to main.

### merge_branch

**Put a branch live.** Scope `site:write`, writes, destructive.

Publishes a branch: main moves to it and visitors see its files at
once. Refused when main has moved since the branch was updated (call
update_branch first), and while one of its scenarios fails
(run_tests). Where the site's tables are files, merging changes them
too, and a branch with a step that waits for the owner is refused:
they merge it on their Branches screen. The branch is deleted
afterwards unless keep_branch. Undo with revert on a new branch.

Arguments:

- `branch` (string, required): The branch to put live, from list_branches.
- `keep_branch` (boolean): Keep the branch after the merge. Default false.
- `note` (string): One sentence, in the customer's words, kept with the commit.

## Languages

### set_site_languages

**Say which languages a site speaks.** Scope `site:write`, writes.

Declares the languages a site serves. This is the first call of
building a multilingual site, and nothing about languages works until
it has been made: no path prefix, no switcher, no `lang` attribute,
no hreflang, and no `locales/` catalog is read.

This takes effect at once: it is a site setting, not a draft, so the
prefixed addresses go live whether or not the pages are translated.
Send `locales: []` to go back to one language. Read
`vzk/languages` first.

Arguments:

- `default` (string): The language served on the site's plain paths, as a tag like "en" or "pt-br". The one language with no prefix of its own.
- `locales` (array of string): Every other language the site speaks, as tags: [ "uk", "ru" ]. This is the order a switcher offers them in, after the default. Listing the default here too is fine and means the same thing. An empty list un-declares every language.

## Site

### update_site_meta

**Set the site's default meta tags.** Scope `site:write`, writes.

Sets what the site's `<head>` says by default: the description a
search result shows, who made it, the colour a phone browser paints
its chrome, and whether search engines should index it at all. Every
page of the site uses these unless that page says something of its
own in its own head.

**Live at once.** This is a site setting, not a file: there is no
branch, nothing to preview and nothing to merge, so a site-wide
`noindex` hides the whole site from search engines the moment it is
sent.

Arguments:

- `description` (string): One or two sentences about the page, shown under its title in a search result and on a shared link. Set at site level it describes the whole site.
- `keywords` (string): Comma-separated words. Search engines have long ignored these; they are here because some other tools still read them.
- `noindex` (boolean): Ask search engines to leave this out of their index. Set at site level it hides the whole site, which is for a site that is not ready to be found.
- `author` (string): Who made the site — usually the owner's own name.
- `theme_color` (string): The colour a phone browser paints its chrome with, as a CSS colour ("#101010").
- `twitter` (string): The site's X handle, with the @ ("@mirahalden"). It is what an X card credits.
- `page_fades` (boolean): Fade from one page of the site into the next instead of cutting. Off unless set, and a page's own value overrides the site's.
- `morph_refreshes` (boolean): Redraw a page that answers with itself, such as a form's thanks, in place and at the same scroll position. Off unless set, and it asks things of the site's own scripts (`vzk/javascript`).
- `translations` (object): The same fields in the site's other languages, keyed by language tag: { "uk": { "description": "…" } }. Merged per language and key by key inside it. Words only — an author and a theme colour are the same in every language.

## Tables

### create_table

**Add a table to this site.** Scope `site:write`, writes.

Gives the site a table of its own: a named set of fields, and somewhere
to put the rows. This is how a contact form gets somewhere to send
what a visitor types. It writes tables/&lt;handle&gt;.json on `branch`, and
merge_branch makes the table. Read `vzk/tables` first.

A reserved handle must keep what its dashboard screen reads, and may
add anything else:

The owner's Enquiries screen reads `enquiries`. It must keep at least one field of the site's choice, and it must stay `readable_by: owner` (it holds what strangers send).

The owner's Posts screen reads `posts`. It must keep `title` (text, what the post is called, in the tab and on a shared link) and `body` (markdown, the post itself), and it must stay `readable_by: public` (its records are pages of the site). By default, rows wait for a published_at date; publication is configurable.

The owner's Portfolio screen reads `portfolio`. It must keep `title` (text, the item's name), `summary` (long_text, a line about the item), `cover` (picture, the item's picture), `body` (markdown, the item's story), and `position` (number, its place in the owner's order), and it must stay `readable_by: public` (its records are pages of the site). By default, rows wait for a published_at date; publication is configurable.

Arguments:

- `handle` (string, required): A lowercase handle, such as "enquiries". This is what a `<form table="…">` names and what the submission path carries. `vizitka` is the platform's own and is refused; the reserved handles in this tool's description are yours to use and must keep their screen's shape.
- `name` (string): What the owner sees this table called on their dashboard: at most 100 characters, with no line break or other control character.
- `fields` (array of object, required): The shape of one row: a list of fields, in the order a form should ask them, at most 100 of them.
  - `name` (string, required): A lowercase handle, such as "email" or "phone_number", and never `id`, `slug`, `url` or `updated_at`, which every row answers itself. A form input is named `fields[<name>]`.
  - `type` (string, one of `text`, `long_text`, `markdown`, `email`, `phone`, `url`, `number`, `date`, `datetime`, `boolean`, `choice`, `reference`, `references`, `picture`, required): What this field holds, and what a submitted value is checked against.
  - `label` (string): What a person reads beside the input: at most 100 characters, never empty or white space alone, and no line break or other control character.
  - `required` (boolean): Whether a row is refused without it. Default false.
  - `options` (array of string): For a `choice` field: the values it may hold, each once, at most 200 of them and each at most 500 characters, with no white space at either end and no line break or other control character.
  - `multiple` (boolean): For a `choice` field: whether a row holds a list of its options rather than one. Narrowed with `filter(p => p.<name>.includes("…"))`, never sortable, and plays no role. Default false.
  - `table` (string): For a `reference` or `references` field: the handle of this site's table whose rows it names, which may be this one.
  - `max` (integer): For a `references` field: the most rows one row's list may name, from 1 to 500. A write naming more is refused. Default 500.
  - `fill` (string, one of `now`, `visitor`): For a `datetime` field: `now` sets it to the time a row is added, whoever adds it. For a `reference` to `people`, on a site whose visitors sign in: `visitor` sets it to the signed-in visitor who adds the row, and leaves it empty for anybody else. No write sets or changes such a field: leave it out of a row's values, and a form has no control for it.
  - `includes` (string, one of `visitor`): For a `references` field to `people`, on a site whose visitors sign in: `visitor` puts the signed-in visitor who adds a row in its list. The rest of the list is the writer's to set.
  - `filterable` (boolean): Whether a page may narrow or search a listing on this field, with `tables.<handle>.filter(p => p.<name> == "…")` or `filter(p => p.<name>.includes(query.q))`. A `reference` is compared with a row, `p.<name> == row`, and a `references` is asked `p.<name>.includes(row)`. Default false. Which comparisons it then answers is what `describe_vocabulary topic=fields` lists beside its type.
  - `role` (string, one of `title`, `body`, `language`, `summary`, `cover`): What the platform itself reads this field for, if anything. One field per role, and a table that names none for a role falls back to the field literally called that. What each role buys is in `describe_vocabulary topic=fields`. Most fields have none.
  - `sortable` (boolean): Whether a page may read a listing in this field's own order, with `tables.<handle>.sortBy(p => p.<name>, "desc")`. Default false, and separate from `filterable`. Only on a type whose values sit in an order, which `describe_vocabulary topic=fields` says beside each one, so not on a `boolean`.
  - `was` (string): The name this field had on main, so merging renames it and every row keeps its value.
  - `default`: What merging gives each row that has no value for this field, as a row would hold it. Needed to make a field required on a table that has rows.
  - `replace` (object): On a `choice`: for each option taken away, the option merging puts in its place in every row.
- `publication` (object or null): Which rows may appear publicly. Set field to null for all rows, or name a declared date field (today or earlier) or boolean field (true). Null restores the default: posts and portfolio use published_at; other tables show all rows. Changes are live at once without editing rows. Table read access still applies.
  - `field` (string or null, required)
- `keep` (string or null): How long the table keeps a row, as a whole number of days from 1 to 3650: "90 days" or "1 day". Rows older than that, counted from the day each was added, are removed from the table once a day, never sooner than a day after the setting is saved or changed, and cannot be brought back from the dashboard or by an agent, so ask the owner before setting it. Taken only for a table that is `readable_by: public`: the owner sets it for any other on their dashboard. Null keeps every row, which is the default.
- `writable_by` (string, one of `owner`, `public`): Who may add a row. `public` is what lets a visitor submit a form. Default `owner`.
- `readable_by` (string, one of `owner`, `public`): Who may read a row. `owner` keeps it off the site entirely and is the right answer for anything a stranger sends. `public` lets a page list the rows, and is what a table needs before it can have a page of its own (pages/&lt;path&gt;/[slug].vzk). Default `owner`.
- `branch` (string): The branch to write tables/&lt;handle&gt;.json on, from create_branch.
- `message` (string): What the change is, in a line. Defaults to naming the files.

### update_table

**Change a table.** Scope `site:write`, writes, destructive.

Changes a table this site already has: what it collects, what it is
called, who may write and read it, which rows are published, and how
long it keeps its rows.

Every change but `publication` is written into tables/&lt;handle&gt;.json
on `branch`, and nothing changes until merge_branch: merging then
moves the rows to the new shape, and the answer's `tables` says how.

`fields` is sent **whole** and takes effect at once: the form on the
site starts asking the new questions on its next render. Rows already
stored are not touched — nothing migrates and nothing is lost, so a
field you drop stops being read while its values stay in the rows that
have them, and a field you add is simply empty on everything written
before it.

`code_access` is what this site's code, the functions its forms run,
may do to the table: `count` its rows, `insert` a row. A page that
shows a count, or changes with it, tells every visitor how many rows
the table holds. Code cannot add rows to a table visitors can read.
`read`, `update` and `delete` go only into a table's file, where the
site has them, and the merge then waits for a person to approve it.

Take a field or a grant out of the site's live pages before taking it
from the table. While a live page still uses one that is gone, every
form on the site stops taking submissions until the pages are changed.

A reserved table keeps the contract `get_site` states on it, and a
definition that breaks it is refused naming what reads it. Read
`vzk/tables` first.

Arguments:

- `handle` (string, required): The table's handle, as get_site lists it.
- `fields` (array of object): The whole shape of one row, replacing what is there, at most 100 fields. Live at once, and rows already stored are untouched. A reserved table has to keep the shape its dashboard screen needs, and a role dropped here is a role the platform reads off no field of this table.
  - `name` (string, required): A lowercase handle, such as "email" or "phone_number", and never `id`, `slug`, `url` or `updated_at`, which every row answers itself. A form input is named `fields[<name>]`.
  - `type` (string, one of `text`, `long_text`, `markdown`, `email`, `phone`, `url`, `number`, `date`, `datetime`, `boolean`, `choice`, `reference`, `references`, `picture`, required): What this field holds, and what a submitted value is checked against.
  - `label` (string): What a person reads beside the input: at most 100 characters, never empty or white space alone, and no line break or other control character.
  - `required` (boolean): Whether a row is refused without it. Default false.
  - `options` (array of string): For a `choice` field: the values it may hold, each once, at most 200 of them and each at most 500 characters, with no white space at either end and no line break or other control character.
  - `multiple` (boolean): For a `choice` field: whether a row holds a list of its options rather than one. Narrowed with `filter(p => p.<name>.includes("…"))`, never sortable, and plays no role. Default false.
  - `table` (string): For a `reference` or `references` field: the handle of this site's table whose rows it names, which may be this one.
  - `max` (integer): For a `references` field: the most rows one row's list may name, from 1 to 500. A write naming more is refused. Default 500.
  - `fill` (string, one of `now`, `visitor`): For a `datetime` field: `now` sets it to the time a row is added, whoever adds it. For a `reference` to `people`, on a site whose visitors sign in: `visitor` sets it to the signed-in visitor who adds the row, and leaves it empty for anybody else. No write sets or changes such a field: leave it out of a row's values, and a form has no control for it.
  - `includes` (string, one of `visitor`): For a `references` field to `people`, on a site whose visitors sign in: `visitor` puts the signed-in visitor who adds a row in its list. The rest of the list is the writer's to set.
  - `filterable` (boolean): Whether a page may narrow or search a listing on this field, with `tables.<handle>.filter(p => p.<name> == "…")` or `filter(p => p.<name>.includes(query.q))`. A `reference` is compared with a row, `p.<name> == row`, and a `references` is asked `p.<name>.includes(row)`. Default false. Which comparisons it then answers is what `describe_vocabulary topic=fields` lists beside its type.
  - `role` (string, one of `title`, `body`, `language`, `summary`, `cover`): What the platform itself reads this field for, if anything. One field per role, and a table that names none for a role falls back to the field literally called that. What each role buys is in `describe_vocabulary topic=fields`. Most fields have none.
  - `sortable` (boolean): Whether a page may read a listing in this field's own order, with `tables.<handle>.sortBy(p => p.<name>, "desc")`. Default false, and separate from `filterable`. Only on a type whose values sit in an order, which `describe_vocabulary topic=fields` says beside each one, so not on a `boolean`.
  - `was` (string): The name this field had on main, so merging renames it and every row keeps its value.
  - `default`: What merging gives each row that has no value for this field, as a row would hold it. Needed to make a field required on a table that has rows.
  - `replace` (object): On a `choice`: for each option taken away, the option merging puts in its place in every row.
- `name` (string): What the owner sees this table called: at most 100 characters, with no line break or other control character.
- `publication` (object or null): Which rows may appear publicly. Set field to null for all rows, or name a declared date field (today or earlier) or boolean field (true). Null restores the default: posts and portfolio use published_at; other tables show all rows. Changes are live at once without editing rows. Table read access still applies.
  - `field` (string or null, required)
- `keep` (string or null): How long the table keeps a row, as a whole number of days from 1 to 3650: "90 days" or "1 day". Rows older than that, counted from the day each was added, are removed from the table once a day, never sooner than a day after the setting is saved or changed, and cannot be brought back from the dashboard or by an agent, so ask the owner before setting it. Taken only for a table that is `readable_by: public`: the owner sets it for any other on their dashboard. Null keeps every row, which is the default.
- `writable_by` (string, one of `owner`, `public`): Who may add a row. Takes effect at once.
- `readable_by` (string, one of `owner`, `public`): Who may read a row. Takes effect at once. `owner` is refused while the table has a page of its own: delete its file on a branch and merge it first, and every row's address goes with it, so say so to the owner before doing it.
- `code_access` (array of string): What this site's code may do to the table, sent whole and replacing what is there. Takes effect at once. A count on a page tells visitors how many rows the table holds. An empty list takes every grant away.
- `branch` (string): Needed for any change but `publication`: the branch to write tables/&lt;handle&gt;.json on. Left out on a frozen site, where taking grants away goes live at once.
- `message` (string): What the change is, in a line. Defaults to naming the files.

## Records

### list_records

**List a table's rows.** Scope `site:read`, read-only.

The newest 50 rows of one of this site's tables, newest first: its
`tenant_record_id`, its slug, its title, whether it is published, and
where it answers. This is how you find the post you are about to edit
or publish. An older row is not missing just because it is not here.

Arguments:

- `handle` (string, required): Public table handle from get_site.

### get_record

**Read one row.** Scope `site:read`, read-only.

One row whole: every value it holds, in the order its table declares
them, plus its `tenant_record_id`, its slug, whether it is published,
and the address it answers at. `list_records` is how you find the slug.

Arguments:

- `handle` (string, required): Public table handle from get_site.
- `slug` (string, required): Row slug from list_records: its address's last segment.
- `language` (string): Row language, where rows share a slug.

### create_record

**Write a post.** Scope `site:write`, writes.

Writes one row into a table this site owns. **A post is a row**, not a
page: a blog is one `posts` table, the page every post renders
through, a row per post, and a page at `/blog` listing them. A page
each would put every post in the owner's Pages sidebar and in every
nav that loops `site.pages`, with no listing and no shared address.

Publication follows the table's rule from get_site: all rows, a date
that has come, or a boolean that is true. A new row
starts the table's active workflows, the same as a visitor's
submission does. Read `vzk/tables` first.

Arguments:

- `handle` (string, required): Public table handle from get_site.
- `values` (object, required): The row's own values, keyed by the field names its table declares: { "title": "How I light a winter wedding", "body": "…markdown…" }. Checked against the definition and cut down to it, so a key the table does not name is dropped, a value of another type is cast to what the field holds (5 under a text field is stored as "5"), and one the type refuses even then comes back as a sentence naming the field. A field pointing at rows takes the `tenant_record_id` list_records shows for each: 14, or [3, 7] where it holds a list.
- `slug` (string): Where this row answers, as the last segment of its address. Leave it out and the title is slugged.

### update_record

**Rewrite a row.** Scope `site:write`, writes, destructive.

Replaces what one row holds. `values` is sent **whole**, so a field
you leave out is dropped: read the row with `get_record`, change the
part you mean to change, and send it all back. Sending only a title
is how a post loses its body.

**There is no draft.** An edit to a published row is on the site as
this returns; `replaced` in the answer is what to send back if it was
wrong. The table's publication rule decides whether the changed row
stays public: all rows, a date that has come, or a boolean that is true.
Leaving a configured publication field out takes the row off the site.
There is no rename: the slug is
the row's address and nothing writes a redirect.

Arguments:

- `handle` (string, required): Public table handle from get_site.
- `slug` (string, required): Row slug from list_records: its address's last segment.
- `language` (string): Row language, where rows share a slug.
- `values` (object, required): The row's own values, keyed by the field names its table declares: { "title": "How I light a winter wedding", "body": "…markdown…" }. Checked against the definition and cut down to it, so a key the table does not name is dropped, a value of another type is cast to what the field holds (5 under a text field is stored as "5"), and one the type refuses even then comes back as a sentence naming the field. A field pointing at rows takes the `tenant_record_id` list_records shows for each: 14, or [3, 7] where it holds a list. Replaces what the row holds, so send the whole row back.

### delete_record

**Delete a row.** Scope `site:write`, writes, destructive.

Deletes one row. **There is no undo through this connection.** Read it
with `get_record` first if there is any chance it is wanted.

Arguments:

- `handle` (string, required): Public table handle from get_site.
- `slug` (string, required): Row slug from list_records: its address's last segment.
- `language` (string): Row language, where rows share a slug.

## Platform

### list_feature_requests

**What this site has already asked the team for.** Scope `site:read`, read-only.

Lists the feature requests this site has filed with the Vizitka team —
newest first, each with its status. Check it before `request_feature`:
a gap that is already `pending` or `filed` does not need filing again.

Takes nothing beyond `tenant_id`.

### request_feature

**File a feature request with the Vizitka team.** Scope `site:read`, writes.

Files a feature request with the Vizitka platform team when the
customer asks for a capability the format cannot express. It
records the request and opens an issue
in the platform's own tracker; it does not read or change this
website.

Arguments:

- `title` (string, required): One line naming the missing capability, such as "Video embeds on a page".
- `request` (string, required): What the customer asked for, in their words.
- `limitation` (string, required): What capability the platform could not express.
- `workaround` (string): What you built instead with today's vocabulary, if anything.
- `tags` (array of string, required): Where the gap is: vocabulary (a missing class, value or template capability), styling (the stylesheet, its tokens or component classes), layout (an arrangement the primitives cannot express), content (text and content features), media (images, video, galleries, uploads), pages (page management and navigation), forms (contact forms and other input), bookings (scheduling and appointments), payments (prices, checkout, invoicing), integrations (third-party services and embeds), seo (titles, descriptions, search visibility), i18n (languages and translation), other (anything the rest do not cover).
