Skip to content

cakemail/cakemail-sdk

Repository files navigation

Cakemail SDK

TypeScript SDK for the Cakemail API.

Installation

npm install @cakemail-org/cakemail-sdk

Quick Start

import { CakemailClient } from '@cakemail-org/cakemail-sdk';

// Initialize the client
const client = new CakemailClient({
  email: 'your-email@example.com',
  password: 'your-password',
});

// Get current account
const account = await client.accounts.getSelf();
console.log('Account:', account);

// List contacts
const contacts = await client.contacts.list({
  page: 1,
  per_page: 20,
  status: 'active',
});
console.log('Contacts:', contacts.data);

Features

  • đź”’ Automatic Authentication - OAuth 2.0 with automatic token refresh
  • 🔄 Retry Logic - Exponential backoff for failed requests
  • 📊 Multi-Tenant Support - Manage child accounts seamlessly
  • đź’Ş Type-Safe - Full TypeScript support with comprehensive types
  • 🎯 User-Friendly Errors - Clear, actionable error messages
  • âś… Well Tested - Comprehensive unit test coverage

Configuration

Email/Password Authentication

const client = new CakemailClient({
  email: 'your-email@example.com',
  password: 'your-password',
});

Access Token Authentication

If you already have an access token from an external OAuth flow, you can use it directly:

const client = new CakemailClient({
  accessToken: 'your-access-token',
  refreshToken: 'your-refresh-token', // Optional but recommended
  expiresIn: 3600, // Optional: Token expiration in seconds (default: 3600)
  scope: 'read write', // Optional: Token scope (default: 'read write')
});

Use cases for access token authentication:

  • Web applications with browser-based OAuth flows
  • Service accounts with pre-generated tokens
  • Integration with external authentication systems
  • Token-based authentication without storing passwords

Note: If a refreshToken is provided, the SDK will automatically refresh expired access tokens. Without a refresh token, you'll need to provide a new access token when the current one expires.

Advanced Configuration

const client = new CakemailClient({
  email: 'your-email@example.com',
  password: 'your-password',
  baseURL: 'https://api.cakemail.dev', // Optional: Custom API URL
  timeout: 30000, // Optional: Request timeout in ms (default: 30000)
  maxRetries: 3, // Optional: Max retry attempts (default: 3)
});

Resources

Accounts

// Get current account
const account = await client.accounts.getSelf();

// Get account by ID
const account = await client.accounts.get(123);

// List accounts
const accounts = await client.accounts.list({
  page: 1,
  per_page: 20,
  status: 'active',
});

// Create account
const newAccount = await client.accounts.create({
  name: 'New Account',
  primary_email: 'new@example.com',
});

// Update account
const updated = await client.accounts.update(123, {
  name: 'Updated Name',
});

// Delete account
await client.accounts.delete(123);

// Get child accounts
const children = await client.accounts.getChildren(123);

Contacts

// Get contact by ID
const contact = await client.contacts.get(456);

// Get contact by email
const contact = await client.contacts.getByEmail('user@example.com');

// List contacts
const contacts = await client.contacts.list({
  page: 1,
  per_page: 20,
  status: 'active',
  list_id: 789,
});

// Create contact
const newContact = await client.contacts.create({
  email: 'new@example.com',
  first_name: 'John',
  last_name: 'Doe',
  custom_attributes: {
    company: 'Acme Corp',
  },
});

// Update contact
const updated = await client.contacts.update(456, {
  first_name: 'Jane',
});

// Delete contact
await client.contacts.delete(456);

// Subscribe to list
await client.contacts.subscribe(456, 789);

// Unsubscribe from list
await client.contacts.unsubscribe(456, 789);

// Add tags
await client.contacts.addTags(456, ['vip', 'newsletter']);

// Remove tags
await client.contacts.removeTags(456, ['newsletter']);

// Bulk operations
const result = await client.contacts.bulkOperation({
  contact_ids: [1, 2, 3],
  operation: 'subscribe',
  parameters: { list_id: 789 },
});

Lists

// Get list by ID
const list = await client.lists.get(789);

// List all lists
const lists = await client.lists.list({
  page: 1,
  per_page: 20,
  status: 'active',
});

// Create list
const newList = await client.lists.create({
  name: 'Newsletter Subscribers',
  sender_name: 'Acme Corp',
  sender_email: 'newsletter@acme.com',
});

// Update list
const updated = await client.lists.update(789, {
  name: 'Updated List Name',
});

// Delete list
await client.lists.delete(789);

// Get list statistics
const stats = await client.lists.getStatistics(789);

Campaigns

// Get campaign by ID
const campaign = await client.campaigns.get(101);

// List campaigns
const campaigns = await client.campaigns.list({
  page: 1,
  per_page: 20,
  status: 'sent',
});

