Uvoz kontakata iz Google Sheetsa u Brevo pomoću Apps Scripta

Ponovljiv primer Google Apps Scripta koji proverava saglasnost, mapira polja, čuva odjave i prikazuje API greške.

google apps script brevo

Koji je kratak odgovor?

Dodajte kolone email, firstName, lastName i consent u list. Podesite BREVO_API_KEY u svojstvima skripte i ID liste u kodu, pa pokrenite syncSheetToBrevo. Skripta obrađuje samo redove sa saglasnošću, proverava odjave u Brevu, uklanja duplikate i staje kod API greške. To je Apps Script koji sami održavate, a ne ugrađeni konektor.

Saznajte više
Na ovoj stranici

List sa dvanaest prijava za događaj lako je jednom izvesti. Pri drugom uvozu jedna adresa se ponavlja, a druga osoba se u međuvremenu odjavila u Brevu. Ponovljiv uvoz mora da prepozna oba slučaja.

Skripta koristi Brevo krajnje tačke za čitanje, pravljenje i ažuriranje kontakata. Namenjena je malim zakazanim uvozima, ne masovnom učitavanju ili ulozi ugrađenog konektora za Google Sheets. Ne šalje e-poštu. Za dokaz saglasnosti i rad skripte odgovorni ste vi.

Pripremite list i listu

Napravite karticu Contacts sa tačno ovim zaglavljima. Za consent koristite polja za potvrdu i stavite TRUE samo kad postoji saglasnost za nameravani marketing.

emailfirstNamelastNameconsent
[email protected]JaneDoeTRUE
[email protected]AlexLeeFALSE

Napravite ili izaberite Brevo listu, pa njen brojčani ID upišite u LIST_ID. Proverite postoje li atributi FNAME i LNAME u Brevu. Samo ta polja se mapiraju. Dokaz o vremenu i načinu davanja saglasnosti čuvajte odvojeno ako list to ne pokazuje. Ćelija TRUE nije dokaz.

U Proširenja → Apps Script → Podešavanja projekta → Svojstva skripte dodajte BREVO_API_KEY. Svojstva dele korisnici skripte, pa ograničite pravo uređivanja. Ključ ne upisujte u ćeliju ni u kod. Pročitajte Googleova pravila pristupa.

Skripta

Zamenite Code.gs ovim primerom i podesite LIST_ID pre prvog pokretanja.

const LIST_ID = 42; // Replace with your existing Brevo list ID.
const TAB = 'Contacts';
const BASE = 'https://api.brevo.com/v3';
function syncSheetToBrevo() {
const key = PropertiesService.getScriptProperties().getProperty('BREVO_API_KEY');
if (!key) throw new Error('Set BREVO_API_KEY in Script Properties first');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(TAB);
if (!sheet) throw new Error('Contacts tab missing');
const values = sheet.getDataRange().getValues();
const headers = values.shift().map(h => String(h).trim());
const columns = ['email', 'firstName', 'lastName', 'consent']
.map(name => headers.indexOf(name));
if (columns.includes(-1)) throw new Error('Required header missing');
const [emailCol, firstCol, lastCol, consentCol] = columns;
const seen = new Set();
const result = { written: 0, skipped: 0 };
for (const row of values) {
const email = String(row[emailCol] || '').trim().toLowerCase();
if (row[consentCol] !== true || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || seen.has(email)) {
result.skipped++;
continue;
}
seen.add(email);
const path = '/contacts/' + encodeURIComponent(email);
const current = brevo_(key, path, 'get');
if (current.code !== 200 && current.code !== 404) {
throw new Error(`Contact read failed (${current.code}) for row ${result.written + result.skipped + 2}`);
}
if (current.code === 200) {
const contact = JSON.parse(current.text);
if (contact.emailBlacklisted || (contact.listUnsubscribed || []).includes(LIST_ID)) {
result.skipped++;
continue;
}
}
const attributes = {};
if (row[firstCol] !== '') attributes.FNAME = String(row[firstCol]);
if (row[lastCol] !== '') attributes.LNAME = String(row[lastCol]);
const body = { attributes, listIds: [LIST_ID] };
const saved = current.code === 404
? brevo_(key, '/contacts', 'post', { email, ...body })
: brevo_(key, path, 'put', body);
if (![201, 204].includes(saved.code)) {
throw new Error(`Contact write failed (${saved.code}) for ${email}: ${saved.text.slice(0, 200)}`);
}
result.written++;
}
Logger.log(JSON.stringify(result));
return result;
}
function brevo_(key, path, method, body) {
for (let attempt = 0; attempt < 3; attempt++) {
const response = UrlFetchApp.fetch(BASE + path, {
method,
contentType: 'application/json',
headers: { 'api-key': key },
payload: body ? JSON.stringify(body) : undefined,
muteHttpExceptions: true,
});
const code = response.getResponseCode();
if (code !== 429) return { code, text: response.getContentText() };
if (attempt === 2) return { code, text: response.getContentText() };
const reset = Object.entries(response.getHeaders()).find(
([name]) => name.toLowerCase() === 'x-sib-ratelimit-reset'
);
const raw = Number(reset && reset[1]);
const seconds = Number.isFinite(raw) && raw > 0 ? raw : 2 ** attempt;
const waitSeconds = seconds + 2 ** attempt;
if (waitSeconds > 60) return { code, text: response.getContentText() };
Utilities.sleep(waitSeconds * 1000);
}
}

