Combined
SDK reference

TypeScript SDK

Configure the TypeScript client, query data, paginate, cancel requests, and handle errors.

Install and configure

Install from the private registry configured for your organization:

pnpm add @combined/context
import { ContextOS } from "@combined/context";

const context = new ContextOS({
  token: process.env.COMBINED_TOKEN,
  baseUrl: process.env.COMBINED_API_URL,
  timeoutMs: 30_000,
});

token, baseUrl, timeoutMs, and a custom Fetch implementation are supported. When omitted, credentials and origin come from COMBINED_TOKEN and COMBINED_API_URL.

Discover and query

const catalogue = await context.catalog.list(accountId);
const result = await context.sql.query({
  accountId,
  sql: `
    SELECT channel_name, COUNT(*) AS messages
    FROM slack_messages
    WHERE sent_at >= ?
    GROUP BY channel_name
    ORDER BY messages DESC
    LIMIT 20
  `,
  parameters: ["2026-08-01T00:00:00Z"],
  maxRows: 20,
});

console.log(result.columns, result.rows, result.truncated);

Catalogue logical names are the only supported SQL relations. Parameterize values; SQL identifiers cannot be parameters and must come from trusted catalogue metadata.

Pagination

let cursor: string | undefined;

do {
  const page = await context.sources.list(accountId, { limit: 100, cursor });
  for (const source of page.data) console.log(source.name, source.state);
  cursor = page.page.nextCursor ?? undefined;
} while (cursor);

Pages contain data, page: { hasMore, nextCursor }, and correlationId. Cursors are opaque and tied to the list shape; restart pagination after changing filters.

Cancellation and idempotency

const controller = new AbortController();

const run = context.sources.run(accountId, sourceId, "manual", {
  signal: controller.signal,
  idempotencyKey: crypto.randomUUID(),
});

RequestOptions accepts signal and idempotencyKey. The client generates a key for mutations when omitted. Reuse an explicit key only for the identical operation and body.

Error handling

import { ContextOSError } from "@combined/context";

try {
  await context.sql.query({ accountId, sql, parameters, maxRows: 100 });
} catch (error) {
  if (error instanceof ContextOSError) {
    console.error(error.status, error.code, error.correlationId, error.details);
  }
  throw error;
}

GET retries are bounded to one attempt after the original request. SQL and mutations are intentionally not retried because the caller must decide whether a repeated action remains safe.

On this page