@blackleafmail/api (1.0.0)

Published 2025-11-23 04:33:12 +00:00 by zachhandley

Installation

@blackleafmail:registry=
npm install @blackleafmail/api@1.0.0
"@blackleafmail/api": "1.0.0"

About this package

BlackLeaf Mail TypeScript SDK

TypeScript SDK for the BlackLeaf Mail API, providing easy access to email, calendar, and contact management features with full type safety.

Table of Contents

Installation

Quick Install (with registry flag)

The fastest way to install is to specify the registry directly:

npm install @blackleafmail/api --registry=https://forge.blackleafdigital.com/api/packages/zachhandley/npm/
# or
pnpm add @blackleafmail/api --registry=https://forge.blackleafdigital.com/api/packages/zachhandley/npm/
# or
yarn add @blackleafmail/api --registry=https://forge.blackleafdigital.com/api/packages/zachhandley/npm/

Set up the registry once for the @blackleafmail scope:

npm config set @blackleafmail:registry https://forge.blackleafdigital.com/api/packages/zachhandley/npm/
npm install @blackleafmail/api

# or with pnpm
pnpm config set @blackleafmail:registry https://forge.blackleafdigital.com/api/packages/zachhandley/npm/
pnpm add @blackleafmail/api

# or with yarn
yarn config set @blackleafmail:registry https://forge.blackleafdigital.com/api/packages/zachhandley/npm/
yarn add @blackleafmail/api

For Teams: Use Project .npmrc

Add a .npmrc file to your project root:

@blackleafmail:registry=https://forge.blackleafdigital.com/api/packages/zachhandley/npm/

Then install normally:

npm install @blackleafmail/api
# or
pnpm add @blackleafmail/api
# or
yarn add @blackleafmail/api

Package Reference in package.json

After publishing, you can reference the package in your package.json:

{
  "dependencies": {
    "@blackleafmail/api": "^1.0.0"
  }
}

This replaces local file references like "file:../../../../BillionMail/sdk/typescript".

Authentication

The SDK supports two authentication methods:

API Key Authentication

import { BlackLeafMailClient } from '@blackleafmail/api';

const client = new BlackLeafMailClient({
  baseURL: 'https://mail.yourserver.com',
  apiKey: 'your-api-key'
});

OAuth 2.0 Authentication

import { BlackLeafMailClient, OAuthHelper } from '@blackleafmail/api';

// Initialize OAuth helper
const oauth = new OAuthHelper({
  baseURL: 'https://mail.yourserver.com',
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret'
});

// Generate authorization URL
const authUrl = oauth.getAuthorizationUrl(
  'https://yourapp.com/callback',
  'email calendar contacts',
  'random-state-string'
);

// After user authorizes, exchange code for token
const tokens = await oauth.exchangeCodeForToken(code, redirectURI);

// Create client with access token
const client = new BlackLeafMailClient({
  baseURL: 'https://mail.yourserver.com',
  accessToken: tokens.access_token
});

// Refresh token when needed
const refreshed = await oauth.refreshToken(tokens.refresh_token);
client.updateAccessToken(refreshed.access_token);

Quick Start

import { BlackLeafMailClient } from '@blackleafmail/api';

const client = new BlackLeafMailClient({
  baseURL: 'https://mail.yourserver.com',
  apiKey: 'your-api-key'
});

// Add a domain
await client.domains.create({
  domain: 'example.com',
  hostname: 'mail.example.com',
  mailboxes: 100
});

// Send an email
await client.emails.send('mailbox-id', {
  to: ['user@example.com'],
  subject: 'Hello World',
  body: 'This is a test email'
});

// List calendars
const calendars = await client.calendar.listCalendars('mailbox-id');

// Get contacts
const contacts = await client.contacts.list('mailbox-id');

Domain Management

The SDK provides comprehensive domain management including automatic DNS setup via Domain Connect.

Adding a Domain

const domain = await client.domains.create({
  domain: 'example.com',
  hostname: 'mail.example.com',
  mailboxes: 100,
  mailboxQuota: 5242880,  // 5GB per mailbox
  quota: 524288000,        // 500GB total
  rateLimit: 12
});

Domain Connect (Automatic DNS Setup)

Check if a domain supports Domain Connect for automatic DNS configuration:

const support = await client.domains.checkDomainConnectSupport('example.com');

if (support.supported) {
  // Generate setup URL
  const { apply_url } = await client.domains.generateDomainConnectURL('example.com');

  // Redirect user to DNS provider
  window.location.href = apply_url;
}

Verify DNS Configuration

const dns = await client.domains.verifyDNS('example.com');

const isReady = dns.SPF.valid &&
                dns.DKIM.valid &&
                dns.DMARC.valid &&
                dns.MX.valid &&
                dns.A.valid;

console.log('Domain ready:', isReady);

List Domains

const result = await client.domains.list({
  page: 1,
  page_size: 20,
  keyword: 'example'  // Optional filter
});

console.log(`Found ${result.total} domains`);

Complete Integration Example

See examples/domain-management.ts for a complete Huly integration flow including:

  • Adding domains
  • Automatic setup via Domain Connect
  • DNS verification polling
  • Fallback to manual setup

Complete Huly Integration Example

// Add domain for a customer
await client.domains.create({
  domain: customerDomain,
  email: customerEmail
});

// Check Domain Connect and redirect
const support = await client.domains.checkDomainConnectSupport(customerDomain);
if (support.supported) {
  const { apply_url } = await client.domains.generateDomainConnectURL(customerDomain);
  // Store in Huly task and send to customer
  await huly.tasks.create({
    title: `Configure DNS for ${customerDomain}`,
    metadata: { setupURL: apply_url }
  });
}

// Poll for verification
const dns = await client.domains.verifyDNS(customerDomain);
if (dns.SPF.valid && dns.DKIM.valid) {
  await huly.notifications.send('Domain is ready!');
}

Email Operations

Send Email

const email = await client.emails.send('mailbox-id', {
  to: ['recipient@example.com'],
  cc: ['cc@example.com'],
  bcc: ['bcc@example.com'],
  subject: 'Important Update',
  body: '<h1>Hello!</h1><p>This is an HTML email.</p>',
  attachments: [
    {
      filename: 'document.pdf',
      content: base64Content,
      contentType: 'application/pdf'
    }
  ]
});

List Emails

const result = await client.emails.list('mailbox-id', {
  page: 1,
  page_size: 50,
  folder: 'INBOX',
  unread: true
});

console.log(`Total emails: ${result.total}`);
result.items.forEach(email => {
  console.log(`${email.subject} from ${email.from}`);
});

Search Emails

const emails = await client.emails.search('mailbox-id', 'important project', {
  from: 'boss@company.com',
  after: '2025-01-01',
  has_attachment: true
});

Get Email Details

const email = await client.emails.get('email-id');
console.log(email.subject);
console.log(email.body);
console.log(email.attachments);

Delete Email

await client.emails.delete('email-id');

Calendar Management

The SDK provides full CalDAV support for calendar operations.

List Calendars

const calendars = await client.calendar.listCalendars('mailbox-id');
calendars.forEach(cal => {
  console.log(`${cal.name}: ${cal.description}`);
});

Create Calendar

const calendar = await client.calendar.createCalendar('mailbox-id', {
  name: 'Work Calendar',
  description: 'My work events',
  color: '#FF5733'
});

List Events

const events = await client.calendar.listEvents('calendar-id', {
  start_date: '2025-08-01',
  end_date: '2025-08-31'
});

events.forEach(event => {
  console.log(`${event.summary} at ${event.start}`);
});

Create Event

const event = await client.calendar.createEvent('calendar-id', {
  summary: 'Team Meeting',
  description: 'Weekly sync-up',
  start: '2025-08-19T10:00:00Z',
  end: '2025-08-19T11:00:00Z',
  location: 'Conference Room A',
  attendees: ['alice@example.com', 'bob@example.com']
});

Update Event

const updated = await client.calendar.updateEvent('event-id', {
  summary: 'Updated Meeting Title',
  start: '2025-08-19T11:00:00Z',
  end: '2025-08-19T12:00:00Z'
});

Delete Event

await client.calendar.deleteEvent('event-id');

Configure Webhook

const webhook = await client.calendar.configureWebhook('calendar-id', {
  url: 'https://yourapp.com/webhooks/calendar',
  events: ['created', 'updated', 'deleted'],
  secret: 'webhook-secret'
});

Contacts Management

The SDK provides full CardDAV support for contact operations.

List Contacts

const result = await client.contacts.list('mailbox-id', {
  page: 1,
  page_size: 100,
  search: 'john'
});

result.items.forEach(contact => {
  console.log(`${contact.name} - ${contact.email}`);
});

Create Contact

const contact = await client.contacts.create('mailbox-id', {
  name: 'John Doe',
  email: 'john@example.com',
  phone: '+1234567890',
  company: 'Acme Corp',
  title: 'Senior Engineer',
  address: {
    street: '123 Main St',
    city: 'San Francisco',
    state: 'CA',
    zip: '94102',
    country: 'USA'
  },
  notes: 'Met at conference 2025'
});

Update Contact

const updated = await client.contacts.update('contact-id', {
  phone: '+1987654321',
  title: 'Lead Engineer'
});

Delete Contact

await client.contacts.delete('contact-id');

Import Contacts

const file = new File([vCardContent], 'contacts.vcf', { type: 'text/vcard' });
const result = await client.contacts.import('mailbox-id', file);

console.log(`Imported ${result.imported} contacts`);
console.log(`Skipped ${result.skipped} duplicates`);

Export Contacts

// Export as vCard
const vcard = await client.contacts.export('mailbox-id', 'vcard');

// Export as CSV
const csv = await client.contacts.export('mailbox-id', 'csv');

// Download the file
const url = URL.createObjectURL(csv);
const a = document.createElement('a');
a.href = url;
a.download = 'contacts.csv';
a.click();

Delta Sync

Microsoft Graph-compatible delta sync for efficient synchronization.

Start Delta Sync

const cursor = await client.sync.startDelta('mailbox-id', [
  'emails',
  'contacts',
  'events'
]);

console.log(`Initial cursor: ${cursor.token}`);

Get Changes

const delta = await client.sync.getChanges(cursor.token, 100);

delta.changes.forEach(change => {
  console.log(`${change.type}: ${change.entity_type} ${change.entity_id}`);
  // change.type: 'created' | 'updated' | 'deleted'
  // change.data: Full entity data (for created/updated)
});

// Save next cursor for subsequent syncs
const nextCursor = delta.nextCursor;

Get Changes Since Date

const delta = await client.sync.getChangesSince(
  'mailbox-id',
  ['emails'],
  new Date('2025-08-01'),
  50
);

Stream Changes (Continuous Sync)

const cursor = await client.sync.startDelta('mailbox-id', ['emails']);

// Continuously poll for changes
for await (const changes of client.sync.streamChanges(cursor.token, 5000)) {
  console.log(`Received ${changes.length} changes`);

  changes.forEach(change => {
    if (change.type === 'created') {
      console.log('New email:', change.data);
    } else if (change.type === 'updated') {
      console.log('Updated email:', change.data);
    } else if (change.type === 'deleted') {
      console.log('Deleted email:', change.entity_id);
    }
  });
}

Service Accounts

Manage API keys and service accounts for programmatic access.

List Service Accounts

const accounts = await client.serviceAccounts.list();

Create Service Account

const account = await client.serviceAccounts.create({
  name: 'Integration Service',
  description: 'API access for integration',
  scopes: ['email:read', 'email:write', 'contacts:read']
});

Generate API Key

const apiKey = await client.serviceAccounts.generateAPIKey(account.id, {
  name: 'Production Key',
  expires_at: '2026-12-31',
  scopes: ['email:read', 'email:write']
});

// IMPORTANT: Save the key securely - it won't be shown again
console.log(`API Key: ${apiKey.key}`);

List API Keys

const keys = await client.serviceAccounts.listAPIKeys(account.id);
keys.forEach(key => {
  console.log(`${key.name} - Created: ${key.created_at}, Expires: ${key.expires_at}`);
});

Revoke API Key

await client.serviceAccounts.revokeAPIKey(account.id, key.id);

Shared Mailboxes

Create and manage shared mailboxes for team collaboration.

List Shared Mailboxes

const mailboxes = await client.sharedMailboxes.list();

Create Shared Mailbox

const mailbox = await client.sharedMailboxes.create({
  email: 'support@example.com',
  name: 'Customer Support',
  description: 'Team inbox for customer support'
});

Grant Access

