---
title: "API & validation reference"
source: "https://docs.vertesiahq.com/content/view-experiences/reference"
markdown: "https://docs.vertesiahq.com/llms/content/view-experiences/reference.md"
---

# API & validation reference

Studio owns project-scoped View persistence under `/api/v1/views`; Zeno owns execution and draft preview. The Vertesia
client exposes both through `client.views`, delegating execution to Zeno. The lower-level `client.store.views` API remains
available when a content-service client is used directly.

## Persistence endpoints

| Method | Endpoint | Purpose |
| --- | --- | --- |
| `GET` | `/api/v1/views` | List Views in the current project |
| `POST` | `/api/v1/views` | Create a View with an immutable ID |
| `GET` | `/api/v1/views/:id` | Retrieve a View and its current revision |
| `PUT` | `/api/v1/views/:id` | Replace the complete mutable configuration |
| `DELETE` | `/api/v1/views/:id` | Permanently delete a View |

```typescript {{ title: 'Create a View' }}
import { VertesiaClient } from '@vertesia/client';
import { VIEW_EXPERIENCE_SCHEMA_VERSION } from '@vertesia/common';
import { salesOrdersView } from './sales-orders-view';

const client = new VertesiaClient({ site: 'api.vertesia.io', apikey: process.env.VERTESIA_API_KEY });

const view = await client.views.create({
    id: 'sales-orders',
    version: VIEW_EXPERIENCE_SCHEMA_VERSION,
    ...salesOrdersView,
});
```

Updates are full replacements and use optimistic concurrency. Retrieve the current View, include its `version` and
`revision`, and handle HTTP `409` by reloading instead of overwriting another author's changes:

```typescript {{ title: 'Update a View' }}
import { VIEW_EXPERIENCE_SCHEMA_VERSION, getViewExperienceConfiguration } from '@vertesia/common';

const current = await client.views.retrieve('sales-orders');
const updated = await client.views.update(current.id, {
    version: VIEW_EXPERIENCE_SCHEMA_VERSION,
    revision: current.revision,
    ...getViewExperienceConfiguration(current),
    description: 'Sales orders for the fulfillment and customer success teams.',
});
```

  Persisted Views require a non-empty `description`. The Studio migration backfills older Views from their names so
  they remain editable. Replace that fallback with a useful purpose-and-audience description the next time you edit
  the View.

## Execution and draft preview

| Method | Endpoint | Purpose |
| --- | --- | --- |
| `POST` | `/api/v1/view-executions/:id/execute` | Execute a persisted (or app-contributed) View |
| `POST` | `/api/v1/view-executions/preview` | Validate and execute an **unsaved** configuration without persisting it |

Preview is the fast authoring loop: send a draft configuration plus the same execution inputs as `execute`, and get
real results back without saving. Studio Assistant and the Studio editor use it to iterate before `create`/`update`.

```typescript {{ title: 'Preview a draft' }}
const preview = await client.views.preview({
    configuration: draftView,
    query: 'orders delayed by weather',
    navigation: { status: ['open'] },
    navigation_queries: { collections: 'renewals' },
    limit: 25,
});
```

Preview validates the configuration and returns `400` with the specific issues if it is invalid, so it doubles as a
validate-and-run check. Agentic preview may invoke the configured model, so it requires interaction execute access
(below).

## Permissions

| Operation | Required permission |
| --- | --- |
| List / retrieve Views | interaction **read** |
| Create / update / delete Views | interaction **write** |
| Execute or preview a View | content **read** |
| Execute or preview an **agentic** View with a query | additionally interaction **execute** |

Normal project and content-security filters are always applied by the server on execution and preview, regardless of
these permissions.

## Configuration validation

`@vertesia/common` exports the JSON Schemas and semantic validators. Use the **persisted** variants for a custom
authoring surface that saves Views through the API:

```typescript {{ title: 'Validation' }}
import {
    PERSISTED_VIEW_EXPERIENCE_CONFIGURATION_JSON_SCHEMA_ID,
    PersistedViewExperienceConfigurationJsonSchema,
    validateViewConfiguration,
    validateViewExperienceId,
} from '@vertesia/common';

const issues = [
    ...validateViewExperienceId('sales-orders'),
    ...validateViewConfiguration(salesOrdersView, 'persisted'),
];

if (issues.length > 0) {
    throw new Error(issues.map((issue) => `${issue.path || 'configuration'} ${issue.message}`).join('; '));
}

monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
    validate: true,
    schemas: [
        {
            uri: PERSISTED_VIEW_EXPERIENCE_CONFIGURATION_JSON_SCHEMA_ID,
            fileMatch: [model.uri.toString()],
            schema: PersistedViewExperienceConfigurationJsonSchema,
        },
    ],
});
```

`validateViewConfiguration(config, mode)` runs structural JSON-Schema checks (shapes, enums, limits, required fields)
**and** the semantic rules JSON Schema cannot express — unique IDs, and references from `default_display`,
`default_sort`, and table columns to configured displays and sort options. Pass `'draft'` for an in-progress
configuration or `'persisted'` for one about to be saved (which additionally requires `description`).

## Schema version

Include `VIEW_EXPERIENCE_SCHEMA_VERSION` from `@vertesia/common` on create and update. The version pins the
configuration contract so the server can reject payloads written against an incompatible schema.