Kookee is a content backend: you author blog posts, changelog entries, help articles, pages, and announcements in one dashboard, and your app fetches them as JSON and renders them with your own components, on your own domain. This walkthrough goes from empty project to three live content surfaces, using the TypeScript SDK. If your framework can fetch JSON and render on the server — Next.js, Astro, Nuxt, SvelteKit, TanStack Start — this maps directly onto it.
1. Create a project and get credentials
Sign up (the free tier needs no credit card and includes the blog, changelog, and help center modules), create a project, and note two credentials:
Project ID — safe to expose in browser code; reads published content, like the API key. It works only while the project's hosted portal is enabled; otherwise the API answers 403
PORTAL_NOT_ENABLED.API key — works for every project, on your servers and in the browser. It only reads published content, and anything shipped to a browser is visible to visitors, so restrict it to your domains via the project's allowed origins.
The rule of thumb: apiKey everywhere, restricted by allowed origins wherever it reaches a browser; projectId in browser code only while the hosted portal stays on.
Write one draft blog post, one changelog entry, and one help article in the dashboard now, and publish them — you want real data on the other end of your first fetch.
2. Install and initialize the SDK
npm install @kookee/sdk
import { Kookee } from '@kookee/sdk';
export const kookee = new Kookee({ apiKey: process.env.KOOKEE_API_KEY });
The SDK is TypeScript-first, dependency-free, and tree-shakeable; every read method accepts an AbortSignal. Instantiate it once in a shared module and import it from your route loaders.
3. The blog: list and detail
Two routes. The list page:
const { data: posts } = await kookee.blog.list({ page: 1, limit: 10 });
Each summary carries title, slug, excerpt, tags, and timestamps — enough for cards. The detail page fetches the full entry by slug:
const post = await kookee.blog.getBySlug(slug);
Detail responses include the rendered body HTML plus the SEO fields you set in the editor, so your document head derives from the same fetch: title, meta description, canonical from the slug, OG image. If getBySlug finds nothing, return a real 404.
Tag pages are the same list call with tags: ['news']; a search box is list({ search: query }); kookee.blog.getTags() gives you the tag cloud with counts.
4. The changelog
Structurally identical, with typed categories:
const { data: entries } = await kookee.changelog.list({ page: 1, limit: 20 });
const features = await kookee.changelog.list({ filter: { changelogType: 'feature' } });
Entries carry a type (feature, fix, improvement, breaking, security, deprecated or other) and an optional version, so your UI can render badges and filters without inventing a convention. Reactions are one call if you want lightweight signal on releases:
await kookee.changelog.react(entryId, { reactionType: 'fire', action: 'add' });
5. The help center: categories, articles, semantic search
Three fetches cover the routes:
const categories = await kookee.help.categories();
const articles = await kookee.help.list({ category: 'getting-started' });
const article = await kookee.help.getBySlug('reset-your-password');
The distinctive part is search, which is semantic — it matches meaning, not keywords, and returns the matched snippet so you can show why each result hit:
const results = await kookee.help.search({ query: 'charged twice this month', limit: 5 });
// results[i].title, results[i].matchedChunk
When your article base is ready for it, the same content powers AI answers — kookee.help.chat returns grounded responses with sources, and chatStream yields deltas for a streaming UI. That is the free tier's metered AI feature (100 answers per month to start), and it needs no extra integration: the articles you just published are the corpus.
6. React shortcuts, if that's your stack
@kookee/react wraps all of the above in hooks — useBlogPosts, useBlogPost, useChangelog, useHelpCategories, useHelpSearch (debounced, safe to wire straight to an input) — each returning { data, isLoading, error } with pagination via loadMore. Server-render with your framework's loader and pass the result as initialData to keep first paint complete. For bodies, <EntryContent entry={post} /> renders the entry HTML, with a stylesheet for code blocks you can import into a Tailwind cascade layer.
Since 1.9.0 the package also ships server-rendered components for all three surfaces; see React components for your blog, changelog and help center.
7. What you get without writing more code
A hosted portal at
your-project.kookee.dev— feedback board, roadmap, changelog, and help center live as soon as you turn on Enable Public Portal on the project's Public Portal page, useful while your own routes are in progress (portal pages are noindexed, so they never compete with your domain in search).Translations: every content type takes a
localeparameter, with per-project fallbacks, when you go multilingual.Comments and reactions across modules, moderated from the dashboard.
An MCP server, so AI agents and assistants can search, read, and draft your content with a personal token for each team member, read only unless you choose otherwise.
The afternoon, itemized
Realistically: project setup and first content, half an hour; blog routes, an hour; changelog, half an hour; help center with search, an hour or two; head metadata wiring, an hour. The free tier's 10,000 monthly API requests comfortably cover a small site's server-rendered traffic, and there is no charge until you outgrow it.
The pattern that makes this fast is the point of the product: Kookee owns authoring, storage, search, and delivery; you own two or three thin routes per surface, built with the components you already have. Create the project, publish a test post, and make the first list call — the rest follows from there.