Authentication
The API accepts two kinds of credentials, and the SDK supports both:
A Personal Access Token (PAT) — sent as
Authorization: Bearer <token>. This is what server-to-server integrations use.A session cookie — the first-party cookie set when you sign in to the console. Used only for same-origin browser code (
credentials: true).
Creating a Personal Access Token
From the dashboard
Create and manage tokens in the console at /account/tokens. A token's plaintext value is shown once, at creation — copy it then; it is stored only as a hash and can never be displayed again. You can revoke a token at any time from the same page.
From the API
The same operation is available at POST /pats, which requires an authenticated session and an admin-scoped credential (issuing tokens is itself an admin action, so a merely-writable token cannot mint more tokens). The SDK exposes a helper:
const { token, pat } = await forumulate.auth.createToken({
name: 'CI publisher',
scopes: ['write'],
});
console.log(token); // "cft_..." — shown onceThe underlying POST /pats endpoint accepts more fields than the helper types. Use client.typed to set them:
const { token } = await forumulate.typed('post', '/pats', {
body: {
name: 'One-space sync',
scopes: ['write'],
expiresInDays: 90, // omit for a token that never expires on its own
spaceIds: ['<space-uuid>'] // confine the token to specific spaces
},
});name— required. A human-readable label so a list of tokens is auditable later.scopes— required, at least one. See below.expiresInDays— optional, up to 3650. Omit for no automatic expiry.spaceIds— optional, up to 50 space UUIDs. A token scoped to a space cannot even list the names of other spaces — which is what makes it safe to hand to a narrow integration. Omit for everywhere its owner can reach.
Tokens are opaque and carry a type prefix: PATs look like cft_…. (OAuth access tokens, cfo_…, authenticate the same way.)
Scopes
Scopes are a strict hierarchy — read ⊂ write ⊂ admin, so a broader scope satisfies a narrower requirement:
The default requirement is derived from the HTTP method (safe methods need read, everything else needs write); specific routes may demand admin. A request whose token lacks the required scope is rejected with 403 and code rbac.forbidden. Scopes apply to token auth only; browser session auth is governed by RBAC capabilities instead.
Passing the token to the client
import { ForumulateClient } from '@forumulate/sdk';
const forumulate = new ForumulateClient({
baseUrl: 'https://forumulate.com/api',
token: process.env.FORUMULATE_TOKEN, // "cft_..."
});Every request the client makes then carries Authorization: Bearer <token>. Keep tokens in a secret store or environment variable — never commit them, and never ship a PAT in browser-delivered code (use credentials: true there instead).