Docs · Developers
TypeScript SDK
@tsg/sourcefinch wraps the REST API. Its types and operation table are generated from the OpenAPI description (/v1/openapi.json), so every API operation is available, with the same names. It has no dependencies (fetch and Web Crypto only) and runs in Node 18+, Deno, Bun, edge runtimes and browsers.
The package is not yet published to npm. Until it is, ask us for a build, or call the REST API directly.
Set up
import { SourceFinch } from "@tsg/sourcefinch";
const sf = new SourceFinch({ apiKey: process.env.SOURCEFINCH_API_KEY });
const info = await sf.info(); // API version and the project this key belongs toCreate a project API key in Console → API & access. Read keys can list and download; write keys can also create sources, start runs and manage destinations. Other bearer tokens work too: pass accessToken, or getToken to refresh them yourself.
Sources and runs
for await (const source of sf.paginate("listSources")) {
console.log(source.id, source.name, source.schedule);
}
const run = await sf.runs.create(sourceId); // 202, queued
const done = await sf.runs.wait(run.id); // polls until it finishes
console.log(done.status, done.usage_rights, done.acquisition_path);Writes send an Idempotency-Key automatically and reuse it when a request is retried (on 429, 502, 503, 504 and network errors, honouring Retry-After), so a retried runs.create never queues a second run.
Records, changes and evidence
const page = await sf.sources.records(sourceId);
page.data; // current records
page.evidence_ids; // the captures they were read from
page.run.usage_rights;
const changes = await sf.changes.list({ source_id: sourceId });
const claims = await sf.runs.claims(runId); // each value with evidence_id and locators
const bytes = await sf.evidence.content(claims.data[0].evidence_id); // hash it and compare with sha256Exports and evidence bundles
import { writeFile } from "node:fs/promises";
await writeFile("run.xlsx", await sf.runs.export(runId, "xlsx")); // csv | json | ndjson | xlsx
await writeFile("run.wacz", await sf.runs.bundle(runId)); // verify offline with the open verifierWebhooks
import { verifyWebhook } from "@tsg/sourcefinch";
const payload = await verifyWebhook({
payload: rawBody, // the exact bytes received, as a string
headers: request.headers, // webhook-id, webhook-timestamp, webhook-signature
secret: process.env.SOURCEFINCH_WEBHOOK_SECRET!, // whsec_…, shown once
});Signatures follow Standard Webhooks; timestamps older than five minutes are rejected. Deduplicate on the payload's idempotency_key.
Errors and any operation by name
import { SourceFinchError } from "@tsg/sourcefinch";
try {
await sf.call("getRun", { path: { run_id: runId } });
} catch (e) {
if (e instanceof SourceFinchError && e.code === "not_found") { /* … */ }
}SourceFinchError carries the HTTP status, the stable problem code (validation, unauthenticated, forbidden, not_found, conflict, policy, plan_limit, rate_limited, …) and the requestId to quote to support.