Sazabi

TypeScript SDK

Install, authenticate, and use the Sazabi TypeScript SDK (@sazabi/sdk) — threads, logs, issues, streaming, and device authorization.

The Sazabi TypeScript SDK (@sazabi/sdk) is a typed client for the Sazabi API. It wraps every operation — threads, logs, issues, projects, and more — and adds streaming transports and device-authorization helpers. It is an ES-module package for server-side JavaScript and TypeScript.

For browser telemetry, use the browser SDK instead — it sends data with a public key, whereas this SDK reads and writes the API with a secret key.

Install

npm install @sazabi/sdk

Create a client

Construct a client with createClient. Provide a credentialProvider whose getToken() returns your secret key (the SDK adds the Bearer prefix itself), and optionally the active organization and project:

import { createClient } from "@sazabi/sdk";

export const sazabi = createClient({
  credentialProvider: {
    getToken: () => process.env.SAZABI_SECRET_KEY!,
  },
});

Options:

OptionRequiredPurpose
credentialProviderYesSupplies getToken() (your secret key, without a Bearer prefix) and optional getOrganizationId() / getProjectId().
apiBaseUrlNoOverrides the API base URL. Defaults to the production API.
intakeBaseUrlNoOverrides the log intake base URL used by logs.forward.
tailBaseUrlNoOverrides the log tail base URL used by logs.tail.
fetchNoA custom fetch implementation.

Create the secret key in the dashboard under Settings > Secret keys (or with sazabi secret-keys create <name>), and read it from an environment variable — never hard-code it.

Core usage

The client groups operations into namespaces. A few common ones:

Threads

// Start an agent thread and wait for the run to finish.
const handle = await sazabi.threads.create({
  message: "Why did checkout error spike?",
});
const run = await handle.waitForCompletion();

// List and fetch threads.
const { threads } = await sazabi.threads.list();
const thread = await sazabi.threads.get({ threadId: threads[0].id });

threads.create and messages.append start long-running agent work and return a deferred handle; call waitForCompletion() (or poll()) to get the run.

Logs

// Ask a natural-language question. Returns a plain-language `answer`, an
// `explanation` of what was measured over which window, and a `queryId`.
// `results: true` adds the rows, value, table or series the answer was written from.
// A question is at most 2,000 characters. It is answered, or reported as taking too
// long (a 504), within about a minute; the SDK sets no timeout of its own, so pass a
// `fetch` that aborts (for example with `AbortSignal.timeout`) to bound a call.
const asked = await sazabi.logs.ask({
  question: "Which checkout errors happened in the last hour?",
  results: true,
});

// Run the same query again with no model. Without a `window` it reads the
// same times the answer read; a relative window ends now, so this asks the
// same question over the last day. Each run returns its own `queryId`. A
// `not_found` answer has a `queryId` too; it is absent only when nothing was
// planned, as for a question refused with `rejection.code` `not_about_logs`
// or `other_project`.
if (asked.queryId) {
  const rerun = await sazabi.logs.executeQuery({
    queryId: asked.queryId,
    window: { kind: "relative", lookbackSeconds: 86400 },
    limit: 100,
  });
}

// Or run a log query spec you write, with no model: which lines, grouped by
// what, measured how. The window defaults to the last hour; the reply's
// `queryId` runs it again later.
const perService = await sazabi.logs.executeQuery({
  spec: {
    version: 2,
    predicate: { op: "and", children: [] },
    dimensions: [{ kind: "service" }],
    measure: { op: "count" },
    output: "table",
  },
  window: { kind: "relative", lookbackSeconds: 21600 },
});

Issues

const { issues } = await sazabi.issues.list();
const issue = await sazabi.issues.get({ issueId: issues[0].id });
await sazabi.issues.resolve({ issueId: issue.id });

Streaming transports

The SDK streams live data over async iterables. Each returns an object you can iterate with for await, and close when you are done.

// Stream agent events for a run.
const stream = await sazabi.runs.stream({ runId });
for await (const event of stream) {
  // handle event
}
await stream.close();
TransportMethodStreams
Agent run eventsruns.stream({ runId })Live agent events for a run.
Agent thread eventsthreads.stream({ threadId })Live agent events for a thread's active run.
Log taillogs.tail({ filters })Matching log records as they arrive.
Log forwardlogs.forward({ publicKey, logs })Forwards OTLP log records into intake.

The agent-event streams expose a cursor; pass it back on reconnect to resume where you left off.

Device authorization

For interactive tools that sign a user in without a pre-provisioned key, the SDK exposes the device-authorization flow:

import { startDeviceAuthorization, pollDeviceAuthorization } from "@sazabi/sdk";

const start = await startDeviceAuthorization();
// Show start.verificationUriComplete to the user, then poll:
const result = await pollDeviceAuthorization({ deviceCode: start.deviceCode });
if (result.status === "authorized") {
  // result.accessToken is now usable as the credential.
}

pollDeviceAuthorization reports pending, authorized, denied, or expired. Poll on the interval the start response returns until the status is terminal.

Further reading