Cum să trimiți contacte Excel în Brevo cu un macro VBA (și alternativa Office Scripts)
Un macro VBA funcțional care postează contacte dintr-o foaie Excel către API-ul Brevo cu un singur clic, plus când să folosești Office Scripts + Power Automate, și compromisurile față de o configurare cu Google Apps Script.
Ai contacte în Excel și le vrei în Brevo. Răspunsul rapid și murdar este să salvezi fișierul ca .csv și să-l imporți, ceea ce e în regulă o singură dată. Pentru orice faci în mod repetat (predarea săptămânală a vânzărilor, un registru de lucru pe care echipa ta îl actualizează zilnic, o listă de parteneri care se reîmprospătează) vrei un buton chiar în Excel care face sincronizarea.
Acest ghid acoperă cele două căi care au sens cu adevărat pentru asta:
- Un macro VBA încorporat în registrul de lucru, fără licențiere, fără cloud, funcționează offline, rulează în momentul în care un utilizator apasă un buton. Răspunsul corect pentru ~80% din cazurile “Excel-la-Brevo”.
- Office Scripts + Power Automate, TypeScript în loc de VBA, rulează în cloud, susține declanșatoare programate. Răspunsul corect dacă registrul de lucru stă în OneDrive/SharePoint și vrei sincronizare nesupravegheată, dar fii conștient de licențierea Power Automate.
Dacă cauți echivalentul Google Sheets, vezi articolul însoțitor despre Apps Script. Și dacă vrei doar un import CSV unic dintr-un script pe laptopul tău, ghidul de import CSV are versiuni Python, Node.js și cURL.
Ce face macro-ul
Când utilizatorul apasă butonul “Sync to Brevo” pe foaie:
- Citește fiecare rând din foaia de lucru activă (rândul antet primul, un contact pe rând).
- Construiește un array JSON formatat pentru parametrul
jsonBodyal Brevo. - Postează pe
https://api.brevo.com/v3/contacts/importcu cheia API stocată a registrului de lucru. - Afișează un mesaj cu rezultatul.
Asta e tot. ~120 de linii de VBA. Mai jos este modulul complet, funcțional.
Aspectul foii pe care îl așteaptă macro-ul
| firstName | lastName | company | city | |
|---|---|---|---|---|
| [email protected] | Jane | Doe | Acme | Berlin |
| [email protected] | John | Smith | Globex | Paris |
email este obligatoriu. Fiecare altă coloană devine un atribut de contact Brevo, mapat după antetul coloanei (cu majuscule) la numele atributului. Așa firstName → FIRSTNAME, company → COMPANY. Atributele personalizate (orice dincolo de setul standard) trebuie să existe mai întâi în contul tău Brevo, definește-le sub Contacte → Setări → Atribute de contact.
Pasul 1: Deschide editorul VBA
În Excel: apasă Alt + F11. Editorul VBA se deschide. În panoul Project din stânga, click dreapta pe registrul de lucru și alege Insert → Module. Apare un Module1 gol.
Pasul 2: Lipește macro-ul complet
Înlocuiește conținutul Module1 cu acesta:
' ===========================================================================' Brevo contact sync for Excel' Reads the active sheet's rows and POSTs them to Brevo's import API.' ===========================================================================Option Explicit
Private Const BREVO_API_BASE As String = "https://api.brevo.com/v3"Private Const BREVO_LIST_ID As Long = 42 ' <- your Brevo list IDPrivate Const BATCH_SIZE As Long = 1000
' --- Public entry points (the ones you assign to ribbon buttons) -----------
Public Sub SyncSheetToBrevo() Dim apiKey As String apiKey = GetApiKey() If apiKey = "" Then MsgBox "No API key configured. Run ConfigureApiKey first.", _ vbExclamation, "Brevo Sync" Exit Sub End If
Dim ws As Worksheet Set ws = ActiveSheet
Dim emailCol As Long emailCol = FindEmailColumn(ws) If emailCol = 0 Then MsgBox "Sheet must have an 'email' column in row 1.", _ vbExclamation, "Brevo Sync" Exit Sub End If
Dim lastRow As Long, lastCol As Long lastRow = ws.Cells(ws.Rows.Count, emailCol).End(xlUp).Row lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column If lastRow < 2 Then MsgBox "No contact rows found.", vbInformation, "Brevo Sync" Exit Sub End If
Dim contacts As Collection Set contacts = New Collection
Dim r As Long, c As Long For r = 2 To lastRow Dim email As String email = LCase(Trim(CStr(ws.Cells(r, emailCol).Value))) If email <> "" And InStr(email, "@") > 0 Then Dim json As String json = "{""email"":""" & EscapeJson(email) & """,""attributes"":{"
Dim attrFirst As Boolean attrFirst = True For c = 1 To lastCol If c <> emailCol Then Dim val As String val = CStr(ws.Cells(r, c).Value) If val <> "" Then If Not attrFirst Then json = json & "," Dim attrName As String attrName = UCase(Trim(CStr(ws.Cells(1, c).Value))) json = json & """" & attrName & """:""" & EscapeJson(val) & """" attrFirst = False End If End If Next c
json = json & "}}" contacts.Add json End If Next r
If contacts.Count = 0 Then MsgBox "No valid contact rows found.", vbInformation, "Brevo Sync" Exit Sub End If
Dim totalSent As Long Dim batchNum As Long Dim okCount As Long, failCount As Long Dim batchStart As Long
For batchStart = 1 To contacts.Count Step BATCH_SIZE batchNum = batchNum + 1 Dim batchEnd As Long batchEnd = batchStart + BATCH_SIZE - 1 If batchEnd > contacts.Count Then batchEnd = contacts.Count
Dim payload As String payload = "{""jsonBody"":[" Dim i As Long For i = batchStart To batchEnd If i > batchStart Then payload = payload & "," payload = payload & contacts(i) Next i payload = payload & "],""listIds"":[" & BREVO_LIST_ID & _ "],""updateExistingContacts"":true,""emptyContactsAttributes"":false}"
Dim ok As Boolean ok = PostToBrevo(apiKey, payload) If ok Then okCount = okCount + 1 totalSent = totalSent + (batchEnd - batchStart + 1) Else failCount = failCount + 1 End If Next batchStart
MsgBox "Sent " & totalSent & " contact(s) in " & batchNum & " batch(es)." _ & vbCrLf & "Successful batches: " & okCount _ & vbCrLf & "Failed batches: " & failCount, _ vbInformation, "Brevo Sync"End Sub
Public Sub ConfigureApiKey() Dim key As String key = InputBox("Paste your Brevo API key (xkeysib-...):", "Brevo API Key") If key = "" Then Exit Sub key = Trim(key) If Left(key, 8) <> "xkeysib-" Then MsgBox "That doesn't look like a Brevo API key (should start with xkeysib-).", _ vbExclamation, "Brevo API Key" Exit Sub End If
On Error Resume Next ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Delete On Error GoTo 0
ThisWorkbook.CustomDocumentProperties.Add _ Name:="BrevoApiKey", _ LinkToContent:=False, _ Type:=msoPropertyTypeString, _ Value:=key
ThisWorkbook.Save MsgBox "API key saved inside the workbook.", vbInformation, "Brevo API Key"End Sub
' --- Private helpers --------------------------------------------------------
Private Function GetApiKey() As String On Error Resume Next GetApiKey = ThisWorkbook.CustomDocumentProperties("BrevoApiKey").Value On Error GoTo 0End Function
Private Function FindEmailColumn(ws As Worksheet) As Long Dim lastCol As Long, c As Long lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column For c = 1 To lastCol If LCase(Trim(CStr(ws.Cells(1, c).Value))) = "email" Then FindEmailColumn = c Exit Function End If Next c FindEmailColumn = 0End Function
Private Function PostToBrevo(apiKey As String, payload As String) As Boolean Dim http As Object Set http = CreateObject("MSXML2.XMLHTTP") http.Open "POST", BREVO_API_BASE & "/contacts/import", False http.SetRequestHeader "api-key", apiKey http.SetRequestHeader "Content-Type", "application/json" http.SetRequestHeader "Accept", "application/json" http.Send payload PostToBrevo = (http.Status = 202) If Not PostToBrevo Then Debug.Print "Brevo error " & http.Status & ": " & http.responseText End IfEnd Function
Private Function EscapeJson(s As String) As String Dim r As String r = Replace(s, "\", "\\") r = Replace(r, """", "\""") r = Replace(r, vbCrLf, "\n") r = Replace(r, vbLf, "\n") r = Replace(r, vbCr, "\n") r = Replace(r, vbTab, "\t") EscapeJson = rEnd FunctionPasul 3: Salvează registrul de lucru ca .xlsm
Macro-urile VBA persistă doar în registre de lucru cu macro-uri activate. Save As → alege Excel Macro-Enabled Workbook (.xlsm). Formatul .xlsx simplu elimină macro-urile în tăcere, mulți oameni își pierd codul așa prima dată.
Pasul 4: Configurează cheia API
Rulează ConfigureApiKey o dată. Fie:
- În editorul VBA, apasă oriunde în interiorul sub-rutinei
ConfigureApiKeyși apasăF5, sau - În Excel, Developer → Macros, alege
ConfigureApiKey, Run.
Lipește cheia ta xkeysib-.... Macro-ul o stochează ca o proprietate de document personalizată în interiorul registrului de lucru, nu este în codul sursă, nu este în registry și călătorește cu fișierul (deci fii conștient: dacă trimiți .xlsm prin e-mail cuiva, cheia API merge cu el).
Dacă preferi să pui cheia undeva în afara registrului de lucru, schimbă stocarea în registry-ul Windows:
' Replace the body of ConfigureApiKey with:SaveSetting "Brevo", "Sync", "ApiKey", key
' And GetApiKey with:GetApiKey = GetSetting("Brevo", "Sync", "ApiKey", "")SaveSetting/GetSetting scrie sub HKCU\Software\VB and VBA Program Settings\Brevo\Sync, per-utilizator, nu per-registru de lucru. Folosește asta dacă mai multe registre de lucru ar trebui să împartă o cheie sau dacă nu vrei cheia în fișier.
Pasul 5: Adaugă un buton pe foaie
Asta este ce transformă lucrul într-o experiență cu un singur clic pentru utilizatorii non-tehnici.
Insert → Shapes → Rectangle, plasează unul pe foaie, etichetează-l “Sync to Brevo.” Click dreapta pe formă → Assign Macro → alege SyncSheetToBrevo. Gata.
Sau, pentru o interfață mai șlefuită, adaugă o filă ribbon personalizată prin Office Custom UI Editor, dar pentru majoritatea instrumentelor interne, forma-ca-buton este suficientă.
Pasul 6: Rulează-l
Apasă butonul. Macro-ul citește rândurile, le grupează, postează fiecare grup la Brevo și afișează un mesaj rezumativ. Importul Brevo este asincron, deci mesajul de succes înseamnă “Brevo a acceptat grupul”, crearea efectivă a contactelor se întâmplă pe partea de server în următoarele câteva secunde. Vei primi un rezumat prin e-mail de la Brevo când se termină (cu excepția cazului în care setezi disableNotification: true).
Capcane comune
Butonul nu face nimic și nu există eroare. Macro-urile sunt dezactivate. Uită-te la bara galbenă de securitate din partea de sus a foii, apasă Enable Content. Dacă organizația ta blochează macro-urile, vezi calea trust-center / semnare cod mai jos.
Compile error: User-defined type not defined. Ești pe Mac Excel, care nu are MSXML2.XMLHTTP. Mac VBA nu poate face cereri HTTPS direct; folosește calea Office Scripts mai jos în schimb.
400 Bad Request de la Brevo fără cauză evidentă. Aproape întotdeauna una dintre: (a) un atribut personalizat din foaia ta nu există încă în Brevo, creează-l mai întâi; (b) bug de escape JSON, ghilimele sau backslash-uri în valorile celulelor care nu au fost escape-uite. Funcția EscapeJson din cod se ocupă de cazurile standard; dacă datele tale au caractere ciudate, înregistrează payload în fereastra Immediate (Debug.Print payload) și inspectează.
401 Unauthorized. Header greșit. Este api-key (litere mici, cu cratimă), nu Authorization. Macro-ul îl folosește pe cel corect, dar dacă ai copiat un fragment de altundeva, verifică din nou.
Excel îngheață la importuri mari. Macro-ul rulează sincron pe firul UI. Pentru 50.000+ de rânduri, vei urmări Excel-ul atârnând 10-30 de secunde în timp ce construiește JSON-ul și așteaptă Brevo. Fie acceptă, fie comută MSXML2.XMLHTTP la varianta sa asincronă, dar la acea scară ești mai bine în Power Automate (secțiunea următoare).
Când VBA nu este suficient: Office Scripts + Power Automate
VBA nu poate face sincronizare cloud programată. Dacă ai nevoie:
- Registrul de lucru să se sincronizeze cu Brevo în fiecare oră fără ca cineva să-l deschidă
- Registrul de lucru în OneDrive/SharePoint, editat din web
- Un departament IT care interzice macro-urile desktop
…atunci vrei Office Scripts (echivalentul Microsoft cloud al Apps Script) plus Power Automate (stratul lor de programare și HTTP).
Împărțirea: Office Scripts citește foaia și returnează datele de contact. Power Automate ia acele date și le postează la Brevo pe un declanșator.
Office Script-ul (Excel pentru web → Automate → New Script):
function main(workbook: ExcelScript.Workbook): {email: string, attributes: Record<string, string>}[] { const sheet = workbook.getActiveWorksheet(); const range = sheet.getUsedRange(); if (!range) return [];
const values = range.getValues() as string[][]; if (values.length < 2) return [];
const headers = values[0].map(h => String(h).trim()); const emailIdx = headers.findIndex(h => h.toLowerCase() === "email"); if (emailIdx === -1) throw new Error("Sheet must have an 'email' column");
const contacts: {email: string, attributes: Record<string, string>}[] = []; for (let r = 1; r < values.length; r++) { const row = values[r]; const email = String(row[emailIdx] ?? "").trim().toLowerCase(); if (!email || !email.includes("@")) continue;
const attributes: Record<string, string> = {}; for (let c = 0; c < headers.length; c++) { if (c === emailIdx) continue; const v = row[c]; if (v === null || v === "") continue; attributes[headers[c].toUpperCase()] = String(v); } contacts.push({ email, attributes }); } return contacts;}Fluxul Power Automate:
- Declanșator: Recurrence (la fiecare 1 oră), sau buton manual, sau “When a row is modified” dacă vrei sincronizare bazată pe schimbare.
- Acțiune: Excel Online → Run script, îndreaptă-l către registrul de lucru și scriptul de mai sus. Salvează valoarea sa de retur ca
contacts. - Acțiune: HTTP (acesta este conectorul Premium, vezi nota de licențiere de mai jos).
- Metodă:
POST - URI:
https://api.brevo.com/v3/contacts/import - Headere:
api-key: xkeysib-...,Content-Type: application/json - Corp:
{"jsonBody": @{outputs('Run_script')?['body/result']},"listIds": [42],"updateExistingContacts": true}
- Metodă:
- Acțiune: Condition → dacă codul de stare ≠ 202, trimite o alertă Teams/email.
Verificare a realității licențierii: acțiunea HTTP este un conector Power Automate Premium. Pe planurile Microsoft 365 Business Basic/Standard primești conectorii standard, dar nu Premium. Cea mai ieftină soluție este addon-ul Power Automate Premium (~15 USD/utilizator/lună la momentul scrierii), sau mută HTTP-ul către o mică Azure Function pe care fluxul standard o poate apela. Dacă ești deja pe E3/E5 cu Premium inclus, ești gata.
Acesta este motivul principal pentru care povestea Apps Script este mai curată: UrlFetchApp din Apps Script este gratuit și nerestricționat, în timp ce echivalentul Microsoft pune apelul de rețea în spatele unui nivel de conector plătit.
VBA vs Office Scripts vs Apps Script, când să alegi ce
| Necesitate | Cea mai bună opțiune |
|---|---|
| Buton cu un singur clic într-un registru de lucru pe care echipa ta îl deschide deja zilnic | Macro VBA (acest ghid, jumătatea de sus) |
| Registru de lucru în OneDrive/SharePoint, sincronizare auto orară | Office Scripts + Power Automate (necesită Premium pentru HTTP) |
| Doar Mac Excel, nu se poate folosi VBA | Office Scripts + Power Automate |
| Datele stau în Google Sheets, nu în Excel | Apps Script (gratuit, declanșatoare programate încorporate) |
| Import unic, nu va mai fi nevoie | Save As → CSV și folosește scriptul de import CSV |
| Import în masă dintr-un fișier >10 MB | CSV cu fileUrl, vezi ghidul CSV |
De ce asta bate Zapier / platformele no-code
Pentru un job recurent Excel-la-Brevo, instrumentele de automatizare terțe (Zapier, Make, n8n) percep taxe per-task și pun o terță parte între datele tale și Brevo. Abordarea VBA are zero costuri continue, niciun flux de date prin terță parte și trăiește în interiorul fișierului, când registrul de lucru se mută, integrarea se mută cu el. Office Scripts + Power Automate este similar, dar cu Microsoft ca terță parte (deja în stiva ta dacă ești pe M365).
Întreaga idee a endpoint-ului Brevo POST /v3/contacts/import este că nu ai nevoie de o platformă lipici, instrumentele tale știu deja să facă cereri HTTP.