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/sdkCreate 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:
| Option | Required | Purpose |
|---|---|---|
credentialProvider | Yes | Supplies getToken() (your secret key, without a Bearer prefix) and optional getOrganizationId() / getProjectId(). |
apiBaseUrl | No | Overrides the API base URL. Defaults to the production API. |
intakeBaseUrl | No | Overrides the log intake base URL used by logs.forward. |
tailBaseUrl | No | Overrides the log tail base URL used by logs.tail. |
fetch | No | A 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();| Transport | Method | Streams |
|---|---|---|
| Agent run events | runs.stream({ runId }) | Live agent events for a run. |
| Agent thread events | threads.stream({ threadId }) | Live agent events for a thread's active run. |
| Log tail | logs.tail({ filters }) | Matching log records as they arrive. |
| Log forward | logs.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.