Un mediu de lucru în care agentul care se ocupă de campaniile tale Google Ads și Meta citește rapoartele, propune schimbarea cu motivul scris și execută numai în limitele pe care i le-ai dat.
Serviciul e o ofertă, nu o realizare, și o spunem primii: nu avem azi un sistem care schimbă bugete sau licitații în Google Ads sau Meta. Avem construite și rulate piesele din care se face unul — trimiterea semnalului de conversie reală către Google, un motor care cumpără promovare plătită cu dry-run pornit implicit, un registru care scrie fiecare acțiune care costă, și un întrerupător care oprește un orchestrator fără cale de ocolire.
Ofertă, cu condiții„offer”, nu „delivered”, și motivul se poate verifica în câteva secunde. Am căutat în peste 150 de depozite proprii câmpurile prin care API-urile de reclamă mișcă bani — `budget_micros`, `campaignBudget`, `target_cpa`, `target_roas`, `bid_strategy` — și nu apar nicăieri, nici la Google, nici la Meta. Nu gestionăm azi bugete de reclamă. Ce apare, și se poate deschide fișier cu fișier, sunt piesele care despart o automatizare de un accident: trimiterea conversiilor offline către Google prin Data Manager API, 264 de linii într-un proiect de producție al nostru; un motor de promovare plătită care cumpără boostere, etichete și pachete cu dry-run pornit implicit; un A/B tester bayesian care refuză să declare câștigător sub 100 de afișări per variantă, și orchestratorul de campanii din MEGA CRM cu întrerupător fără `--force`. Serviciul e descris din aceste piese. Nu dintr-un panou care nu există.
Un agent care se ocupă de campanii nu e un buton pe care scrie „optimizează”. E un program care are voie să citească anumite rapoarte, voie să scrie anumite câmpuri și nicio voie în rest. Partea grea nu e să propună o schimbare — modelele fac asta bine și repede. Partea grea e să scrii unde se oprește, cum dovedește ce a făcut, și ce se întâmplă când greșește. Despre asta e lucrarea, și de acolo începem discuția.
Spunem de la început ce nu avem, ca să nu aflați de la altcineva. Nu rulează azi la noi un sistem care ridică un buget în Google Ads sau care schimbă o licitație în Meta. Ce rulează, și se poate arăta linie cu linie, sunt mecanismele de siguranță pe care le-am scris pentru alte automatizări care cheltuie bani reali: dry-run pornit implicit, contor pe fiecare acțiune plătită, temporizare la eroare ca să nu se repete greșeala în buclă, prag de dovadă înainte de o decizie, și un întrerupător care refuză să pornească orchestratorul.
Piesa pe care o avem deja construită nu e o schimbare de buget, ci un semnal — și e cea de la care propunem să se înceapă. Google învață din ce îi spui că e conversie. Dacă îi spui „formular trimis”, va căuta oameni care trimit formulare. Am scris integrarea care îi spune „cerere ajunsă real în sistemul clientului”, cu identificatorul clicului și cu identificatori de utilizator trecuți prin SHA-256, prin Data Manager API. Asta se poate începe imediat, pentru că e cod existent, nu o promisiune.
Restul mediului de lucru — stratul care citește rapoarte, stratul care propune, stratul care execută — se construiește pe același tipar pe care l-am folosit unde am cheltuit deja bani prin cod. Se începe strict cu citire, se trece la propuneri fără execuție, și abia la urmă se deschide execuția, câmp cu câmp, cu limită scrisă pe fiecare.
Ce cuprinde
Lucrarea, pe componente
Citește rapoartele, nu tabloul de bord
Se stabilește de la început ce citește agentul și de unde: cheltuiala pe campanie și pe grup de anunțuri, conversiile atribuite, costul pe achiziție, cota de afișare pierdută din buget față de cea pierdută din poziție — două cifre diferite care cer decizii diferite. Citirea se face prin API, cu un cont de serviciu separat, nu prin capturi de ecran din interfață. Stratul de citire se livrează și se pornește primul, singur, fără nicio permisiune de scriere pe cont.
Propune schimbarea cu motivul scris lângă ea
O propunere nu e o cifră. E câmpul care se schimbă, valoarea veche, valoarea nouă, fereastra de date pe care s-a luat decizia, și de ce. Propunerile se strâng într-un raport pe care îl citește un om, în forma „aș ridica bugetul zilnic al campaniei X de la A la B pentru că în ultimele N zile a fost limitată de buget în M% din licitații, la un cost pe achiziție sub pragul convenit”. Dacă motivul nu se poate scrie, propunerea nu se face.
Trei praguri: ce face singur, ce cere un om, ce nu face niciodată
Fiecare câmp intră într-una din trei categorii, scrise înainte de a porni ceva. Singur: schimbări sub un procent convenit, în interiorul unui plafon zilnic, pe campanii marcate ca deschise. Cu aprobare: orice depășire a plafonului, orice campanie nouă, orice public nou, orice modificare de licitație. Niciodată: oprirea unei campanii pe care nu el a pornit-o, schimbarea metodei de plată, atingerea conturilor de facturare. Lista se scrie în contract, nu în cod, și codul o citește.
Dry-run pornit implicit — tiparul pe care îl aducem cu noi
În motorul nostru de promovare plătită de pe 999.md, funcția care decide dacă se cheltuie bani e scrisă invers față de cum e scris de obicei: dry-run e activ dacă variabila de mediu nu are exact valoarea `false`. Nesetată, goală, `true`, scrisă greșit — toate păstrează dry-run pornit. E o alegere deliberată: o greșeală de configurare nu poate deschide robinetul, poate doar să-l țină închis. Aceeași funcție există în două locuri independente, motorul de promovare și executorul de republicare.
Fiecare acțiune care costă se scrie într-un registru
În sistemul de promovare, orice execuție intră într-un tabel `agent_costs` cu anunțul, utilizatorul, tipul acțiunii, suma în lei, dacă a reușit, răspunsul brut al furnizorului și un marcaj `dry_run`. Scrierea se face în aceeași tranzacție cu actualizarea programării, deci nu poate exista o cheltuială fără urmă sau o urmă fără cheltuială. Când e dry-run, suma înregistrată e zero, dar rândul există — se vede exact ce s-ar fi întâmplat.
Semnalul de conversie reală — piesa care există deja
Când cineva ajunge dintr-un click plătit și trimite o cerere, semnalul pleacă spre Google abia după ce cererea intră în sistemul clientului, nu la apăsarea butonului. Integrarea folosește `https://datamanager.googleapis.com/v1/events:ingest`, trimite identificatorul clicului, momentul, valoarea și moneda, plus identificatori de utilizator trecuți prin SHA-256 — adresa normalizată cu litere mici, telefonul adus la formatul internațional. Fără identificatorul clicului, trimiterea se oprește singură și se marchează ca sărită, pentru că nu ar fi atribuibilă.
Consimțământul se pune înaintea etichetelor, nu după
Consent Mode v2 se inițializează cu toate scopurile de marketing și analiză pe `denied`, înainte ca vreun script Google să pornească, cu o fereastră de așteptare de 500 ms pentru actualizare. Se trece pe `granted` doar la interacțiunea persoanei cu bannerul, fără reîncărcarea paginii. Stocarea funcțională și cea de securitate rămân pornite, pentru că fără ele pagina nu funcționează. E scris o dată și rulează pe fiecare vizită.
Decizia dintre două variante se ia pe dovadă, nu pe impresie
Testerul A/B pe care l-am construit modelează fiecare variantă ca Beta-Binomial cu prior uniform și estimează probabilitatea ca una să fie mai bună prin 10.000 de eșantioane Monte Carlo. Nu declară câștigător sub 100 de afișări per variantă și sub o probabilitate de 0,95. Dacă la 168 de ore nu s-a decis, închide experimentul și spune că a expirat, în loc să numească un câștigător pe care nu-l poate susține.
Un întrerupător care nu se poate ocoli
Orchestratorul de campanii din sistemul nostru intern verifică o variabilă de mediu înainte de orice. Dacă e pusă, refuză să pornească — nu există `--force`, nu există argument care să treacă peste. A fost adăugat după un incident real de trimiteri duplicate, nu ca decor. Același tipar se pune și aici: un comutator la nivel de cont care oprește toate scrierile, lăsând citirea și raportarea să meargă mai departe.
Cum arată
Traseul unei decizii: citire prin API cu cont fără drept de scriere → propunere cu câmp, valoare veche, valoare nouă și motiv → filtrul celor trei liste (singur / cu aprobare / niciodată) → execuție cu dry-run pornit implicit și plafon zilnic → rând în registrul de costuri, scris în aceeași tranzacție. Comutatorul de oprire taie execuția și lasă citirea pornită.
01
Inventarul conturilor și al drepturilor
Se listează conturile de reclamă, cine are acces azi și cu ce rol, ce conversii sunt definite și care dintre ele sunt de fapt duplicate ale aceluiași eveniment. Se stabilesc cele trei liste de câmpuri — singur, cu aprobare, niciodată. Livrăm: inventarul scris, lista conversiilor cu ce măsoară fiecare cu adevărat, și acordul pe cele trei liste.
02
Stratul de citire, pornit singur
Se conectează citirea prin API cu un cont care nu are drept de scriere, și se pornește raportul zilnic. Nimic nu se schimbă în conturi în această etapă — se vede doar ce vede agentul. E momentul în care ies la iveală conversiile care numărau greșit, campaniile limitate de buget fără să știe nimeni, și publicurile care se suprapun. Livrăm: raportul zilnic și lista a ceea ce am găsit citind.
03
Semnalul de conversie reală
Se captează identificatorul clicului pe pagina de aterizare, se leagă de cererea din sistemul vostru și se trimite spre Google abia când cererea e confirmată în aval, nu la apăsarea butonului. Se pornește reîncercarea pentru trimiterile eșuate, în fereastra de 90 de zile. Livrăm: integrarea funcțională, un mod de verificare care trimite fără să înregistreze, și jurnalul cu ce a plecat și ce s-a sărit și de ce.
04
Stratul de propuneri, fără execuție
Agentul începe să scrie propuneri: câmp, valoare veche, valoare nouă, motiv, fereastră de date. Nimic nu se execută. Se rulează așa până când propunerile devin plictisitoare — adică până când persoana care le citește ar fi apăsat oricum „da” la aproape toate. Livrăm: fluxul de propuneri, cu tot ce a propus și ce ați aprobat sau respins, ca să se vadă unde greșește.
05
Deschiderea execuției, câmp cu câmp
Se deschide execuția pentru primul câmp din lista „singur”, cu dry-run pornit implicit și cu plafon zilnic. Se verifică în registru că ce s-a scris e ce s-a propus. Apoi al doilea câmp. Livrăm: registrul de acțiuni, comutatorul care oprește toate scrierile lăsând citirea pornită, și procedura scrisă pentru ce se face când agentul a greșit.
Traseul unei decizii: citire prin API cu cont fără drept de scriere → propunere cu câmp, valoare veche, valoare nouă și motiv → filtrul celor trei liste (singur / cu aprobare / niciodată) → execuție cu dry-run pornit implicit și plafon zilnic → rând în registrul de costuri, scris în aceeași tranzacție. Comutatorul de oprire taie execuția și lasă citirea pornită.
Datele
Ce atingem, unde stau și cât rămân
Întrebările pe care le pune oricine are un responsabil cu protecția datelor — puse aici înainte să le pună el.
Ce date atinge
Rapoarte de campanie, grup de anunțuri și cuvânt-cheie citite prin API. Identificatorul de click al reclamei, capturat din adresa paginii de aterizare și păstrat pe cererea clientului. Evenimente de conversie cu moment, valoare și monedă. La cererea explicită, adresa de email și telefonul persoanei care a convertit, dar numai sub formă de rezumat criptografic.
Ce pleacă spre Google și în ce formă
Identificatorul clicului pleacă în clar — el asta e, un identificator de click. Emailul și telefonul pleacă doar ca SHA-256, calculat local înainte de trimitere: emailul curățat de spații și trecut la litere mici, telefonul adus la format internațional. Adresa poștală nu se trimite deloc, pentru că schema Google o cere completă cu cod poștal și cod de regiune, iar codul poștal nu e consecvent în datele reale — motivul e scris în cod, nu presupus.
Unde stau și cine le vede
Datele de campanie și evenimentele stau în baza proiectului vostru, pe infrastructura convenită. Credențialele conturilor de reclamă stau în fișier de mediu citit de serviciul de sistem, nu în cod și nu în depozitul de surse. Jetonul de acces se ține în memorie cel mult 55 de minute, deși furnizorul îl dă valabil 3.600 de secunde — marja e intenționată, ca să nu se folosească vreodată unul expirat.
Cât timp mai poate fi trimis un eveniment
Fereastra practică e de 90 de zile de la momentul conversiei — atât acceptă Google pentru conversii offline. Reîncercarea pentru trimiterile eșuate lucrează exact în această fereastră, în loturi, cu maximum cinci încercări per cerere. După cinci încercări, cererea se lasă în pace și rămâne marcată ca netrimisă, în loc să fie reîncercată la nesfârșit.
Ce nu facem cu datele
Nu construim public din listele voastre de clienți fără o bază legală scrisă și fără să apară în informarea pe care o vede persoana. Nu trecem date între conturile a doi clienți diferiți. Nu păstrăm copii ale rapoartelor de campanie în afara sistemului convenit. Registrul de prelucrări se completează înainte de prima trimitere, nu după.
Un caz
O campanie care plătea pentru formulare, nu pentru cereri
Situația
O platformă din Chișinău care primește cereri online cumpăra trafic din căutare. Conversia definită în contul de reclamă era „formular trimis”. Algoritmul de licitare făcea exact ce i se cerea: aducea oameni care trimit formulare. Câte dintre ele deveneau cereri reale, mai departe în lanț, nu ajungea niciodată înapoi la platforma de reclamă.
Ce am construit
Am mutat momentul conversiei mai târziu în lanț. Identificatorul clicului se captează pe pagina de aterizare și se păstrează pe cerere. Cererea pleacă mai departe către sistemul din aval; dacă acesta întoarce un identificator propriu — adică a intrat cu adevărat — abia atunci pleacă evenimentul către Google, prin Data Manager API, cu momentul, valoarea, moneda și identificatori de utilizator trecuți prin SHA-256. Fără identificator de click, trimiterea se oprește singură și se marchează ca sărită. Am adăugat un job de reîncercare care caută cererile cu identificator de click și fără marcaj de trimitere, în ultimele 90 de zile, în loturi de 50, cu cel mult cinci încercări.
Ce a ieșit
Semnalul pe care îl primește platforma de reclamă descrie acum cererile care ajung la destinație, nu apăsările de buton. Toate operațiunile sunt inerte dacă integrarea nu e configurată sau e oprită — funcția verifică toate cele șapte credențiale înainte de orice, și întoarce „sărit” dacă lipsește una. Erorile se jurnalizează, dar nu se propagă: dacă Google nu răspunde, cererea clientului merge mai departe neatinsă.
Ce nu spune cazul
Aceasta e o integrare de semnal, nu de gestionare a campaniei. Nu schimbă niciun buget și nicio licitație. Efectul asupra costului pe achiziție nu a fost măsurat de noi și nu îl publicăm — în cod există o așteptare scrisă de autor, nu o măsurătoare, iar între cele două e toată diferența.
Întrebări
Ce ne întreabă oamenii înainte să sune
Cine poartă răspunderea când agentul crește un buget și cheltuiala urcă?
Voi. Și de aceea mediul e construit ca voi să puteți purta răspunderea în cunoștință de cauză, nu ca să vă surprindă. Concret: agentul nu are nicio permisiune de scriere pe care să nu i-o fi dat cineva pe nume, în scris, câmp cu câmp; fiecare câmp deschis are un plafon peste care propunerea merge la aprobare umană; fiecare acțiune executată se scrie într-un registru cu valoarea veche, valoarea nouă și motivul, în aceeași tranzacție cu execuția; și există un comutator care oprește toate scrierile fără să oprească citirea. Noi răspundem pentru ce am construit — că limitele pe care le-ați scris sunt respectate de cod, că registrul e complet, că oprirea funcționează. Nu răspundem pentru rezultatul comercial al unei schimbări pe care ați aprobat-o, cum nu răspunde nici agenția care apasă butonul manual. Dacă cineva vă promite altceva, întrebați-l ce scrie în contract la capitolul limitarea răspunderii.
Aveți azi un sistem care gestionează campanii Google Ads sau Meta?
Nu. Nicăieri în codul nostru nu apare vreun câmp prin care se schimbă un buget, o licitație sau o strategie de licitare la Google sau la Meta. Am verificat căutând exact acele nume de câmpuri în toate depozitele. Ce avem e trimiterea conversiilor către Google, un motor care cumpără promovare plătită pe o altă platformă, și mecanismele de siguranță din jurul lor. Serviciul se construiește pornind de acolo, și e marcat ca ofertă tocmai ca să nu existe confuzie.
Atunci ce anume ați construit deja și cheltuie bani reali?
Motorul nostru de promovare plătită de pe o platformă de anunțuri clasificate din Moldova. Pune etichete plătite, pornește boostere cu limită zilnică și preț pe click, activează pachete și republică anunțuri după un interval calculat din viteza reală a categoriei. Refuză să pornească un booster a cărui limită zilnică nu e mai mare decât prețul pe click — o verificare mică, dar exact tipul de verificare care lipsește de obicei. Fiecare acțiune trece prin verificarea de dry-run înainte de orice apel care costă.
Ce poate schimba singur și ce cere aprobare?
Se decide împreună, înainte, și se scrie. Punctul nostru de plecare în discuție: singur — ajustări sub un procent convenit din bugetul zilnic, în interiorul unui plafon zilnic absolut, pe campaniile pe care le-ați marcat ca deschise; cu aprobare — orice depășire a plafonului, campanii noi, publicuri noi, orice atingere a licitațiilor; niciodată — oprirea campaniilor pornite de altcineva, metode de plată, conturi de facturare. Dacă vreți totul pe aprobare la început, e o alegere validă și noi o preferăm.
De unde știe agentul că o conversie e reală și nu un formular gol?
Pentru că semnalul nu pleacă la apăsarea butonului. Pleacă abia când cererea e confirmată în aval, în sistemul care contează pentru voi — un identificator de cerere primit înapoi de la CRM sau de la partener. Până atunci nu s-a întâmplat nimic care merită numit conversie. Diferența nu e cosmetică: algoritmul de licitare al Google învață din ce îi trimiți, deci dacă îi trimiți formulare, îți va aduce oameni care completează formulare.
Ce se întâmplă cu datele personale ale celor care convertesc?
Emailul și telefonul pleacă doar ca rezumat criptografic SHA-256, calculat local înainte de trimitere, cu normalizare — email fără spații și cu litere mici, telefon adus la format internațional. Adresa poștală nu se trimite deloc. Nimic nu pleacă înainte ca persoana să fi acceptat scopul de marketing în banner, pentru că Consent Mode pornește cu totul pe `denied`. Baza legală și informarea se scriu înainte de prima trimitere, nu după prima întrebare a cuiva.
Ce se întâmplă dacă apelul către platforma de reclamă cade la jumătate?
Se înregistrează eșecul cu răspunsul brut, iar acțiunea se amână — în executorul nostru de republicare, cu maximum dintre răcirea configurată și cinci minute, explicit ca să nu se transforme o eroare într-o buclă de apeluri. Trimiterile de conversii eșuate se reiau printr-un job separat, în loturi de câte 50, cu cel mult cinci încercări per cerere, în fereastra de 90 de zile. După a cincea încercare se oprește și rămâne marcată netrimisă, ca să se vadă, nu ca să dispară.
Cum îl opresc, complet și repede?
Comutatorul de scriere. Citirea și raportarea continuă, execuția se oprește. În sistemele noastre existente acest lucru e implementat în două feluri, amândouă verificabile: o variabilă de mediu care trebuie să aibă exact valoarea `false` ca să se cheltuie bani — orice altceva ține robinetul închis — și un întrerupător pe orchestratorul de campanii care refuză pornirea și nu are `--force`. Nu depindem de un buton într-o interfață care poate să nu se încarce.
Îmi garantați o scădere a costului pe achiziție?
Nu, și merită spus de ce, pentru că în codul nostru există exact o astfel de cifră. În comentariul din capul integrării de conversii scrie o așteptare — cât ar putea scădea costul pe achiziție și cât ar putea urca cota de afișare. E o așteptare scrisă de cel care a construit-o, nu o măsurătoare, și de aceea nu o veți găsi nicăieri pe acest site ca rezultat. Ce vă putem promite e că măsurătoarea se va putea face: cu conversii care înseamnă ceva și cu un registru al fiecărei schimbări, comparația înainte-după devine posibilă. Fără ele, nu e.
Pe ce se sprijină fiecare afirmație de mai sus (20 surse)
Nicăieri în depozitele proprii nu apar câmpurile prin care se schimbă bugete sau licitații în Google Ads sau MetaProjects --glob '!node_modules':zero rezultate, 06.09.2026
Trimiterea conversiilor offline către Google se face prin Data Manager API, la `https://datamanager.googleapis.com/v1/events:ingest`google-ads.service.ts:219
Endpointul clasic `uploadClickConversions` nu mai e disponibil pentru integrări noi — motivul e scris în cod, cu trimitere la documentația Googlegoogle-ads.service.ts:189-194
Integrarea verifică toate cele șapte credențiale înainte de orice și e inertă dacă lipsește una sau dacă e oprită din `GADS_ENABLED`google-ads.service.ts:34-45
Fără identificator de click, trimiterea se oprește singură și se marchează „sărit”, pentru că nu ar fi atribuibilăgoogle-ads.service.ts:179-184
Emailul se normalizează cu litere mici, telefonul se aduce la format internațional, ambele pleacă doar ca SHA-256; adresa poștală nu se trimite pentru că lipsește codul poștal consecventgoogle-ads.service.ts:90-135
Jetonul de acces se păstrează în memorie cel mult 55 de minute, deși furnizorul îl dă valabil 3.600 de secundegoogle-ads.service.ts:25 și 76-80
Reîncercarea trimiterilor eșuate: cel mult 5 încercări, fereastră de 90 de zile, loturi de 50cron.tasks.ts:347-349
Consent Mode v2 pornește cu toate scopurile de marketing și analiză pe `denied`, cu 500 ms fereastră de așteptare; funcțional și securitate rămân `granted`consent-mode.ts:36-49
Fără identificatorul de conversie și eticheta din mediu, urmărirea conversiei în browser e complet inertăgoogle-ads.ts:19-27
Motorul de promovare plătită: dry-run activ implicit, doar valoarea explicită `false` pornește execuția care cheltuie baniindex.ts:34-41
Boosterul refuză configurația în care limita zilnică nu depășește prețul pe clickindex.ts:124-125
Alertă de sold scăzut pe contul de la care se cumpără promovarea, cu prag configurabilindex.ts:408-414
Executorul de republicare: dry-run implicit pornit, aceeași regulă a valorii explicite `false`executor.ts:20-24
Fiecare execuție scrie un rând în `agent_costs` cu suma, reușita, răspunsul brut și marcajul `dry_run`, în aceeași tranzacție cu actualizarea programăriiexecutor.ts:82-124
La eroare, următoarea execuție se amână cu maximum dintre răcirea configurată și cinci minute, explicit ca să nu se ajungă la o buclă de apeluriexecutor.ts:111-112
Intervalul de republicare se recalculează din viteza reală a categoriei, cu marjă de siguranță 0,8, poziție-țintă limitată între 1 și 99 și rezervă de 30 de minutescheduler.ts:10-17 și 48-56
Testerul A/B: prag de probabilitate 0,95, minimum 100 de afișări per variantă, 10.000 de eșantioane Monte Carlo, expirare la 168 de orebayesian.ts:124-130
Sub minimul de afișări, testerul întoarce explicit `min_views_not_met` și niciun câștigătorbayesian.ts:134-140
Orchestratorul de campanii din sistemul intern are un întrerupător fără cale de ocolire — descris pe larg în fișa MEGA CRMmega-crm.json:capabilities → „Un întrerupător care nu se poate ocoli”
Citările interne arată numele fișierului și linia. Calea completă rămâne în depozitul nostru; o putem parcurge împreună, la cerere.