npm package: @multisender.app/multisender-sdk
Description: TypeScript SDK for Multisender Enterprise API - mass token distributions across EVM chains
Latest version: 0.1.1

# Multisender SDK

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

TypeScript SDK for the Multisender Enterprise API - streamline mass token distributions across multiple EVM chains.

## Features

- 🚀 **Simple API** - Intuitive methods for token distributions
- 📦 **Full TypeScript Support** - Complete type safety and IntelliSense
- 🔗 **Multi-Chain** - Support for Ethereum, Polygon, Arbitrum, Optimism, Base, and more
- 📊 **CSV Support** - Import recipients from CSV files
- 🛠️ **CLI Tools** - Command-line interface for quick operations
- ✅ **Tested** - Comprehensive test coverage with Bun test runner
- 🌐 **Dual Builds** - ESM and CommonJS support

## Installation

```bash
npm install @multisender.app/multisender-sdk
```

Or with yarn:

```bash
yarn add @multisender.app/multisender-sdk
```

Or with Bun:

```bash
bun add @multisender.app/multisender-sdk
```

## Quick Start

```typescript
import { Multisender } from '@multisender.app/multisender-sdk';

// Initialize the SDK
const multisender = new Multisender({
  apiKey: 'your-api-key-here',
});

// Create a token distribution
const result = await multisender.distributions.distribute({
  chainId: 1, // Ethereum mainnet
  tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  tokenSymbol: 'USDC',
  recipients: [
    ['0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', '100'],
    ['0x1234567890123456789012345678901234567890', '200'],
  ],
});

console.log('Distribution created:', result.distribution.id);
```

## OpenAPI-First Workflow

This SDK is generated from an OpenAPI spec and committed under `src/gen`.

### Environment placeholders

- `OPENAPI_SPEC_URL` (required, points to the OpenAPI JSON endpoint)
- `OPENAPI_SPEC_AUTH_HEADER` (optional)
- `OPENAPI_SPEC_AUTH_TOKEN` (optional)

### Commands

```bash
# Regenerate SDK client/types from OpenAPI
bun run generate:api

# Verify generated artifacts are committed and up to date
bun run generate:api:check
```

### Release policy

- Treat spec changes as the source of truth for SDK surface changes.
- Include spec revision/source in release notes when publishing.
- Document migration notes for any spec-driven breaking change.

### Migration: OpenAPI-aligned generated operations

The generated client under `src/gen` now follows the OpenAPI `operationId` format `resource_method`. In the published generated entrypoint this becomes camelCase exports without a `client` prefix, for example `projectGetProjectInfo`, `listsCreate`, and `distributionsPrepareTransactions`.

This is a breaking change only for `@multisender.app/multisender-sdk/generated`. The high-level façade (`Multisender` and the `*Service` classes) keeps the same public method names and behavior.

Import the generated surface from `@multisender.app/multisender-sdk/generated` and use the new resource-based exports.

**Removed generated export names:**

- `clientApiKeysGetProjectInfo` and `clientApiKeysGetProjectMembers`
- `clientLists*`, `clientDistributions*`, and `clientCatalogs*`
- Older short names such as `create2`, `findAll2`, and `getProjectInfo`

**Removed from the SDK (no longer in OpenAPI):**

- Project API key CRUD (`listApiKeys`, `createApiKey`, `updateApiKey`, `deleteApiKey`).
- Token catalog methods (`getTokens`, `getToken`, `searchTokens`).

**Lists:** Removing an entry from a list uses **list item id** and `lists.removeListItem` (path `/recipients/items/{listItemId}`), not recipient id on the old route.

**Distributions:** `CreateDistributeRequest` is a discriminated union of **recipients** or **csv** (exactly one); draft create uses `CreateDistributionRequest` (`listId` **or** `recipients`, not both per spec).

## API Reference

### Initialize SDK

```typescript
const multisender = new Multisender({
  apiKey: string;           // Required: Your API key
  baseUrl?: string;         // Optional: Custom API base URL
  timeout?: number;         // Optional: Request timeout in ms (default: 30000)
  headers?: Record<string, string>; // Optional: Additional headers
});
```

### Distributions

#### Create Distribution

Provide **exactly one** of `recipients` or `csv` (the API rejects requests with both or neither).

**Recipients:**

```typescript
await multisender.distributions.distribute({
  chainId: 1,
  tokenAddress: '0x…',
  tokenSymbol: 'USDC',
  recipients: [
    ['0x…', '100'],
    ['0x…', '200'],
  ],
  // account: '0x…',        // optional sender wallet
  // idempotencyKey: '…',   // optional dedupe key
});
```

**CSV string:**

```typescript
await multisender.distributions.distribute({
  chainId: 1,
  tokenAddress: '0x…',
  tokenSymbol: 'USDC',
  csv: 'address,amount\n0x…,100\n',
  // account: '0x…',
  // idempotencyKey: '…',
});
```

#### List Distributions

```typescript
const distributions = await multisender.distributions.list({
  page?: number;            // Page number (default: 1)
  limit?: number;           // Items per page (default: 20)
  orderBy?: string;         // Field to order by
  orderDir?: 'ASC' | 'DESC'; // Order direction
  search?: string;          // Search term
});
```

#### Get Distribution

```typescript
const distribution = await multisender.distributions.get('dist_123');
```

#### Get Distribution Statistics

```typescript
const stats = await multisender.distributions.getStats('dist_123');
// Returns: totalRecipients, totalAmount, completedTransactions, etc.
```

#### Cancel Distribution

```typescript
await multisender.distributions.cancel('dist_123');
```

