SDK JavaScript
O SDK JavaScript oficial fornece uma forma conveniente de interagir com a API do Brevo a partir das suas aplicações Node.js e ambientes de browser.
Instalação
# Usando npmnpm install @brevo/brevo-js
# Usando yarnyarn add @brevo/brevo-js
# Usando pnpmpnpm add @brevo/brevo-jsInício Rápido
Inicializar o SDK
import { ApiClient, TransactionalEmailsApi, ContactsApi, EventsApi } from '@brevo/brevo-js';
// Configurar cliente de APIconst defaultClient = ApiClient.instance;const apiKey = defaultClient.authentications['api-key'];apiKey.apiKey = 'your-brevo-api-key';
// Inicializar instâncias de APIconst emailApi = new TransactionalEmailsApi();const contactsApi = new ContactsApi();const eventsApi = new EventsApi();Configuração do Ambiente
// ficheiro .envBREVO_API_KEY=xkeysib-your-api-key-hereBREVO_API_URL=https://api.brevo.com/v3TAJO_WEBHOOK_SECRET=your-webhook-secret
// config.jsexport const brevoConfig = { apiKey: process.env.BREVO_API_KEY, apiUrl: process.env.BREVO_API_URL || 'https://api.brevo.com/v3', webhookSecret: process.env.TAJO_WEBHOOK_SECRET};Funcionalidades Principais
1. Gestão de Clientes
class TajoCustomerService { constructor() { this.contactsApi = new ContactsApi(); }
// Criar novo cliente de fidelidade async createCustomer(customerData) { const createContact = { email: customerData.email, attributes: { FIRSTNAME: customerData.firstName, LASTNAME: customerData.lastName, PHONE: customerData.phone, LOYALTY_ID: customerData.loyaltyId, LOYALTY_POINTS: customerData.points || 0, LOYALTY_TIER: customerData.tier || 'Bronze', SIGNUP_DATE: new Date().toISOString(), TOTAL_SPENT: customerData.totalSpent || 0, PREFERRED_CATEGORIES: customerData.categories || [], BIRTHDAY: customerData.birthday, MARKETING_CONSENT: customerData.marketingConsent || true }, listIds: [this.getListForTier(customerData.tier || 'Bronze')], updateEnabled: true };
try { const response = await this.contactsApi.createContact(createContact); console.log('Cliente criado no Brevo:', response.id); return response; } catch (error) { console.error('Erro ao criar cliente:', error); throw error; } }
// Atualizar dados de fidelidade do cliente async updateCustomer(email, updates) { const updateContact = { attributes: updates, listIds: updates.LOYALTY_TIER ? [this.getListForTier(updates.LOYALTY_TIER)] : undefined };
try { await this.contactsApi.updateContact(email, updateContact); console.log('Cliente atualizado:', email); } catch (error) { console.error('Erro ao atualizar cliente:', error); throw error; } }
// Obter cliente por e-mail async getCustomer(email) { try { const response = await this.contactsApi.getContactInfo(email); return response; } catch (error) { if (error.status === 404) { return null; // Cliente não encontrado } throw error; } }
// Método auxiliar para obter ID da lista por nível getListForTier(tier) { const tierLists = { 'Bronze': 1, 'Silver': 2, 'Gold': 3, 'Platinum': 4 }; return tierLists[tier] || 1; }}2. E-mails Transacionais
class TajoEmailService { constructor() { this.emailApi = new TransactionalEmailsApi(); }
// Enviar notificação de pontos ganhos async sendPointsEarnedEmail(customerEmail, orderData) { const sendSmtpEmail = { sender: { name: "Tajo Loyalty", }, to: [{ email: customerEmail, name: orderData.customerName }], subject: `Ganhou ${orderData.pointsEarned} pontos de fidelidade!`, htmlContent: this.generatePointsEmailHTML(orderData), textContent: this.generatePointsEmailText(orderData), params: { customerName: orderData.customerName, pointsEarned: orderData.pointsEarned, orderNumber: orderData.orderNumber, totalPoints: orderData.totalPoints }, tags: ['loyalty', 'points-earned'] };
try { const response = await this.emailApi.sendTransacEmail(sendSmtpEmail); return response; } catch (error) { console.error('Erro ao enviar e-mail de pontos:', error); throw error; } }
// Enviar notificação de upgrade de nível async sendTierUpgradeEmail(customerEmail, upgradeData) { const sendSmtpEmail = { templateId: this.getTierUpgradeTemplateId(upgradeData.newTier), to: [{ email: customerEmail }], params: { customerName: upgradeData.customerName, newTier: upgradeData.newTier, previousTier: upgradeData.previousTier, benefits: upgradeData.benefits, pointsBalance: upgradeData.pointsBalance }, tags: ['loyalty', 'tier-upgrade', upgradeData.newTier.toLowerCase()] };
return await this.emailApi.sendTransacEmail(sendSmtpEmail); }
// Gerar conteúdo HTML para e-mail de pontos generatePointsEmailHTML(orderData) { return ` <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;"> <h1 style="color: #2c5aa0;">🎉 Boa notícia, ${orderData.customerName}!</h1> <p>Ganhou <strong>${orderData.pointsEarned} pontos</strong> na sua compra recente!</p>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;"> <h3>Detalhes do Pedido:</h3> <p><strong>Pedido #:</strong> ${orderData.orderNumber}</p> <p><strong>Pontos Ganhos:</strong> ${orderData.pointsEarned}</p> <p><strong>Total de Pontos:</strong> ${orderData.totalPoints}</p> </div>
<p style="margin-top: 30px;"> <a href="https://yourdomain.com/loyalty/rewards" style="background: #2c5aa0; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px;"> Ver Recompensas </a> </p> </div> `; }
generatePointsEmailText(orderData) { return ` Boa notícia, ${orderData.customerName}!
Ganhou ${orderData.pointsEarned} pontos na sua compra recente!
Detalhes do Pedido: - Pedido #: ${orderData.orderNumber} - Pontos Ganhos: ${orderData.pointsEarned} - Total de Pontos: ${orderData.totalPoints}
Veja as suas recompensas em: https://yourdomain.com/loyalty/rewards `; }}3. Rastreamento de Eventos
class TajoEventTracker { constructor() { this.eventsApi = new EventsApi(); }
// Rastrear eventos de fidelidade async trackLoyaltyEvent(customerEmail, eventType, properties = {}) { const createEvent = { email: customerEmail, event: eventType, properties: { timestamp: new Date().toISOString(), platform: 'tajo', ...properties } };
try { const response = await this.eventsApi.createEvent(createEvent); return response; } catch (error) { console.error(`Erro ao rastrear evento ${eventType}:`, error); throw error; } }
// Métodos específicos de rastreamento de eventos async trackPurchase(customerEmail, purchaseData) { return this.trackLoyaltyEvent(customerEmail, 'Purchase Completed', { order_id: purchaseData.orderId, order_total: purchaseData.total, currency: purchaseData.currency, items_count: purchaseData.itemsCount, points_earned: purchaseData.pointsEarned, loyalty_tier: purchaseData.customerTier, categories: purchaseData.categories }); }
async trackPointsRedemption(customerEmail, redemptionData) { return this.trackLoyaltyEvent(customerEmail, 'Points Redeemed', { points_used: redemptionData.pointsUsed, reward_type: redemptionData.rewardType, reward_value: redemptionData.rewardValue, remaining_points: redemptionData.remainingPoints, redemption_method: redemptionData.method }); }
async trackTierUpgrade(customerEmail, upgradeData) { return this.trackLoyaltyEvent(customerEmail, 'Tier Upgraded', { previous_tier: upgradeData.previousTier, new_tier: upgradeData.newTier, points_required: upgradeData.pointsRequired, benefits_unlocked: upgradeData.benefitsUnlocked }); }
async trackReferral(customerEmail, referralData) { return this.trackLoyaltyEvent(customerEmail, 'Referral Made', { referral_method: referralData.method, referee_email: referralData.refereeEmail, referral_bonus: referralData.bonus, referral_code: referralData.code }); }}Utilização Avançada
1. Operações em Massa
class TajoBulkService { constructor() { this.contactsApi = new ContactsApi(); this.batchSize = 50; // Tamanho de lote recomendado }
// Importação de clientes em massa async importCustomers(customers) { const batches = this.createBatches(customers, this.batchSize); const results = [];
for (const batch of batches) { try { const contacts = batch.map(customer => ({ email: customer.email, attributes: { FIRSTNAME: customer.firstName, LASTNAME: customer.lastName, LOYALTY_POINTS: customer.points, LOYALTY_TIER: customer.tier, TOTAL_SPENT: customer.totalSpent } }));
const response = await this.contactsApi.importContacts({ contacts, listIds: [1], // Lista padrão updateEnabled: true });
results.push(response); } catch (error) { console.error('Erro de importação em lote:', error); // Continuar com outros lotes } }
return results; }
createBatches(array, batchSize) { const batches = []; for (let i = 0; i < array.length; i += batchSize) { batches.push(array.slice(i, i + batchSize)); } return batches; }}2. Tratamento de Erros e Lógica de Retry
class TajoApiClient { constructor() { this.maxRetries = 3; this.retryDelay = 1000; // 1 segundo }
async apiCallWithRetry(apiCall, retries = this.maxRetries) { try { return await apiCall(); } catch (error) { if (retries > 0 && this.isRetryableError(error)) { console.log(`A tentar novamente chamada de API. Tentativas restantes: ${retries - 1}`); await this.delay(this.retryDelay); return this.apiCallWithRetry(apiCall, retries - 1); } throw error; } }
isRetryableError(error) { // Tentar novamente em limites de taxa, erros de servidor e problemas de rede return error.status >= 500 || error.status === 429 || error.code === 'NETWORK_ERROR'; }
delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }}3. Gestão de Webhooks
import express from 'express';import crypto from 'crypto';
const app = express();
// Endpoint de webhook para eventos Brevoapp.post('/webhooks/brevo', express.raw({type: 'application/json'}), (req, res) => { const signature = req.headers['x-brevo-signature']; const payload = req.body;
// Verificar assinatura do webhook if (!verifyWebhookSignature(payload, signature)) { return res.status(401).json({ error: 'Assinatura inválida' }); }
const event = JSON.parse(payload);
switch (event.event) { case 'delivered': handleEmailDelivered(event); break; case 'opened': handleEmailOpened(event); break; case 'clicked': handleEmailClicked(event); break; case 'bounced': handleEmailBounced(event); break; default: console.log('Tipo de evento não tratado:', event.event); }
res.status(200).json({ success: true });});
function verifyWebhookSignature(payload, signature) { const expectedSignature = crypto .createHmac('sha256', process.env.TAJO_WEBHOOK_SECRET) .update(payload) .digest('hex');
return crypto.timingSafeEqual( Buffer.from(signature, 'hex'), Buffer.from(expectedSignature, 'hex') );}
async function handleEmailOpened(event) { // Atualizar score de envolvimento do cliente const customerEmail = event.email; const campaignType = event.tags?.includes('loyalty') ? 'loyalty' : 'general';
// Rastrear envolvimento no seu sistema await updateCustomerEngagement(customerEmail, 'email_opened', { campaign_type: campaignType, subject: event.subject, timestamp: event.ts });}Testes
Testes Unitários
import { jest } from '@jest/globals';import { TajoCustomerService } from '../src/customer-service.js';
describe('TajoCustomerService', () => { let customerService; let mockContactsApi;
beforeEach(() => { mockContactsApi = { createContact: jest.fn(), updateContact: jest.fn(), getContactInfo: jest.fn() }; customerService = new TajoCustomerService(); customerService.contactsApi = mockContactsApi; });
test('deve criar cliente com sucesso', async () => { const customerData = { firstName: 'João', lastName: 'Silva', loyaltyId: 'LYL-001', tier: 'Bronze' };
mockContactsApi.createContact.mockResolvedValue({ id: 123 });
const result = await customerService.createCustomer(customerData);
expect(mockContactsApi.createContact).toHaveBeenCalledWith({ attributes: expect.objectContaining({ FIRSTNAME: 'João', LASTNAME: 'Silva', LOYALTY_ID: 'LYL-001', LOYALTY_TIER: 'Bronze' }), listIds: [1], updateEnabled: true });
expect(result.id).toBe(123); });});Testes de Integração
describe('Testes de Integração Brevo', () => { let customerService;
beforeAll(() => { customerService = new TajoCustomerService(); });
test('deve sincronizar cliente para o Brevo', async () => { const testCustomer = { email: `test-${Date.now()}@example.com`, firstName: 'Teste', lastName: 'Utilizador', loyaltyId: `LYL-${Date.now()}`, tier: 'Bronze', points: 100 };
// Criar cliente const createResult = await customerService.createCustomer(testCustomer); expect(createResult.id).toBeDefined();
// Verificar se o cliente existe const retrievedCustomer = await customerService.getCustomer(testCustomer.email); expect(retrievedCustomer.email).toBe(testCustomer.email); expect(retrievedCustomer.attributes.LOYALTY_POINTS).toBe(100);
// Limpar await customerService.deleteCustomer(testCustomer.email); });});Otimização de Desempenho
Cache
import Redis from 'ioredis';
class TajoCachedService extends TajoCustomerService { constructor() { super(); this.redis = new Redis(process.env.REDIS_URL); this.cacheTTL = 300; // 5 minutos }
async getCustomer(email) { const cacheKey = `customer:${email}`;
// Tentar cache primeiro const cached = await this.redis.get(cacheKey); if (cached) { return JSON.parse(cached); }
// Obter da API const customer = await super.getCustomer(email);
// Fazer cache do resultado if (customer) { await this.redis.setex(cacheKey, this.cacheTTL, JSON.stringify(customer)); }
return customer; }}Limitação de Taxa
import { RateLimiter } from 'limiter';
class TajoRateLimitedService extends TajoCustomerService { constructor() { super(); // Limites da API Brevo: 300 chamadas por minuto this.limiter = new RateLimiter({ tokensPerInterval: 300, interval: 'minute' }); }
async makeApiCall(apiCall) { await this.limiter.removeTokens(1); return apiCall(); }
async createCustomer(customerData) { return this.makeApiCall(() => super.createCustomer(customerData)); }}Próximos Passos
- Guia de Integração da Plataforma, Guia de configuração completo
- Configuração de Webhook, Configurar eventos em tempo real
- Python SDK, SDK para linguagem alternativa
- Referência de API, Documentação completa da API