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.

Featured image for article: Cum să trimiți contacte Excel în Brevo cu un macro VBA (și alternativa Office Scripts)

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:

  1. 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”.
  2. 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:

  1. Citește fiecare rând din foaia de lucru activă (rândul antet primul, un contact pe rând).
  2. Construiește un array JSON formatat pentru parametrul jsonBody al Brevo.
  3. Postează pe https://api.brevo.com/v3/contacts/import cu cheia API stocată a registrului de lucru.
  4. 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

emailfirstNamelastNamecompanycity
[email protected]JaneDoeAcmeBerlin
[email protected]JohnSmithGlobexParis

email este obligatoriu. Fiecare altă coloană devine un atribut de contact Brevo, mapat după antetul coloanei (cu majuscule) la numele atributului. Așa firstNameFIRSTNAME, companyCOMPANY. 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 ID
Private 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 0
End 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 = 0
End 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 If
End 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 = r
End Function

Pasul 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:

  1. Declanșator: Recurrence (la fiecare 1 oră), sau buton manual, sau “When a row is modified” dacă vrei sincronizare bazată pe schimbare.
  2. 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.
  3. 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
      }
  4. 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

NecesitateCea mai bună opțiune
Buton cu un singur clic într-un registru de lucru pe care echipa ta îl deschide deja zilnicMacro 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 VBAOffice Scripts + Power Automate
Datele stau în Google Sheets, nu în ExcelApps Script (gratuit, declanșatoare programate încorporate)
Import unic, nu va mai fi nevoieSave As → CSV și folosește scriptul de import CSV
Import în masă dintr-un fișier >10 MBCSV 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.

Lectură suplimentară

Frequently Asked Questions

Poate un macro VBA să apeleze cu adevărat API-ul Brevo din interiorul Excel?
Da. VBA poate face cereri HTTP prin MSXML2.XMLHTTP, care vine cu fiecare instalare modernă de Windows. Macro-ul postează JSON pe api.brevo.com/v3/contacts/import, același endpoint pe care l-ar lovi un script Python sau Node. Mac Excel o poate face și el prin Office Scripts + Power Automate, dar VBA pe desktop Mac nu poate (fără MSXML).
Pot rula asta pe un program ca declanșatoarele de timp Apps Script?
Nu doar din VBA, Excel trebuie să fie deschis pentru ca Application.OnTime să se declanșeze. Pentru sincronizare programată nesupravegheată ai două opțiuni: (1) Windows Task Scheduler care deschide registrul de lucru și rulează macro-ul, sau (2) Office Scripts declanșat de un flux Power Automate programat (acoperit mai târziu în acest ghid).
Este VBA sigur? Compania mea blochează macro-urile.
Macro-urile VBA rulează cu acces la sistemul de fișiere și registry, deci blocarea lor în mod implicit este o politică sensibilă. Două căi în jurul ei: (1) semnează macro-ul cu un certificat de semnare a codului și pune registrul de lucru într-o Trusted Location, sau (2) sari peste VBA cu totul și folosește Office Scripts (TypeScript, sandboxed, fără acces la sistemul de fișiere). Office Scripts este calea modernă recomandată de Microsoft.
Cum se compară asta cu abordarea Google Apps Script?
Apps Script este mai aproape de set-and-forget, rulează în cloud-ul Google, declanșatoare programate încorporate, fără licență Excel/Power Automate. VBA câștigă pentru lucrul offline și sincronizarea manuală cu un singur clic dintr-un registru de lucru pe care echipa ta îl are deja deschis. Office Scripts + Power Automate este echivalentul cloud/programat pe partea Microsoft, dar Power Automate are nevoie adesea de un conector premium plătit pentru HTTP de ieșire.
Începe gratuit cu Brevo