TypeScript SDK
npm install @orbit/sdk → typed client with auto-pagination and webhook verification.
Server-side only. API keys (ok_live_…) are workspace secrets — never
ship them to a browser or mobile app. Route calls through your backend.
Install
npm install @orbit/sdk
# or: bun add / pnpm add / yarn addRequires Node ≥ 20 (global fetch + Web Crypto). Zero runtime dependencies. Dual ESM/CJS with full type declarations.
Quickstart
import { OrbitClient } from "@orbit/sdk";
const orbit = new OrbitClient({
apiKey: process.env.ORBIT_API_KEY!,
baseUrl: "https://<your-deployment>.convex.site",
});
const { projectId } = await orbit.search({
brief: "Senior fintech engineers in Berlin with payments experience",
});
const status = await orbit.waitForCompletion(projectId, {
timeoutMs: 10 * 60 * 1000,
});
if (status.status === "awaiting_input") {
console.log("Clarification needed:", status.clarificationQuestion);
} else {
for await (const person of orbit.listAllResults(projectId)) {
console.log(person.fullName, person.title, person.linkedinUrl);
}
}Client API
new OrbitClient({ apiKey, baseUrl, fetch?, maxRetries? })| Method | Returns |
|---|---|
orbit.search({ brief, callbackUrl? }) | Promise<SearchResponse> |
orbit.getProject(projectId) | Promise<ProjectStatus> |
orbit.getResults(projectId, { limit?, offset? }) | Promise<ResultsResponse> |
orbit.listAllResults(projectId) | AsyncGenerator<ResultRow> |
orbit.exportCsv(projectId) | Promise<string> |
orbit.waitForCompletion(projectId, opts?) | Promise<ProjectStatus> |
listAllResults auto-paginates at the 200-item cap until all results are returned.
waitForCompletion polls every pollIntervalMs (default 3 000 ms) until done or awaiting_input; throws OrbitTimeoutError past timeoutMs; honors an AbortSignal.
CSV export
const csv = await orbit.exportCsv(projectId);
// byte-identical to Orbit's in-app exportWebhook verification
import { verifyWebhookSignature, type CallbackEvent } from "@orbit/sdk";
const valid = await verifyWebhookSignature({
payload: rawBody, // unparsed request body string
signature: req.headers["x-orbit-signature"], // "sha256=<hex>"
secret: process.env.ORBIT_WEBHOOK_SECRET!,
});
if (!valid) return new Response("bad signature", { status: 401 });
const event = JSON.parse(rawBody) as CallbackEvent;
// event.projectId, event.status, event.resultsUrlError handling
import { OrbitApiError, OrbitTimeoutError } from "@orbit/sdk";
try {
await orbit.search({ brief });
} catch (err) {
if (err instanceof OrbitApiError) {
// err.code: "unauthorized" | "not_found" | "invalid_request" |
// "limit_exceeded" | "rate_limited" | "internal_error"
}
if (err instanceof OrbitTimeoutError) {
// waitForCompletion exceeded timeoutMs
}
}Transient faults (network errors + 5xx) retry automatically with doubling backoff up to maxRetries (default 2). 4xx never retries.
search retries on network errors, which can create a duplicate project if
the server accepted the first request but the response was lost. Set
maxRetries: 0 if duplicate projects are worse than a manual retry.