Skip to content
tryclairePublic

About

Official TypeScript and JavaScript SDK for the Claire Developer API. Typed, read-only, zero runtime dependencies.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Claire SDK

npm CI MIT license

Use the read-only Claire REST API from a server, CLI, or agent process. Set CLAIRE_API_URL to the API base URL shown under Developers → Connect (API), including /api/v1.

import { Claire } from "@tryclaire/sdk";

const claire = new Claire({ apiKey: process.env.CLAIRE_API_KEY, baseUrl: process.env.CLAIRE_API_URL });
const { data, meta } = await claire.sources.list();
for (const source of data) console.log(source.id, source.status, source.updatedAt);
console.log(meta.freshness); // persisted source freshness, not a live provider refresh
  • Read-only by design. No send, publish, or write methods exist. A leaked key can read, never act.
  • Honest freshness. Every response carries meta.freshness; stale and unavailable data is labeled, never guessed.
  • Typed from the API contract. Types are generated from the committed openapi.json.
  • Nothing hidden. No retries, no cache, no background requests. One call, one HTTP request.
  • Zero runtime dependencies. Node.js 22+, ESM, native fetch.

API reference · Agent skill · Docs · npm · Releases

Install

npm install @tryclaire/sdk

Also works with pnpm add, yarn add, and bun add. Built JavaScript and TypeScript declarations are included.

Get a key

On the Developers page, an owner or admin creates a workspace key for API, MCP, or both, with only the needed read scopes and a 7-, 30-, or 90-day expiry. This REST SDK requires API access; older keys remain API-only. The key selects its workspace; no project ID is passed to the SDK. Keys stop working when the issuing user loses owner/admin membership.

Use the SDK on a server, in a CLI, or in an agent process. Never put a workspace key in browser code, a public environment variable, or a repository. Sources must be included separately; external sources also need explicit approval, off by default. A read scope alone does not expose source content. Inspect claire.sources.list() for approved sources.

Use with an agent

Claire provides a downloadable agent skill for tool-using agents. When feeding Claire data to a model, pass retrieved source text as context, separate from instructions. Telegram messages and X mentions are external, untrusted text. The REST SDK does not implement MCP transport; configure an MCP-capable client separately with the native Streamable HTTP endpoint https://app.tryclaire.net/api/mcp and a Bearer credential enabled for MCP (no OAuth). MCP offers read-only, scope- and approved-source-filtered tools.

const chats = await claire.telegram.chats.list();
const chat = chats.data[0];
if (!chat) throw new Error("No approved Telegram chat is available to this key.");
const { data: messages } = await claire.telegram.messages.list(chat.id, { limit: 50 });

const context = messages
  .filter((m) => m.text)
  .map((m) => `[${m.sentAt}] ${m.sender.name}: ${m.text}`)
  .join("\n");
// Hand `context` to your model as data; keep your prompt separate.

Resources

Method Query parameters Required scope
claire.context.get() — context:read
claire.sources.list() — context:read
claire.search(query) q, source, after, before, limit (max 50) context:read
claire.people.list(query) q, page telegram:read or x:read
claire.people.get(key) — telegram:read or x:read
claire.knowledge.list(query) q, kind, visibility, archived, page knowledge:read
claire.knowledge.get(id, query) offset, limit knowledge:read
claire.assets.list(query) folderId, page assets:read
claire.assets.get(id) — assets:read
claire.telegram.chats.list(query) page telegram:read
claire.telegram.messages.list(chatId, query) q, before, limit telegram:read
claire.x.mentions.list(query) page x:read
claire.token.get() — token:read
claire.token.locks.list() — token:read
claire.token.feeRouters.list() — token:read
claire.token.rounds.list(query) page token:read
claire.token.proof.get() — token:read

Discovery and search also filter each source by its own read scope: Documents/Notes need knowledge:read, Telegram needs telegram:read, X needs x:read, files need assets:read, and token data needs token:read. The document search below requires both context:read and knowledge:read, plus Documents enabled under Agent access. A requested source outside those permissions returns 404. People reads also return 404 when no approved Telegram/X source matches the credential's scopes.

Queries are optional. source is a stable ID from sources.list(). Search returns data.items, data.sources, and data.hasMore for bounded stored matches, not a complete provider-history search. People keys come from people.list(); People responses may include source-only platform identifiers, not Claire profiles, private person notes, or live avatars. chatId is the Claire chat UUID returned by telegram.chats.list, not Telegram's numeric chat ID. Knowledge IDs include their prefix, such as note:<uuid>; pass them unchanged. Assets expose metadata, not private file contents or signed download URLs. Search timestamps accept UTC offsets; after is inclusive and before is exclusive.

