npm package: @stephen_turtles/bluma-sdk
Description: Official TypeScript/Node.js SDK for the Bluma API
Author: Bluma
Latest version: 1.2.0

# @stephen_turtles/bluma-sdk

Official TypeScript/Node.js SDK for the Bluma API

## Installation

```bash
npm install @stephen_turtles/bluma-sdk
# or
yarn add @stephen_turtles/bluma-sdk
# or
pnpm add @stephen_turtles/bluma-sdk
```

## Quick Start

```typescript
import { Bluma } from '@stephen_turtles/bluma-sdk';

const bluma = new Bluma({
  apiKey: process.env.BLUMA_API_KEY
});

// Generate a video
const video = await bluma.videos.create({
  templateId: 'meme-dialogue',
  context: {
    prompt: 'Create a funny dialogue between a programmer and their computer'
  }
});

console.log('Video ID:', video.id);

// Wait for completion
const completed = await bluma.videos.waitFor(video.id);
console.log('Video ready:', completed.url);
```

## Features

- ✅ **Full TypeScript support** with exported types
- ✅ **Automatic retries** with exponential backoff
- ✅ **Webhook verification** utilities
- ✅ **Polling helpers** for video completion
- ✅ **Custom error classes** for each status code
- ✅ **ESM and CommonJS** support

## Configuration

```typescript
const bluma = new Bluma({
  apiKey: process.env.BLUMA_API_KEY,
  baseUrl: 'https://api.bluma.app/api/v1', // optional
  timeout: 30000, // 30 seconds
  maxRetries: 3,
  retryDelay: 1000, // 1 second
  retryMultiplier: 2 // exponential backoff
});
```

## API Reference

### Videos

```typescript
// Create video
const video = await bluma.videos.create({
  templateId: 'meme-dialogue',
  context: { prompt: 'Create a funny video' },
  webhookUrl: 'https://myapp.com/webhook', // optional
  metadata: { userId: 'user_123' } // optional
});

// Get video status
const video = await bluma.videos.get('batch_abc123');

// Wait for completion with progress
const completed = await bluma.videos.waitFor('batch_abc123', {
  pollInterval: 5000,
  timeout: 600000,
  onProgress: (progress) => console.log(`Progress: ${progress}%`)
});

// Download video
const download = await bluma.videos.download('batch_abc123');
```

### Templates

```typescript
// List all templates
const templates = await bluma.templates.list();

// Get template details
const template = await bluma.templates.get('meme-dialogue');
```

### Credits

```typescript
// Get balance
const balance = await bluma.credits.getBalance();
console.log(`${balance.credits} credits remaining`);

// Get history
const history = await bluma.credits.getHistory({ limit: 50 });
```

### Webhooks

```typescript
// Create webhook
const webhook = await bluma.webhooks.create({
  url: 'https://myapp.com/webhooks/bluma',
  events: ['video.completed', 'video.failed']
});
console.log('Secret:', webhook.secret); // Save this!

// List webhooks
const webhooks = await bluma.webhooks.list();

// Delete webhook
await bluma.webhooks.delete('webhook_abc123');

// Get deliveries
const deliveries = await bluma.webhooks.getDeliveries('webhook_abc123');
```

### Webhook Verification

```typescript
import { Bluma } from '@stephen_turtles/bluma-sdk';
import express from 'express';

const app = express();

app.post('/webhooks/bluma',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-bluma-signature'];
    const payload = req.body.toString();

    try {
      const event = Bluma.webhooks.verify(
        payload,
        signature,
        process.env.BLUMA_WEBHOOK_SECRET
      );

      console.log('Event:', event.type);
      res.sendStatus(200);
    } catch (error) {
      console.error('Invalid signature');
      res.status(401).send('Unauthorized');
    }
  }
);
```

## Error Handling

```typescript
import {
  ValidationError,
  AuthenticationError,
  InsufficientCreditsError,
  RateLimitError,
  NotFoundError,
  APIError
} from '@stephen_turtles/bluma-sdk';

try {
  const video = await bluma.videos.create({...});
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Invalid input:', error.message);
  } else if (error instanceof InsufficientCreditsError) {
    console.error('Out of credits!');
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter}s`);
  } else if (error instanceof APIError) {
    console.error(`API error: ${error.status} - ${error.message}`);
  }
}
```

## Type Guards

```typescript
import { isVideoCompleted, isVideoFailed } from '@stephen_turtles/bluma-sdk';

const video = await bluma.videos.get('batch_abc123');

if (isVideoCompleted(video)) {
  // TypeScript knows video.url is defined
  console.log(video.url);
} else if (isVideoFailed(video)) {
  // TypeScript knows video.error is defined
  console.error(video.error.detail);
}
```

## TypeScript

Full TypeScript support with exported types:

```typescript
import type { Video, VideoStatus, Template } from '@stephen_turtles/bluma-sdk';

function processVideo(video: Video) {
  if (video.status === VideoStatus.Completed) {
    console.log(video.url);
  }
}
```

## License

MIT

## Support

- **Documentation:** https://docs.bluma.app
- **GitHub:** https://github.com/bluma/bluma-typescript
- **Email:** sdk@bluma.app