await client.sharedMailboxes.grantAccess(
  mailbox.id,
  'service-account-id',
  'write'  // 'read' | 'write' | 'admin'
);

Revoke Access

await client.sharedMailboxes.revokeAccess(mailbox.id, 'service-account-id');

Error Handling

The SDK provides typed error classes for proper error handling.

import {
  BlackLeafMailClient,
  AuthenticationError,
  ValidationError,
  RateLimitError,
  NotFoundError,
  ServerError
} from '@blackleafmail/api';

try {
  await client.emails.send('mailbox-id', emailData);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Authentication failed:', error.message);
    // Refresh token or re-authenticate
  } else if (error instanceof ValidationError) {
    console.error('Invalid data:', error.errors);
    // Fix validation errors
  } else if (error instanceof RateLimitError) {
    console.error('Rate limit exceeded, retry after:', error.retryAfter);
    // Wait and retry
  } else if (error instanceof NotFoundError) {
    console.error('Resource not found:', error.message);
  } else if (error instanceof ServerError) {
    console.error('Server error:', error.message);
    // Retry with backoff
  } else {
    console.error('Unexpected error:', error);
  }
}

Huly Integration

Complete integration pattern for managing customer domains through Huly.

Add Domain with Automated Setup

async function addCustomerDomain(customerDomain: string, customerEmail: string) {
  // 1. Create domain in BlackLeaf Mail
  const domain = await client.domains.create({
    domain: customerDomain,
    hostname: `mail.${customerDomain}`,
    email: customerEmail,
    mailboxes: 10
  });

  // 2. Check if Domain Connect is supported
  const support = await client.domains.checkDomainConnectSupport(customerDomain);

  if (support.supported) {
    // 3. Generate automatic setup URL
    const { apply_url } = await client.domains.generateDomainConnectURL(customerDomain);

    // 4. Create Huly task with setup link
    await huly.tasks.create({
      title: `Configure DNS for ${customerDomain}`,
      description: `Automatic DNS setup available`,
      assignee: customerEmail,
      metadata: {
        domain: customerDomain,
        setupURL: apply_url,
        provider: support.provider
      },
      actions: [
        {
          label: 'Configure DNS',
          url: apply_url
        }
      ]
    });

    return { method: 'automatic', setupURL: apply_url };
  } else {
    // 5. Fallback to manual setup
    const dns = await client.domains.verifyDNS(customerDomain);

    // 6. Create Huly task with manual instructions
    await huly.tasks.create({
      title: `Configure DNS for ${customerDomain}`,
      description: 'Manual DNS configuration required',
      assignee: customerEmail,
      metadata: {
        domain: customerDomain,
        records: dns
      },
      instructions: generateManualInstructions(dns)
    });

    return { method: 'manual', records: dns };
  }
}

Poll for DNS Verification

async function waitForDNSVerification(domain: string, maxAttempts = 30) {
  for (let i = 0; i < maxAttempts; i++) {
    const dns = await client.domains.verifyDNS(domain);

    const isReady = dns.SPF.valid &&
                    dns.DKIM.valid &&
                    dns.DMARC.valid &&
                    dns.MX.valid &&
                    dns.A.valid;

    if (isReady) {
      // Notify via Huly
      await huly.notifications.send({
        title: 'Domain Ready!',
        message: `${domain} is fully configured and ready to send emails`,
        priority: 'high'
      });

      return { verified: true, dns };
    }

    // Update Huly task with progress
    await huly.tasks.update(taskId, {
      status: 'in_progress',
      metadata: {
        verification_progress: {
          SPF: dns.SPF.valid,
          DKIM: dns.DKIM.valid,
          DMARC: dns.DMARC.valid,
          MX: dns.MX.valid,
          A: dns.A.valid
        }
      }
    });

    // Wait 30 seconds before next check
    await new Promise(resolve => setTimeout(resolve, 30000));
  }

  return { verified: false };
}

Development

Setup

# Clone the repository
git clone https://github.com/aaPanel/BillionMail.git
cd BillionMail/sdk/typescript

# Install dependencies
npm install
# or
pnpm install

Build

# Build the SDK
npm run build

# Build in watch mode
npm run build:watch

Code Quality

# Run linter
npm run lint

# Fix linting issues
npm run lint:fix

# Format code
npm run format

Testing

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

Integration Tests

