The official TrackPost Node.js SDK provides a type-safe, promise-based interface for sending emails and managing your account.

Table of Contents

Installation

npm

npm install trackpost

yarn

yarn add trackpost

pnpm

pnpm add trackpost

Quick Start

import { TrackPostClient } from 'trackpost';

const client = new TrackPostClient({
  apiKey: 'tp_live_your_api_key'
});

async function sendWelcomeEmail() {
  const response = await client.emails.send({
    to: '[email protected]',
    from: '[email protected]',
    subject: 'Welcome!',
    html: '<h1>Welcome to our app!</h1>'
  });
  
  console.log('Email sent:', response.id);
}

sendWelcomeEmail();

Configuration

Basic Configuration

const client = new TrackPostClient({
  apiKey: 'tp_live_your_api_key',
  baseURL: 'https://api.trackpost.de/v1', // optional
  timeout: 30000, // 30 seconds (optional)
  retries: 3 // number of retries on failure (optional)
});

Environment Variables

// .env
TRACKPOST_API_KEY=tp_live_your_api_key

// client.ts
const client = new TrackPostClient({
  apiKey: process.env.TRACKPOST_API_KEY!
});

Sending Emails

Basic Email

const email = await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Welcome!',
  html: '<h1>Welcome!</h1>',
  text: 'Welcome!' // optional, auto-generated if not provided
});

console.log(email.id); // msg_abc123
console.log(email.status); // 'sent'

Multiple Recipients

const email = await client.emails.send({
  to: ['[email protected]', '[email protected]'],
  from: '[email protected]',
  subject: 'Team Update',
  html: '<h1>Weekly Update</h1>'
});

CC and BCC

const email = await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  cc: ['[email protected]'],
  bcc: ['[email protected]'],
  subject: 'Important Update',
  html: '<h1>Update</h1>'
});

Reply-To

const email = await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  reply_to: '[email protected]',
  subject: 'Your Order',
  html: '<h1>Order Confirmation</h1>'
});

Attachments

import * as fs from 'fs';

const pdfBuffer = fs.readFileSync('./invoice.pdf');

const email = await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Your Invoice',
  html: '<p>Please find your invoice attached.</p>',
  attachments: [
    {
      filename: 'invoice.pdf',
      content: pdfBuffer.toString('base64'),
      contentType: 'application/pdf'
    }
  ]
});

Using Templates

Send with Template

const email = await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  template_id: 'welcome_email',
  variables: {
    user_name: 'John Doe',
    company: 'Acme Inc',
    activation_link: 'https://yourapp.com/activate?token=abc123'
  }
});

Template with Subject

Templates can include dynamic subjects:

const email = await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  template_id: 'order_confirmation',
  variables: {
    order_id: 'ORD-12345',
    total: '$99.99'
  }
});

Managing Templates

Create Template

const template = await client.templates.create({
  name: 'welcome_email',
  subject: 'Welcome to {{company}}, {{user_name}}!',
  html: `
    <h1>Welcome, {{user_name}}!</h1>
    <p>We're excited to have you at {{company}}.</p>
    <a href="{{activation_link}}">Activate your account</a>
  `,
  text: 'Welcome {{user_name}}! Activate your account: {{activation_link}}'
});

console.log(template.id); // tpl_abc123

List Templates

const templates = await client.templates.list();

templates.forEach(template => {
  console.log(`${template.id}: ${template.name}`);
});

Get Template

const template = await client.templates.get('tpl_abc123');
console.log(template.html);

Update Template

const template = await client.templates.update('tpl_abc123', {
  subject: 'Welcome to {{company}}!',
  html: '<h1>Welcome!</h1><p>New content here.</p>'
});

Delete Template

await client.templates.delete('tpl_abc123');

Render Template (Preview)

const preview = await client.templates.render('tpl_abc123', {
  user_name: 'Test User',
  company: 'Test Company',
  activation_link: 'https://example.com/activate'
});

console.log(preview.html);
console.log(preview.text);
console.log(preview.subject);

Managing Emails

List Emails

const emails = await client.emails.list({
  limit: 10,
  status: 'delivered',
  from_date: '2025-01-01',
  to_date: '2025-01-31'
});

Get Email

const email = await client.emails.get('msg_abc123');
console.log(email.status);
console.log(email.delivered_at);

Cancel Scheduled Email

await client.emails.cancel('msg_abc123');

Error Handling

Try-Catch Pattern

import { TrackPostError } from 'trackpost';

