SnabbSajt · Docs
For website ownersFor developers
DeveloperSite kit

Headless delivery preview

Let your marketing team edit in SnabbSajt while your own app serves the published site.

Developer preview. The published-site API, read-only keys, settings and deploy hook work, and connect, pull, link and push have all shipped in @snabbsajt/cli@0.4.0. What is still missing is a real repository having gone through the whole journey end to end, meaning a token in your own host, a publish and a deploy hook firing, so this page is intentionally not listed in the public docs navigation yet. Do not run npx @snabbsajt/cli init: that command is designed but not built.

Your marketing team can edit the website in SnabbSajt and press Publish while your own app fetches the published snapshot and renders it with your own components, on your own hosting.

Content lives in SnabbSajt. Code lives in your git. Neither syncs into the other.

What works today

  • A site owner, or a retained builder with the explicit delivery grant, can switch a site from hosted to headless delivery and manage its keys.
  • A read-only key can access one site's published snapshot. That key can never reach the draft.
  • A separate test-version key reads the unpublished draft, for your preview deployment. See Staging below.
  • Publishing can call your deploy hook and records whether it succeeded.
  • Keys can be revoked immediately.
  • snabbsajt connect, pull, link and push are published (0.4.0). See the CLI reference.
  • The starter template renders either source through the same components: your local src/site.ts while you build, and the published snapshot once SNABBSAJT_SITE_ID and SNABBSAJT_DELIVERY_TOKEN are set. The mapping is renderModelFromPublished / renderModelFromPackage in @snabbsajt/site-kit, so your own app can use it without the template.

Pilot setup

In SnabbSajt, open Settings → Developers → Var hemsidan visas and choose Hos er själva. Add the separate preview and published website URLs, then create a key under Nycklar för er app and copy it when it is shown. We store only its hash, so it cannot be shown again.

For a pilot, SnabbSajt also provides the API base URL. Keep these values in your server-only environment:

SNABBSAJT_API_URL=https://<deployment>.convex.site
SNABBSAJT_SITE_ID=<site-id>
SNABBSAJT_DELIVERY_TOKEN=<shown-once-key>

Fetch the published snapshot from a Server Component, route handler or build step:

const {
  SNABBSAJT_API_URL,
  SNABBSAJT_SITE_ID,
  SNABBSAJT_DELIVERY_TOKEN,
} = process.env;

if (
  !SNABBSAJT_API_URL ||
  !SNABBSAJT_SITE_ID ||
  !SNABBSAJT_DELIVERY_TOKEN
) {
  throw new Error("SnabbSajt environment variables are missing");
}

const response = await fetch(
  `${SNABBSAJT_API_URL}/v1/sites/${SNABBSAJT_SITE_ID}/published`,
  {
    headers: {
      Authorization: `Bearer ${SNABBSAJT_DELIVERY_TOKEN}`,
    },
    cache: "no-store",
  },
);

if (!response.ok) {
  throw new Error(`SnabbSajt delivery failed (${response.status})`);
}

const site = await response.json();

The response contains version, siteId, stage: "published", versionId, publishedAt and the public snapshot. Add ?locale=en or ?locale=pl for a translated snapshot. A valid key receives 404 not_published until the site has been published once.

Keep the key on the server

The delivery key is read-only and scoped to one site, but it is still a credential. Never expose it through a NEXT_PUBLIC_ variable or ship it in a browser bundle.

Staging: the unpublished version

There is no third environment to configure. Your draft in SnabbSajt IS your staging content, and the published snapshot IS production. Two worlds, which is what the editor already gives every site.

To render the draft from your own preview deployment:

  1. In SnabbSajt, under Nycklar för er app, choose Skapa nyckel för testversionen. It starts with sajt_draft_ rather than sajt_pub_, so you can see which one you pasted.
  2. Put that key in your preview environment only. It reads work nobody has chosen to publish yet.
  3. Ask for the draft with ?stage=draft:
const response = await fetch(
  `${SNABBSAJT_API_URL}/v1/sites/${SNABBSAJT_SITE_ID}/published?stage=draft`,
  { headers: { Authorization: `Bearer ${STAGING_TOKEN}` }, cache: "no-store" },
);

Or from the SDK and the CLI:

const { snapshot } = await sajt.getPublishedSite({ stage: "draft" });
snabbsajt pull --stage draft

A draft answer carries stage: "draft" and no versionId and no publishedAt, because there is no version: a draft is whatever it is right now. That absence is deliberate. It is what stops a staging build presenting itself as a production one in a cache key or a build log. renderModelFromPublished accepts either shape, so the same components render both and your preview cannot drift from production.

A production sajt_pub_ key asking for ?stage=draft gets a plain 401 unauthorized - the same answer a key that does not exist gets. That is on purpose: the error must not tell whoever holds a weaker key that a stronger one exists for this site. So do not read that 401 as "wrong stage".

?locale= is ignored for a draft. Translations are produced at publish, so a draft has only the site's primary language, exactly like the editor's own preview.

Rebuild staging when the content changes

Paste a second deploy hook under Bygg om testversionen. We call it when the draft changes, so before anyone publishes.

It is heavily debounced: at most one call every few minutes per site, however much typing happened in between. An owner writing a headline moves the draft on every keystroke and a build costs you money, so a burst of edits becomes a single rebuild. Bygg om nu beside the field answers the "did it pick up my change?" question immediately.

The two hooks send different bodies, so one endpoint can serve both: {"source":"snabbsajt","event":"publish"} for production and {"source":"snabbsajt","event":"draft_changed"} for staging. A failing staging hook is recorded and never touches a publish.

Rebuild when someone publishes

Paste your host's deploy hook under Var hemsidan visas. Vercel, Netlify, AWS Amplify, Cloudflare Pages and GitLab pipelines all provide one.

SnabbSajt POSTs to that URL after a successful publish. A failed hook does not roll back the published content, and the outcome remains visible in settings.

A marketer publishing never deploys your code. The hook rebuilds whatever commit your host already considers current. A developer deploying never changes content.

Rate limits

The endpoint expects a build or server cache, not a browser request per visitor: 120 requests per minute per IP and 240 per minute per site.

Revoking access

Every key is listed under Settings → Developers → Nycklar för er app, labelled so you can see which ones read the unpublished version. Revoking one takes effect immediately. A revoked key is rejected like an unknown one with the same 401 unauthorized response. Production and test-version keys are minted, listed and revoked separately, and they share one per-site limit of ten.

Automated setup

The canonical SDK source implements snabbsajt connect and snabbsajt pull with device-code approval, .snabbsajt.json and a server-only SNABBSAJT_DELIVERY_TOKEN. Those commands must not be documented as available until the matching npm package is published and tested from a clean external repository.

Leaving

Remove the three server environment variables and the fetch integration. Your last downloaded snapshot is ordinary JSON and remains yours.


Didn't find the answer, or is something wrong here? Tell us.

Last updated on

On this page