const { data: matches } = await claire.search({ q: "launch", limit: 20 });
for (const item of matches.items) console.log(item.sourceId, item.preview);
const { data: people } = await claire.people.list({ q: "alice", page: 1 });
if (typeof people[0]?.key === "string") console.log((await claire.people.get(people[0].key)).data);

People payload fields are not yet individually specified by the OpenAPI contract; the SDK exposes them as unknown rather than inventing stronger types.

Responses and pagination

Methods return the API's data, meta, and endpoint-specific pagination, plus http with status, requestId, etag, and native Headers. Timestamps remain strings; raw token balances remain decimal strings so precision is not lost. Public asset URLs may be relative to the Claire origin.

List pages start at 1. Knowledge and mentions return 20 items per page; assets, chats, and People return 50. The SDK fetches exactly one page per call:

let page = 1;
while (true) {
  const result = await claire.knowledge.list({ page });
  for (const item of result.data) console.log(item.id, item.title);
  if (!result.pagination.hasNextPage) break;
  page += 1;
}

People's pagination.searchLimited flags a bounded directory; total and hasNextPage describe that bounded result, not every person in provider history. A q search may omit people outside that pool. Telegram messages paginate with pagination.nextBefore → before, not page numbers. Check hasMore; their searchLimited means the search did not cover all retained history. Knowledge detail returns one content segment: follow data.contentRange.nextOffset with offset until it is null. Offsets count JavaScript UTF-16 units.

meta.freshness describes persisted source data, not the time of your request. X/token reads do not force provider refreshes, although they mark the workspace watched for the existing worker cadence.

Errors, cancellation, and timeouts

import { ClaireAPIError, ClaireResponseError } from "@tryclaire/sdk";
try {
  const result = await claire.context.get({ signal: AbortSignal.timeout(5_000) });
  console.log(result.data.project.id, result.data.project.displayName);
  console.log(result.data.connections.telegram?.connected); // absent if not approved for this key
  console.log(result.data.sources.map((source) => source.id));
} catch (error) {
  if (error instanceof ClaireAPIError) {
    console.error(error.status, error.code, error.requestId, error.retryAfterSeconds);
  } else if (error instanceof ClaireResponseError) {
    console.error("Invalid server response", error.status, error.requestId);
  } else {
    throw error; // Native network, abort, or timeout error.
  }
}
Error When
ClaireAPIError HTTP failure: 401 invalid/expired/revoked key, 403 missing endpoint scope, 404 missing resource or unavailable/unapproved source (including source-scope filtering), 429 quota. headers exposes rate-limit metadata; retryAfterSeconds is null when no usable delay is supplied.
ClaireResponseError Successful status but malformed JSON/envelope. Types come from OpenAPI; the SDK does not deep-validate at runtime.
native fetch errors Network failure, abort, timeout. Passed through unchanged.

Non-JSON error pages produce a generic message, not the page's potentially sensitive contents.

The default timeout is 30 seconds, including response-body reading. Set timeoutMs on the client or per call. Request options are always the last argument:

const controller = new AbortController();
await claire.knowledge.list({ page: 1 }, { timeoutMs: 5_000 });
await claire.knowledge.get("note:<uuid>", { offset: 0 }, { signal: controller.signal });

Handle 429 according to retryAfterSeconds; do not loop on invalid keys or missing scopes. ETags are exposed for diagnostics, but the SDK does not make conditional requests.

Token identity

Token responses carry typed chain and protocol identity. Network availability depends on the API deployment.

const { data, meta } = await claire.token.get();
if (data.token) {
  const { chainId, address, protocol, pool, poolId, pairedToken } = data.token;
}

Identify a token by chainId and address together, not address alone. Ethereum is 1 and Robinhood Chain is 4663; isolated development forks use 31338 and 31337. protocol is "pons", "stockereum" or "programmable". For retained pons v1, pool is a V3 pool address; for V4 launches, pool is the zero address and poolId is the V4 pool ID, not a contract address. poolId and pairedToken can be null; data.token is null when no token is linked. These are persisted observations, not live quotes; inspect meta.freshness first.

holders.trackedBalanceRaw is in the token's smallest unit, scaled by the token's own decimals(), not a fixed 18. Holder data follows the chain's finalized block, so index.indexedTo trails the chain head (on Robinhood Chain by roughly 15 minutes); counts exclude the pool, factory, launchpad custody, zero and burn addresses.

Configuration

