> ## Documentation Index
> Fetch the complete documentation index at: https://messages.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK

> Official TypeScript/Node.js client library

## Install

<CodeGroup>
  ```bash npm theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  npm install @messages-dev/sdk
  ```

  ```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  pnpm add @messages-dev/sdk
  ```

  ```bash yarn theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  yarn add @messages-dev/sdk
  ```

  ```bash bun theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  bun add @messages-dev/sdk
  ```
</CodeGroup>

## Initialize

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
import { createClient } from "@messages-dev/sdk";

const client = createClient();
```

The client automatically reads `MESSAGES_API_KEY` from your environment. To pass a key explicitly:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const client = createClient({ apiKey: "sk_live_..." });
```

## Send a message

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
await client.sendMessage({
  from: "+15551234567",
  to: "+15559876543",
  text: "Hello from Messages.dev!",
});
```

## Receive messages via webhooks

Create a webhook from the **Webhooks** page in your [dashboard](https://app.messages.dev)
and copy the signing secret. Then handle incoming events on your server:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
import { verifyWebhook } from "@messages-dev/sdk";

app.post("/webhooks", async (req, res) => {
  const event = await verifyWebhook(
    req.body,
    req.headers["x-webhook-signature"],
    "your_webhook_secret",
  );

  switch (event.event) {
    case "message.received":
      console.log(`New message from ${event.data.sender}: ${event.data.text}`);
      break;
    case "message.sent":
      console.log(`Message delivered: ${event.data.id}`);
      break;
  }

  res.sendStatus(200);
});
```

If the signature is invalid, `verifyWebhook` throws a `SignatureVerificationError`.

## List messages

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const messages = await client.listMessages({
  from: "+15551234567",
  to: "+15559876543",
});
```

## Reactions

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
await client.sendReaction({
  from: "+15551234567",
  to: "+15559876543",
  messageId: "msg_abc123",
  type: "love",
});

const reactions = await client.listReactions({
  messageId: "msg_abc123",
});
```

## Typing indicators

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
await client.startTyping({
  from: "+15551234567",
  to: "+15559876543",
});

await client.stopTyping({
  from: "+15551234567",
  to: "+15559876543",
});
```

## Read receipts

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
await client.sendReadReceipt({
  from: "+15551234567",
  to: "+15559876543",
});

const receipts = await client.listReadReceipts({
  from: "+15551234567",
  to: "+15559876543",
});
```

## Files

Upload files to send as message attachments:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const file = await client.uploadFile({
  from: "+15551234567",
  file: Buffer.from(imageData),
  filename: "photo.jpg",
  mimeType: "image/jpeg",
});
await client.sendMessage({
  from: "+15551234567",
  to: "+15559876543",
  text: "Check this out!",
  attachments: [file.id],
});

const { url } = await client.getFileUrl({ id: "file_abc123" });
```

## Delivery tracking

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
await client.getOutboxItem({ id: "obx_xyz789" });
```

## Lines

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const lines = await client.listLines();

for (const line of lines.data) {
  console.log(line.handle, line.service, line.isActive);
}
```

## Chats

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const chats = await client.listChats({
  from: "+15551234567",
});

for (const chat of chats.data) {
  console.log(chat.identifier, chat.name, chat.lastMessageAt);
}
```

## Webhooks

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
await client.createWebhook({
  from: "+15551234567",
  url: "https://example.com/webhooks",
  events: ["message.received", "message.sent"],
});

const webhooks = await client.listWebhooks({
  from: "+15551234567",
});

await client.deleteWebhook({ id: "wh_abc123" });
```

## Pagination

List methods return a page object with `data`, `hasMore`, and `nextCursor`. Use `for await...of` to iterate through all results:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const page = await client.listMessages({
  from: "+15551234567",
  to: "+15559876543",
  limit: 10,
});

if (page.hasMore) {
  const nextPage = await client.listMessages({
    from: "+15551234567",
    to: "+15559876543",
    cursor: page.nextCursor,
  });
}

const messages = await client.listMessages({
  from: "+15551234567",
  to: "+15559876543",
});

for await (const message of messages) {
  console.log(message.text);
}
```

## Error handling

All errors extend `MessagesError` and include the error type, code, and message from the API:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
import { createClient, AuthenticationError, AuthorizationError, InvalidRequestError, NotFoundError } from "@messages-dev/sdk";

try {
  await client.sendMessage({
    from: "+15551234567",
    to: "+15559876543",
    text: "Hello!",
  });
} catch (err) {
  if (err instanceof AuthenticationError) {
    console.error("Bad API key:", err.code);
  } else if (err instanceof AuthorizationError) {
    console.error("Forbidden:", err.code);
  } else if (err instanceof InvalidRequestError) {
    console.error("Bad request:", err.code, err.param);
  } else if (err instanceof NotFoundError) {
    console.error("Not found:", err.code);
  }
}
```

Every error includes:

| Property    | Type                  | Description                                    |
| ----------- | --------------------- | ---------------------------------------------- |
| `type`      | `string`              | Error category (e.g. `authentication_error`)   |
| `code`      | `string`              | Machine-readable code (e.g. `invalid_api_key`) |
| `message`   | `string`              | Human-readable explanation                     |
| `param`     | `string \| undefined` | The parameter that caused the error            |
| `status`    | `number`              | HTTP status code                               |
| `requestId` | `string`              | Unique request ID for debugging                |

## Configuration

All options are passed to `createClient`:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
const client = createClient({
  apiKey: "sk_live_...",
  baseUrl: "https://api.messages.dev",
  timeout: 30_000,
  maxRetries: 2,
});
```

## TypeScript

The SDK is written in TypeScript and exports types for all resources:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
import { createClient } from "@messages-dev/sdk";
import type {
  Line,
  Chat,
  Message,
  Reaction,
  Webhook,
  OutboxItem,
  WebhookEvent,
} from "@messages-dev/sdk";
```
