familiar-sdk
v0.2.0
Published
JavaScript/TypeScript client for the familiar API (early development)
Readme
familiar-sdk
Early development. This is an MVP release. Expect breaking changes.
JavaScript and TypeScript client for familiar — a hosted tool router with memory.
Install
npm install familiar-sdkQuick start
import { Familiar } from "familiar-sdk";
const familiar = new Familiar({ token: "fam_your_token" });
const result = await familiar.input({
text: "Start a countdown",
channel: { type: "web", id: "session_123" },
});
console.log(result.messages.at(-1)?.content);Create an account
If you don't have a token yet:
import { Familiar } from "familiar-sdk";
const { account, token } = await Familiar.createAccount();
console.log(token.value); // store this — shown onceSet your AI provider key
Before familiar can process messages, each integration needs an OpenRouter key:
await familiar.integration.update({
aiApiKey: "sk-or-v1-your_openrouter_key",
});Get a key at openrouter.ai/keys.
Sync tools
await familiar.tools.sync({
tools: [
{
toolName: "spreadsheet.update_row",
description: "Update a row in the spreadsheet",
inputSchema: {
type: "object",
properties: {
row_id: { type: "string" },
values: { type: "object" },
},
required: ["row_id", "values"],
},
},
{
toolName: "calendar.schedule",
description: "Schedule a meeting",
baseUrl: "https://calendar.example.com",
inputSchema: {
type: "object",
properties: {
title: { type: "string" },
startTime: { type: "string" },
},
required: ["title", "startTime"],
},
},
],
});Per-tool URL override:
- add
baseUrlto any tool to route its execution to a different URL - useful when tools live on different services or sub-domains
- if omitted, the integration's default base URL is used
Error handling
All errors throw a FamiliarError with a code field:
import { Familiar, FamiliarError } from "familiar-sdk";
try {
await familiar.input({ text: "hello", channel: { type: "web", id: "s1" } });
} catch (err) {
if (err instanceof FamiliarError) {
console.log(err.code); // e.g. "configuration_required"
console.log(err.message); // human-readable explanation
console.log(err.status); // HTTP status code
}
}Common error codes:
unauthenticated— missing or invalid tokenconfiguration_required— no AI provider key set on the integrationinvalid_request— bad request payloadrate_limited— too many requests
API
new Familiar({ token, host? })
Create a client. host defaults to https://familiar.monster.
Familiar.createAccount({ host? })
Create a new account. Returns { account, token }. No token required.
familiar.input({ text, channel, userId?, threadId?, integrationId?, tools? })
Send a conversation turn. Returns { threadId, messages, execution }.
familiar.simulate({ text, channel, userId?, threadId?, integrationId?, tools? })
Dry-run a conversation turn. Returns what familiar would do without persisting messages, executing tools, or consuming quota.
const result = await familiar.simulate({
text: "Update the sales sheet",
channel: { type: "web", id: "session_123" },
});
console.log(result.response.type); // "direct_reply" | "clarification" | "tool_call"
console.log(result.response.content); // the planned response text or tool name
console.log(result.response.reasoning);// model reasoning
console.log(result.simulated); // truefamiliar.inputStream({ text, channel, userId?, threadId?, integrationId?, tools? })
Stream the assistant response for direct replies. Returns an async generator of SSE events.
for await (const event of familiar.inputStream({
text: "Tell me about the sales sheet",
channel: { type: "web", id: "session_123" },
})) {
if (event.event === "decision") {
console.log("Action:", event.action);
}
if (event.event === "delta") {
process.stdout.write(event.content); // stream text chunks
}
if (event.event === "done") {
console.log("Thread:", event.threadId);
console.log("Messages:", event.messages);
}
if (event.event === "error") {
console.error("Stream error:", event.message);
}
}Tool calls and clarifications are not streamed — they emit a single decision event followed by done.
familiar.tools.sync({ tools })
Sync the tool set for the current token-backed integration.
familiar.integration.get()
Get the current integration configuration.
familiar.integration.update({ aiApiKey?, baseUrl? })
Update the AI provider key or executor base URL. Pass null to clear a value.
familiar.integration.status()
Get the full integration status: config, account usage, and runtime stats (tool count, thread count).
const status = await familiar.integration.status();
console.log(status.account.plan); // "free" | "paid"
console.log(status.account.actionCount);
console.log(status.runtime.toolCount); // number of synced tools
console.log(status.runtime.threadCount); // number of threadsfamiliar.integration.health()
Get the operational health of the current integration.
const health = await familiar.integration.health();
console.log(health.overall); // "healthy" | "warning" | "degraded"
console.log(health.executor.base_url_configured); // boolean
console.log(health.executor.recent_failures); // failures in last 24h
console.log(health.tools.active); // number of active tools
console.log(health.callbacks.recent_activity); // any async callbacks recently
console.log(health.delivery.recent_failures); // delivery failures in last 24hfamiliar.account.get()
Get the authenticated account and token info.
const { account, token } = await familiar.account.get();
console.log(account.id);
console.log(token.prefix);
console.log(token.lastUsedAt);familiar.account.usage()
Get current usage stats for the authenticated account.
const usage = await familiar.account.usage();
console.log(usage.plan); // "free" | "paid"
console.log(usage.actionCount); // total actions used
console.log(usage.freeActionsUsed); // free tier actions consumed
console.log(usage.freeActionsRemaining); // null if paid, number if freefamiliar.threads.list({ userId? })
List threads for the current integration. Optionally filter by userId.
const { threads } = await familiar.threads.list();
// threads: [{ threadId, title, isPrivate, updatedAt }]familiar.threads.create({ channel, title?, isPrivate?, userId?, integrationId? })
Create a new thread.
const thread = await familiar.threads.create({
channel: { type: "web", id: "session_123" },
title: "My thread",
});
console.log(thread.threadId);familiar.threads.update({ threadId, title, userId?, integrationId? })
Rename a thread.
await familiar.threads.update({ threadId: "thread_abc", title: "New title" });familiar.threads.delete({ threadId, userId?, integrationId? })
Delete a thread by ID.
await familiar.threads.delete({ threadId: "thread_abc" });familiar.memory.getUserMemory({ userId? })
Get shared memory for the current integration user.
const { memory } = await familiar.memory.getUserMemory();
console.log(memory);familiar.memory.getThreadMemory({ threadId })
Get thread-local memory for a specific thread.
const { memory } = await familiar.memory.getThreadMemory({ threadId: "thread_abc" });
console.log(memory);familiar.audit.events({ status?, limit? })
Query recent audit events for the current integration.
const { events } = await familiar.audit.events({ limit: 20 });
// events: [{ event, requestId, status, code, detail, metadata, at }]
// Filter to errors only
const errors = await familiar.audit.events({ status: "error", limit: 10 });