# Svalta

> Svalta hosts websites that AI agents build. You, the user's own coding agent, start from a designed template, fill it with the user's business through the content API, adjust the look through a managed Astro source workspace, show the user a private preview, and hand saving over to the user's browser. Svalta builds the site in a sandbox and hosts it. The owner edits text and photos later in Svalta Studio.

API base URL: `https://www.svalta.com/v1`. Send `Accept: application/json` on every call and `Content-Type: application/json` with JSON bodies. Every POST, PATCH, PUT and DELETE needs a fresh `Idempotency-Key` (a UUID); reuse a key only to retry the exact same request.

## Build a site in ten steps

The user needs no account until they want to keep the site. Never make them learn the API.

1. **Ask about the business, once.** In one short message ask: what the business is called and what it does, who it is for, what a visitor should do (call, book, visit, buy, get in touch), which pages they need, whether they have a logo and photos, any colours or websites they like, the site's language, and whether the business is in the EU. Fill gaps with sensible defaults and say what you chose.
2. **Pick a starter.** `GET /v1/starter-blueprints` and choose the closest `blueprint_key` (the current list is under Starter packs below, with previews at https://www.svalta.com/templates). A starter is a designed starting point, not a rule.
3. **Start the site.** `POST /v1/site-starts` with headers `X-Svalta-Bootstrap-Key: <new UUID>` and `Idempotency-Key: <new UUID>` and body `{"blueprint_key": "kestrel-partners", "site_name": "Harbour Bakery", "trigger_channel": "ai", "claim_intent": "guest"}`. Store `data.trial_secret` privately; it is shown once. Send it as `X-Svalta-Trial-Secret` on every call below. Never start a second site to work around an error.
4. **Wait for setup.** Poll `GET /v1/trial-session` every few seconds until `data.trial_session.setup_run.progress_state` is `completed`. `meta.agent` lists your quotas and next actions.
5. **Read before you change.** `GET /v1/trial-session/workspace/file?path=src/DESIGN-BRIEF.md` (the starter's design brief: tokens, fonts and every page block with examples). Then `GET /v1/trial-session/project/pages`, `/project/entries` (page text lives in each page's content entry: match `page_content_entry_id` to the entry `id` and read `current_version.fields_json`), `/project/site-settings`, `/project/theme-tokens`, `/project/navigation`, `/project/media` and `/project/seo`.
6. **Write the content.** `PATCH /v1/trial-session/project/theme-tokens` with `{"theme_tokens": {"primary": "#1f3b2d", "accent": "#e9b44c"}}`. `PATCH /v1/trial-session/project/site-settings` for tagline, contact details, footer and the header button; for a site that is not in British English also send `"language"` (for example `"de-DE"`) and `"timezone"` (for example `"Europe/Berlin"`) in the same call, which set the page language, dates and the site's 404 page. `PATCH /v1/trial-session/project/pages/{page}` with the complete `page_content_fields` map (`lede`, `body`, `featured_media`, `layout_blocks`); `POST /v1/trial-session/project/pages` with `title`, `slug` and `page_content_fields` for a new page. `PATCH /v1/trial-session/project/entries/{entry}` with `title` and the complete `fields_json`. `PUT /v1/trial-session/project/seo` with `owner_type`, `owner_id`, `title` and `description`. `PATCH /v1/trial-session/project/navigation` for the menu.
7. **Add real photos.** `POST /v1/trial-session/project/media/uploads` with `{"original_filename": "shop-front.jpg"}`, send the file as multipart field `blob` to the returned `data.blob.url` with `data.blob.headers`, then POST `data.complete.url` with `data.complete.headers`. Use the returned `data.media_reference.media_asset_id` as `{"media_asset_id": 123}` in blocks, entries and `branding.logo_asset` or `branding.favicon_asset`. Guest limits: 25 uploads of up to 5 MB (PNG, JPEG, WebP or SVG).
8. **Change source only when needed.** `GET /v1/trial-session/workspace` for `current_revision.revision_ulid`, read files with `/workspace/files` and `/workspace/file?path=...`, then `POST /v1/trial-session/workspace/changesets` with `{"base_revision_ulid": "...", "operations": [{"op": "upsert", "path": "src/styles/site.css", "content": "..."}]}` and `POST /v1/trial-session/workspace/changesets/{changeset}/apply`. Batch all source edits into one changeset.
9. **Build, look, fix.** `POST /v1/trial-session/workspace/builds` with `{"workspace_revision_ulid": "<current revision>", "content_snapshot_mode": "draft_preview"}`, poll `GET /v1/trial-session/workspace/builds/{build}` until `succeeded` (on failure read `/workspace/builds/{build}/logs` and fix the cause). Content edits also need a new build. Then `GET /v1/trial-session/preview` without following redirects: the `Location` header is the private preview, and page paths go after it (`<Location>/about`). Open every page at 1440 px and 390 px wide in your own browser or screenshot tool, and run `GET /v1/trial-session/preview/review?path=/about` for each page. Fix what you find, then give the user the preview link. A trial has 30 builds, 1 at a time; batch your changes.
10. **Hand over saving.** When the user wants to keep the site, `POST /v1/trial-session/claim-handoff` with `{}` and give them `data.claim_url` privately. They confirm in their own browser; never register, enter passwords or accept terms for them. Poll `data.status_url` with header `X-Svalta-Claim-Handoff: <data.status_token>` every 10 to 15 seconds until `data.status` is `claimed`, then stop using the trial secret.

## Make it look great

- **Keep the starter's craft.** Change theme tokens, fonts, photos and copy first. Restructure the layout only when the user asks. Every starter reads colours, fonts and radius from `--theme-*` CSS variables (`@svalta/astro/theme.css`); keep using them so the owner's Theme page in Studio keeps working.
- **One strong first screen:** a headline that says what the business does and for whom, one supporting sentence, one main button, one real photo.
- **Real photos.** Use the user's own photos, or find suitably licensed ones with your own tools (for example under the Unsplash or Pexels licence). Download them and upload them through the media routes; never hotlink. Pick photos of the actual kind of business, about 2000 px wide and under 1 MB, and write real alt text.
- **No filler.** Drop sections the business has nothing real for. Never invent testimonials, reviews, numbers, awards, clients, team members, prices or contact details.
- **Nothing temporary on the site.** Never put preview, test, draft or placeholder notices into page text, footers or form messages (no "private preview", "test message", "placeholder"). The owner publishes this exact site. Put open items in your chat summary instead. Where real details are missing, leave them out or ask. Only the Impressum and privacy pages may contain clearly marked [square bracket] gaps for details only the owner knows; list those gaps in chat.
- **Use the business blocks:** `gallery`, `price_list` (also a menu), `opening_hours` (with address), `faq`, `team` and `logo_strip`, next to `hero`, `rich_text`, `feature_grid`, `cta_band` and `testimonial_list`. The design brief shows the fields.
- **Fonts:** each starter embeds three faces. Pick a pairing with the `font_display` and `font_body` tokens (Modern, Warm serif or Bold condensed, listed in the brief). Other font names fall back to system fonts.
- **Every visible word in the CMS.** Headings, text, button labels, prices and list items go in page blocks, entries or site settings, never typed into `.astro` files. The review warns when a page's built HTML does not contain its own CMS text.
- **Mobile first and readable:** check every page at 390 px, keep text contrast at 4.5:1 or better, and keep tap targets at least 44 px tall.
- **Legal pages.** If the business is in the EU, add a legal notice (Impressum in Germany and Austria) and a privacy page with the user's real details: legal name, address, contact, and register or VAT numbers where they apply. Ask for them; never invent them. If the user cannot give them yet, tell them the site should not go live without these pages.
- **Before you say it is finished:** no sample text or sample photo from the starter is left, every link works, the favicon and share image are set, and you have looked at every page on both widths.

## Reference

- [Design guide](https://www.svalta.com/docs/ai-and-automation/site-design.md): theme tokens, font pairings, every page block with examples, photos and legal pages.
- [Embed videos, maps and booking calendars](https://www.svalta.com/docs/ai-and-automation/embeds.md): YouTube, Vimeo, Google Maps, OpenStreetMap, Calendly, Cal.com, Spotify and SoundCloud through `@svalta/astro/SvaltaEmbed.astro` with click-to-load; iframes from other hosts are blocked.
- [Guest quickstart](https://www.svalta.com/docs/getting-started/create-site.md) and [trial sessions API](https://www.svalta.com/docs/api/trial-sessions.md): every trial route, including adding and removing entries.
- [Site starts](https://www.svalta.com/docs/api/site-starts.md), [starter blueprints](https://www.svalta.com/docs/api/starter-blueprints.md), [preview review](https://www.svalta.com/docs/api/trial-preview-review.md), [media](https://www.svalta.com/docs/api/media.md).
- [Managed Astro source workspaces](https://www.svalta.com/docs/ai-and-automation/managed-astro-workspaces.md) and [workspaces API](https://www.svalta.com/docs/api/workspaces.md): source rules, changesets, builds and failures.
- [Forms](https://www.svalta.com/docs/api/forms.md), [saving a website](https://www.svalta.com/docs/getting-started/claim-workspace.md), [errors and recovery](https://www.svalta.com/docs/api/errors.md), [external agents](https://www.svalta.com/docs/ai-and-automation/external-agents.md).

## Limits in this deployment

- Guest trial: 30 draft builds per trial, 1 running at a time; up to 30 pages; 25 uploaded images. `meta.agent.quotas` on `GET /v1/trial-session` is always current.
- Saved projects: 100 builds per rolling 24 hours, one at a time.
- Rate limits: on HTTP 429, wait for `Retry-After` and retry the same request with the same idempotency key. Uploading many photos quickly can hit the limit.

## Content and source

Source authority: the Svalta source workspace API is the source of truth for managed Astro sites. A local folder, ZIP extract, or agent note is only scratch. Svalta is not a Git hosting remote; do not expect branches, pull requests, pushes, or external CI. Use source workspace revisions, changesets, builds, preview links, releases, and rollback operations.

Content/presentation split: store structured business content and user-owned media in Svalta CMS and media APIs. Use managed Astro source for presentation, layout, styling, client islands, and public build-time data views. Every content field reaches the build unless its key starts with `private_`.

Operating modes: external local agents may fetch a source workspace revision into a disposable local mirror because their editing model is file-based. That mirror is only scratch used to calculate Svalta changesets. The in-product Svalta assistant works directly against the remote source workspace through tools and does not require source files on the user's computer.

Git-shaped vocabulary: source `revision` is commit-like, `base_revision` is the parent revision edited against, source `changeset` is patch-like, `base_revision_stale` is the conflict signal, managed build is CI-like but run by Svalta, and release is deployment-like but preview, publish, hosting, and rollback stay inside Svalta.

When `workspace_available=true`, read `current_revision.revision_ulid` before every changeset. Before package, lockfile, hydration, Astro config, or browser-runtime edits on claimed projects, read `GET /v1/projects/{project}/workspace/build-policy`; guest trials cannot call that route, so use the pinned package and hydration contract below. Publish only after claim, through release operations: preview links and draft-preview builds never publish, and publish and rollback return synchronous `data.result` envelopes.

## Credentials

- Guest trial: `X-Svalta-Trial-Secret` or the `svalta_trial` cookie on `/v1/trial-session/*` routes.
- Browser account: signed-in Studio session with project access.
- Team token: `Authorization: Bearer <token>` with the needed abilities, including `api:site-starts:create`, `api:workspace:read`, `api:workspace:write`, `api:workspace-builds:read`, `api:workspace-builds:write`, `api:publish:preview`, `api:publish:execute`, or `api:publish:rollback`.
- After a claim, reconnecting an agent needs an explicitly authorized, scoped account API key. Never create one automatically.



## Managed Astro Rules

- Writable paths: `src/**`, non-reserved `public/**`, root `astro.config.mjs`, `package.json`, `package-lock.json`, `tsconfig.json`, and root `tailwind.config.*`, `postcss.config.*`, or `svalta.config.*`. Public source files are accepted for build input, but normal visitor assets should use Svalta media APIs because build delivery serves non-HTML artifacts only from `/_assets/astro/{artifact_set_public_id}/...`.
- Common managed presentation source files include `src/**/*.astro`, `src/**/*.ts`, `src/**/*.js`, `src/**/*.tsx`, `src/**/*.jsx`, and `src/**/*.css`.
- Forbidden paths: absolute paths, parent traversal, directory targets, hidden path segments, `.git`, `node_modules`, `.env*`, nested lockfiles, generated `.svalta/**`, and reserved public runtime paths such as `public/robots.txt`, `public/sitemap.xml`, `public/llms.txt`, `public/agents.md`, `public/.well-known/**`, `public/_access/**`, `public/_assets/**`, `public/_media/**`, `public/_preview/**`, and `public/_astro/**`.
- Default build policy: `starter-astro-v1` with `starter-astro-v1-2026-08-28`; default client interactivity: `bundled_client` with `astro-client-interactivity-v1-2026-08-28`.
- Direct packages are pinned by policy. Base packages include `astro@7.2.9`, `@svalta/astro@1.0.0`, `typescript@7.0.2`, `tailwindcss@4.3.3`, and `@tailwindcss/vite@4.3.3`; bundled client adds `@astrojs/react@6.0.4`, `react@19.2.8`, and `react-dom@19.2.8`.
- Supported dynamic behavior: static Astro output from generated Svalta data, same-origin assets under `/_assets/astro/{artifact_set_public_id}/...`, Svalta media API assets, and React islands with `client:load`, `client:idle`, `client:visible`, or `client:media`.
- Not supported: server rendering, adapters, middleware, endpoint routes, Actions, server islands, tenant server APIs, external browser resources (fonts, scripts, styles, images from other hosts), external connect targets, embeds outside the allowlist (use `SvaltaEmbed`), client-side redirects, inline event/style attributes, Astro `is:inline` directives, `client:only`, `server:defer`, unsafe eval, and direct `.svalta/**` writes.

## Dynamic-Site Capability Matrix

| Request | First managed Astro release |
| --- | --- |
| Directories, catalogs, calculators, galleries, tabs, accordions, menus, client-side filters, client-side sort/pagination, and lightweight search | Supported when they run from explicitly public build-time data in the browser |
| 100-property real estate directory | Store properties in Svalta CMS records, emit only visitor-public fields through public-data projection, and filter/search with a React client island |
| Simple map/list UI | Supported: a Google Maps or OpenStreetMap embed through `SvaltaEmbed` (click-to-load), or same-origin static data |
| Visitor writes or contact forms | Use the documented Svalta forms primitive. No general tenant-defined form backend |
| Server-side filtering/search, private visitor personalization, tenant API routes, tenant sessions/auth, arbitrary server jobs, external databases, external map scripts, external tile providers, external API calls, embeds outside the allowlist, external assets, or static JSON endpoint routes | Not supported unless a documented Svalta-owned primitive says otherwise |

Rule of thumb: if behavior can run from public data in the browser, build it as a client island. If it requires private data, secrets, per-user server logic, or writes, use a documented Svalta-owned primitive or mark it unsupported.

## Svalta forms

Visitor form submissions are enabled in this deployment. The starters' contact page already renders the native contact form: a `rich_text` block with `"form_key": "contact"` marks the page and its `heading`, `body` and `cta_label` frame the form. Submissions go to `/_forms/{public_form_id}/submissions` and appear in Studio under Forms and leads. Published form descriptors reach the build as `.svalta/forms.json`; read them with `getForms()` or `getForm(key)`. Messages sent from a preview are test messages and are not emailed; tell the user that in chat, never on the site. To render a form in your own source:

```astro
---
import SvaltaForm from '@svalta/astro/SvaltaForm.astro';
import { getForm } from '@svalta/astro';
const form = getForm('contact');
---
<SvaltaForm form={form} routePath="/contact" placementKey="contact-main" submitLabel="Send message" />
```

`placementKey` must be unique on that route and `routePath` must be the page's real path. Trials create or change forms with the `/v1/trial-session/workspace/forms` routes; claimed projects use `/v1/projects/{project}/forms` and read availability from `public_interaction_primitives.forms` in the build-policy response. Never invent Astro Actions, endpoints, external form providers or browser connects. If forms are switched off, show the contact details instead. Details: [Forms API and Astro examples](https://www.svalta.com/docs/api/forms.md).

## Recovery

- Stale edits: if a changeset returns `status=conflict` and `failure_code=base_revision_stale`, read `current_revision.revision_ulid`, inspect `conflicts[]`, recreate the changeset against that revision, and apply the new draft.
- Compile failures: if a build returns `failure_code=astro_compile_failed`, read the build logs, fix the reported source syntax, import, type, or Astro error, apply a new changeset, then build the new revision. Retrying the same revision will fail the same way.
- Build failures: read `failure_code`, `retryable`, and build logs. Fix dependency, client interactivity, server runtime, external resource, inline markup, public data projection, quota, timeout, memory, output, or file-count issues before retrying.
- Preview not ready: `GET /v1/trial-session/preview` returns 202 with `data.preview.status`. `build_required` means build the current revision, `pending` means wait for the running build, `failed` means read the logs.
- Trial setup: follow `trial_session.setup_run.safe_next_action`. If setup is `stale_pending`, keep polling briefly, then report the setup run id and state to the user.
- Rate limits and idempotency: follow `Retry-After` and keep method, path, body and idempotency key stable.

## Starter Packs

These are the Astro starter packs a site can start from. Offer them to the user by name; previews live at https://www.svalta.com/templates.

- [Kestrel Partners](https://www.svalta.com/docs/api/starter-blueprints.md): `kestrel-partners`; presentation_driver=`astro`; workspace_required=`true`: A clear, confident company website with services, selected work, insights and a working contact form. Made for consultancies, agencies and professional practices.
- [Weeknight Club](https://www.svalta.com/docs/api/starter-blueprints.md): `weeknight-club`; presentation_driver=`astro`; workspace_required=`true`: A bright, confident site for a club, studio or local service that sells sessions and memberships. Timetable, pricing, questions and a working enquiry form.

## API Docs

- [API overview](https://www.svalta.com/docs/api.md): Main public API entry point.
- [API authentication](https://www.svalta.com/docs/api/auth.md): Team API tokens, headers, and auth failures.
- [Starter blueprints API](https://www.svalta.com/docs/api/starter-blueprints.md): Discover starter packs.
- [Site starts API](https://www.svalta.com/docs/api/site-starts.md): Start a private trial site from a starter pack.
- [Trial sessions API](https://www.svalta.com/docs/api/trial-sessions.md): Read, edit, recover, and claim guest trial sessions.
- [Trial preview review API](https://www.svalta.com/docs/api/trial-preview-review.md): Structural review of a draft build.
- [Workspaces API](https://www.svalta.com/docs/api/workspaces.md): Read, change, build, preview, and diagnose managed Astro source.
- [Preview links API](https://www.svalta.com/docs/api/preview-links.md): Create private links for draft releases and successful draft-preview builds.
- [Releases API](https://www.svalta.com/docs/api/releases.md): Create, validate, publish, schedule, and roll back releases after claim.
- [External agent API](https://www.svalta.com/docs/api/external-agents.md): Cross-cutting operation map for agent integrations.
- [Operations API](https://www.svalta.com/docs/api/operations.md): Poll async setup work.
- [Errors and recovery](https://www.svalta.com/docs/api/errors.md): Error envelope and retry behavior.

## Machine Contracts

- [Documentation search index](https://www.svalta.com/docs/search-index.json): Searchable docs metadata.
- [Operation catalog](https://www.svalta.com/docs/api/contracts/catalog): Machine-readable operation catalog.
- [Ability catalog](https://www.svalta.com/docs/api/contracts/ability-catalog): Capability and ability mapping.
- [Contract specification](https://www.svalta.com/docs/api/contracts/spec): Shared API response contracts.
- [UI/API parity matrix](https://www.svalta.com/docs/api/contracts/parity-matrix): Workflow parity notes.

## Optional

- [Human docs home](https://www.svalta.com/docs): Browser documentation.
- [Markdown docs home](https://www.svalta.com/docs.md): Markdown counterpart for the docs home.