try {
  const email = await client.emails.send({
    to: '[email protected]',
    from: '[email protected]',
    subject: 'Hello',
    html: '<h1>Hi!</h1>'
  });
} catch (error) {
  if (error instanceof TrackPostError) {
    console.log('Status:', error.status); // 400, 401, 429, etc.
    console.log('Code:', error.code); // 'rate_limit_exceeded'
    console.log('Message:', error.message);
    console.log('Request ID:', error.requestId);
  }
}

Error Types

StatusCodeDescription
400validation_errorInvalid request parameters
401authentication_errorInvalid API key
403authorization_errorInsufficient permissions
404not_foundResource not found
429rate_limit_exceededRate limit hit
500internal_errorServer error

Retry Logic

The SDK automatically retries on network errors:

const client = new TrackPostClient({
  apiKey: 'tp_live_...',
  retries: 3,
  retryDelay: 1000 // ms between retries
});

TypeScript Support

Type Definitions

import { 
  Email, 
  Template, 
  SendEmailOptions,
  CreateTemplateOptions 
} from 'trackpost';

async function sendEmail(options: SendEmailOptions): Promise<Email> {
  return await client.emails.send(options);
}

Custom Types

interface User {
  email: string;
  name: string;
}

async function sendWelcomeEmail(user: User): Promise<void> {
  await client.emails.send({
    to: user.email,
    from: '[email protected]',
    template_id: 'welcome',
    variables: {
      user_name: user.name
    }
  });
}

Advanced Usage

Rate Limit Handling

import { TrackPostError } from 'trackpost';

async function sendWithRetry(emailData: SendEmailOptions, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await client.emails.send(emailData);
    } catch (error) {
      if (error instanceof TrackPostError && error.status === 429) {
        // Rate limit hit, wait and retry
        const delay = Math.pow(2, i) * 1000; // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}

Batch Sending

async function sendBatchEmails(users: User[]) {
  const batchSize = 10;
  
  for (let i = 0; i < users.length; i += batchSize) {
    const batch = users.slice(i, i + batchSize);
    
    await Promise.all(
      batch.map(user => 
        client.emails.send({
          to: user.email,
          from: '[email protected]',
          template_id: 'welcome',
          variables: { user_name: user.name }
        }).catch(error => {
          console.error(`Failed to send to ${user.email}:`, error);
        })
      )
    );
    
    // Rate limit protection
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
}

Webhook Verification

import { verifyWebhookSignature } from 'trackpost';
import * as crypto from 'crypto';

app.post('/webhooks/trackpost', (req, res) => {
  const signature = req.headers['x-trackpost-signature'];
  const payload = req.body;
  
  const isValid = verifyWebhookSignature(
    payload,
    signature,
    process.env.WEBHOOK_SECRET!
  );
  
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook
  console.log('Event:', payload.type);
  res.status(200).send('OK');
});

Common Patterns

User Registration Flow

app.post('/register', async (req, res) => {
  try {
    const user = await createUser(req.body);
    
    // Send welcome email
    await client.emails.send({
      to: user.email,
      from: '[email protected]',
      template_id: 'welcome_email',
      variables: {
        user_name: user.name,
        activation_link: generateActivationLink(user)
      }
    });
    
    res.json({ success: true, user });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Password Reset

app.post('/forgot-password', async (req, res) => {
  const user = await findUserByEmail(req.body.email);
  
  if (user) {
    const token = generateResetToken(user);
    
    await client.emails.send({
      to: user.email,
      from: '[email protected]',
      template_id: 'password_reset',
      variables: {
        reset_link: `https://yourapp.com/reset?token=${token}`,
        expiry: '1 hour'
      }
    });
  }
  
  // Always return success to prevent email enumeration
  res.json({ success: true });
});

Testing

Using Test Keys

// Use test keys in development
const client = new TrackPostClient({
  apiKey: process.env.NODE_ENV === 'production' 
    ? process.env.TRACKPOST_LIVE_KEY! 
    : process.env.TRACKPOST_TEST_KEY!
});

Mocking for Tests

// test/mocks/trackpost.ts
export const mockTrackPostClient = {
  emails: {
    send: jest.fn().mockResolvedValue({
      id: 'msg_test123',
      status: 'sent'
    })
  }
};

// In your test
jest.mock('trackpost', () => ({
  TrackPostClient: jest.fn().mockImplementation(() => mockTrackPostClient)
}));

Migration from Other Services

From SendGrid

// SendGrid
await sgMail.send({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Hello',
  html: '<h1>Hi!</h1>'
});

// TrackPost
await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Hello',
  html: '<h1>Hi!</h1>'
});

From Nodemailer

// Nodemailer
await transporter.sendMail({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Hello',
  html: '<h1>Hi!</h1>'
});

// TrackPost
await client.emails.send({
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Hello',
  html: '<h1>Hi!</h1>'
});

Next Steps