PAMbaseDocs
Reference

SDK.

@pambase/sdk is the TypeScript client for the REST API. It is a thin, typed wrapper: one PAMbaseApp instance per connection, plus standalone helpers for the OAuth handshake and webhook verification. Every method maps to an endpoint you can read about on the API page.

Install

bash
pnpm add @pambase/sdk

Construct

Server-side client. Create one instance per connection token.

typescript
import { PAMbaseApp } from "@pambase/sdk";
const pam = new PAMbaseApp({
baseUrl: "https://api.pambase.io", // http://localhost:4000 in dev
connectionToken: storedToken, // from exchangeCodeForToken(...)
});
OptionTypeNotes
baseUrlstringAPI origin
connectionTokenstringSent as Authorization: Bearer
fetchImpl?typeof fetchOverride the fetch implementation (tests, edge runtimes)
retry?RetryConfigOverride retry behavior (see Retry below)
Server-side only

The connection token grants access to a user's memory. Keep it on your server; never ship it to a browser. The same goes for client_secret in the OAuth helpers.

Methods

Identity

MethodReturnsEndpoint
getIdentityBrief(){ brief, ai_name, generated_at, source_memory_count, cached }GET /v1/identity/brief
systemPrompt()stringConvenience: brief formatted as a system prompt
getConnection()Connection & { ai, app }GET /v1/connection
typescript
const { brief, ai_name } = await pam.getIdentityBrief();
const systemPrompt = await pam.systemPrompt(); // ready to prepend to your LLM call
// Need the raw profile (display name + tone note)? It's on the connection:
const { ai } = await pam.getConnection(); // ai.name, ai.personality?.description
Identity is light by design

The profile is just a display name and an optional one-line tone note — read it from getConnection() (the ai field). Apps bring their own persona and model — see Identity.

Memory & signals

MethodReturnsEndpoint
remember(input | input[]){ accepted, memoryIds }POST /v1/memories
recall({ query, scope?, limit? }){ memories: SearchedMemory[] }GET /v1/memories?query=
listMemories({ scope?, limit?, cursor? }){ memories, next_cursor? }GET /v1/memories
iterateMemories({ scope?, pageSize? })AsyncIterable<Memory>(paged GET /v1/memories)
emitSignal({ type, payload? }){ accepted }POST /v1/signals
getContext({ intent, query?, limit? })ContextBundleGET /v1/context
typescript
// Write natural-language memory (any app; pick a scope from the taxonomy).
await pam.remember({ content: "Ran 5k in 28:10.", kind: "event", scope: "fitness.running" });
// Recall the relevant memories; bring your LLM to interpret them.
const { memories } = await pam.recall({ query: "marathon training", scope: "fitness", limit: 5 });
// Emit an ephemeral signal for subscribers (not stored as memory).
await pam.emitSignal({ type: "workout.completed", payload: { activity: "run" } });

See Remember & recall, Signals, and the data model for response shapes.

Hosted chat (Gateway)

MethodReturnsEndpoint
chat({ intent, userMessage?, appContext?, ephemeral? })GatewayChatResponsePOST /v1/gateway/chat
chatStream(request, signal?)AsyncGenerator<ChatStreamEvent>POST /v1/gateway/chat/stream
typescript
const res = await pam.chat({ intent: "coach", userMessage: "How did my week go?" });
console.log(res.reply, res.toneTags);
// Streaming — discriminate on event.type:
for await (const ev of pam.chatStream({ intent: "coach", userMessage: "Recap?" })) {
if (ev.type === "delta") process.stdout.write(ev.text);
else if (ev.type === "memory_recorded") console.log("\nrecorded", ev.candidate.scope);
else if (ev.type === "done") console.log("\n", ev.usage.model, ev.usage.tokensOut);
else if (ev.type === "error") throw new Error(ev.message);
}

Set ephemeral: true to skip recording memory candidates for the turn. See Gateway and Chat.

Scheduled triggers

MethodReturnsEndpoint
schedule({ fireAt, kind, payload? }){ id, fireAt }POST /v1/schedule
listSchedules(){ triggers: [{ id, fireAt, kind, status, payload }] }GET /v1/schedule
cancelSchedule(id){ ok: true }DELETE /v1/schedule/:id
typescript
const trg = await pam.schedule({
fireAt: new Date(Date.now() + 86_400_000), // accepts Date or ISO string
kind: "morning-checkin",
payload: { tip: "Easy run today" },
});
await pam.cancelSchedule(trg.id);

