A system where you can see who has been reached, through which channel, and what comes next.
We build an organisation's commercial system: organisations and contacts, a stage pipeline, the channels that bring in data by themselves — call, email, form, voice agent — and the brakes that stop a send before it becomes a problem. We are not selling you our CRM; we build one for the way you work.
Construit dejaDouă implementări proprii, în producție, citate mai jos. (1) MEGA CRM — sistemul cu care echipa noastră își ține propria activitate comercială: `server.js` are 9.516 linii și 99 de rute HTTP, în depozit sunt 96 de migrări SQL și circa 95.000 de linii de TypeScript în interfață, iar ultimul commit e din 18 august 2026. (2) Conectorii de CRM din platforma Kallina: 12 funcții de server pentru amoCRM, 4 pentru Bitrix24, plus un flux OAuth documentat pentru Zoho CRM și 10 funcții pentru Google Sheets. Precizarea care contează: **MEGA CRM nu e de vânzare.** Îl arătăm ca dovadă de meserie, nu ca produs — nu are izolare strictă a datelor între clienți și nu are un flux de instalare care să nu presupună acces la server. Serviciul e „construim un sistem ca acesta pentru tine”, nu „îți dăm o licență la al nostru”.
Cele mai multe firme nu au o problemă de CRM, au o problemă de evidență: nu se știe cine a fost contactat, prin ce canal, ce s-a spus și ce urmează. Un instrument gata făcut rezolvă asta când munca ta seamănă cu munca pentru care a fost gândit instrumentul. Când nu seamănă — pentru că lucrezi cu administrații publice și cu firme private în același timp, sau pentru că jumătate din atingeri vin dintr-un agent vocal — ajungi să chinui instrumentul, iar echipa ține adevărul într-un fișier separat. Atunci merită construit ceva.
Sistemul pe care îl arătăm ca dovadă e cel cu care lucrăm noi. E structurat pe proiecte separate, fiecare cu tipul lui de organizație și cu schema lui de câmpuri, ca aceeași aplicație să funcționeze și pentru o primărie, și pentru o firmă privată, fără coloane noi în bază la fiecare tip nou. Sub organizație stau contactele, deasupra stau campaniile. Un lead urcă printr-un traseu de șapte stadii, de la „necontactat” până la „contract semnat”, și poate fi atins prin nouă canale diferite, de la apelul agentului vocal până la vizită.
Partea grea, și cea care se învață pe pielea proprie, nu e trimiterea. E oprirea. Sistemul are ore de lucru scrise în cod, liste de excludere, deduplicare pe adresa scrisă cu litere mici — nu pe organizație —, o perioadă de răcire între campanii, un plafon zilnic împărțit între campanii și un întrerupător care refuză să pornească orchestratorul, fără cale de ocolire. Fiecare dintre ele a fost adăugat după ce a lipsit o dată. Le enumerăm nu ca listă de funcții, ci ca listă de lecții.
Al doilea lucru pe care îl construim aproape de fiecare dată e alimentarea automată. Un CRM în care datele se scriu cu mâna se golește în trei săptămâni. La noi, apelul agentului vocal se scrie singur pe fișa organizației, cu transcript și rezumat, și mută leadul mai departe pe traseu dacă rezultatul apelului o justifică. Răspunsurile la emailuri se culeg din cutia poștală la fiecare zece minute și se leagă de trimiterea care le-a provocat. Deschiderile și clicurile se urmăresc separat. Omul intervine ca să decidă, nu ca să introducă date.
What it covers
The work, piece by piece
Datele intră în flux, cu deduplicare pe trei chei
Un fișier de până la 50 MB se încarcă și se procesează în flux, în loturi de 200 de rânduri, ca să nu depășească termenul de execuție al bazei, iar progresul se întoarce în timp real, câte o linie pentru fiecare pas. Înainte de import se construiește un index de deduplicare pe trei chei — codul fiscal, adresa de email și telefonul — și se validează adresele: format, domenii de unică folosință, tipare invalide. Aceeași organizație nu intră de două ori sub două denumiri scrise diferit.
Fiecare atingere se scrie pe fișa organizației
Nouă canale de comunicare, de la agentul vocal și apelul uman până la vizită, și un traseu de șapte stadii cu etichete reale: necontactat, contactat, răspuns primit, ofertă trimisă, interesat activ, negociere, contract semnat. Fiecare stadiu are scris lângă el ce procent ar trebui să treacă mai departe — nu ca promisiune, ci ca reper pentru cel care se uită la pâlnie și vrea să știe unde se pierde.
Apelul agentului vocal mută leadul singur
Când agentul termină un apel, rezultatul intră în sistem printr-un webhook a cărui semnătură se verifică: transcript, rezumat, sentiment, adresa înregistrării, durata. Organizația se identifică după numărul de telefon, printr-o funcție scrisă în bază pentru formatele în care apar numerele în date reale, iar apelul se leagă de fișa ei. Dacă rezultatul e „interesat” sau „întâlnire programată”, leadul urcă automat pe traseu și îi este actualizată data ultimului contact.
Trimiterea are frâne, nu doar accelerație
Ore de lucru scrise în cod: 08:00–18:00, fus orar Chișinău, luni până sâmbătă. Liste de excludere pentru școli, licee, grădinițe, ambasade și consulate. Deduplicare pe adresa scrisă cu litere mici — nu pe organizație, distincție care contează enorm când mai multe firme folosesc aceeași cutie poștală. Răcire de cinci zile între campanii pentru aceeași adresă. Plafon zilnic global, împărțit proporțional între campanii, plus un plafon per rulare, ca trimiterile să fie împrăștiate pe mai multe ore, nu în bloc.
Un întrerupător care nu se poate ocoli
Orchestratorul de campanii verifică o variabilă de mediu înainte de orice altceva. Dacă e pusă, refuză să pornească și iese — nu există argument de linie de comandă care să treacă peste, nu există „forțează”. A fost adăugat pe 1 mai 2026, după un incident de trimiteri duplicate. Un sistem care trimite în numele tău are nevoie de o oprire pe care o poate apăsa cineva care nu e programator.
Dezabonare într-un singur clic, în trei trepte
Prima treaptă e antetul standard de dezabonare într-un clic, conform RFC 8058 — cerința pe care furnizorii mari de email o impun pentru trimiteri în volum. A doua e un centru de preferințe, unde dezabonarea se poate și anula. A treia e ștergerea completă la cerere, care șterge adresa din șase tabele, o adaugă pe o listă permanentă de blocare și lasă o urmă în jurnal. Toate trei sunt protejate cu un jeton legat de destinatar, ca nimeni să nu poată dezabona pe altcineva.
Răspunsurile și reacțiile se culeg singure
Răspunsurile la emailuri se citesc din cutia poștală prin Microsoft Graph, la fiecare zece minute, și se leagă automat de trimiterea care le-a provocat. Starea livrării se sincronizează de la furnizorul de trimitere. Deschiderile se urmăresc cu pixel, clicurile prin redirect, iar panourile de livrabilitate arată domeniile cu probleme. Un serviciu separat urmărește creditul rămas la furnizorii plătiți și dă alertă înainte să se termine — nu după.
Căutare după ce face o organizație, nu doar după cum se numește
Pe lângă căutarea în text integral, întrebarea se transformă într-un vector cu un model de înglobare și potrivirea se face în bază printr-o funcție proprie, cu prag de similaritate reglabil (implicit 0,25) și cu limitare pe proiect. Practic: „firme care fac transport frigorific” găsește și organizații care nu au niciunul din cuvintele acelea în denumire.
Conectare la CRM-ul pe care îl ai deja
Dacă echipa ta lucrează deja într-un CRM, nu îl înlocuim din reflex. Avem scris și rulat conectorul pentru amoCRM — 12 funcții de server, inclusiv fluxul OAuth complet, reîmprospătarea jetonului și verificarea stării — plus Bitrix24 cu OAuth și webhook, un ghid OAuth pentru Zoho și zece funcții pentru Google Sheets, de la citirea structurii unei foi până la importul de contacte.
What it looks like
The route, step by step.
01
Întâi modelul, apoi interfața
Ce e o organizație la tine, ce e un contact, ce e o oportunitate, ce câmpuri sunt obligatorii și care sunt doar utile. Discuția asta pare lentă și e singura care nu se poate recupera mai târziu: un model greșit se plătește la fiecare raport pe care îl ceri după aceea.
02
Traseul și stadiile, scrise ca reguli, nu ca obicei
Stadiile prin care trece un lead, ce le mută mai departe și ce le mută înapoi, cine e responsabil în fiecare. Scriem și regulile automate — ce rezultat de apel urcă leadul, ce inactivitate îl coboară — pentru că un traseu care se mișcă doar când cineva își aduce aminte să apese nu e un traseu, e o listă.
03
Canalele care aduc date singure
Apelurile din telefonie sau de la agentul vocal, răspunsurile din cutia poștală, formularele de pe site, webhookurile din alte sisteme. Fiecare canal se conectează și se testează separat, iar pentru fiecare stabilim ce se întâmplă cu datele care nu se potrivesc cu nimic din bază — pentru că acelea sunt cele care se pierd tăcut.
04
Frânele înainte de accelerație
Ore de trimitere, liste de excludere, deduplicare, răcire, plafoane, dezabonare, oprire de urgență. Le punem înainte de prima campanie, nu după prima reclamație. E partea în care experiența noastră costă cel mai puțin și valorează cel mai mult, pentru că e făcută din greșeli deja plătite.
05
Predarea: proceduri, roluri, documentație
Cine are ce rol, cum se adaugă un om, cum se oprește o campanie, unde se uită când ceva nu a plecat, ce se face la o cerere de ștergere. Un sistem comercial pe care doar constructorul știe să-l oprească e un risc, nu un activ.
Canalele care converg spre aceeași fișă: apelul agentului vocal, apelul uman, emailul și răspunsul din cutia poștală, formularul de pe site, webhookul din alt sistem — și frânele care decid ce pleacă înapoi.
Datele
What we touch, where it lives, and how long it stays
The questions anyone with a data protection officer asks — asked here before they do.
Unde stau datele și cum sunt structurate
PostgreSQL prin Supabase, cu 96 de migrări versionate. Structura e ierarhică: proiecte, sub ele organizații cu câmpuri flexibile în JSONB și vector de căutare în text integral, sub ele contacte, iar campaniile atârnă de proiect. Fiecare proiect își definește propria schemă de câmpuri — de aceea aceeași aplicație ține și administrații publice, și companii private, fără să crească baza la fiecare tip nou de organizație.
Ce scrie în subsolul fiecărui email trimis
Identitatea completă a firmei — denumire, cod fiscal, sediu înregistrat — adresa responsabilului cu protecția datelor, temeiul legal invocat și de unde provine adresa: registre publice și profiluri publice, spus explicit, nu subînțeles. Plus dezabonare într-un clic și o variantă de rezervă, cu răspuns într-un cuvânt. Ce ține de temeiul legal se reverifică la fiecare schimbare de lege — o trimitere în masă care citează un temei depășit e o problemă chiar dacă restul e impecabil.
Cine ce vede
Accesul e prin autentificare, rutele aplicației sunt închise în spatele unei porți, iar zona de administrare are o poartă în plus. Rolurile sunt separate: administrare, proprietar, vânzări, prospectare și un rol distinct pentru agentul automat — pentru că un agent care scrie în sistem nu trebuie să aibă drepturile unui om. Nu există înregistrare deschisă.
Ce verificăm explicit la instalare
Că fiecare secret e efectiv setat pe server: secretul cu care se semnează webhookurile care intră și secretul cu care se semnează jetoanele de dezabonare. Sunt verificări care par formale până când nu sunt: o verificare de semnătură nu are ce compara dacă secretul lipsește, iar un jeton previzibil e un jeton care nu protejează pe nimeni. Le trecem pe lista de predare, nu în presupuneri.
Ce se șterge la cerere și ce rămâne
Ștergerea completă scoate adresa din șase tabele și o pune pe o listă permanentă de blocare, astfel încât un import ulterior să nu o readucă. Rămâne o urmă în jurnal, cu motivul — pentru că trebuie să poți demonstra, peste un an, că cererea a fost onorată. Agregatele statistice, care nu identifică pe nimeni, rămân și ele.
A case
One address received 42 emails in a single day. It was not a loop.
Situația
Într-o campanie de prospectare pe organizații, o singură cutie poștală a primit 42 de mesaje în aceeași zi. Prima ipoteză, cea comodă, e o buclă în cod. Nu era.
Ce am construit
Extragerea destinatarilor era făcută per organizație, nu per adresă. Patruzeci și două de organizații foloseau aceeași cutie poștală — situație absolut obișnuită în mediul de afaceri local, unde un contabil, o firmă-mamă sau un administrator deservesc mai multe entități. Fiecare organizație era, corect, un destinatar unic; adresa nu era. Numărul total de persoane afectate în acel incident a fost 11. Reparația a fost în trei părți, nu în una: deduplicarea s-a mutat pe adresa scrisă cu litere mici, s-a introdus o răcire de cinci zile între campanii pentru aceeași adresă, iar toate scripturile de trimitere au fost obligate să treacă printr-un modul comun de protecție — cel care refuză să înregistreze o trimitere fără identificatorul campaniei. În aceeași zi a fost adăugat și întrerupătorul care oprește complet orchestratorul, fără cale de ocolire.
Ce a ieșit
Astăzi, deduplicarea pe adresă, lista de dezabonări, filtrele de domeniu și răcirea de cinci zile sunt garanții ale modulului comun, nu obiceiuri ale scriptului care se întâmplă să ruleze. Un script manual scris în grabă nu mai poate ocoli protecțiile, pentru că nu mai are voie să trimită singur.
Ce nu spune cazul
Lecția nu se transferă automat: fiecare bază de date are propria formă de duplicat. La una e adresa de email, la alta e numărul de telefon al unui grup de firme, la a treia e o persoană cu două funcții. Primul lucru pe care îl facem la un import nou e să căutăm care e forma de duplicat acolo — nu presupunem că e aceeași.
Întrebări
What people ask us before they call
Îmi vindeți CRM-ul cu care lucrați voi?
Nu, și motivul e tehnic, nu comercial. Sistemul nostru e croit pentru o singură echipă: nu are separare strictă a datelor între clienți și nu are un flux de instalare care să nu presupună acces la server. Ca să fie dat cuiva, ar trebui rescrise exact acele două lucruri. Îl arătăm din același motiv pentru care un tâmplar își arată atelierul — spune mai mult despre meserie decât o broșură. Ce vindem e construcția unui sistem pentru cum lucrezi tu.
De ce aș construi, în loc să cumpăr un CRM gata făcut?
De cele mai multe ori nu ar trebui, și ți-o spunem noi. Un instrument gata făcut e răspunsul corect când munca ta seamănă cu ipoteza pentru care a fost gândit. Construcția devine justificată în trei situații concrete: ai tipuri de organizații fundamental diferite care nu încap în același set de câmpuri; jumătate din atingeri vin din sisteme automate care trebuie să scrie singure în fișă; sau ai nevoie de reguli de trimitere pe care instrumentul nu ți le dă și pe care nu-ți permiți să le încalci. Dacă niciuna nu se aplică, îți spunem să cumperi.
Puteți lega sistemul de CRM-ul pe care îl folosim deja?
Da. Avem scris și rulat conectorul pentru amoCRM — 12 funcții de server, cu fluxul OAuth, reîmprospătarea jetonului, aducerea leadurilor și verificarea stării — și pentru Bitrix24, cu OAuth și webhook. Pentru Zoho CRM avem parcursul OAuth documentat pe regiuni de centru de date. Iar când sursa reală de adevăr a echipei e o foaie de calcul, avem zece funcții pentru Google Sheets, de la citirea structurii foii până la import și actualizare de contacte.
Cum ajung apelurile în CRM, concret?
Printr-un webhook semnat, trimis la sfârșitul apelului. Sistemul ia numărul de telefon și caută organizația printr-o funcție scrisă în bază pentru formatele în care apar numerele în date reale — nu printr-o comparație de șiruri, care ar rata jumătate din cazuri. Apelul se salvează cu transcript, rezumat, sentiment, adresa înregistrării și durată, legat de fișă, iar regula din cod poate urca leadul pe traseu în funcție de rezultatul apelului.
Cum vă asigurați că nu ajungem să trimitem spam?
Prin șase mecanisme care se verifică unul pe altul, nu printr-o intenție bună: ore de trimitere, liste de excludere pe tip de instituție, deduplicare pe adresă scrisă cu litere mici, răcire de cinci zile pentru aceeași adresă, plafoane zilnice și per rulare, și un întrerupător care oprește orchestratorul fără cale de ocolire. Mai jos e cazul concret care ne-a învățat de ce deduplicarea trebuie făcută pe adresă, nu pe organizație.
Ce se întâmplă când cineva cere să nu mai fie contactat?
Are trei trepte, toate protejate cu un jeton legat de destinatar. Dezabonarea într-un clic, direct din antetul emailului, conform RFC 8058. Un centru de preferințe, unde decizia se poate și anula. Și ștergerea completă, care scoate adresa din șase tabele și o adaugă pe o listă permanentă de blocare — astfel încât un import făcut peste șase luni să nu o readucă înapoi. Ultima parte e cea pe care majoritatea sistemelor o ratează.
Unde stau datele și cine le poate vedea?
Într-o bază PostgreSQL, pe infrastructura convenită la începutul proiectului, cu migrări versionate — adică fiecare schimbare de structură e un fișier, nu o intervenție manuală. Accesul e pe roluri distincte, cu un rol separat pentru agenții automați, pentru că un sistem care scrie în bază nu trebuie să aibă drepturile unui om. Nu există înregistrare deschisă și nu există suprafață publică.
Puteți importa baza noastră veche, cu tot cu dubluri?
Da, și tocmai dublurile sunt motivul pentru care importul e construit așa. Fișierul se procesează în flux, în loturi de 200 de rânduri, cu un index de deduplicare pe cod fiscal, email și telefon construit înainte de scriere, plus validarea adreselor: format, domenii de unică folosință, tipare invalide. Ce nu se potrivește nu se aruncă tăcut — se raportează, ca să decizi tu.
Îmi garantați o creștere a vânzărilor?
Nu. Putem garanta că vei ști cine a fost atins, prin ce canal, ce s-a spus și ce urmează — și că sistemul se oprește singur când ar trebui. Cifrele de rezultat depind de ofertă, de piață și de echipa care sună, adică de lucruri pe care nu le controlăm. Nu publicăm procente de îmbunătățire pe care nu le-am măsurat, nici pentru noi, nici pentru altcineva.
Pe ce se sprijină fiecare afirmație de mai sus (24 surse)
MEGA CRM nu are suprafață publică de produs: `crm.megapromoting.com` nu răspunde; aplicația e închisă în spatele autentificăriiprotected-route.tsx:—
server.js = 9.516 linii, 99 de rute HTTP; 96 de migrări SQL; circa 95.000 de linii TS în interfață; ultimul commit 18.08.2026server.js (wc -l = 9516; grep -cE '^app\.(get|post|put|patch|delete)\(' = 99); .git (git log -1):—
Șapte stadii de pipeline cu etichete reale, de la „Necontactat” la „Contract Semnat”, fiecare cu o țintă de conversie scrisă lângă elconstants.ts:12-20
Nouă canale de comunicare; roluri admin / owner / sales / outreach / ai_agentuser.ts:7 (UserRole):28+
Webhook de apel: organizația se identifică printr-o funcție de bază pe număr de telefon; „interested” sau „meeting_scheduled” urcă leadul pe traseuserver.js:1724-1790
Import în flux, loturi de 200 de rânduri, index de deduplicare pe cod fiscal + email + telefon, plafon de fișier 50 MBserver.js:5308-5394 (BATCH_SIZE la :5377, buildDeduplicationIndex la :5386); :61-63 (limita de fișier)
Validare de adrese la import: format, domenii de unică folosință, tipare invalideserver.js:4755-4770
Ore de trimitere 08:00–18:00, fus orar Europe/Chisinau, luni–sâmbătăexecute-campaign.mjs:206-215
Liste de excludere: școli, licee, grădinițe, ambasade, consulate, domenii guvernamentale străine; plus detectare de greșeli de tastareexecute-campaign.mjs:160-204
Modulul comun de protecție garantează deduplicarea pe adresă în litere mici (nu pe entity_id), răcirea de 5 zile între campanii, filtrele de domeniu și refuzul de a înregistra o trimitere fără campaign_idREADME.md:30-40
Incidentul din 1 mai 2026: o adresă a primit 42 de emailuri într-o zi pentru că extragerea era per entity_id, nu per email; 42 de firme foloseau aceeași adresă; 11 persoane afectateREADME.md:3-7, 134-142
Întrerupător de siguranță: variabila CAMPAIGNS_DISABLED oprește orchestratorul, fără --forcerun-all-campaigns.mjs:52-66
Plafon zilnic global împărțit proporțional între campanii, plus plafon per rulare, cu cron eșalonat pe mai multe orerun-all-campaigns.mjs:8-24, 194-217
RFC 8058: antetele List-Unsubscribe și List-Unsubscribe-Post one-click, cu jeton legat de destinatarexecute-campaign.mjs:76-80, 936-941
Ștergere completă la cerere: verificare de jeton, ștergere din 6 tabele, listă permanentă de blocare, urmă în jurnalserver.js:7005-7080
Centru de preferințe cu dezabonare și reabonare, protejat cu același jetonserver.js:6942-6990
Subsolul emailurilor conține identitatea firmei, codul fiscal, sediul, adresa responsabilului cu protecția datelor, temeiul legal și sursa adreseisend-tier1-marketing.mjs:531, 643, 739, 791
Culegerea răspunsurilor din cutia poștală prin Microsoft Graph la fiecare 10 minute; deschiderile la fiecare 2 minuteREADME.md:92-93, 124-130
Căutare semantică: model de înglobare text-embedding-3-large prin Azure, prag de similaritate implicit 0,25, limitare pe proiectserver.js:8112-8140
Monitorizare de credit la furnizori, cu alerte înainte de epuizareserver.js:5638-5680, 5794
Structura multi-proiect: proiecte cu schemă proprie de câmpuri, organizații cu custom_data JSONB și vector de căutare, contacte sub organizație, campanii pe proiectDATABASE-ARCHITECTURE.md:11-45, 51-78
12 funcții de server pentru amoCRM (inclusiv OAuth complet, reîmprospătare de jeton, verificare de stare), 4 pentru Bitrix24 (OAuth + webhook), 10 pentru Google Sheets (ls -d amocrm-* = 12; bitrix24-* = 4; *sheet* = 10):—
Ghid OAuth pentru Zoho CRM, pe regiuni de centru de dateZOHO_CRM_SETUP.md:1-25
Sistemul nu e pregătit să fie dat altcuiva: lipsesc separarea strictă a datelor între clienți și un flux de instalare fără acces la servermega-crm.json:implementation[2]
Citările interne arată numele fișierului și linia. Calea completă rămâne în depozitul nostru; o putem parcurge împreună, la cerere.