# Set up test environment
cp .env.example .env.test
# Edit .env.test with test credentials

# Run integration tests
npm run test:integration

Example Test

import { BlackLeafMailClient } from '../src';

describe('BlackLeaf Mail Client', () => {
  let client: BlackLeafMailClient;

  beforeAll(() => {
    client = new BlackLeafMailClient({
      baseURL: process.env.TEST_BASE_URL!,
      apiKey: process.env.TEST_API_KEY!
    });
  });

  it('should send an email', async () => {
    const email = await client.emails.send('test-mailbox-id', {
      to: ['test@example.com'],
      subject: 'Test Email',
      body: 'This is a test'
    });

    expect(email.id).toBeDefined();
    expect(email.subject).toBe('Test Email');
  });

  it('should list domains', async () => {
    const result = await client.domains.list({ page: 1, page_size: 10 });

    expect(result.items).toBeInstanceOf(Array);
    expect(result.total).toBeGreaterThanOrEqual(0);
  });
});

API Reference

Client Configuration

Option Type Required Default Description
baseURL string Yes - Base URL of the BlackLeaf Mail API
apiKey string No* - API key for authentication
accessToken string No* - OAuth access token for authentication
timeout number No 30000 Request timeout in milliseconds
maxRetries number No 3 Maximum retry attempts for failed requests
retryDelay number No 1000 Delay between retries in milliseconds

*Either apiKey or accessToken is required

Resource Methods

Domains

  • list(params?) - List domains
  • create(domain) - Create a domain
  • get(domainId) - Get domain details
  • update(domainId, updates) - Update domain
  • delete(domainId) - Delete domain
  • checkDomainConnectSupport(domain) - Check Domain Connect support
  • generateDomainConnectURL(domain) - Generate Domain Connect setup URL
  • verifyDNS(domain) - Verify DNS configuration

Emails

  • list(mailboxId, params?) - List emails
  • get(emailId) - Get email details
  • send(mailboxId, email) - Send an email
  • delete(emailId) - Delete email
  • search(mailboxId, query, params?) - Search emails

Calendar

  • listCalendars(mailboxId) - List calendars
  • createCalendar(mailboxId, calendar) - Create calendar
  • listEvents(calendarId, params?) - List events
  • createEvent(calendarId, event) - Create event
  • updateEvent(eventId, event) - Update event
  • deleteEvent(eventId) - Delete event
  • configureWebhook(calendarId, webhook) - Configure webhook

Contacts

  • list(mailboxId, params?) - List contacts
  • get(contactId) - Get contact details
  • create(mailboxId, contact) - Create contact
  • update(contactId, contact) - Update contact
  • delete(contactId) - Delete contact
  • import(mailboxId, file) - Import contacts
  • export(mailboxId, format) - Export contacts

Sync

  • startDelta(mailboxId, entityTypes) - Start delta sync
  • getChanges(cursor, limit?) - Get changes
  • getChangesSince(mailboxId, entityTypes, since, limit?) - Get changes since date
  • streamChanges(cursor, pollInterval?) - Stream changes continuously

Service Accounts

  • list() - List service accounts
  • create(account) - Create service account
  • generateAPIKey(accountId, keyConfig) - Generate API key
  • listAPIKeys(accountId) - List API keys
  • revokeAPIKey(accountId, keyId) - Revoke API key

Shared Mailboxes

  • list() - List shared mailboxes
  • create(mailbox) - Create shared mailbox
  • grantAccess(mailboxId, serviceAccountId, permission) - Grant access
  • revokeAccess(mailboxId, serviceAccountId) - Revoke access

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Pull Request Process

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure tests pass (npm test)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

License

MIT License - see the LICENSE file for details.

Support

Changelog

See CHANGELOG.md for version history and release notes.

Dependencies

Dependencies

ID Version
axios ^1.6.0

Development dependencies

ID Version
@types/jest ^29.0.0
@types/node ^20.0.0
jest ^29.0.0
ts-jest ^29.0.0
typescript ^5.0.0

Keywords

email calendar contacts caldav carddav
Details
npm
2025-11-23 04:33:12 +00:00
174
BlackLeaf Mail
MIT
latest
37 KiB
Assets (1)
api-1.0.0.tgz 37 KiB
Versions (1) View all
1.0.0 2025-11-23