When a trigger fires, PAMbase delivers a schedule.fired webhook — see Webhooks.

Discovery

MethodReturnsEndpoint
getCatalog()PAMbaseCatalogGET /v1/catalog
listUserScopes(){ scopes: [{ scope, count, standard }] }GET /v1/scopes

Render the canonical catalog on the Catalog page, and discover what a given user actually has at runtime. See Discovery.

Standalone helpers

Pure functions for the OAuth handshake and webhook verification — no client instance required.

typescript
import {
buildConnectUrl,
exchangeCodeForToken,
verifyWebhookSignature,
} from "@pambase/sdk";
// 1. Send the user to the Hub to consent:
const url = buildConnectUrl({
hubBaseUrl: "https://pambase.io",
appSlug: "my-app",
redirectUri: "https://my-app.com/callback",
state: csrfToken,
scopes: ["identity:read", "memory:read:fitness", "memory:write:fitness"],
});
// 2. On the callback, exchange the code for a token:
const { connection_token, ai_id, scopes } = await exchangeCodeForToken({
apiBaseUrl: "https://api.pambase.io",
code,
clientId: process.env.PAMBASE_CLIENT_ID!,
clientSecret: process.env.PAMBASE_CLIENT_SECRET!,
});
// 3. Verify inbound webhooks:
const ok = await verifyWebhookSignature({
payload: rawBody,
signature: req.headers["x-pambase-signature"],
secret: process.env.PAMBASE_WEBHOOK_SECRET!,
toleranceSeconds: 300,
});

Full flow in Connect flow; signing details in Webhooks.

Errors

Every failure throws a subclass of PAMbaseError (with .code, .status, .message). Catch the specific class you care about. The full mapping lives on the Errors page.

ClassStatusExtra
PAMbaseErrorBase class
ValidationError400Zod issues in .details
UnauthorizedError401Re-run the OAuth connect flow
ScopeDeniedError403.scope
NotFoundError404
RateLimitError429.retryAfterMs
ServerError≥500Retried automatically
NetworkErrorTransport failure; retried automatically
typescript
import {
UnauthorizedError, ScopeDeniedError, RateLimitError, classifyError,
} from "@pambase/sdk";
try {
await pam.remember({ content: "Ran 5k this morning.", kind: "event", scope: "fitness" });
} catch (err) {
if (err instanceof UnauthorizedError) {
redirectToConnect(); // token missing/expired/revoked
} else if (err instanceof ScopeDeniedError) {
console.warn("Need scope:", err.scope); // request it in your manifest
} else if (err instanceof RateLimitError) {
await sleep(err.retryAfterMs); // then retry
} else {
throw classifyError(err);
}
}

Retry

Every request auto-retries NetworkError, RateLimitError, and 5xx responses by default. Tune it via the constructor's retry option, or wrap your own work with withRetry.

typescript
import { withRetry, type RetryConfig } from "@pambase/sdk";
const retry: RetryConfig = {
maxAttempts: 3, // default
initialDelayMs: 200, // default
maxDelayMs: 4000, // default
jitter: 0.25, // default
shouldRetry: (err) => true, // optional custom predicate
};
const pam = new PAMbaseApp({ baseUrl, connectionToken, retry });
await withRetry(() => pam.getIdentityBrief(), retry);

Pagination

List endpoints return Paginated<T> = { items, next_cursor? }. Use iterate(fetcher) to stream pages lazily, or collect(fetcher, max = 1000) to gather them. iterateMemories() is a ready-made wrapper for memory.

typescript
import { collect } from "@pambase/sdk";
const all = await collect(
(cursor) => pam.listMemories({ scope: "fitness", cursor }),
500, // safety cap
);

Standard-name constants

Typo-safe constants for the standard vocabulary are exported. Any string is still accepted — these just give you autocomplete and the standard flag in discovery results.

typescript
import {
STANDARD_SCOPE_NAMESPACES,
MEMORY_KIND_NAMES,
STANDARD_INTENT_NAMES,
} from "@pambase/sdk";
import type {
StandardScopeNamespace, MemoryKindName, StandardIntent,
} from "@pambase/sdk";

Types

All response types are exported from @pambase/sdk. Their shapes are documented on the data model page.

typescript
import type {
AIIdentity, Memory, SearchedMemory, ContextBundle,
IdentityBrief, Connection, ConnectedApp,
GatewayChatRequest, GatewayChatResponse, ChatStreamEvent,
PAMbaseCatalog, Paginated,
} from "@pambase/sdk";