### Lists

#### Create Recipient List

```typescript
const list = await multisender.lists.create({
  name: 'VIP Customers',
  notes?: 'Optional notes',
});
```

#### List All Lists

```typescript
const lists = await multisender.lists.list({
  page?: number;
  limit?: number;
});
```

#### Get List

```typescript
const list = await multisender.lists.get('list_123');
```

#### Update List

```typescript
const list = await multisender.lists.update('list_123', {
  name?: 'Updated Name',
  notes?: 'Updated notes',
});
```

#### Delete List

```typescript
await multisender.lists.delete('list_123');
```

#### Add Recipients

```typescript
// Add single recipient
await multisender.lists.addRecipient('list_123', {
  address: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
  addressType: 'EVM',
  label?: 'Customer A',
  tags?: ['vip', 'early-adopter'],
});

// Add multiple recipients
await multisender.lists.addRecipientsBulk('list_123', {
  items: [
    { address: '0x...', addressType: 'EVM', label: 'Customer A' },
    { address: '0x...', addressType: 'EVM', label: 'Customer B' },
  ],
});
```

#### Remove list item

Removes a row from a list by **list item id** (the id of the list-item resource, not the recipient id on the legacy route):

```typescript
const { message } = await multisender.lists.removeListItem('list_123', 'list_item_456');
```

`*Detailed()` methods are removed; primary SDK methods now return the full detailed payload.

### Project

#### Get Project Info

```typescript
const project = await multisender.project.getInfo();
```

#### Project members

```typescript
const members = await multisender.project.getMembers();
```

API key management (list/create/update/delete) is not part of this SDK’s OpenAPI surface anymore; manage keys in the Multisender product or your upstream API as documented there.

### Catalogs

```typescript
// Supported chains and chain metadata
const chains = await multisender.catalogs.getChains();
const chain = await multisender.catalogs.getChain('1');
```

Token catalog HTTP endpoints are no longer exposed in the generated client; resolve token metadata via your own sources or the Multisender API docs if new endpoints are added to the spec.

## CSV Support

### Parse CSV

```typescript
import { parseCsv } from '@multisender.app/multisender-sdk';

const recipients = parseCsv(csvString, {
  hasHeader: true,
  delimiter: ',',
  skipRows: 0,
});
```

### Generate CSV

```typescript
import { toCsv } from '@multisender.app/multisender-sdk';

const csv = toCsv([
  ['0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', '100'],
  ['0x1234567890123456789012345678901234567890', '200'],
], true); // includeHeader
```

### CSV Format

```csv
address,amount
0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6,100
0x1234567890123456789012345678901234567890,200
```

## CLI Usage

The SDK includes a command-line interface for quick operations.

### Install Globally

```bash
npm install -g @multisender.app/multisender-sdk
```

### Commands

#### Distribute Tokens

```bash
multisender distribute \
  --api-key YOUR_API_KEY \
  --chain 1 \
  --token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
  --symbol USDC \
  --recipients recipients.csv
```

#### List Distributions

```bash
multisender distributions list --api-key YOUR_API_KEY
```

#### Get Distribution Details

```bash
multisender distributions get dist_123 --api-key YOUR_API_KEY
```

#### Manage Lists

```bash
# Create list
multisender lists create --api-key YOUR_API_KEY --name "My List"

# List all
multisender lists list --api-key YOUR_API_KEY

# Get list
multisender lists get list_123 --api-key YOUR_API_KEY

# Delete list
multisender lists delete list_123 --api-key YOUR_API_KEY
```

#### Local development

To use the CLI against a local API (e.g. `http://localhost:3000`), pass `--base-url`:

```bash
multisender lists list --api-key YOUR_API_KEY --base-url http://localhost:3000
multisender distributions list --api-key YOUR_API_KEY --base-url http://localhost:3000
```

## Supported Chains

| Chain | Chain ID |
|-------|----------|
| Ethereum | 1 |
| Optimism | 10 |
| Polygon | 137 |
| Base | 8453 |
| Arbitrum | 42161 |

## Error Handling

```typescript
import { ApiError, NetworkError, ValidationError } from '@multisender.app/multisender-sdk';

try {
  await multisender.distributions.distribute({ /* ... */ });
} catch (error) {
  if (error instanceof ApiError) {
    console.error('API Error:', error.status, error.body);
  } else if (error instanceof NetworkError) {
    console.error('Network Error:', error.message);
  } else if (error instanceof ValidationError) {
    console.error('Validation Error:', error.message);
  }
}
```

## Examples

See the [examples](./examples) directory for complete examples:

- [Basic Node.js Script](./examples/node-script.ts)
- [CSV Distribution](./examples/csv-distribution.ts)
- [Managing Lists](./examples/manage-lists.ts)
- [React App](./examples/react-app.tsx)

## Development

Use Bun for package and script management.

```bash
bun install
bun run build
bun run test
bun run test:coverage
bun run typecheck
bun run lint
```

## Browser Support

The SDK works in both Node.js (18+) and modern browsers that support native `fetch`.

## Requirements

- **Runtime:** Node.js 18+ or Bun 1+ (for development and consumption)
- **TypeScript 5+** (for development)

## License

MIT License - see [LICENSE](./LICENSE) file for details.

## Links

- [NPM Package](https://www.npmjs.com/package/@multisender.app/multisender-sdk)
- [API Documentation](https://docs.multisender.app)
- [GitHub Repository](https://github.com/multisender/sdk)
- [Issue Tracker](https://github.com/multisender/sdk/issues)

## Support

For support, email support@multisender.app or open an issue on GitHub.