Skip to main content

Introduction

This guide covers security best practices, performance optimization, and integration guidelines to help you build robust applications with the Lovi API.

🔒 Security Best Practices

API Key Management

✅ DO:
  • Store API keys in environment variables or secure configuration management
  • Use different API keys for different environments (development, staging, production)
  • Rotate API keys regularly (recommended: every 90 days)
  • Monitor API key usage and set up alerts for unusual activity
  • Use the principle of least privilege (separate keys for different functions if available)
❌ DON’T:
  • Hardcode API keys in source code or commit them to version control
  • Share API keys via email, chat, or other unsecured channels
  • Use production API keys in development environments
  • Log complete API keys in application logs

Authentication Security

// ✅ Good - Store in environment variables
const ACCESS_KEY = process.env.LOVI_ACCESS_KEY;

// ❌ Bad - Hardcoded in source
const ACCESS_KEY = 'your-actual-api-key-here';

Request Security

Always use HTTPS:
// ✅ Good - HTTPS only
const BASE_URL = 'https://cloud.lovi.ai';

// ❌ Bad - Insecure HTTP
const BASE_URL = 'http://cloud.lovi.ai';
Validate input data:
function validatePhoneNumber(number) {
  // Remove any non-digit characters
  const cleaned = number.replace(/\D/g, '');

  // Check if it's a valid international format
  if (!/^\d{10,15}$/.test(cleaned)) {
    throw new Error('Invalid phone number format');
  }

  return cleaned;
}

⚡ Performance Optimization

Connection Management

Reuse HTTP connections:
// ✅ Good - Reuse connection pool
const https = require('https');
const agent = new https.Agent({
  keepAlive: true,
  maxSockets: 10
});

const fetch = require('node-fetch');

async function makeRequest(url, data) {
  return fetch(url, {
    method: 'POST',
    agent: agent,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  });
}

Batch Processing

Process notifications in batches:
async function sendNotificationsBatch(notifications, batchSize = 10) {
  const results = [];

  for (let i = 0; i < notifications.length; i += batchSize) {
    const batch = notifications.slice(i, i + batchSize);
    const batchPromises = batch.map(notification =>
      sendNotification(notification)
    );

    const batchResults = await Promise.allSettled(batchPromises);
    results.push(...batchResults);

    // Rate limiting: wait between batches
    if (i + batchSize < notifications.length) {
      await sleep(100); // 100ms delay
    }
  }

  return results;
}

Caching Strategies

Cache template information:
class TemplateCache {
  constructor(ttl = 3600000) { // 1 hour TTL
    this.cache = new Map();
    this.ttl = ttl;
  }

  async getTemplate(templateName) {
    const cached = this.cache.get(templateName);

    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }

    const template = await fetchTemplateFromAPI(templateName);
    this.cache.set(templateName, {
      data: template,
      timestamp: Date.now()
    });

    return template;
  }
}

📊 Rate Limiting & Throttling

Understanding Rate Limits

OperationLimitWindow
Authentication10 requests1 minute
Notifications100 requests1 minute
Template retrieval50 requests1 minute
Template creation5 requests1 hour

Rate Limit Handling

class RateLimitHandler {
  constructor() {
    this.requestQueue = [];
    this.processing = false;
  }

  async makeRequest(url, data) {
    return new Promise((resolve, reject) => {
      this.requestQueue.push({ url, data, resolve, reject });
      this.processQueue();
    });
  }

  async processQueue() {
    if (this.processing || this.requestQueue.length === 0) return;

    this.processing = true;

    while (this.requestQueue.length > 0) {
      const request = this.requestQueue.shift();

      try {
        const response = await this.executeRequest(request);
        request.resolve(response);
      } catch (error) {
        if (error.status === 429) {
          // Rate limited - put request back and wait
          this.requestQueue.unshift(request);
          await sleep(error.retryAfter * 1000);
          continue;
        }
        request.reject(error);
      }

      // Small delay between requests
      await sleep(100);
    }

    this.processing = false;
  }
}

🏗️ Architecture Patterns

Repository Pattern

class NotificationRepository {
  constructor(apiClient) {
    this.apiClient = apiClient;
  }

