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 to

Create 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 sha256

Exports 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 verifier

Webhooks

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.