// Create campaign
const newCampaign = await client.campaigns.create({
  name: 'Monthly Newsletter',
  subject: 'Your Monthly Update',
  sender_name: 'Acme Corp',
  sender_email: 'newsletter@acme.com',
  list_ids: [789],
  content: {
    html: '<h1>Hello!</h1><p>Welcome to our newsletter.</p>',
    text: 'Hello! Welcome to our newsletter.',
  },
});

// Update campaign
const updated = await client.campaigns.update(101, {
  subject: 'Updated Subject Line',
});

// Schedule campaign
await client.campaigns.schedule(101, {
  scheduled_at: '2024-12-31T10:00:00Z',
});

// Send campaign immediately
await client.campaigns.send(101);

// Pause campaign
await client.campaigns.pause(101);

// Resume campaign
await client.campaigns.resume(101);

// Cancel campaign
await client.campaigns.cancel(101);

// Get campaign statistics
const stats = await client.campaigns.getStatistics(101);

// Send test email
await client.campaigns.sendTest(101, ['test@example.com']);

Multi-Tenant Operations

For parent accounts managing child accounts, you can specify an accountId option:

// Access child account data
const childContacts = await client.contacts.list(
  { status: 'active' },
  { accountId: 456 } // Child account ID
);

// Create resource in child account
const newContact = await client.contacts.create(
  {
    email: 'new@example.com',
    first_name: 'John',
  },
  { accountId: 456 }
);

Error Handling

The SDK provides detailed error types for better error handling:

import {
  CakemailError,
  CakemailAPIError,
  CakemailAuthError,
  CakemailNetworkError,
  CakemailValidationError,
} from '@cakemail-org/cakemail-sdk';

try {
  const contact = await client.contacts.get(999);
} catch (error) {
  if (error instanceof CakemailAPIError) {
    console.error('API Error:', error.getUserFriendlyMessage());
    console.error('Status Code:', error.statusCode);
    console.error('Response:', error.responseBody);
  } else if (error instanceof CakemailAuthError) {
    console.error('Authentication failed:', error.message);
  } else if (error instanceof CakemailNetworkError) {
    console.error('Network error:', error.message);
  } else if (error instanceof CakemailValidationError) {
    console.error('Validation error:', error.message);
  }
}

User-Friendly Error Messages

API errors include user-friendly messages via getUserFriendlyMessage():

try {
  await client.contacts.create({ email: 'invalid-email' });
} catch (error) {
  if (error instanceof CakemailAPIError) {
    // Shows: "Invalid request: Invalid email format. Please check your parameters."
    console.error(error.getUserFriendlyMessage());
  }
}

Authentication

Automatic Token Management

The SDK automatically handles OAuth 2.0 token lifecycle:

  • Authenticates on first API request
  • Caches access tokens
  • Refreshes tokens before expiration (5-minute buffer)
  • Retries failed requests after token refresh

Manual Authentication

// Explicitly authenticate (optional)
await client.authenticate();

// Get token information
const tokenInfo = client.getTokenInfo();
console.log('Token valid:', tokenInfo.isValid);
console.log('Expires at:', new Date(tokenInfo.expiresAt!));

// Clear cached token (forces re-authentication)
client.clearAuthCache();

Retry Logic

The SDK automatically retries failed requests with exponential backoff:

  • Network errors: Retries automatically
  • 5xx server errors: Retries automatically
  • 401 errors: Refreshes token and retries once
  • Max retries: 3 attempts (configurable)
  • Backoff: 1s, 2s, 4s, 8s (max 10s)

TypeScript Support

The SDK is written in TypeScript and includes comprehensive type definitions:

import {
  Account,
  Contact,
  List,
  Campaign,
  CreateContactRequest,
  UpdateContactRequest,
  ContactStatus,
  CampaignStatus,
} from '@cakemail-org/cakemail-sdk';

const contact: Contact = await client.contacts.get(123);

const createRequest: CreateContactRequest = {
  email: 'new@example.com',
  first_name: 'John',
  status: ContactStatus.Active,
};

Command-Line Interface

For quick testing, automation scripts, and terminal access, the Cakemail CLI is also available:

Installation:

# via Homebrew (macOS/Linux)
brew tap cakemail/cakemail
brew install cakemail-cli

# via npm
npm install -g @cakemail-org/cakemail-cli

# via npx (no installation)
npx @cakemail-org/cakemail-cli --help

Example usage:

cakemail campaigns list
cakemail contacts create --email user@example.com --first-name John
cakemail campaigns send --id 123

The CLI uses this SDK internally and is ideal for:

  • Quick testing and debugging
  • DevOps automation and CI/CD pipelines
  • Non-developer users (marketers, operations teams)
  • Scripting and batch operations

See the CLI documentation for complete details.

Development

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Generate coverage report
npm run test:coverage

# Lint code
npm run lint

License

MIT

Related Projects

Support

For issues and questions:

About

TypeScript SDK for Cakemail API

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •