SDKs and integrations
Everything we publish for developers, on one page. There are two kinds, and which one you want depends on what you are building:
Management API SDKs, the CLI and MCP run on your side with a token. They create spaces, write and publish pages, moderate the forum and read anything your token can reach. Use them from a backend, a script, CI or an AI agent.
Website SDKs and integrations put your published Forumulate content on another site: comments under an article, a docs search box, the changelog bell, a feed of posts. They read public endpoints from the reader's browser and need no token at all.
Never put a token in a website integration. Anything a browser loads, a reader can read.
Every package below lives in a public repository at github.com/forumulate/<repo>. Each repository's README is the full reference; this page is the map, with enough to get each one running.
Registries. Every JavaScript package is on npm: the SDK, CLI and MCP server, @forumulate/embed and all of its framework packages. The Go SDK and the Go web integration need no registry, because go get reads the repository. The Python, Ruby and PHP packages are coming soon to PyPI, RubyGems and Packagist; until then, install them straight from their repository, as each quickstart below shows:
pip install git+https://github.com/forumulate/<repo> # Python
gem "<gem>", git: "https://github.com/forumulate/<repo>" # Ruby, in the GemfileOnce a package reaches its registry, the ordinary npm install @forumulate/<name> (or pip install, gem, composer require) installs the same code.
The Management API
Base URL: https://forumulate.com/api. The developer quickstart walks through your first five calls with curl, and API tokens, scopes and capabilities explains the two gates a token passes. Mint a token at Account → Tokens.
The SDKs, the CLI and the MCP server read the same environment, so one setup configures all of them:
Variable | Default | What it is |
|---|---|---|
| none | Personal Access Token, sent as |
|
| The site. The API is its |
| none | An exact API base, for a local API that has no |
SDKs
All three are generated from the same OpenAPI spec the API reference is built from, so a route in the reference is a method in each SDK.
Language | Package | Repository |
|---|---|---|
TypeScript |
| |
Python |
| |
Go |
|
TypeScript. No runtime dependencies; Node 18+, Bun, Deno and the browser.
npm install @forumulate/sdkimport { ForumulateClient } from '@forumulate/sdk';
const forumulate = ForumulateClient.fromEnv(); // FORUMULATE_TOKEN, FORUMULATE_URL
const { orgs } = await forumulate.orgs.list();Every operation in the reference is a method named after its operationId: ForumTopics_get is forumulate.forum.topics.get(…), Webhook_rotate is forumulate.webhooks.rotate(…). Each takes one argument, with the path parameters at the top level and query and body nested, and the arguments and answer are typed from the spec. forumulate.request('Space_get', { orgId, spaceId }) calls the same operation by its id.
// A list awaits to its first page, and iterates every row across every page.
for await (const post of forumulate.blog.list({ orgId, spaceId })) console.log(post.title);
await forumulate.webhooks.update({ orgId, webhookId, body: { active: false } });The client retries what is safe to retry (429, 503 and friends, honouring Retry-After), gives every POST and PATCH an Idempotency-Key so a retried write runs once, and throws a ForumulateError with the envelope's code, status, details and requestId. constructWebhookEvent verifies a delivery and parses it in one call. The package README lists the namespaces and every option.
Python.
Python 3.8 or newer; its one dependency is requests.
pip install git+https://github.com/forumulate/forumulate-python # PyPI (pip install forumulate): coming soonfrom forumulate import Forumulate
client = Forumulate() # FORUMULATE_TOKEN, FORUMULATE_URL
orgs = client.orgs.list()["orgs"] # one page; client.orgs.list_all() follows the cursorGo.
go get github.com/forumulate/forumulate-goGo 1.21 or newer; standard library only.
client := forumulate.NewClient() // FORUMULATE_TOKEN, FORUMULATE_URL; forumulate.WithToken(…) and friends override them
res, err := client.Orgs.List(ctx, nil)
if err != nil { log.Fatal(err) }
for _, org := range res.Orgs { fmt.Println(org.Slug) }Any other language works against the same API with an ordinary HTTP client: the spec is served at https://forumulate.com/api/openapi.json, and every error has one shape, described in API conventions.
The CLI: @forumulate/cli
The forumulate command, for terminals and CI: list what is in an organization, publish a folder of markdown (docs-as-code), search, snapshot a space, check a custom domain, and doctor, which checks a live site from the outside the way readers and crawlers see it. Node 20 or newer. Repository: cli.
npm install -g @forumulate/cli
forumulate login # prompts for the token, stores it
forumulate publish acme <spaceId> ./docs --dry-run # each .md file becomes a pageforumulate login keeps the token in ~/.config/forumulate/credentials.json, readable only by you; in CI set FORUMULATE_TOKEN instead, which always wins. doctor needs no token. The command line, and forumulate doctor covers every command and every check.
MCP: @forumulate/mcp
Connects Claude Desktop, Claude Code, Cursor or VS Code to your organization. It is a small stdio bridge to Forumulate's hosted MCP servers, so a tool we add appears in your client without updating the package. Repository: mcp.
claude mcp add forumulate -e FORUMULATE_TOKEN=ak_… -- npx -y @forumulate/mcpThere are two hosted servers, picked with FORUMULATE_MCP:
write(the default) acts as you, with your token, across every space you can reach. Aread-scoped token can call its read tools; the write tools refuse it.readserves one published docs space, anonymously, and can never write. Give it to a support bot or a coding agent working against your product. SetFORUMULATE_ORGandFORUMULATE_SPACEto the ids.
If your token reaches more than one organization, set FORUMULATE_ORG. The repository README has the config block for each client, and the native remote configuration that needs no npx.
Website SDKs
These put published content on a site you already run. Every one of them takes the same two values, which you find in your console address (/o/<organization id>/…) and in each space's settings:
org: the organization id or slug.space: the space to read. Comments read a forum space, the changelog bell a changelog space, search a docs space.
None of them needs a token. They read public endpoints only, so a space must be published and public for them to show anything.
The JavaScript packages install from npm and the CDN line works today. The Python, Ruby and PHP integrations install from their GitHub repository until their PyPI, RubyGems and Packagist releases land.
What you can embed
Element | What it shows |
|---|---|
| The comment thread for the current page, with a reply box when the space allows reader replies. The first visit creates the thread. |
| A box over a docs space: search it, or ask a question answered from it ( |
| The same docs box, search only. |
| A "What's new" bell with an unread badge and a popover of recent releases. |
| A forum's latest, newest, hot or top topics — or one category's or tag's — styled by the forum's own post look ( |
| A list of recent blog posts, forum topics, docs pages or changelog releases ( |
Comments find their thread by the page's address. On a page whose address is not the article's (inside an iframe, on a preview domain), pass the real one with article-url, or pin one thread with topic.
The core: @forumulate/embed
The elements above are standard Web Components, so they work in any page and any framework. Load them from a CDN:
<script type="module" src="https://cdn.jsdelivr.net/npm/@forumulate/embed@1"></script>
<forumulate-comments org="<org-id>" space="<space-id>"></forumulate-comments>Or install them in a bundled app:
npm install @forumulate/embedimport '@forumulate/embed/elements'; // registers every <forumulate-*> elementThe package root registers nothing on import: it exports defineForumulateElements(), the mount functions (mountComments, mountWidget, mountChangelog) and createClient(), a typed client for the public read API. Repository: embed. Theming attributes (theme, accent, radius, width, font) are the same on every element, and every wrapper below takes them as props. On a custom domain, set api to https://<your domain>/api.
By your stack
Each wrapper is a thin, typed layer over the core: the same elements as native components, with server rendering handled where the framework has it. Pick yours; each quickstart is install, import, use. Every component is named Forumulate<Element>: ForumulateComments, ForumulateWidget, ForumulateSearch, ForumulateChangelog, ForumulateFeed.
JavaScript frameworks
React · react
// npm install @forumulate/react
import { ForumulateComments } from '@forumulate/react';
<ForumulateComments org="<org-id>" space="<space-id>" />;Next.js · next
// npm install @forumulate/next
import { ForumulateComments } from '@forumulate/next';
<ForumulateComments org="<org-id>" space="<space-id>" />; // renders from a Server Component; hooks are in '@forumulate/next/client'Remix and React Router 7 · remix
// npm install @forumulate/remix
import { ForumulateComments } from '@forumulate/remix';
<ForumulateComments org="<org-id>" space="<space-id>" />;Vue · vue
<!-- npm install @forumulate/vue -->
<script setup>
import { ForumulateComments } from '@forumulate/vue';
</script>
<template><ForumulateComments org="<org-id>" space="<space-id>" /></template>Nuxt · nuxt
// npm install @forumulate/nuxt
export default defineNuxtConfig({
modules: ['@forumulate/nuxt'],
forumulate: { org: '<org-id>', space: '<space-id>' },
}); // nuxt.config.ts
// then, in any page: <ForumulateComments />Svelte and SvelteKit · svelte
<!-- npm install @forumulate/svelte -->
<script>import { ForumulateComments } from '@forumulate/svelte';</script>
<ForumulateComments org="<org-id>" space="<space-id>" />SolidJS · solid
// npm install @forumulate/solid
import { ForumulateComments } from '@forumulate/solid';
<ForumulateComments org="<org-id>" space="<space-id>" />;Qwik · qwik
// npm install @forumulate/qwik
import { ForumulateComments } from '@forumulate/qwik';
<ForumulateComments org="<org-id>" space="<space-id>" />;Preact · preact
// npm install @forumulate/preact
import { ForumulateComments } from '@forumulate/preact';
<ForumulateComments org="<org-id>" space="<space-id>" />;Angular · angular
// npm install @forumulate/angular
import { ForumulateCommentsComponent } from '@forumulate/angular'; // standalone: add to your component's imports
// template: <fm-comments org="<org-id>" space="<space-id>"></fm-comments>Static and content sites
Astro · astro
---
import { ForumulateComments } from '@forumulate/astro/components'; // npm install @forumulate/astro
---
<ForumulateComments org="<org-id>" space="<space-id>" />Gatsby · gatsby
// npm install @forumulate/gatsby
import { ForumulateComments } from '@forumulate/gatsby';
<ForumulateComments org="<org-id>" space="<space-id>" />;
// also a source plugin: posts, pages, topics and releases as GraphQL nodesDocusaurus · docusaurus
// npm install @forumulate/docusaurus
plugins: [['@forumulate/docusaurus', { org: '<org-id>', comments: { space: '<space-id>' } }]], // docusaurus.config.js
// comments appear under every doc page; list path prefixes in comments.exclude to skip someEleventy · eleventy
// npm install @forumulate/eleventy
import forumulate from '@forumulate/eleventy';
eleventyConfig.addPlugin(forumulate, { org: '<org-id>', space: '<space-id>' });
// in a template: {% forumulateComments %}Hugo · hugo
hugo mod get github.com/forumulate/hugo # then add it under [[module.imports]] in hugo.toml
# set org and space under [params.forumulate] in hugo.toml
# in a layout: {{ partial "forumulate/comments.html" . }}Jekyll · jekyll
gem "jekyll-forumulate", git: "https://github.com/forumulate/jekyll" # Gemfile, in group :jekyll_plugins
# set forumulate: { org: "<org-id>", space: "<space-id>" } in _config.yml
# in a layout: {% forumulate_comments %}Server-rendered apps
Django · django
# pip install git+https://github.com/forumulate/django, then add "forumulate_django" to INSTALLED_APPS
FORUMULATE = {"ORG": "<org-id>", "SPACE": "<space-id>"} # settings.py
# template: {% load forumulate %}{% forumulate_comments %}Flask · flask
# pip install git+https://github.com/forumulate/flask
from flask_forumulate import Forumulate
app.config.update(FORUMULATE_ORG="<org-id>", FORUMULATE_SPACE="<space-id>")
Forumulate(app) # template: {{ forumulate_comments() }}Ruby on Rails · rails
gem "forumulate-rails", git: "https://github.com/forumulate/rails" # Gemfile
# config/initializers/forumulate.rb: Forumulate::Rails.configure { |c| c.org = "<org-id>"; c.space = "<space-id>" }
# view: <%= forumulate_comments %>Laravel · laravel
composer config repositories.forumulate vcs https://github.com/forumulate/laravel
composer require forumulate/laravel:dev-main
# .env: FORUMULATE_ORG=<org-id> and FORUMULATE_SPACE=<space-id>
# Blade: <x-forumulate-comments />Go (net/http, html/template) · forumulate-go-web
// go get github.com/forumulate/forumulate-go-web
// import forumulate "github.com/forumulate/forumulate-go-web"
fm := forumulate.New(forumulate.Config{Org: "<org-id>", Space: "<space-id>"})
tmpl := template.New("page").Funcs(fm.FuncMap())
// template: {{ forumulateComments }}Content management systems
WordPress · wordpress
A plugin with a settings page, a Forumulate block and four shortcodes. It can replace WordPress comments on the post types you choose (classic and block themes alike), and shows blog, changelog or docs feeds read on the server and cached. Requires WordPress 6.1 and PHP 7.4.
Download the repository as a ZIP, then Plugins → Add New → Upload Plugin → Activate
Settings → Forumulate: organization id, default space id, Check connection
In a post: the Forumulate block, or [forumulate_comments] [forumulate_feed type="blog"]The plugin needs no token. An optional one is used only by your server, to check the connection and to find where a docs space is published; it is stored encrypted, or set as FORUMULATE_TOKEN in wp-config.php, and never reaches a reader's browser.
Ghost · ghost
Copy partials/forumulate/ from the repository into your theme's partials/ folder
Merge config.custom from package.custom.json into the theme's package.json, upload the theme
Settings → Design → Theme: set Forumulate org and space
In post.hbs, where comments go: {{> "forumulate/comments"}}No theme edits? The repository README has the Code injection snippets instead.
Drupal · drupal
composer config repositories.forumulate vcs https://github.com/forumulate/drupal
composer require forumulate/forumulate:dev-main && drush en forumulate
# Configuration → Web services → Forumulate: organization id and space id
# Structure → Block layout: place the "Forumulate comments" block on your content typeNo-code site builders
Every builder that accepts custom HTML takes the same two lines: the core from the CDN, then the element. Paste the whole snippet into one embed:
<script type="module" src="https://cdn.jsdelivr.net/npm/@forumulate/embed@1"></script>
<forumulate-comments org="<org-id>" space="<space-id>"></forumulate-comments>Swap forumulate-comments for any element in the table above.
Builder | Where to paste it |
|---|---|
Plain HTML | Anywhere in the page. Put the |
Webflow | An Embed element where the comments go. In a CMS template page, bind |
Shopify | A Custom Liquid section in the theme editor (blog post or product template), or |
Framer | An Embed component set to HTML, on the page or the CMS detail page. |
Squarespace | A Code block in the post or page. Code blocks need a plan that allows custom code. |
Wix | Add → Embed code → Embed HTML. Wix puts it in an iframe, so set |
Without any package
The embed scripts the elements are built on are served by the API and work today with a single <script> tag. Configuration goes in data- attributes, and the script renders where it is placed:
<script
src="https://forumulate.com/api/embed/comments.js"
data-org="<org-id>"
data-space="<space-id>"
data-theme="dark"
></script>/api/embed/widget.js is the docs search box (data-mode="ask" for answers), /api/embed/forum.js is a forum's topic list and /api/embed/changelog.js is the changelog bell. All four take data-mount, a CSS selector to render into instead of the script's position. Comments and search also take data-accent, data-radius, data-width and data-font.
The changelog widget reads its look from the changelog's own theme (theme.changelog.widget: where the panel opens, the unread count, which card parts show) and each embed may override it: data-mode (dropdown, sidebar-left, sidebar-right, modal, inline), data-selector (open from your own button), data-counter="false", data-limit, data-announce="false" and data-user-<field> to show a reader only their releases (data-user-plans="Pro"). Every card links to the release's own page on the changelog's address. window.Forumulate.changelog has open(), close(), toggle(), count() and onCount(cb).
The forum embeds wear the forum's own post look (theme.forum.post: avatar size and shape, which name, absolute or relative dates, #N, the "Author" chip, reputation, staff colour) and speak the reader's language: data-locale, else the page's <html lang>, in any language the reader ships, with the owner's own words from theme.forum.translations. Every link goes to the forum's own address. Each embed may override the look: data-username (name, username, both), data-avatars (on, off or a size in px), data-avatar-shape, data-dates (absolute, relative, off), data-truncate (characters, with "Read more") and data-target="_self". Comments also take data-per-page (replies before "Load more") and data-composer="off". The topic list takes data-order (latest, new, hot, top), data-period, data-category, data-tag, data-limit (1–30), data-title, data-excerpt="on", data-density="compact", data-view-all="off" and data-author, data-replies, data-date, data-category-name, data-status (each off to hide).
Zapier and Make
A forum can drive a Zap or a Make scenario with a personal access token (Authorization: Bearer <token>); the token's scopes and space limits apply.
GET
/orgs/{orgId}/automation/metests the connection and names its owner; GET/orgs/{orgId}/automation/forumslists the forums it may use.Polling triggers, newest first: GET
/orgs/{orgId}/spaces/{forumId}/automation/triggers/{new_topic|new_reply|topic_solved}?limit=25answers{ items }, each a flat object (id,title,url,excerpt,authorName,categoryName,tags,createdAt).idis what to de-duplicate on.Instant triggers (REST hooks, staff tokens): POST
/orgs/{orgId}/spaces/{forumId}/automation/hookswith{ "trigger": "new_topic", "targetUrl": "https://hooks.zapier.com/…" }answers the subscriptionidand, once, its signingsecret; each delivery posts the same item the poll lists, signedx-forumulate-signature: v1,sha256=…. DELETE…/automation/hooks/{id}unsubscribes, and a 410 from the receiver unsubscribes too. Integrations lists every subscription.Actions: POST
…/automation/actions/topicswith{ title, body, category?, tags? }(Markdown body; category by slug or id) and POST…/automation/actions/replieswith{ topic, body }(topic by id or slug). Both follow every rule a member's post follows.
Telegram
Integrations → Telegram posts new topics, replies and solved questions to a chat, group or channel through your own bot: the bot token from @BotFather and the chat id (a group's topic id optional). The token is stored encrypted and never shown again.
Slack: /forumulate in your workspace
Connect a Slack workspace and its members can search your docs without leaving Slack. /forumulate <query> (or /forumulate search <query>) answers with the top five matches, and /forumulate ask <question> answers with a grounded reply and the pages it drew on. ask needs an AI provider connected to the organization. Only public docs spaces are ever searched, so a command cannot bring a private page into a channel. Replies are visible only to the person who ran the command.
Connect from Integrations in the console, or through the API with org.manage_integrations. GET /orgs/{orgId}/slack/install-url gives the Slack authorize address to open. After you approve the install, Slack sends the browser to GET /integrations/slack/oauth/callback, which stores the workspace's bot token and returns you to the console's Integrations page with ?slack=connected, or ?slack=error if the exchange failed. The callback trusts only a state the install address signed for your organization, so it cannot attach a workspace to an organization that did not ask. GET /orgs/{orgId}/slack says whether a workspace is connected and which one; DELETE /orgs/{orgId}/slack disconnects it. An organization has one workspace, and connecting again replaces it.
Slack calls POST /integrations/slack/command each time somebody runs the command. The request is checked against the signing secret of Forumulate's own Slack app, and must be less than five minutes old: anything else answers 401. That is why the command works in a workspace that installed our app, and the workspace's team id is how the request finds your organization. A workspace that is not connected gets a reply saying so.
Which one do I want?
You want to… | Use |
|---|---|
Publish docs from a Git repository | The CLI, |
Build a feature on top of your forum or docs | The SDK for your backend's language |
Let an AI assistant read or edit your content |
|
Put comments, search or a changelog on your own site | The website SDK for your stack, or the HTML snippet |
Check a live site the way a crawler sees it |
|
Questions, and requests for a stack that is not here, go to the community.
Zapier, Terraform, React and WordPress
The Terraform provider for status pages, the Zapier app and the forum REST hooks for Zapier and Make, the status components in @forumulate/react, the events and courses WordPress plugin, and the HubSpot, Salesforce, Mailchimp and Kit syncs have their own guide: Zapier, Terraform, React and WordPress. It also says which of them are listed in a public directory yet, and what to use until they are.
In the API
Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.
Automation Account
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List me automation in an organization |
| |
| List forums in an organization |
|
Connector Kb
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the manifest |
| |
| List chunks |
| |
| Get the search |
| |
| File of llms (connector kb) |
| |
| Create the ask |
| |
| Create a question |
| |
| Create a suggestion |
|
Extension
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List extensions in an organization |
| |
| Get the available in an organization |
| |
| Install the extension |
| |
| Update the extension |
| |
| Delete the extension |
| |
| Test the extension |
| |
| List deliveries in an organization (extensions) |
| |
| Rotate the extension |
| |
| Create the upgrade in an organization |
|
Integrations
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the catalog in an organization (integrations) |
| |
| List integrations in an organization |
| |
| Create an integration in an organization |
| |
| Update the integration |
| |
| Delete the integration |
| |
| Test the integration |
|
Library Domain
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List a content library’s custom domains |
| |
| Publish a content library’s hosted output at a custom domain |
| |
| Give a content library’s hosted output a first-party subdomain |
| |
| Dry-run a custom domain for a content library |
| |
| Verify a content library’s custom domain |
| |
| Take a content library’s verified domain live |
| |
| What to configure for a content library’s domain |
| |
| Remove a content library’s custom domain |
| |
| Also redirect www. to a content library’s domain |
| |
| Restrict who may read a content library’s domain |
| |
| Retire a content library’s domain with a 301 to a new home |
|
Library Sdk
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| The library's client SDKs |
| |
| Publish the library client to a connected registry |
| |
| Download the library client as a .tar.gz |
|
Library Site
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List a content library’s navigation menus |
| |
| List a content library’s redirects |
| |
| Replace a content library’s manual redirects |
|
Mcp Agent
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the MCP server in an organization |
| |
| Create the MCP server in an organization |
| |
| Get the readonly in an organization |
| |
| Create the readonly in an organization |
|
Open Api Sdk Public
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List languages in a space (public reader) |
| |
| Get the SDK (public reader) |
| |
| Get the package in a space (public reader SDK) |
|
Org Cloudflare
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the cloudflare in an organization |
| |
| Replace the cloudflare in an organization |
| |
| Delete the cloudflare in an organization |
| |
| Test the cloudflare |
|
Public Registry
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List extensions |
| |
| Get the extension |
|
Registry
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Publish the extension |
| |
| Unpublish the extension |
| |
| Install registry in an organization |
| |
| Create the rate in an organization |
| |
| Submit review in an organization |
| |
| Create the pricing in an organization |
| |
| Create the purchase in an organization (extensions) |
|
Render
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Render the extension |
| |
| Create the action in an organization |
| |
| Create the surface in an organization |
| |
| Create the surface action in an organization |
|
Sdk Publishing
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List registries in an organization |
| |
| Replace the registry |
| |
| Delete the registry |
| |
| Test the registry |
| |
| Create the register in a space (docs OpenAPI specs SDK) |
| |
| List packages in a space |
| |
| Publish the package |
| |
| List publishes in an organization |
|
Slack Admin
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| Get the slack in an organization |
| |
| Replace the slack in an organization |
| |
| Delete the slack in an organization |
| |
| Install URL in an organization |
|