  async sendWhatsAppNotification(notification) {
    const payload = this.formatWhatsAppPayload(notification);
    return await this.apiClient.post('/notify', payload);
  }

  async sendVoiceNotification(notification) {
    const payload = this.formatVoicePayload(notification);
    return await this.apiClient.post('/notify/voice', payload);
  }

  formatWhatsAppPayload(notification) {
    return {
      contact: {
        number: this.validatePhoneNumber(notification.phoneNumber),
        name: notification.contactName
      },
      language_template: notification.language,
      name_template: notification.templateName,
      recipient_id: notification.phoneNumber,
      notification_type: notification.type,
      campaign_name: notification.campaign
    };
  }
}

Factory Pattern for Notifications

class NotificationFactory {
  static create(type, data) {
    switch (type) {
      case 'whatsapp':
        return new WhatsAppNotification(data);
      case 'voice':
        return new VoiceNotification(data);
      default:
        throw new Error(`Unknown notification type: ${type}`);
    }
  }
}

class WhatsAppNotification {
  constructor(data) {
    this.contact = { number: data.phoneNumber, name: data.name };
    this.template = data.templateName;
    this.type = 'whatsapp';
  }

  toPayload() {
    return {
      contact: this.contact,
      name_template: this.template,
      // ... other properties
    };
  }
}

🧪 Testing Strategies

Unit Testing

// Mock API responses for testing
const mockAPIClient = {
  post: jest.fn()
};

describe('NotificationService', () => {
  test('should send WhatsApp notification successfully', async () => {
    mockAPIClient.post.mockResolvedValue({
      success: true,
      notification_id: 'test-123'
    });

    const service = new NotificationService(mockAPIClient);
    const result = await service.sendWhatsApp({
      phoneNumber: '34666033135',
      templateName: 'welcome',
      contactName: 'John Doe'
    });

    expect(result.success).toBe(true);
    expect(mockAPIClient.post).toHaveBeenCalledWith('/notify', expect.any(Object));
  });
});

Integration Testing

// Use test environment with real API calls
describe('Integration Tests', () => {
  const testApiKey = process.env.TEST_API_KEY;
  const apiClient = new LoviAPIClient(testApiKey);

  test('should handle authentication flow', async () => {
    const authResult = await apiClient.authenticate(
      process.env.TEST_EMAIL,
      process.env.TEST_PASSWORD
    );

    expect(authResult.access_token).toBeDefined();
    expect(authResult.token_type).toBe('bearer');
  });
});

🔍 Monitoring & Logging

Structured Logging

const logger = require('winston');

logger.configure({
  format: logger.format.combine(
    logger.format.timestamp(),
    logger.format.errors({ stack: true }),
    logger.format.json()
  ),
  transports: [
    new logger.transports.File({ filename: 'api-calls.log' })
  ]
});

async function loggedAPICall(endpoint, data) {
  const requestId = generateRequestId();

  logger.info('API request started', {
    request_id: requestId,
    endpoint: endpoint,
    method: 'POST',
    timestamp: new Date().toISOString()
  });

  try {
    const response = await makeAPICall(endpoint, data);

    logger.info('API request completed', {
      request_id: requestId,
      endpoint: endpoint,
      status_code: response.status,
      success: true,
      duration: Date.now() - startTime
    });

    return response;
  } catch (error) {
    logger.error('API request failed', {
      request_id: requestId,
      endpoint: endpoint,
      error: error.message,
      status_code: error.statusCode,
      duration: Date.now() - startTime
    });

    throw error;
  }
}

Health Checks

class APIHealthMonitor {
  constructor(apiClient) {
    this.apiClient = apiClient;
    this.healthStatus = { healthy: true, lastCheck: null };
  }

  async checkHealth() {
    try {
      const start = Date.now();
      await this.apiClient.get('/templates?limit=1');
      const duration = Date.now() - start;

      this.healthStatus = {
        healthy: true,
        lastCheck: new Date().toISOString(),
        responseTime: duration,
        status: 'operational'
      };
    } catch (error) {
      this.healthStatus = {
        healthy: false,
        lastCheck: new Date().toISOString(),
        error: error.message,
        status: 'degraded'
      };
    }

    return this.healthStatus;
  }
}

