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.