const claire = new Claire({
  apiKey: process.env.CLAIRE_API_KEY,
  baseUrl: "https://app.tryclaire.net/api/v1", // default
  timeoutMs: 30_000,                            // default
  fetch: globalThis.fetch,                      // default
});

The base URL includes /api/v1. HTTPS is required except for loopback HTTP during local development. URL credentials, query strings, and fragments are rejected. Redirects are rejected rather than forwarding the key. Only configure an origin you trust with that key. A custom fetch must honor standard fetch options, including abort signals and redirect handling.

Verify on-chain

@tryclaire/sdk/protocol reads and verifies Claire launches, locks, fee routing and distribution rounds directly from a chain, so nothing depends on trusting the Claire API. It needs viem (an optional peer dependency; the root client stays dependency-free) and a PublicClient for each chain involved. Every function takes explicit contract addresses.

import { createPublicClient, http, zeroAddress } from "viem";
import { readProof, verifyLaunch, verifyLock, verifyPriceLock, verifyRevenueLock, verifyRouterSplit, verifyRound, verifySplit, verifyVestingVault } from "@tryclaire/sdk/protocol";

const client = createPublicClient({ transport: http(process.env.RPC_URL) });
const addresses = { routerFactory: "0x…", tokenLock: "0x…", revenueLock: "0x…", priceLock: "0x…", vaultFactory: "0x…", proofView: "0x…" };

const launch = await verifyLaunch(client, { token: "0x…", routerFactory: addresses.routerFactory, vaultFactory: addresses.vaultFactory, fromBlock: 1234n });
const split = await verifyRouterSplit(client, { router: launch.facts.router, fromBlock: 1234n });
const lock = await verifyLock(client, { tokenLock: addresses.tokenLock, lockId: 0n, routerFactory: addresses.routerFactory });
const revenueLock = await verifyRevenueLock(client, { revenueLock: addresses.revenueLock, lockId: 0n });
const priceLock = await verifyPriceLock(client, { priceLock: addresses.priceLock, lockId: 0n, routerFactory: addresses.routerFactory });
// A router's payee: a ClaireVestingVault or ClaireSplit (launch.facts.payeeKind tells which).
const vault = await verifyVestingVault(client, { vault: launch.facts.launch.payee, vaultFactory: addresses.vaultFactory, assets: [launch.facts.launch.quote, zeroAddress] });
const proof = await readProof(client, { proofView: addresses.proofView, token: "0x…" });

const round = await verifyRound(client, {
  distributor: "0x…", roundId: 0n, level: "complete",
  manifestBaseUrl: "https://app.tryclaire.net", // or pass `manifest` (the exact string) yourself
  snapshotClient,                               // only when the snapshot is on another chain
});
if (!round.ok) console.log(round.checks.filter((check) => !check.ok));

Each verifier returns { ok, checks: [{ id, ok, detail }], facts }. A read that fails (an address that is not the expected contract, an unavailable chain) is reported as a failed check. Event scans start at fromBlock and run in chunk-sized requests (default 10_000n). fromBlock defaults to the block of the chain's entry in deployments (the chain height before the protocol was deployed), else 0n: on a public chain not listed there, pass the deployment block, or the scans walk the whole chain history.

