Google Workspaces - Přestaňte platit za centrální správu podpisů v Gmailu

Měli jsme správu e-mailových podpisů zdarma.

Byl to benefit od našeho tehdejšího distributora Google Workspace. Fungoval perfektně – všichni měli konzistentní, brandovaný podpis, nikdo nad tím nepřemýšlel.

Letos jsme přešli k jinému partnerovi – lepší podmínky, lepší podpora. Ale tento benefit jsme v balíčku nedostali.

Začal jsem se dívat, co stojí standalone SaaS alternativy:

Nástroj

Cena

Exclaimer

$0.90 – $1.75 / uživatel / měsíc (Starter až Pro, roční billing)

WiseStamp

od $1.90 / uživatel / měsíc

BulkSignature

od $1.45 / uživatel / měsíc

Při 25 lidech je to 600–1 200 Kč měsíčně za nástroj, jehož jedinou funkcí je nasadit HTML šablonu do Gmailu.

Řekl jsem si: to ne.

Otevřel jsem Claude, popsal co potřebuji – centrální správa podpisů přes Google Apps Script, service account, Domain-wide Delegation, automatický weekly trigger. Hodinu a půl ladění (hlavně jeden záludný OAuth detail, který většina návodů online vůbec nezmiňuje) – a bylo hotovo.

Tento článek je výsledek. Vše co potřebujete k tomu, abyste to zreplikovali.

Jak to funguje

Systém používá Google Apps Script se service accountem, který má Domain-wide Delegation (DWD). To umožňuje skriptu vystupovat jménem libovolného uživatele v doméně a nastavit mu podpis v Gmailu – bez individuálního přihlašování a bez jakéhokoliv zásahu ze strany zaměstnanců.

Apps Script → načte data uživatelů z Admin SDK (jméno, pozice, telefon, fotka) → přihlásí se jako každý uživatel přes service account + OAuth2 JWT → zavolá Gmail API a nastaví podpis → opakuje pro všechny aktivní uživatele → trigger se spouští automaticky každé pondělí v 8:00

Klíčový detail: Standardní Apps Script OAuth nemůže používat Domain-wide Delegation pro Gmail API. Musíte použít service account s JWT autentizací. Je to záměrné omezení ze strany Google a většina online návodů to vůbec nezmiňuje.

Co potřebujete

  • Google Workspace (jakýkoliv plán)

  • Přístup na Google Cloud Console (zdarma)

  • Role Super Admin v Google Workspace

  • Přibližně 30 minut na první nastavení

Část 1 – Google Cloud Console

1.1 Vytvoření projektu

  1. Jděte na console.cloud.google.com

  2. Klikněte na rozbalovací menu projektů vlevo nahoře → New Project

  3. Název: company-signatures → Create

1.2 Zapnutí API

  1. Levé menu → APIs & ServicesLibrary

  2. Vyhledejte Admin SDK API → Enable

  3. Vyhledejte Gmail API → Enable

1.3 OAuth Consent Screen

  1. Levé menu → APIs & ServicesCredentials

  2. Klikněte Configure consent screen (žlutý banner)

  3. Vyberte InternalCreate

  4. Vyplňte název aplikace, support e-mail, vývojářský kontakt → Save and Continue × 3

1.4 Vytvoření service accountu

  1. Levé menu → IAM & AdminService Accounts

  2. + Create Service Account

  3. Název: signatures-sa → Create and Continue

  4. Přeskočte sekce Grant access → Done

1.5 Zjištění Client ID

  1. Klikněte na nově vytvořený service account

  2. Sjeďte dolů → rozbalte Advanced settings

  3. V sekci Domain-wide Delegation je uvedeno Client ID – číslo si zkopírujte

ℹ️ Checkbox „Enable Domain-wide Delegation" Google z UI odstranil. DWD se aktivuje přímo v Workspace Admin zadáním Client ID (viz Část 2).

1.6 Stažení JSON klíče

  1. Na stránce service accountu → záložka Keys

  2. Add KeyCreate new keyJSONCreate

  3. Soubor se automaticky stáhne – uchovejte ho bezpečně, nikdy ho necommitujte do Gitu

