npm package: @senddy.io/senddy
Description: Official Node.js/TypeScript SDK for Senddy
Latest version: 0.1.0
Deprecated: Renamed to senddy-node. Install 'senddy-node' instead — https://www.npmjs.com/package/senddy-node

# Senddy TypeScript SDK

Official TypeScript/JavaScript SDK for the Senddy email API.

## Installation

Requires **Node.js 18.13+**.

```bash
npm install senddy
```

## Quick Start

```typescript
import { EmailClient } from 'senddy';

const client = new EmailClient('sk_your_api_key');

// Send an email
const result = await client.emails.send({
  from: 'sender@senddy.io',
  to: 'recipient@example.com',
  subject: 'Hello',
  html: '<p>Welcome!</p>',
});

console.log(result.id); // email_abc123
```

## Configuration

```typescript
const client = new EmailClient('sk_your_api_key', {
  baseUrl: 'https://api.senddy.io', // default: http://localhost:3012
  timeout: 30_000, // ms, default: 30000
  retries: 2, // default: 2
  headers: { 'X-Custom': 'value' }, // extra headers on every request
});
```

If no API key is passed, the SDK reads from the `SENDDY_API_KEY` environment variable.

## Emails

### Send

```typescript
const result = await client.emails.send(
  {
    from: 'sender@senddy.io',
    to: ['alice@example.com', 'bob@example.com'],
    subject: 'Hello',
    html: '<p>Hi there</p>',
    text: 'Hi there', // optional plain-text fallback
    cc: 'cc@example.com', // optional
    bcc: 'bcc@example.com', // optional
    reply_to: 'reply@example.com', // optional
    tags: { campaign: 'welcome' }, // optional metadata
    attachments: [
      {
        // optional
        filename: 'report.pdf',
        content: base64String,
        content_type: 'application/pdf',
      },
    ],
  },
  {
    idempotencyKey: 'unique-key', // optional, prevents duplicate sends
  },
);
```

### Get

```typescript
const email = await client.emails.get('email_abc123');
console.log(email.status); // 'delivered'
console.log(email.recipients); // recipient details
console.log(email.events); // delivery events
```

### List

```typescript
const { data, pagination } = await client.emails.list({
  limit: 25,
  offset: 0,
  status: 'delivered',
  since: '2026-01-01T00:00:00Z',
});
```

### Download EML

```typescript
const { content, contentType } = await client.emails.download('email_abc123');
// content is a Buffer containing the raw EML
```

## Suppressions

### List

```typescript
const { data, pagination } = await client.suppressions.list({
  limit: 50,
  search: 'example.com',
  reason: 'hard_bounce',
});
```

### Create

```typescript
const entry = await client.suppressions.create({
  email_address: 'block@example.com',
});
```

### Delete

```typescript
const result = await client.suppressions.delete('block@example.com');
console.log(result.removed); // true
```

## Error Handling

```typescript
import { ValidationError, RateLimitError, APIError } from 'senddy';

try {
  await client.emails.send({
    /* ... */
  });
} catch (err) {
  if (err instanceof ValidationError) {
    console.error('Validation:', err.details);
  } else if (err instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${err.retryAfter}s`);
  } else if (err instanceof APIError) {
    console.error(`API error ${err.statusCode}: ${err.message}`);
  }
}
```

### Error Types

| Class                 | Status | Description                                 |
| --------------------- | ------ | ------------------------------------------- |
| `ValidationError`     | 400    | Invalid request, includes `.details` array  |
| `AuthenticationError` | 401    | Missing or invalid API key                  |
| `ForbiddenError`      | 403    | Insufficient permissions                    |
| `NotFoundError`       | 404    | Resource not found                          |
| `RateLimitError`      | 429    | Rate limit exceeded, includes `.retryAfter` |
| `InternalError`       | 5xx    | Server error                                |

## Retries

The SDK automatically retries on 429 and 5xx responses with exponential backoff and jitter. Retries respect the `Retry-After` header. Non-retryable errors (4xx except 429) are thrown immediately.

## Requirements

- Node.js >= 18.13.0
- Zero runtime dependencies (uses native `fetch`)

## License

MIT