Documentation
Sign inGet started

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.

Install

Code example
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, from messagebird/bird-sdk-typescript.

Construct a client

Code example
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 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

Code example
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.

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 and Python SDKs share this architecture. See SDK 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.

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.
Code example
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():
Code example
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 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 for the cross-SDK contract and Webhooks for the platform setup.

Next steps

  • Email quickstart: Use send, get, list, channel defaults, and the response shapes.
  • SDK concepts: Learn about idempotency, retries, pagination, regions, and webhooks across all Bird SDKs.
  • API reference: 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.