📱 Mobile & Frontend Integration

React Hook Example

import { useState, useEffect } from 'react';

function useLoviNotification() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const sendNotification = async (notificationData) => {
    setLoading(true);
    setError(null);

    try {
      const response = await fetch('/api/send-notification', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(notificationData)
      });

      if (!response.ok) {
        throw new Error('Failed to send notification');
      }

      const result = await response.json();
      return result;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  };

  return { sendNotification, loading, error };
}

🔧 Configuration Management

Environment Configuration

// config/lovi.js
module.exports = {
  development: {
    baseURL: 'https://cloud.lovi.ai',
    accessKey: process.env.LOVI_DEV_ACCESS_KEY,
    rateLimits: {
      requests: 10,
      window: 60000 // 1 minute
    },
    retries: 3,
    timeout: 30000
  },
  production: {
    baseURL: 'https://cloud.lovi.ai',
    accessKey: process.env.LOVI_PROD_ACCESS_KEY,
    rateLimits: {
      requests: 100,
      window: 60000
    },
    retries: 5,
    timeout: 60000
  }
};

Client Configuration

class LoviAPIClient {
  constructor(config) {
    this.baseURL = config.baseURL;
    this.accessKey = config.accessKey;
    this.timeout = config.timeout;
    this.rateLimiter = new RateLimiter(config.rateLimits);
    this.retryCount = config.retries;
  }

  async request(endpoint, options = {}) {
    await this.rateLimiter.acquire();

    const url = `${this.baseURL}${endpoint}?access_key=${this.accessKey}`;
    const config = {
      timeout: this.timeout,
      ...options
    };

    return this.retryRequest(url, config);
  }
}

📚 Documentation & Team Guidelines

Code Documentation

/**
 * Send a WhatsApp notification using a template
 *
 * @param {Object} notification - Notification data
 * @param {string} notification.phoneNumber - Recipient phone number (international format, no +)
 * @param {string} notification.templateName - Name of approved template
 * @param {string} notification.language - Language code (e.g., 'es_ES', 'en_US')
 * @param {Object} notification.variables - Template variables
 * @param {string} [notification.scheduledTime] - ISO 8601 datetime for scheduling
 * @param {string} [notification.timezone] - IANA timezone code
 *
 * @returns {Promise<Object>} API response with notification_id
 *
 * @throws {ValidationError} When required parameters are missing
 * @throws {APIError} When API request fails
 *
 * @example
 * const result = await sendWhatsAppNotification({
 *   phoneNumber: '34666033135',
 *   templateName: 'welcome_user',
 *   language: 'es_ES',
 *   variables: { name: 'Juan', course: 'JavaScript' }
 * });
 */
async function sendWhatsAppNotification(notification) {
  // Implementation...
}

Team Standards

API Integration Checklist:
  • ✅ Implement proper error handling
  • ✅ Add request/response logging
  • ✅ Include unit tests
  • ✅ Validate input parameters
  • ✅ Handle rate limiting
  • ✅ Use environment-specific configurations
  • ✅ Document all functions and classes
  • ✅ Implement health checks
  • ✅ Add monitoring and alerts

🚨 Common Pitfalls & Solutions

Pitfall: Hardcoded Values

// ❌ Bad
const templateName = 'welcome_template_final_v2';

// ✅ Good
const templateName = config.templates.welcome;

Pitfall: Poor Error Handling

// ❌ Bad
try {
  await sendNotification(data);
} catch (error) {
  console.log('Error:', error);
}

// ✅ Good
try {
  await sendNotification(data);
} catch (error) {
  logger.error('Notification failed', {
    error: error.message,
    data: sanitizeForLog(data),
    stack: error.stack
  });

  throw new NotificationError(
    'Failed to send notification',
    error.statusCode,
    error.requestId
  );
}

Pitfall: Ignoring Rate Limits

// ❌ Bad - Send all at once
notifications.forEach(notification => {
  sendNotification(notification);
});

// ✅ Good - Respect rate limits
await sendNotificationsBatch(notifications, 10);
Remember: Following these best practices will help you build more reliable, secure, and maintainable integrations with the Lovi API.