Sačuvajte, izaberite syncSheetToBrevo i kliknite Pokreni. Google traži pristup Sheetsu i spoljnim zahtevima. U Izvršavanjima pogledajte završni zapis {written, skipped}. Odgovor 404 pri čitanju vodi pravljenju kontakta, 200 ažuriranju. Druge greške zaustavljaju rad. Uspešan upis potvrđuje zahtev za kontakt, a ne slanje kampanje.

Skripta uklanja ponovljene adrese u jednom pokretanju i ponovo proverava Brevo u sledećem. Nikad ne postavlja emailBlacklisted na false i ne menja e-mail. Ako Brevo prijavi blokadu ili odjavu sa te liste, red se preskače i uz TRUE u listu. Ne koristite skriptu da poništite odjavu. Brevo upozorava da promena adrese blokiranog kontakta može skinuti blokadu.

Proverite pokretanje pre zakazivanja

Koristite probnu listu i kontakte koje nadzirete. Pokrenite sa jednim redom TRUE, jednim FALSE i ponovljenom adresom: očekujte jedan upis i dva preskočena reda. Odjavite probni kontakt u Brevu i ponovite: za njega ne sme biti upisa. Za test greške privremeno postavite nevažeći LIST_ID, proverite HTTP grešku u Izvršavanjima, vratite ID i pokušajte opet. Ne testirajte na pravim podacima kupaca. Kod je lokalno proveravan i sa simuliranim Brevo odgovorima, bez stvarnog upisa.

Za raspored otvorite Apps Script → Okidači → Dodaj okidač, izaberite syncSheetToBrevo i vremenski interval. Okidači rade pod nalogom osobe koja ih je napravila; taj nalog mora da zadrži pristup listu. Za mali list krenite jednom dnevno i pratite greške. Google ograničava trajanje i URL Fetch pozive; velike izvore podelite na ograničene zadatke.

Pri 429 skripta čita x-sib-ratelimit-reset i ponavlja najviše dvaput. Ako potrebno čekanje premašuje 60 sekundi, vraća 429 za kasnije pokretanje. Druge greške zaustavljaju rad; sledeći put kreće od početka. Kontakt može da se odjavi između čitanja i upisa. Primer koristite samo ako je taj kratki prozor prihvatljiv. Jača garancija traži usklađivanje događaja, pouzdan izvor saglasnosti i poslednju proveru pre slanja.

Za veće listove asinhrona Brevo tačka za uvoz vraća processId. To potvrđuje prihvatanje zadatka, ne završetak. Proverite rezultat pre nego što objavite uspeh. Put po pojedinačnim kontaktima pokazuje grešku na odgovarajućem redu.

Često Postavljana Pitanja

Da li je potreban ugrađeni konektor za Google Sheets?

Ne. Primer radi u Google Apps Script projektu vezanom za list i poziva Brevo API za kontakte. Skriptu, API ključ i zakazani okidač održavate sami.

Koji redovi se šalju u Brevo?

Samo oni sa TRUE u consent i ispravnom e-mail adresom. Skripta uklanja duplikate pri svakom pokretanju i proverava Brevo pre upisa.

Da li novo pokretanje ponovo prijavljuje odjavljen kontakt?

Skripta preskače kontakte koje Brevo označi kao emailBlacklisted ili odjavljene sa ciljne liste. Nikada ne šalje emailBlacklisted: false i ne menja adresu. Pre produkcije proverite i druge izvore saglasnosti.

Kako ponoviti neuspešno pokretanje?

Pročitajte grešku u izvršavanjima Apps Scripta, otklonite uzrok i ponovo pokrenite istu funkciju. Upisi koriste istu adresu i ID liste, pa već uspešan red ne pravi drugi kontakt.

Zatražite rani pristup

Unesite ime i poslovnu adresu e-pošte. Poslaćemo vam vezu za potvrdu zahteva za pristup Taju.

Preuzmite Brevo