# TypeScript SDK

`@messagebird/sdk` is the official TypeScript SDK for the Bird API. It is fully typed, ESM-only, and edge-ready. It runs on Node.js 20.3+ and modern edge runtimes (Cloudflare Workers, Vercel Edge, Deno) using web-standard APIs (`fetch`, `AbortSignal`, Web Crypto). This page covers the client. To send email with the SDK, start with the [TypeScript email quickstart](/docs/get-started/quickstarts/typescript/email).

## Install

```bash
npm install @messagebird/sdk
# pnpm add @messagebird/sdk
# yarn add @messagebird/sdk
# bun add @messagebird/sdk
```

The package is published as [`@messagebird/sdk` on npm](https://www.npmjs.com/package/@messagebird/sdk), from [messagebird/bird-sdk-typescript](https://github.com/messagebird/bird-sdk-typescript).

## Construct a client

<!-- bird:snippet client.options -->

```typescript
const bird = new BirdClient({
  apiKey: process.env.BIRD_API_KEY!,
  region: "eu1", // optional; overrides the region from the key prefix
  baseUrl: "http://localhost:8080", // optional; overrides region (local or self-hosted)
  timeout: 60_000, // per-attempt timeout in ms (default 60_000)
  maxRetries: 2, // retry budget for transient failures (default 2)
});
```

Only `apiKey` is required. The region is inferred from the key's `bk_{region}_` prefix (a `bk_eu1_…` key routes to `https://eu1.platform.bird.com`), so most clients are constructed with the key alone. See [region inference](/docs/sdks/concepts#region-inference) for the resolution rules. You can also set channel defaults at construction (for example `email: { from: "hello@acme.com" }` makes `from` optional on every send) and the webhook signing secret via `webhooks: { secret }`.

## First call

<!-- bird:snippet email.send -->

```typescript
const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  subject: "Hello from Bird",
  html: "<p>My first Bird email.</p>",
});
console.log(msg.id, msg.status); // "em_…", "accepted"
```

`await` resolves directly to the API result, which is an email message with its `em_*` ID in this example. For a runnable walkthrough, follow the [TypeScript quickstart](/docs/get-started/quickstarts/typescript/email).

## Two-layer design

The SDK has a generated layer and a hand-owned layer. Wire types and low-level HTTP plumbing are generated from Bird's OpenAPI specification, keeping request and response shapes aligned with the contract. The hand-written layer provides `bird.email.send(...)`, retries, idempotency, pagination, and errors. Wire fields pass through in `snake_case` (`category`, `created_at`); SDK-defined identifiers, such as method names and `idempotencyKey`, use `camelCase`. The [Go](/docs/sdks/go) and [Python](/docs/sdks/python) SDKs share this architecture. See [SDK concepts](/docs/sdks/concepts) for details.

## Automatic idempotency and retries

Every mutation (POST, PATCH, DELETE) gets an auto-generated `Idempotency-Key` header. The SDK reuses that key across every retry attempt, preventing a retried send from delivering twice. Pass `{ idempotencyKey: "order-1234" }` in the per-call options to set the key yourself.

Retries are on by default (`maxRetries: 2`). The client retries network failures, per-attempt timeouts, and transient statuses (408, 429, 500, 502, 503, 504) with jittered exponential backoff, honoring the server's `Retry-After` header when present. Deterministic failures (4xx like 401, 404, 422) are never retried. Set `maxRetries: 0` to disable, or override per call. The full lifecycle is described in [SDK concepts](/docs/sdks/concepts#safe-retries).

## Errors

Methods throw on failure with a typed hierarchy you narrow with `instanceof`. `BirdError` is the root. `BirdAPIError` covers every error response from the server, with one subclass per error `type`. These include `BirdAuthError` (401), `BirdRateLimitError` (429, with `retryAfter`), `BirdValidationError` (422, with per-field `details`), and `BirdPayloadTooLargeError` (413). Transport failures with no HTTP response use the sibling classes `BirdConnectionError` and `BirdTimeoutError`.

<!-- bird:snippet email.errors -->

```typescript
import { BirdRateLimitError, BirdValidationError, BirdAPIError } from "@messagebird/sdk";

try {
  await bird.email.send({
    from: { email: "onboarding@messagebird.dev", name: "Bird" },
    to: ["delivered@messagebird.dev"],
    subject: "Hello from Bird",
    html: "<p>My first Bird email.</p>",
  });
} catch (err) {
  if (err instanceof BirdRateLimitError) console.log(`rate limited; retry in ${err.retryAfter}s`);
  else if (err instanceof BirdValidationError) console.error(err.details);
  else if (err instanceof BirdAPIError) console.error(err.code, err.requestId);
  else throw err;
}
```

Every `BirdAPIError` carries `statusCode`, `type`, `code` (the stable `E#####` error code), `requestId`, and `docUrl`. Branch on the class (or the coarse `type`) for control flow; use `code` when you need to match one specific failure. Prefer branching on a value instead of catching? Every call also has `.safe()`:

<!-- bird:snippet email.safe -->

```typescript
const { data, error } = await bird.email
  .send({
    from: { email: "onboarding@messagebird.dev", name: "Bird" },
    to: ["delivered@messagebird.dev"],
    subject: "Hello from Bird",
    html: "<p>My first Bird email.</p>",
  })
  .safe();
if (error) console.error(error.message);
else console.log(data.id);
```

## Webhooks

`bird.webhooks.unwrap(rawBody, headers)` verifies an inbound delivery's [Standard Webhooks](https://www.standardwebhooks.com/) signature and returns a typed, discriminated event. Pass the raw request body because parsing and re-serializing it changes the signed bytes. A bad signature, stale timestamp, or malformed headers throws `BirdWebhookVerificationError`. See [webhook verification](/docs/sdks/concepts#webhook-verification) for the cross-SDK contract and [Webhooks](/docs/guides/webhooks) for the platform setup.

## Next steps

- [Email quickstart](/docs/get-started/quickstarts/typescript/email): Use `send`, `get`, `list`, channel defaults, and the response shapes.
- [SDK concepts](/docs/sdks/concepts): Learn about idempotency, retries, pagination, regions, and webhooks across all Bird SDKs.
- [API reference](/docs/api): Review the underlying HTTP API. `bird.request<T>()` reaches endpoints that the typed surface does not cover yet, with the same authentication, retries, and idempotency.