npm package: @mailflodev/sdk
Description: Official TypeScript SDK for MailFlo email sending service
Author: MailFlo
Latest version: 0.2.0

# @mailflodev/sdk

[![npm version](https://img.shields.io/npm/v/@mailflodev/sdk.svg)](https://www.npmjs.com/package/@mailflodev/sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.6+-blue.svg)](https://www.typescriptlang.org/)

Official TypeScript SDK for [MailFlo](https://mailflo.dev) — transactional email sending service.

- Zero runtime dependencies
- Dual CJS/ESM builds
- Full TypeScript support
- Never throws — all methods return `{ data, error }`
- Automatic retries with exponential backoff

## Installation

```bash
npm install @mailflodev/sdk
```

## Quick Start

```typescript
import { MailFlo } from '@mailflodev/sdk';

const mailflo = new MailFlo('mf_live_...');

const { data, error } = await mailflo.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '<h1>Hello!</h1>',
});

if (error) {
  console.error(error.message);
} else {
  console.log('Sent:', data.id);
}
```

## Constructor

```typescript
// String (API key only)
const mailflo = new MailFlo('mf_live_...');

// Options object
const mailflo = new MailFlo({
  apiKey: 'mf_live_...',
  baseUrl: 'https://console.mailflo.dev', // default
  timeout: 30000,                          // default, ms
  retryAttempts: 2,                        // default
});
```

## API Reference

### `emails.send(params)`

Send a single email. Returns `Result<SendEmailResult>`.

```typescript
const { data, error } = await mailflo.emails.send({
  from: 'App <noreply@yourdomain.com>',
  to: 'user@example.com',          // string or string[]
  subject: 'Hello!',
  html: '<h1>Hello</h1>',
  text: 'Hello',                    // optional plain text
  replyTo: 'support@yourdomain.com', // optional
  cc: 'cc@example.com',             // optional, string or string[]
  bcc: 'bcc@example.com',           // optional, string or string[]
  headers: { 'X-Custom': 'value' }, // optional
  tags: [{ name: 'campaign', value: 'welcome' }], // optional
  attachments: [{ filename: 'invoice.pdf', content: base64String }], // optional
  scheduledAt: '2025-12-25T00:00:00Z', // optional, ISO 8601
});
```

### `emails.get(id)`

Get an email by ID. Returns `Result<Email>`.

```typescript
const { data, error } = await mailflo.emails.get('email-id');
```

### `emails.list(options?)`

List sent emails with pagination. Returns `Result<ListEmailsResult>`.

```typescript
const { data, error } = await mailflo.emails.list({ page: 1, pageSize: 25 });

if (data) {
  console.log(data.data);       // Email[]
  console.log(data.pagination); // { page, pageSize, total, totalPages }
}
```

### `batch.send(params[])`

Send up to 100 emails at once. Returns `Result<BatchSendResult>`.

```typescript
const { data, error } = await mailflo.batch.send([
  { from: 'hi@example.com', to: 'a@example.com', subject: 'Hi A', html: '<p>Hi</p>' },
  { from: 'hi@example.com', to: 'b@example.com', subject: 'Hi B', html: '<p>Hi</p>' },
]);

if (data) {
  for (const email of data.data) {
    console.log('Sent:', email.id);
  }
}
```

## React Email

Render React Email components directly. Requires `@react-email/render` as a peer dependency:

```bash
npm install @react-email/render
```

```tsx
import { WelcomeEmail } from './emails/welcome';

const { data, error } = await mailflo.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  react: WelcomeEmail({ name: 'User' }),
});
```

## Attachments

```typescript
import { readFileSync } from 'fs';

const { data, error } = await mailflo.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Your invoice',
  html: '<p>Please find your invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      content: readFileSync('./invoice.pdf').toString('base64'),
      content_type: 'application/pdf',
    },
  ],
});
```

## Scheduled Sending

```typescript
const { data, error } = await mailflo.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Scheduled email',
  html: '<p>This email was scheduled!</p>',
  scheduledAt: '2025-12-25T09:00:00Z',
});
```

## Error Handling

All methods return `{ data, error }` and never throw. Check `error` before using `data`:

```typescript
const { data, error } = await mailflo.emails.send({ /* ... */ });

if (error) {
  console.error(error.name);       // e.g. "AuthenticationError", "NetworkError"
  console.error(error.message);    // human-readable message
  console.error(error.statusCode); // HTTP status code (when available)
  return;
}

// data is guaranteed non-null here
console.log(data.id);
```

The constructor is the only method that throws — if the API key is missing or invalid:

```typescript
try {
  const mailflo = new MailFlo('bad_key');
} catch (err) {
  // AuthenticationError: Invalid API key
}
```

## Migrating from Resend

MailFlo's API is designed to be compatible with [Resend](https://resend.com). Here are the key differences:

| Resend | MailFlo |
|--------|---------|
| `new Resend('re_...')` | `new MailFlo('mf_live_...')` |
| `resend.emails.send(...)` | `mailflo.emails.send(...)` |
| `resend.batch.send(...)` | `mailflo.batch.send(...)` |
| `{ data, error }` response | `{ data, error }` response |

Minimal migration:

```diff
- import { Resend } from 'resend';
+ import { MailFlo } from '@mailflodev/sdk';

- const resend = new Resend('re_...');
+ const mailflo = new MailFlo('mf_live_...');

- const { data, error } = await resend.emails.send({ ... });
+ const { data, error } = await mailflo.emails.send({ ... });
```

## License

[MIT](./LICENSE)