Skip to content

Self-Hosting: Headless CMS Quickstart

Build your own site against Vivreal as a headless CMS — API key, site ID, and a minimal fetch example

intermediate7 min readFor developers

Self-Hosting: Headless CMS Quickstart

You picked Self-hosted when you created your site (or you're about to). This guide is for the developer who's building and shipping the frontend.

In self-hosted mode, Vivreal is a headless CMS:

  • Your content team gets Vivreal's portal UI to organize collections, schemas, media, and publish dates
  • Your development team gets an API key and a site ID to pull that content into any frontend
  • Vivreal does not ship, prescribe, or host a rendering layer — the frontend is 100% yours

You're free to use any framework (Next.js, Remix, Astro, SvelteKit, Nuxt, Solid, plain HTML, a mobile app) on any hosting platform (Vercel, Netlify, AWS, Cloudflare, your own infra).

What You Get

After creating a self-hosted site in the portal, you have:

ValueWhere to find itWhat it's for
API keyGroup Settings → API KeysAuthenticates Client API calls. Scoped to your group. Keep it server-side.
Site IDSites page → your site → SettingsScopes queries to this site's content. Use it as a query parameter in Client API calls.
Seeded collectionsContent tab in the portalVivreal created schemas and starter page configs based on the template you picked. Your frontend reads from these collections.

Keep your API key server-side

Treat the Vivreal API key like any other secret. Never ship it to the browser. Put it in an environment variable on your hosting platform and only use it from server-side code (API routes, server components, server actions, edge functions, or a backend proxy).

Your First Fetch

Here's the minimum needed to pull content from Vivreal into any backend or server component.

const API_KEY = process.env.VIVREAL_API_KEY;
const SITE_ID = process.env.VIVREAL_SITE_ID;
const COLLECTION_ID = "your-collection-id"; // from the portal

const res = await fetch(
  `https://client.vivreal.io/tenant/collectionObjects?collectionId=${COLLECTION_ID}&siteId=${SITE_ID}`,
  {
    headers: {
      Authorization: API_KEY,
    },
  }
);

if (!res.ok) {
  throw new Error(`Vivreal API returned ${res.status}`);
}

const { data } = await res.json();
// data is an array of collection objects ready to render

That's it. You now have your content as JSON. How you render it, cache it, revalidate it, theme it, and deploy it is up to you.

What Self-Hosted Is NOT

To set expectations — self-hosted mode does not include:

  • A starter template repo or codebase — Vivreal's hosted-mode templates (Ecommerce Store, Showcase) are an implementation detail of hosted mode. Self-hosted is deliberately scaffold-free.
  • A rendering SDK — there's no @vivreal/renderer for self-hosted. Use whatever your team already uses.
  • Deployment automation — Vivreal doesn't wire your hosting. You pick Vercel, Netlify, AWS, wherever.
  • Asset delivery past the CMS — media URLs come back in Client API responses, but CDN caching, image optimization on your pages, etc. is on your side.
  • Site-level theming or config beyond what's in collections — if you want per-site design tokens, encode them in a collection and fetch them like any other content.

Scope every query by site ID

If your group has multiple sites pointing at overlapping collections, siteId keeps each site's queries clean. Always include it.

Cache aggressively, revalidate on writes

Vivreal content updates within seconds server-side, but you don't need to re-fetch on every request. Use your framework's caching primitives (ISR in Next.js, cache() in Remix, etc.) and invalidate on webhook events (see below).

Subscribe to webhooks for cache invalidation

Vivreal emits webhook events (content.created, content.updated, content.deleted, integration.*) when your content changes. Wire a webhook endpoint on your site that purges or rebuilds the relevant cache entries.

See Webhooks Overview for the full event list, signature verification, and retry policy.

Never hard-code collection field names

Field names are schema-driven and can change in the portal. Always iterate over the data object rather than destructuring hard-coded field names, or keep a single mapping layer that's easy to update when the schema changes.

Common Questions

Do I need to rebuild my site when content changes? No — content updates are served live from the Client API. The only time you rebuild is when your frontend code changes. For incremental page generation (e.g., Next.js ISR), trigger revalidation from a webhook handler.

Can I have both hosted and self-hosted sites in the same group? Yes. Create one site in each mode — they share the same group, the same collections, and the same API key. Each site has its own site ID, so you can scope queries independently.

Can I use the Client API without creating a site first? No — the API key is group-scoped, but every Client API query requires a siteId. Create at least one site (any mode) to get a site ID.

What if I want to move to hosted later? You can create a new hosted site in the same group, pointing at the same collections, and run both side-by-side or migrate your domain from one to the other. Your content stays intact.

Next Steps

Source of truth

  • Self-hosted mode is resolved in VR_Secure_API/src/createSites/services/createSiteCollectionData.js — triggers schema seeding but skips the Step Functions deploy pipeline
  • Client API authentication: VR_Client_Auth Lambda authorizer validates the API key at API Gateway
  • Client API routes: VR_Client_API/src/api/index.js (8 endpoints under /tenant/)
  • Webhooks are emitted by VR_CMS_API/src/shared/emitWebhookEvent.js and delivered by VR_Secure_API/src/webhookDelivery/services/deliver.js