The knowledge-source feed

Embed
Preview of the card
<a href="https://forumulate.com/guides/knowledge-source-feed"><img src="https://forumulate.com/guides/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/36aa4df0-1e95-4cef-8ae9-795169441345/pages/ec1f401b-5b5f-4c68-b587-9771272aa7a3/social-card.svg?v=3e7ae33eec8b" alt="The knowledge-source feed" width="600" height="315"></a>

Integrating a product with Forumulate? Already built for you names what exists, with the one call for each.

Any chat or answer product — Intercom, Zendesk, Crisp, Drift, Help Scout, Freshchat, Front, a retrieval pipeline you run yourself, or Chatly — can keep its index exactly in step with what you publish. The vendor is only a label; the feed is the same for all of them.

Credentials

Read-only, any one of:

Syncing

GET /connector/kb/pages answers published pages, whole, as Markdown:

{
  "data": [
    {
      "pageId": "…", "spaceId": "…", "kind": "page",
      "url": "https://docs.acme.com/getting-started",
      "title": "Getting started", "description": null,
      "markdown": "# Getting started\n…",
      "contentHash": "sha256:…",
      "updatedAt": "2026-10-24T09:12:03Z",
      "visibility": "public",
      "freshness": { "verifiedAt": "2026-10-01T14:00:00.000Z", "ageDays": 23, "stale": false, "confidence": 0.89 },
      "locale": "en"
    }
  ],
  "tombstones": [{ "pageId": "…", "spaceId": "…", "deletedAt": "…", "reason": "unpublished" }],
  "page": { "limit": 50, "hasMore": false, "nextCursor": null },
  "sync": { "checkpoint": "…" }
}
  • The first call (no since) is a full sync: every published page, no tombstones.

  • Store sync.checkpoint verbatim and send it back as since. You get what changed after it — pages that were published or edited, and tombstones for pages that were trashed, unpublished or destroyed.

  • page.hasMore: true means call again now, with since=page.nextCursor; false means you are caught up.

  • Re-index a page only when its contentHash changes.

  • freshness and locale are the same values GET …/items reports for the page, computed by the same code:

    • verifiedAt is when someone last confirmed the page is correct, or null if no one has.

    • ageDays counts days since that, or since the last edit if the page was never confirmed.

    • stale is true when ageDays passes the organization's staleness policy.

    • confidence runs from 0 to 1 and falls slowly over the policy window, so multiply it into your ranking. A page no one has confirmed is discounted to 80%.

    • locale is the language declared for the page (canonical BCP-47, such as pt-BR), or null. It is never filled in from the space's default language.

    • Confirming a page or correcting its language doesn't move it into the next sync. Refresh these values on your full re-sync.

  • spaceIds= narrows the call to some of the spaces the credential covers; a space it does not cover is 403 space_not_granted.

Instead of polling, subscribe to kb.changed. It fires per burst of publishing with {spaceId, pageIds, cursor}; pass cursor as since. An integration token or a partner link subscribes itself — POST /connector/kb/webhook {"url": "https://…"}, no org member needed (see Give a third party read access) — and hears only its own spaces.

Seeing who reads

The studio's chat panel shows "Read by Intercom, last at …" for each integration that has read a space, from GET /orgs/{orgId}/spaces/{spaceId}/kb-readers — so a sync that went quiet is visible.

Identity in chat

A signed-in reader should not be asked who they are again by the chat widget. A widget whose vendor declares how to receive an identity token is handed one after it loads:

  • the organization signs a 120-second ES256 assertion for the reader — sub, email, name, and their sign-in subject {iss, sub} when they signed in through your identity provider — with aud set to that one widget;

  • the vendor verifies it against the organization's public keys at GET /public/orgs/{orgId}/connector-identity/jwks.json.

Rotate the signing key with POST /orgs/{orgId}/connector-identity/rotate; the old key stays published for a day so nothing in flight fails.

Chat widgets load through the consent contract: a chat widget waits for the suite's support_chat category (a consent platform without it answers as preferences). Chatly is told to follow the page's window.__fdConsent rather than show a banner of its own.

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 SDKs are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP agent server — its api_* family tools, or call_api. Where a CLI command or a hand-made MCP tool exists too, the last column names it. Operation names link to the API reference.

Connector Knowledge Admin

Operation

Request

What it does

Shortcuts

ConnectorKnowledgeAdmin_readers

GET /orgs/{orgId}/spaces/{spaceId}/kb-readers

Who reads this space’s knowledge base, and when they last did

client.connectorKnowledgeAdmin.readers()

ConnectorKnowledgeAdmin_rotate

POST /orgs/{orgId}/connector-identity/rotate

Rotate the key that signs chat identity assertions

client.connectorKnowledgeAdmin.rotate()

The Forumulate teamUpdated