Import kontaktů z Google Sheets do Brevo pomocí Apps Script

Opakovatelný příklad Google Apps Script: ověření souhlasu, mapování polí, ochrana odhlášení a viditelné chyby API.

google apps script brevo

Jaká je stručná odpověď?

Přidejte do listu sloupce email, firstName, lastName a consent. Nastavte BREVO_API_KEY ve vlastnostech skriptu a ID seznamu v kódu, pak spusťte syncSheetToBrevo. Skript bere jen řádky se souhlasem, kontroluje odhlášení v Brevo, odstraňuje duplicity a zastaví se při chybě API. Jde o Apps Script, který spravujete Vy, nikoli o nativní konektor.

Zjistit více
Na této stránce

List s dvanácti registracemi na akci snadno vyexportujete jednou. Při druhém importu se jedna adresa opakuje a další člověk se mezitím v Brevo odhlásil. Opakovatelný import musí rozpoznat obojí.

Skript používá endpointy Brevo pro čtení, vytvoření a aktualizaci kontaktů. Je určen pro malé plánované importy, ne pro hromadné načítání nebo jako nativní konektor Google Sheets. E-maily neposílá. Za důkaz souhlasu a provoz skriptu odpovídáte Vy.

Připravte list a seznam

Vytvořte kartu Contacts přesně s těmito záhlavími. Pro consent použijte zaškrtávací políčka a TRUE nastavte jen se souhlasem pro zamýšlený marketing.

emailfirstNamelastNameconsent
[email protected]JaneDoeTRUE
[email protected]AlexLeeFALSE

Vytvořte nebo vyberte seznam Brevo a jeho číselné ID zadejte do LIST_ID. Ověřte, že kontaktní atributy FNAME a LNAME v Brevo existují. Mapují se jen tato dvě pole. Záznam o čase a způsobu získání souhlasu uchovejte zvlášť, pokud jej list nedokáže doložit. Buňka TRUE sama o sobě důkazem není.

V Rozšíření → Apps Script → Nastavení projektu → Vlastnosti skriptu přidejte BREVO_API_KEY. Vlastnosti jsou sdílené mezi uživateli skriptu, proto omezte oprávnění k úpravám. Klíč nedávejte do buňky ani do zdrojového kódu. Viz pravidla přístupu Google.

Skript

Nahraďte Code.gs tímto příkladem a před prvním spuštěním nastavte LIST_ID.

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);
}
}

Uložte projekt, vyberte syncSheetToBrevo a klikněte na Spustit. Google požádá o přístup k Sheets a externím požadavkům. Ve Spuštěních najdete souhrn {written, skipped}. Odpověď 404 při čtení vede k vytvoření kontaktu, 200 k aktualizaci. Jiné chyby běh zastaví. Úspěšný zápis potvrzuje kontaktní požadavek, nikoli odeslání kampaně.

Skript odstraní duplicitní adresy v jednom běhu a v dalším se znovu zeptá Brevo. Nikdy nenastavuje emailBlacklisted na false ani nemění e-mail. Pokud Brevo ukazuje blokaci nebo odhlášení z daného seznamu, řádek přeskočí i při TRUE v listu. Nepoužívejte jej k vrácení odhlášení. Brevo upozorňuje, že změna adresy blokovaného kontaktu může blokaci zrušit.

Ověřte jeden běh před plánováním

Použijte testovací seznam a kontakty pod Vaší kontrolou. Spusťte skript s jedním řádkem TRUE, jedním FALSE a duplicitní adresou: očekávejte jeden zápis a dva přeskočené řádky. Testovací kontakt v Brevo odhlaste a spusťte znovu: nesmí dojít k zápisu. Pro test chyby nastavte dočasně neplatné LIST_ID, ověřte HTTP chybu ve Spuštěních, vraťte správné ID a opakujte. Netestujte na skutečných zákaznických datech. Kód byl také ověřen místně s napodobenými odpověďmi Brevo, bez živého zápisu.

Pro pravidelné běhy otevřete Apps Script → Spouštěče → Přidat spouštěč, vyberte syncSheetToBrevo a časový interval. Instalovatelné spouštěče běží pod účtem tvůrce; účet musí mít stále přístup k listu. U malého listu začněte denně a kontrolujte chyby. Google omezuje délku běhu a volání URL Fetch; větší zdroje rozdělte na omezené úlohy.

Při 429 skript čte x-sib-ratelimit-reset a opakuje nejvýše dvakrát. Pokud nutné čekání přesáhne 60 sekund, vrátí 429 pro pozdější běh. Jiné chyby běh zastaví; další začne od začátku. Kontakt se může odhlásit mezi čtením a zápisem. Příklad použijte jen tehdy, když je toto krátké okno přijatelné. Přísnější záruky vyžadují soulad událostí, autoritativní zdroj souhlasu a poslední kontrolu před odesláním.

Pro větší listy asynchronní importní endpoint Brevo vrací processId. Potvrzuje přijetí úlohy, ne dokončení. Před prohlášením úspěchu zkontrolujte výsledek. Tato cesta po jednotlivých kontaktech ukáže chybu u dotčeného řádku.

Často Kladené Otázky

Potřebujete nativní konektor Google Sheets?

Ne. Příklad běží v projektu Google Apps Script navázaném na list a volá kontaktní API Brevo. Skript, API klíč a naplánovaný spouštěč spravujete Vy.

Které řádky se odešlou do Brevo?

Pouze řádky s TRUE ve sloupci consent a platnou e-mailovou adresou. Skript v každém běhu odstraní duplicity a před zápisem zkontroluje Brevo.

Přihlásí další běh znovu odhlášený kontakt?

Skript přeskočí kontakty, které Brevo označuje jako emailBlacklisted nebo odhlášené z cílového seznamu. Nikdy neposílá emailBlacklisted: false ani nemění adresu. Před produkcí ověřte i další zdroje souhlasu.

Jak zopakovat neúspěšný běh?

Přečtěte chybu v záznamu spuštění Apps Script, opravte příčinu a spusťte stejnou funkci znovu. Zápisy používají stejnou adresu a ID seznamu, takže dříve úspěšný řádek nevytvoří druhý kontakt.

Požádejte o přednostní přístup

Zadejte své jméno a pracovní e-mailovou adresu. Pošleme vám odkaz pro potvrzení žádosti o přístup k Tajo.

Získat Brevo