⚠️ Tento JSON klíč umožňuje impersonaci kohokoliv ve vaší doméně. Uložte ho do password manageru a neposkytujte nikomu.

Část 2 – Google Workspace Admin

Tímto udělíte service accountu oprávnění vystupovat jménem všech uživatelů.

  1. Jděte na admin.google.com

  2. Navigujte na: Security → Access and data control → API controls

  3. Domain wide delegationManage Domain Wide Delegation

  4. Add new

  5. Client ID: vložte číslo z kroku 1.5

  6. OAuth Scopes – vložte přesně toto:

https://www.googleapis.com/auth/gmail.settings.basic,https://www.googleapis.com/auth/gmail.settings.sharing,https://www.googleapis.com/auth/admin.directory.user.readonly

  1. Authorize

ℹ️ Změna se projeví do 15 minut. Pokud dostanete chybu Delegation denied hned po nastavení, počkejte chvíli a zkuste znovu.

Část 3 – Apps Script

3.1 Vytvoření projektu

  1. Jděte na script.google.comNew project

  2. Přejmenujte na Company Signatures

3.2 Vložení kódu

  1. Smažte výchozí obsah v Code.gs → vložte obsah souboru Code.gs

  2. Klikněte + vedle Files → Script → pojmenujte Template

  3. Vložte obsah souboru Template.gs

3.3 Přidání OAuth2 knihovny

  1. Levé menu → + vedle Libraries

  2. Vložte toto Script ID:

1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF

  1. Look up → vyberte nejvyšší verzi → Identifier nechte jako OAuth2Add

3.4 Přidání Admin SDK

  1. Levé menu → + vedle Services

  2. Vyberte Admin SDK Directory APIAdd

⚠️ Gmail API jako službu nepřidávejte – volá se přímo přes OAuth2 knihovnu.

3.5 Propojení s Cloud projektem

  1. ⚙️ Project SettingsGoogle Cloud Platform (GCP) Project

  2. Change project → zadejte číslo projektu z Cloud Console → Set project

3.6 Uložení JSON klíče do Script Properties

JSON klíč je příliš dlouhý pro UI. Použijte tento workaround:

  1. Vytvořte dočasný soubor Setup.gs ve vašem projektu

  2. Vložte tento kód a nahraďte placeholder celým obsahem JSON souboru:

function saveJson() { const json = `VLOZ_SEM_CELY_OBSAH_JSON_SOUBORU`; PropertiesService.getScriptProperties() .setProperty('SERVICE_ACCOUNT_JSON', json); Logger.log('Uloženo!'); } 

  1. Spusťte saveJson → ověřte, že log říká Uloženo!

  2. Soubor Setup.gs ihned smažte – klíč nesmí zůstat viditelný v kódu

Část 4 – Spuštění a nasazení

Vždy nejdřív vyberte funkci z rozbalovacího menu v Apps Script editoru, pak klikněte Run.

Krok 1 – Otestujte na vlastním účtu

Funkce: step2_testOnlyMe → Run

Očekávaný výstup:

🧪 Testuju podpis pro: vas@vasadomena.czvas@vasadomena.cz (Vaše Jméno) ✅ Hotovo! Zkontroluj podpis v Gmailu.

Zkontrolujte výsledek: Gmail → ⚙️ → Zobrazit všechna nastaveníObecnéPodpis

Krok 2 – Nasazení všem

Funkce: step3_deployEveryone → Run

🚀 Nasazuji podpisy všem uživatelům domény... Nalezeno 23 aktivních uživatelů
jana.novakova@firma.cz (Jana Nováková)
petr.svoboda@firma.cz (Petr Svoboda) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ OK: 23 ❌ Chyby: 0 Celkem: 23

Krok 3 – Automatický weekly trigger

  1. Levé menu → ⏰ Triggers+ Add Trigger

  2. Nastavte: Function = step3_deployEveryone, Event source = Time-driven, Type = Week timer, Day = Monday, Time = 8am–9am

  3. Save

Nový zaměstnanec dostane podpis automaticky nejbližší pondělí. Pro okamžité nasazení spusťte step3_deployEveryone ručně.

Část 5 – Úprava šablony podpisu

Upravte soubor Template.gs dle vašeho brandu. Funkce dostává tyto proměnné automaticky:

Proměnná

Zdroj

name

Google Workspace profil – celé jméno

title

Employee info – Job title

email

Primární Gmail adresa

phone

Phone numbers – typ „Work"

photo

URL profilové fotky (thumbnailPhotoUrl)

icons.*

URL ikon z CONFIG.iconUrls

CONFIG.address

Adresa firmy (stejná pro všechny)

CONFIG.website

Web firmy (stejný pro všechny)

ℹ️ Ikony se načítají jako externí URL (ne base64), aby HTML zůstalo pod limitem 10 000 znaků, který Gmail vyžaduje.

Po každé změně Template.gs spusťte step2_testOnlyMe pro kontrolu před nasazením všem.

Odkud se berou data zaměstnanců

Vše se tahá automaticky z Google Workspace:

Pole v podpisu

Kde to nastavit

Jméno

Admin Console → Users → Name

Pozice

Admin Console → Users → Employee info → Job title

Telefon

Admin Console → Users → Phone numbers → typ „Work"

Profilová fotka

Uživatel si nastaví sám na myaccount.google.com, nebo admin v Admin Console

Pokud uživatel nemá vyplněný telefon, řádek s telefonem se v podpisu automaticky nezobrazí.

Řešení problémů

Chyba

Řešení

SERVICE_ACCOUNT_JSON not set

JSON klíč nebyl uložen. Opakujte krok 3.6.

Delegation denied

Počkejte 15 minut po nastavení DWD. Zkontrolujte Client ID.

HTTP 400 – Signature exceeds 10,000 chars

Ikony musí být URL, ne base64.

Fotka se nezobrazuje

Uživatel nemá profilovou fotku v Google Workspace.

Service not enabled

Zapněte Admin SDK API v Cloud Console (krok 1.2).

Cannot get token

DWD není správně nastaveno nebo špatné Client ID.

Výsledek

Celkové náklady: 0 Kč / měsíc
Čas na nastavení: ~30 minut
Údržba: nulová – noví zaměstnanci dostávají podpisy automaticky

K vybudování tohoto řešení jsem využil Claude. Popsal jsem architekturu, společně jsme prošli OAuth záludností a celá věc byla hotová za odpoledne.

Použijte to, upravte si to, sdílejte to dál. Pokud to u vás funguje, rád to uslyším.

Patrik Samko – CEO @ Bizztreat, the data detectives
LinkedIn


Část 6 – Kód: Code.gs

Co tento soubor dělá

Code.gs je mozek celého systému. Obsahuje:

  • CONFIG – jediné místo kde konfigurujete logo, ikony, adresu, web a sociální sítě

  • saveJson() – dočasná funkce pro uložení JSON klíče service accountu (spustí se jednou, pak smaže)

  • step2_testOnlyMe() – testování podpisu na vlastním účtu před ostrým nasazením

  • step3_deployEveryone() – nasazení podpisu všem uživatelům domény

  • Interní funkce pro OAuth2 autentizaci přes service account a sestavení HTML podpisu

Co upravit v CONFIG

Hodnota

Co změnit

myEmail

Váš admin e-mail (pro testování)

logoUrl

URL vašeho loga (PNG, veřejně dostupné)

iconUrls.*

HEX barvu za posledním / v URL (např. 0078d4 = modrá)

companyName

Název vaší firmy

address

Adresa firmy

website

URL webu firmy

linkedinUrl

URL LinkedIn stránky firmy (nebo "" pro skrytí)

youtubeUrl

URL YouTube kanálu (nebo "" pro skrytí)

Ikony generuje signaturehound.com zdarma – stačí změnit hex barvu v URL.

Kód

Část 7 – Kód: Template.gs

Co tento soubor dělá

Template.gs obsahuje jedinou funkci buildSignatureHtml(), která přijme data uživatele a vrátí HTML podpisu jako string. Sem sáhněte pokud chcete změnit vzhled – layout, barvy, přidat/odebrat řádky.

Kód v Code.gs tuto funkci volá automaticky.

Nejčastější úpravy

Co chcete změnit

Kde v kódu

Barvu oddělovacího pruhu

background-color:#000000 – nahraďte hex

Barvu odkazu na web

color:#000000 – v sekci Web

Velikost fotky

width="96" height="96"

Velikost loga

width="124"

Přidat Twitter/X

Zkopírovat blok youtubeIcon, přidat CONFIG.twitterUrl do CONFIG

Po každé změně spusťte step2_testOnlyMe a zkontrolujte výsledek v Gmailu.

Kód

/**
 * ╔══════════════════════════════════════════════════════════════╗
 * ║               Šablona podpisu – Template.gs                 ║
 * ╚══════════════════════════════════════════════════════════════╝
 *
 * TADY upravuješ vzhled podpisu. Po každé změně spusť
 * step2_testOnlyMe v Code.gs a zkontroluj výsledek v Gmailu.
 *
 * Dostupné proměnné (doplní se automaticky z Workspace profilu):
 *   name    – celé jméno uživatele
 *   title   – pracovní pozice (Employee info → Job title)
 *   email   – primární e-mailová adresa
 *   phone   – pracovní telefon, prázdný string pokud není vyplněn
 *   photo   – URL profilové fotky, null pokud uživatel nemá fotku
 *
 * Dostupné objekty z Code.gs:
 *   icons.logo      – logo firmy
 *   icons.email     – ikona e-mailu
 *   icons.phone     – ikona telefonu
 *   icons.location  – ikona adresy
 *   icons.web       – ikona webu
 *   icons.linkedin  – ikona LinkedInu
 *   icons.youtube   – ikona YouTube
 *   CONFIG.address     – adresa firmy
 *   CONFIG.website     – web firmy
 *   CONFIG.companyName – název firmy
 *   CONFIG.linkedinUrl – URL LinkedIn profilu firmy
 *   CONFIG.youtubeUrl  – URL YouTube kanálu firmy
 *
 * DŮLEŽITÉ LIMITY:
 *   Gmail má limit 10 000 znaků na podpis.
 *   Ikony musí být načítány jako URL (ne base64) – jinak limit překročíš.
 *   Vyhni se externím fontům (Google Fonts) – Gmail je ignoruje.
 *   Bezpečné fonty: Arial, Helvetica, Verdana, Georgia, Times New Roman
 */

function buildSignatureHtml(name, title, email, phone, photo, icons) {

  // ── FOTKA ──────────────────────────────────────────────────────
  // Zobrazíme profilovou fotku jako kruh, nebo šedý placeholder pokud není
  var photoCell = photo
    ? '<img src="' + photo + '" width="96" height="96" style="border-radius:50%;width:96px;height:96px;object-fit:cover;display:block;margin:0 auto;" alt="' + name + '">'
    : '<div style="width:96px;height:96px;border-radius:50%;background:#e8e8e8;margin:0 auto;"></div>';

  // ── TELEFON ────────────────────────────────────────────────────
  // Řádek s telefonem zobrazíme jen pokud uživatel telefon vyplněný má
  var phoneRow = phone
    ? '<tr><td style="padding:2px 8px 2px 0;vertical-align:middle;"><img src="' + icons.phone + '" width="14" height="14" style="display:block;"></td><td style="font-size:12px;color:#888888;padding:2px 0;vertical-align:middle;">' + phone + '</td></tr>'
    : "";

  // ── SOCIÁLNÍ SÍTĚ ──────────────────────────────────────────────
  // Zobrazíme jen ikony pro sítě s vyplněnou URL (prázdné CONFIG hodnoty = nezobrazovat)
  var linkedinIcon = CONFIG.linkedinUrl
    ? '<td style="padding-right:6px;"><a href="' + CONFIG.linkedinUrl + '" style="text-decoration:none;"><img src="' + icons.linkedin + '" width="26" height="26" alt="LinkedIn" style="display:block;"></a></td>'
    : "";

  var youtubeIcon = CONFIG.youtubeUrl
    ? '<td><a href="' + CONFIG.youtubeUrl + '" style="text-decoration:none;"><img src="' + icons.youtube + '" width="26" height="26" alt="YouTube" style="display:block;"></a></td>'
    : "";

  // ── HLAVNÍ HTML TABULKA ────────────────────────────────────────
  // Používáme HTML tabulku místo CSS flexbox/grid – jen tabulky jsou
  // spolehlivě zobrazeny ve všech e-mailových klientech.
  var html = [];
  html.push('<table cellpadding="0" cellspacing="0" border="0" style="font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#888888;max-width:540px;">');
  html.push('<tr>');

  // Levý sloupec: fotka + logo
  html.push('<td style="vertical-align:top;padding-right:18px;width:130px;text-align:center;">');
  html.push('<table cellpadding="0" cellspacing="0" style="width:130px;">');
  html.push('<tr><td style="padding-bottom:10px;text-align:center;">' + photoCell + '</td></tr>');
  html.push('<tr><td style="text-align:center;"><img src="' + icons.logo + '" width="124" height="auto" alt="' + CONFIG.companyName + '" style="display:block;margin:0 auto;max-width:124px;"></td></tr>');
  html.push('</table></td>');

  // Oddělovač – změň #000000 na barvu svého brandu
  html.push('<td style="width:1px;background-color:#000000;padding:0;">&nbsp;</td>');

  // Pravý sloupec
  html.push('<td style="vertical-align:top;padding-left:18px;">');

  // Jméno, pozice, firma
  html.push('<table cellpadding="0" cellspacing="0">');
  html.push('<tr><td style="font-size:15px;font-weight:bold;color:#1a1a1a;font-family:Arial,Helvetica,sans-serif;line-height:1.3;padding-bottom:1px;">' + name + '</td></tr>');
  html.push('<tr><td style="font-size:12px;color:#888888;padding-bottom:1px;">' + title + '</td></tr>');
  html.push('<tr><td style="font-size:12px;color:#888888;padding-bottom:10px;">' + CONFIG.companyName + '</td></tr>');
  html.push('</table>');

  // Kontaktní údaje
  html.push('<table cellpadding="0" cellspacing="0" style="margin-bottom:8px;">');
  // E-mail
  html.push('<tr>');
  html.push('<td style="padding:2px 8px 2px 0;vertical-align:middle;"><img src="' + icons.email + '" width="14" height="14" style="display:block;"></td>');
  html.push('<td style="font-size:12px;color:#888888;padding:2px 0;vertical-align:middle;"><a href="mailto:' + email + '" style="color:#888888;text-decoration:none;">' + email + '</a></td>');
  html.push('</tr>');
  // Telefon (prázdný pokud nevyplněn)
  html.push(phoneRow);
  // Adresa
  html.push('<tr>');
  html.push('<td style="padding:2px 8px 2px 0;vertical-align:middle;"><img src="' + icons.location + '" width="14" height="14" style="display:block;"></td>');
  html.push('<td style="font-size:12px;color:#888888;padding:2px 0;vertical-align:middle;">' + CONFIG.address + '</td>');
  html.push('</tr>');
  // Web – změň barvu (#000000) na barvu svého brandu
  html.push('<tr>');
  html.push('<td style="padding:2px 8px 2px 0;vertical-align:middle;"><img src="' + icons.web + '" width="14" height="14" style="display:block;"></td>');
  html.push('<td style="font-size:12px;padding:2px 0;vertical-align:middle;"><a href="' + CONFIG.website + '" style="color:#000000;font-weight:bold;text-decoration:none;">' + CONFIG.website.replace("https://", "").replace("http://", "") + '</a></td>');
  html.push('</tr>');
  html.push('</table>');

  // Sociální sítě
  html.push('<table cellpadding="0" cellspacing="0"><tr>' + linkedinIcon + youtubeIcon + '</tr></table>');

  html.push('</td></tr></table>');
  return html.join("\n");
}