webhouse.appwebhouse.appdocs

Use CMS Admin as a headless backend inside your own Next.js site. Read content, trigger deploys, and embed the AI chat — all authenticated with a permanent wh_ Access Token.

Overview

WebHouse CMS Admin is a full headless backend. Any Next.js (or other) site can call its REST API with a permanent wh_ Access Token — no OAuth redirect, no cookie session. This lets you build custom admin panels, booking management, form inboxes, and even an AI chat inside your own branded UI.

1. Create an Access Token

Go to Account Preferences → Access Tokens → Create custom token.

Choose only the permissions your site needs:

Use casePermissions
Read contentcontent.read
Full content CRUDcontent.read content.create content.edit content.publish content.delete
Trigger deploysdeploy:trigger deploy:read
Form inboxforms.read

Set Site scope to restrict the token to a specific site. Store it in .env:

CMS_API_TOKEN=wh_xxxxxxxxxxxxxxxxxxxx
CMS_API_URL=https://webhouse.app

Never expose the token client-side. Use it in server components, API routes, or getServerSideProps only.

2. Read Content

typescript
// app/posts/page.tsx
export default async function PostsPage() {
  const res = await fetch(
    `${process.env.CMS_API_URL}/api/cms/posts?status=published`,
    {
      headers: { Authorization: `Bearer ${process.env.CMS_API_TOKEN}` },
      next: { revalidate: 60 },
    }
  );
  const { documents } = await res.json();
  return <ul>{documents.map((p: any) => <li key={p.slug}>{p.data.title}</li>)}</ul>;
}

3. Content API Reference

GET    /api/cms/{collection}          List documents
GET    /api/cms/{collection}/{slug}   Get by slug
POST   /api/cms/{collection}         Create
PATCH  /api/cms/{collection}/{slug}  Update
DELETE /api/cms/{collection}/{id}    Trash

Query params: status, locale, limit, offset, tags

4. Site Admin Building Blocks

Trigger a deploy

typescript
await fetch(`${process.env.CMS_API_URL}/api/admin/deploy`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.CMS_API_TOKEN}` },
});

Read form submissions

typescript
const res = await fetch(
  `${process.env.CMS_API_URL}/api/admin/forms/contact/submissions`,
  { headers: { Authorization: `Bearer ${process.env.CMS_API_TOKEN}` } }
);
const { submissions } = await res.json();

5. Embed the AI Chat

The CMS chat runs the same Claude model and tools as CMS Admin. Proxy it through a server route in your site:

typescript
// app/api/chat/route.ts
export async function POST(request: Request) {
  const body = await request.text();
  const upstream = await fetch(`${process.env.CMS_API_URL}/api/cms/chat`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.CMS_API_TOKEN}`,
    },
    body,
  });
  return new Response(upstream.body, {
    headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' },
  });
}

Then consume the stream in a client component with a standard ReadableStream parser. The chat streams SSE events with event: text, event: tool_call, event: tool_result, and event: done.

Restrict tools by granting only the permissions you want on the Access Token — the chat only executes tools matching the token's permission set.

6. ICD Revalidation

Configure the revalidate webhook in Site Settings → Deploy → Revalidate URL to connect Next.js ISR with CMS content publishing:

typescript
// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
export async function POST(req: Request) {
  const secret = req.headers.get('x-webhouse-secret');
  if (secret !== process.env.REVALIDATE_SECRET) return new Response('Unauthorized', { status: 401 });
  const { slug, collection } = await req.json();
  revalidatePath(`/${collection}/${slug}`);
  return Response.json({ revalidated: true });
}

Further reading

Previous
Producing articles via API
JSON API →Edit on GitHub →