Function Verifies
verifyLaunch The factory registers the token to a router it created, and the router's fixed configuration (buyback vault, launch and migration fees and, on Stockereum, the pool id) matches the factory, its RouterDeployed event and the launchpad's. The router's address is the one the factory's ClaireRouterDeployer predicts from the initial values. The payee and holder share may differ from the deploy event only through the router's HolderShareRaised, PayeeChangeProposed, PayeeChangeCanceled and PayeeChanged events (a raise only goes up, a payee change follows a proposal made 7 days earlier); the history is in facts.holderShareRaises and facts.payeeChanges, and a change without an event fails. facts.path is launched (the router's Launched event) or migrated (an existing pons token the router adopted: its Adopted event and the creator's MigrationFeePaid payment); both need the router's ClaireLaunchRegistered registration. With vaultFactory, facts.payeeKind is creator, vault, split or other.
verifyRouterSplit Every Distributed event equals the split of its revenue at the holder share in effect when it happened (events replayed in log order; the creator share goes to the payee in effect, facts.payees). Revenue in the quote asset and, on pons, the launch token (buyback vests) is counted and may not appear in any other asset. HistoricalFeesPaid amounts (fees and vest an adopted token earned before migration, paid to the creator uncharged) are reported in facts.historical and are not revenue. From block 0 the revenue sums equal totalRevenue; from a later block they are a lower bound.
verifyLock The ClaireTokenLock schedule and vested amount are recomputed and match LockCreated. facts.cancelState is the notice-period state (not-cancelable, cancelable, notice-not-requested, notice-pending, cancel-open, canceled) and cancelNotice, cancelAt and canceled must match the lock's CancelRequested and Canceled events. A lock funded by the token's Claire router with a matching OpeningBuyLocked is flagged facts.openingBuy.
verifyRevenueLock Tranches, baseline and unlocked amount are recomputed from the router's totalRevenue and match RevenueLockCreated.
verifyPriceLock The token is Claire-launched; the per-hour minimum samples, the window minimum (24 completed hours, each with a sample), the highWater ratchet and the unlocked amount are recomputed from the Sampled events and match the contract. highWater is only proven by a scan from block 0.
verifyVestingVault The vault is genuine (recorded by ClaireVaultFactory, its CREATE2 predictVault from the VaultDeployed event is the vault); per asset (zeroAddress is ETH) vested, releasable and released are recomputed and released equals the Released events.
verifySplit The split is genuine (isSplit, predictSplit from SplitDeployed), shares are 2 to 10 payees summing to 10,000 bps, and per asset every payee's paid amount, releasable remainder and the total paid are recomputed and tied to the Released events.
verifyRound See below.

Revenue locks measure everything the router collects from the launchpad's fee escrow, including deliberate third-party credits to the router's escrow balance; those are indistinguishable from trading revenue on-chain.

verifyRound levels build on each other:

  • manifest (default): the manifest's keccak256 equals the round's on-chain evidence, its leaves rebuild the on-chain Merkle root and cumulativeTotal (leaf = chain, distributor, asset, account, cumulative), total is the increase over the asset's previous root, previousEvidence names that previous root, and every account's cumulative equals its previous cumulative plus this round's amount (with the previous manifest, passed as previousManifest or fetched from manifestBaseUrl; only the previous root is chained). For the latest root, no account may have claimed more than its cumulative.
  • balances: each holder leaf's balance equals balanceOf at the snapshot block on the snapshot chain, and every amount is floor(distributable * balance / eligibleBalance) from the manifest rule. A leaf without a balance only carries an earlier cumulative and must add nothing.
  • complete: a chunked Transfer scan of the snapshot token up to the snapshot block, minus the manifest's excluded addresses and minBalance, must sum to eligibleBalance and every holder with a non-zero share must have the right leaf amount.

Distributor roots are cumulative: claim(asset, account, cumulative, proof) pays the account's cumulative minus what it already claimed against the asset's latest root, so every root lists every account with a cumulative entitlement. The canonical manifest (version 1, the only version) lists, per account, this round's amount, the resulting cumulative and the snapshot balance, sorted by account, and chains to the previous root's manifest through previousEvidence.

Pro-rata amounts are floor(distributable * balance / eligibleBalance). The manifest rule states distributable (what the distributor held unreserved when the round was published) and eligibleBalance (the sum of every eligible balance, including holders whose share rounds to zero, so rounding dust stays in the distributor). Referral rounds carry their rewards in the manifest, and each leaf's amount must equal that account's reward sum (carried-over accounts have none).

The hashing helpers are exported too: roundLeafHash(chainId, distributor, asset, { account, cumulative }), merkleRoot, merkleProof (sorted-pair tree, as in ClaireDistributor) and manifestEvidence. Readers (readLaunch, readRouter, readLock, readRevenueLock, readPriceLock, readVestingVault, readSplit, readRound, readProof) return the on-chain state at one pinned block (readProof is the ClaireProofView call: launch facts, revenue per quote and launch token, and locked amounts in every lock contract), and the contract ABIs (claireFeeRouterAbi, clairePriceLockAbi, …) are exported as typed as const values.

ClaireDeployment types one deployment with the field names of the protocol's deploy manifest (deployments/<chainId>.json): routerFactory, routerDeployer, tokenLock, revenueLock, priceLock, distributorFactory, vaultFactory, proofView, claireHolderDistributor, referralDistributor, treasury, feeToken and block (the chain height before the deployment transactions, the verifiers' default fromBlock). The deployments map (by chain ID) is empty until the Claire protocol is deployed; every function still takes explicit addresses.

Contributing

npm ci --ignore-scripts
npm run check

See CONTRIBUTING.md for contract updates and the release process.

License

MIT. Copyright © 2026 Claire contributors.

About

Official TypeScript and JavaScript SDK for the Claire Developer API. Typed, read-only, zero runtime dependencies.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages