---
title: SDKs
description: Official client libraries for the Edgaze API, starting with TypeScript: typed calls, safe retries, live run streaming, and webhook verification.
source: https://www.edgaze.ai/docs/api/sdks
section: api
---
# SDKs

> Official client libraries for the Edgaze API, starting with TypeScript: typed calls, safe retries, live run streaming, and webhook verification.

Edgaze maintains official SDKs for the v1 REST API. Each SDK is generated against the [OpenAPI document](/docs/api/openapi) and tested against the live API. Use an SDK for retries, pagination, streaming, and webhook verification instead of writing them yourself.

**Audience: Developers calling the Edgaze API from application code.**

| Language                | Package       | Source                                                                        |
| ----------------------- | ------------- | ----------------------------------------------------------------------------- |
| TypeScript / JavaScript | `@edgaze/sdk` | [edgaze-ai/edgaze-typescript](https://github.com/edgaze-ai/edgaze-typescript) |

## TypeScript

The TypeScript SDK runs on Node 18 and later, Deno, and other runtimes that provide `fetch` and Web Crypto. It has no runtime dependencies.

```bash
npm install @edgaze/sdk
```

Create a key in [Developer → API keys](/developer/keys) and set it as `EDGAZE_API_KEY`. Keep the key on your server: the SDK refuses to run in a browser, where every visitor could read it.

### Run a workflow

```ts
import Edgaze from "@edgaze/sdk";

const client = new Edgaze(); // reads EDGAZE_API_KEY

const run = await client.runs.createAndWait({
  workflow: "550e8400-e29b-41d4-a716-446655440000",
  inputs: { topic: "Tides" },
});

if (run.status === "completed") {
  for (const output of run.outputs) console.log(output.name, output.value);
} else {
  console.error(run.status, run.reason);
}
```

Key `inputs` by each field's `nodeId` from [Get Workflow](/docs/api/get-workflow). `createAndWait` returns failed and cancelled runs rather than throwing, so check `status`.

### Stream progress live

```ts
const { run } = await client.runs.create({ workflow: workflowId, inputs });

for await (const message of client.runs.stream(run.id)) {
  if (message.kind === "event" && message.event.type === "node.output.delta") {
    process.stdout.write(message.event.data?.text ?? "");
  }
}
```

The stream replays what already happened, then follows new events. It reconnects with `Last-Event-ID` after the server's five-minute roll-over, a dropped connection, or a connection that goes silent, so no event is lost or repeated. It ends once the run is finished. See [Streaming & Real-time](/docs/api/streaming) for the frames it reads.

### Pagination

List methods can be iterated directly. The SDK fetches the next page when it needs it, and run listings use the cursor, so runs created while you page are never repeated or skipped:

```ts
for await (const run of client.runs.list({ status: "failed" })) {
  console.log(run.id, run.startedAt);
}
```

Await a list method instead to get one page, with `hasNextPage()` and `getNextPage()`.

### Retries and idempotency

The SDK retries connection errors, timeouts, and responses with status `408`, `425`, `429`, and `5xx`. It makes two retries by default, with exponential backoff that honours `Retry-After`. Every `runs.create` call sends an `Idempotency-Key`, so a retry returns the original run instead of starting a second one. Pass your own key to make retries safe across process restarts:

```ts
await client.runs.create(
  { workflow: workflowId, inputs },
  { idempotencyKey: `invoice-${invoice.id}` },
);
```

### Errors

Every error from the API is an `APIError` subclass with the HTTP `status`, the error `code`, and the `requestId`:

```ts
import { APIError, NotFoundError } from "@edgaze/sdk";

try {
  await client.runs.retrieve(runId);
} catch (err) {
  if (err instanceof NotFoundError) {
    // the run does not exist or is not visible to this key
  } else if (err instanceof APIError) {
    console.error(err.status, err.code, err.requestId);
  } else {
    throw err;
  }
}
```

Branch on `code`, not on the message. New codes can appear without notice (see [Versioning & Deprecation](/docs/api/versioning)), so handle an unrecognized code by its status. Quote `requestId` when you contact support. The codes are listed in [Errors](/docs/api/errors).

### Webhooks

`unwrapWebhook` verifies the `edgaze-signature` header against the raw body and rejects deliveries signed more than five minutes ago:

```ts
import { unwrapWebhook, WebhookVerificationError } from "@edgaze/sdk";

export async function POST(request: Request) {
  try {
    const { event, deliveryId } = await unwrapWebhook({
      payload: await request.text(),
      headers: request.headers,
      secret: process.env.EDGAZE_WEBHOOK_SECRET!,
    });
    // Store event keyed by deliveryId, then do the work asynchronously.
    return new Response(null, { status: 200 });
  } catch (err) {
    if (err instanceof WebhookVerificationError) return new Response(null, { status: 400 });
    throw err;
  }
}
```

Pass the body exactly as received. Parsing and re-serializing the JSON changes the bytes, and the signature no longer matches. See [Webhooks](/docs/api/webhooks) for the payloads and retry behavior.

### Updates

The SDK follows semver. Because v1 only gains additions, upgrading within a major version is safe. When the OpenAPI document changes, the SDK repository picks it up automatically, and its tests fail until the SDK covers any new endpoint or parameter. The [changelog](https://github.com/edgaze-ai/edgaze-typescript/blob/main/CHANGELOG.md) lists every release.
