Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Modellen Tools & Services
Oplossingen
Organisaties Ontwikkelaars Internet Service Providers Managed Service Providers AI-in-a-Box
Kenniscentrum
Ondersteuning Documentation Blog Downloads
Bedrijf
Over ons Onderzoek Vacatures Investeren Contact
Inloggen

TypeScript SDK

Official TypeScript / JavaScript client for ScaiGrid. Promise-based, with async iterators for streaming. Same ergonomics as the Python and .NET SDKs, idiomatic where it matters.

Install#

bash
1
npm install @scailabs/scaigrid

Node 18+ (uses native fetch). Both ESM (import) and CJS (require) bundles ship in the package.

Quickstart#

ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { ScaiGrid } from "@scailabs/scaigrid";

const client = new ScaiGrid({ apiKey: "sgk_..." });

// List models
const models = await client.models.list({ modality: "chat" });
console.log(models.map((m) => m.slug));

// Streaming chat
for await (const chunk of client.inference.chatStream({
  model: "anthropic/claude-haiku-4-5",
  messages: [{ role: "user", content: "Hello!" }],
  metadata: { agentId: "..." },           // flows into accounting
})) {
  process.stdout.write(chunk.delta?.content ?? "");
}

Authentication#

Exactly one of three modes per client:

ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 1. Static API key (most common)
const client = new ScaiGrid({ apiKey: "sgk_..." });

// 2. Pre-minted JWT
const client = new ScaiGrid({ accessToken: jwt });

// 3. OAuth client_credentials against ScaiKey — cached + auto-refresh
const client = ScaiGrid.fromClientCredentials({
  clientId: "...",
  clientSecret: "...",
  tokenUrl: "https://scaikey.scailabs.ai/api/v1/platform/oauth/token",
});

For full control, implement the AuthProvider interface and pass it as auth:

ts
1
2
3
4
5
6
7
import { ScaiGrid, type AuthProvider } from "@scailabs/scaigrid";

class MyAuth implements AuthProvider {
  async header() { return "Bearer …"; }
}

const client = new ScaiGrid({ auth: new MyAuth() });

Resources#

ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Models
await client.models.list({ modality: "chat" });
await client.models.get("anthropic/claude-haiku-4-5");

// Inference — chat (Promise), chatStream (AsyncIterable), embed
await client.inference.chat({ model, messages });
for await (const chunk of client.inference.chatStream({ model, messages })) {  }
await client.inference.embed({ model: "text-embedding-3-small", input: ["…"] });

// Modules — generic proxy
await client.modules.proxy("scaimatrix", "POST", "/collections", {
  jsonBody: { name: "…", embedding_model: "…" },
});

Module sub-clients#

ScaiBot#

ts
1
2
3
4
5
6
7
8
9
const bot = await client.scaibot.bots.create({
  name: "support",
  model: "anthropic/claude-haiku-4-5",
  displayName: "Support Bot",
});

const calls = await client.scaibot.voice.listCalls({ limit: 20 });
const transcript = await client.scaibot.voice.getTranscript(callId);
const cost = await client.scaibot.voice.getCost(callId);

ScaiSpeak#

ts
1
2
3
4
5
6
7
const { audio } = await client.scaispeak.synthesize({
  voiceId: "voice_...",
  text: "Hello, world!",
});
// `audio` is a Uint8Array of decoded audio bytes.

const voices = await client.scaispeak.voices.list({ language: "nl" });

ScaiDial#

ts
1
2
3
4
5
6
7
8
9
const extensions = await client.scaidial.extensions.list();
await client.scaidial.extensions.create({
  number: "1042", type: "bot", targetRef: bot.id,
});

await client.scaidial.dialplans.addRule(dialplanId, {
  actionType: "goto_extension",
  actionParams: { extension_number: "1042" },
});

Streaming#

chatStream returns an AsyncIterable<ChatChunk>. Iterate with for await, abort via AbortSignal:

ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const controller = new AbortController();
setTimeout(() => controller.abort(), 5_000);

try {
  for await (const chunk of client.inference.chatStream({
    model, messages,
  }, { signal: controller.signal })) {
    process.stdout.write(chunk.delta?.content ?? "");
  }
} catch (err) {
  if (err instanceof TimeoutError) {
    // Stream took too long.
  } else {
    throw err;
  }
}

Errors#

Every exception extends ScaiGridError with a .code that matches the server's ScaiGridError.code:

ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import {
  NotFoundError,
  RateLimitedError,
  BudgetExceededError,
  ScaiGridError,
} from "@scailabs/scaigrid";

try {
  await client.inference.chat({ model, messages });
} catch (err) {
  if (err instanceof RateLimitedError) {
    console.warn(`retry after ${err.retryAfter}s`);
  } else if (err instanceof BudgetExceededError) {
    // Tenant or user budget cap hit.
  } else if (err instanceof ScaiGridError) {
    // Catch-all; err.code, err.httpStatus, err.requestId all populated.
  } else {
    throw err;
  }
}

Full taxonomy: AuthError, AuthTokenInvalidError, AuthTokenExpiredError, PermissionDeniedError, NotFoundError, ConflictError, ValidationError, RateLimitedError, BudgetExceededError, BackendError, BackendTimeoutError, InternalServerError, TimeoutError, TransportError.

Request IDs + observability#

Every method accepts requestId. Forwarded as X-Request-ID, propagated through server logs and audit:

ts
1
2
3
4
await client.inference.chat(
  { model, messages },
  { requestId: "req-batch-job-7" },
);

Browser usage — caveats#

The SDK uses native fetch and works in modern browsers. Never ship an API key to the browser — keys grant tenant-scoped access. The recommended browser pattern is a small server-side proxy that authenticates the end-user, then mints a short-lived ScaiGrid JWT to forward via accessToken.

For local development against a real ScaiGrid, set the baseUrl:

ts
1
2
3
4
const client = new ScaiGrid({
  apiKey: "sgk_dev_...",
  baseUrl: "http://localhost:8000",
});

Download#

The current release is on /downloads under ScaiGrid → TypeScript, with SHA-256 verification.

See also#

Updated 2026-06-15 13:16:10 View source (.md) rev 2