Examples
Complete, runnable snippets. Each assumes a client configured with a PAT:
import { ForumulateClient, ForumulateError } from '@forumulate/sdk';
const forumulate = new ForumulateClient({
baseUrl: 'https://forumulate.com/api',
token: process.env.FORUMULATE_TOKEN!,
});1. Create a docs space and publish a page
Builds a public DOCS space, adds a collection, creates a page, sets its content (a TipTap/ProseMirror document), and publishes it — then reads it back through the public (unauthenticated) reader.
async function publishDocsPage(orgId: string) {
const { space } = await forumulate.spaces.create(orgId, {
name: 'Docs',
slug: 'docs',
type: 'DOCS',
visibility: 'PUBLIC',
});
const { collection } = await forumulate.spaces.createCollection(orgId, space.id, {
name: 'Guides',
slug: 'guides',
});
const { page } = await forumulate.spaces.createPage(orgId, space.id, collection.id, {
title: 'Intro',
slug: 'intro',
});
await forumulate.spaces.setPageContent(orgId, space.id, page.id, {
title: 'Intro',
content: {
type: 'doc',
content: [
{ type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Welcome' }] },
{ type: 'paragraph', content: [{ type: 'text', text: 'Hello from the SDK.' }] },
],
},
});
await forumulate.spaces.publishPage(orgId, space.id, page.id);
// Read it back with no auth, through the public reader.
const { hits } = await forumulate.public.search(orgId, space.id, 'welcome');
return { space, page, hits };
}The content is a BlockDoc: { type: 'doc', content: BlockNode[] }, where each node has type, optional content, text, attrs, and marks.
2. Create and list items (the space-agnostic "pieces" surface)
items is the uniform way to write one piece of a space — a docs page, a blog post, a release, an incident — whatever that space holds. You send Markdown sections and the response tells you what a piece's shape says is still missing.
async function seedRelease(orgId: string, spaceId: string) {
// Create a piece with a whole-body markdown.
const { item } = await forumulate.items.create(orgId, spaceId, {
title: 'v1.4.0',
body: '## Highlights\n\n- Faster search\n- New PDF export',
});
// Fill a single named section (idempotent — sending it twice writes it once).
const { conformance } = await forumulate.items.setSection(
orgId, spaceId, item.id, 'summary', 'A performance-focused release.',
);
if (!conformance.conforms) {
console.log('still missing sections:', conformance.missing);
}
// Publish it (kinds that are already live on creation say so and change nothing).
await forumulate.items.publish(orgId, spaceId, item.id);
// List what's in the space.
const { items } = await forumulate.items.list(orgId, spaceId, 50);
return items;
}To page through or incrementally sync a large space, use client.typed with a cursor / updated_since — see Errors & pagination.
3. Start a forum topic and reply to it
Community spaces (type: 'FORUM') use topics and posts. Content is a BlockDoc, same as docs pages.
async function askAndAnswer(orgId: string, spaceId: string) {
const body = (text: string) => ({
type: 'doc' as const,
content: [{ type: 'paragraph', content: [{ type: 'text', text }] }],
});
const { topic } = await forumulate.forum.createTopic(orgId, spaceId, {
title: 'How do I rotate a PAT?',
content: body('What is the recommended way to rotate a token without downtime?'),
kind: 'QUESTION', // optional; e.g. 'QUESTION' | 'DISCUSSION'
tags: ['auth', 'tokens'], // optional
});
// `topic` is untyped in the helper; cast to read its id.
const topicId = (topic as { id: string }).id;
await forumulate.forum.reply(orgId, spaceId, topicId, body('Issue a new token, deploy it, then revoke the old one.'));
// List topics in the space.
const { topics } = await forumulate.forum.listTopics(orgId, spaceId, { sort: 'latest' });
return topics;
}Error handling across all three
Wrap calls and branch on code:
try {
await publishDocsPage(orgId);
} catch (err) {
if (err instanceof ForumulateError && err.code === 'space.slug_taken') {
// pick a different slug
} else {
throw err;
}
}