Cu endpoint-ul nou de căutare după nume și dată de naștere, identificarea se poate face corect la sursă. Douăzeci de noduri se reduc la trei, iar cele opt defecte critice dispar toate odată. Mai jos: ce ceri de la CRM, ce cod pui în n8n, ce unelte configurezi, ce tai din prompt.
Fluxul actual are două webhook-uri de căutare, patru copii ale aceleiași mapări de status, un If care ramifică pe codul de client și șase noduri de login. Toate există ca să compenseze un lucru pe care nu-l putea face: să găsească un client după cine este.
Endpoint-ul nou elimină nevoia de compensare. Ordinea corectă a canalelor de identificare devine:
1. nume + data nașterii → identitatea e dovedită de căutarea însăși
2. cod client + data → identitatea e dovedită de căutarea însăși
3. telefon → găsește dosarul, dar NU dovedește nimic
↳ agentul cere nume + dată și reia de la 1
Diferența față de acum: telefonul nu mai identifică pe nimeni. Găsește un dosar, atât. Asta rezolvă simultan cazul „Enulescu Valentin" (client inexistent, confirmat ca identificat) și cazul DALCRAN AMET / GHIULSEN AMET (doi titulari pe un număr) — pentru că, întrebat de nume și dată, endpoint-ul nou îl găsește pe cel corect, nu pe cel care se întâmplă să fie primul pe număr.
Și mai important: agentul nu mai compară nimic. Nu mai potrivește nume, nu mai judecă date de naștere, nu mai alege între dosare. Primește identity_match și status_text și le folosește ca atare. Tot ce a greșit în teste erau decizii pe care nu ar fi trebuit să le ia.
„Caut, apoi verific ce mi-a întors" este modelul corect — dar verificarea nu e aceeași pe toate canalele, iar tratarea lor la fel este exact bug-ul din versiunea actuală.
Telefonul restrânge. Numele și data nașterii dovedesc. Niciodată invers.
Când găsești clientul după nume și dată, căutarea însăși a fost verificarea — nu mai ai ce confirma. Când îl găsești după telefon, nu ai verificat absolut nimic: știi doar că numărul e cunoscut. Fluxul actual tratează al doilea caz ca pe primul, și de aici vine tot.
| Ce ai trimis | Ce a întors CRM-ul | Răspuns către agent | Ce spune agentul |
|---|---|---|---|
| doar telefon | nimic | found:false |
„Nu găsesc un dosar pe numărul de la care sunați." → cere numele și data nașterii |
| doar telefon | un client | found:trueidentity_match:false |
Nimic despre dosar. „Îmi spuneți vă rog numele complet și data nașterii?" |
| doar telefon | mai mulți clienți | found:trueidentity_match:false |
Identic. Fluxul nu alege niciodată singur între titulari. |
| nume + dată | un client | identity_match:truestatus_text |
Rostește status_text ca atare |
| nume + dată | nimic | found:false |
Cere numele pe litere, apoi codul de client |
| nume + dată | mai mulți potriviți | found:trueidentity_match:false |
Cere codul de client ca departajare |
| telefon găsise pe X, nume + dată dau pe Y | Y | identity_match:truepe Y |
Vorbește despre Y. X se ignoră complet. |
| cod + dată | un client | identity_match:true |
Rostește status_text |
Rândul penultim este cazul DALCRAN AMET. Telefonul +40730661679 duce la GHIULSEN AMET. Apelantul spune „Dalcran Amet, doi mai nouăzeci și trei". Căutarea după nume și dată îl găsește pe el, iar rezultatul obținut după telefon se aruncă — nu se compară, nu se completează, nu se folosește ca rezervă. Altfel te întorci exact la comportamentul din teste, în care agentul a insistat pe „Ahmed" de patru ori.
Numele și data nașterii se cer primele, iar telefonul apelantului călătorește odată cu ele, în aceeași cerere. Nu există o căutare separată „doar după telefon".
„Cu ce vă pot ajuta?" → „Vreau stadiul dosarului."
│
├─ „Sigur. Îmi spuneți numele complet și data nașterii?"
│ └─ „Am notat Ciolan Letiția, doisprezece martie optzeci și șapte.
│ Am înțeles corect?" ← confirmarea
│
├─ „O secundă."
│ cauta_dosar({ name, birthdate, phone: numărul apelantului })
│ └──────┬──────┘ └────────┬────────┘
│ canalul care merge oricum, ca
│ dovedește cine e rezervă, gratis
│
├─ identity_match true ──→ rostesc status_text. GATA.
│
└─ negăsit ─→ „Poate ați aplicat de pe alt număr. Care este?"
└─ cauta_dosar({ name, birthdate, phone: numărul nou })
└─ tot negăsit ─→ „Aveți codul de client?"
└─ tot negăsit → sarcină, închei
Un singur apel de unealtă în cazul obișnuit, în loc de două. Cu telefonul întâi, secvența era: caut după telefon → găsesc → tot trebuie să cer numele și data → caut a doua oară → răspund. Două pauze de așteptare pe linie în loc de una, ca să afli ceva ce oricum aveai nevoie să întrebi.
Mai e un câștig, de tonalitate. Cu telefonul întâi, agentul ajungea inevitabil la formularea din transcriptul real: „Am găsit dosarul dumneavoastră. Pentru a vă putea comunica detaliile, trebuie să vă identific." Sună birocratic și l-a derutat pe apelant — dacă l-ai găsit, de ce mai întrebi cine e? Cerând identificarea din start, întrebarea e firească: orice bancă o pune.
Telefonul rămâne în cerere, dar nu mai identifică pe nimeni. Îl trimiți pentru că e gratis și pentru că salvează apelurile în care transcrierea strică numele. Nu îl trimiți ca să afli cine sună.
Aceasta e diferența față de versiunea actuală, unde telefonul era identificarea. Așa a fost confirmat „Enulescu Valentin", client inexistent, și așa a primit DALCRAN AMET stadiul dosarului lui GHIULSEN AMET.
Fluxul încearcă, într-un singur apel: nume și dată → cod → telefon, și se oprește la primul canal care găsește ceva. Agentul trimite tot ce are și primește un verdict; nu orchestrează el încercările.
Ordinea aceasta contează, nu e o preferință. La apelul cu DALCRAN AMET, telefonul duce la GHIULSEN AMET. Dacă nodul ar căuta întâi după telefon și s-ar opri acolo, ar găsi persoana greșită, ar pica verificarea și ar răspunde „nu vă identific" — deși Dalcran este în sistem și putea fi găsit după numele lui. Răspunsul ar fi corect din punctul de vedere al confidențialității, dar clientul ar rămâne neajutat degeaba.
Când canalul telefonului chiar intră în joc — pentru că numele a fost prost transcris — rezultatul găsit trece obligatoriu prin verificare: nume tolerant plus dată exactă, față de înregistrarea găsită. Un dosar găsit după telefon nu deblochează niciodată nimic de unul singur.
Dacă după ce pui lista de nume în ASR vezi în continuare respingeri nedrepte, ai o pârghie de reglaj: pe canalul telefonului poți accepta dată de naștere exactă plus telefon găsit ca suficiente, chiar dacă numele nu trece pragul. Sunt două dovezi independente, iar o dată de naștere nimerită din întâmplare pe un număr cunoscut e practic imposibilă. Începe însă strict, cu ambele condiții, și relaxează doar dacă datele o cer.
Tentația firească în n8n e să ramifici cu If sau Switch după ce parametri ai. Nu o face aici, din două motive concrete.
Respond separat pe fiecare ramură.HTTP Request12, care citește $('GetClient') de pe ramura GetLead unde nodul acela nu rulează niciodată, nu se poate scrie într-un nod Code. Variabilele sunt în același scope sau nu există.Contrapartida onestă: un nod Code se depanează mai greu vizual, iar cine intră după tine în workflow vede o cutie neagră în loc de un traseu. Dacă echipa nu e confortabilă cu JavaScript, compromisul rezonabil e trei noduri Code mici — caută, verifică, compune mesajul — legate în linie, fără nicio ramificare. Nu însă opt noduri HTTP cu If între ele.
Două endpoint-uri. Primul e cel pe care îl primești oricum; al doilea e obligatoriu și lipsește acum, iar fără el problema dosarelor multiple nu se poate repara.
POST /public/api/get-client-by-name-birthdate
{ "name": "Letiția Ciolan", "birthdate": "12/03/1987", "ai_agent": 1 }
Răspuns dorit:
{ "client": { "id": 43692, "firstname": "LETITIA", "lastname": "CIOLAN",
"birthdate": "1987-03-12", "phone": "+40727750242",
"iban": "", "unique_id": "EZ234" } }
sau, dacă potrivirea e ambiguă:
{ "clients": [ {...}, {...} ] }
Cere-i explicit potrivire tolerantă pe numele de familie. Transcrierea vocală va trimite Globa pentru GLOABA și Colan pentru CIOLAN. Dacă endpoint-ul face potrivire exactă, nu ai rezolvat nimic. Formularea pentru dezvoltator: „caută numele de familie cu toleranță de două litere — Levenshtein — și filtrează rezultatele după data nașterii, care trebuie să fie exactă."
Dacă nu poate face asta în CRM, codul de mai jos are potrivirea fuzzy inclusă și funcționează oricum — dar atunci endpoint-ul trebuie să întoarcă toți clienții cu acea dată de naștere, ca filtrarea pe nume să se facă în n8n.
POST /public/api/get-leads-by-client
{ "client_id": 43692 }
Răspuns dorit:
{ "leads": [
{ "id": 63766, "status": 9, "created_at": "2025-04-02T…",
"sent_date": "2025-05-11", "deadline": null,
"last_transfer_date": "2025-08-01", "iban": "RO49…" },
{ "id": 71204, "status": 6, "created_at": "2026-07-18T…", … }
] }
Acesta e blocajul real. Acum fluxul apelează get-lead cu un lead_id în care pasează, de fapt, un id de client — două numere diferite. Chiar dacă ar fi corect, get-lead întoarce un singur dosar, iar clienții voștri au până la opt. LETIȚIA CIOLAN a auzit „urmează să primiți banii" pentru că singurul dosar citit era unul finalizat din 2025, în timp ce cel din 2026 abia intrase.
Un singur webhook, un singur nod Code, un singur Respond. Înlocuiește tot ce există acum pe ramurile GetLead și GetClient.
Webhook „cauta_dosar" → Code „Cautare si identificare" → Respond to Webhook
responseMode: (codul de mai jos) responseBody:
responseNode {{ JSON.stringify($json) }}
If, Code in JavaScript 10, 11, 12, 13, 14, 15, 16, 17, getIdLead4, getIdLead5, HTTP Request 2, 5, 8, 10, 11, 12 și cele patru Respond to Webhook 6, 8, 9, 10. Toate sunt înlocuite.$env.TAXEUK_USER și $env.TAXEUK_PASS. Dacă instanța ta are accesul la environment blocat, ține login-ul într-un nod HTTP Request separat cu o credențială n8n și pasează token-ul./**
* TaxeUK — cautare dosar si identificare client
* Intrare (body webhook): { name, birthdate, phone, client_code }
* Iesire: { found, identity_match, client_id, leads_total,
* status_code, status_label, status_text }
*/
const B = $input.first().json.body || {};
const CRM = 'https://crm.taxeuk.com/public/api';
const H = { 'Content-Type':'application/json', 'X-Requested-With':'XMLHttpRequest' };
const post = async (cale, body, token) => this.helpers.httpRequest({
method:'POST', url:`${CRM}/${cale}`,
headers: token ? { ...H, Authorization:`Bearer ${token}` } : H,
body, json:true, returnFullResponse:true, ignoreHttpStatusErrors:true,
});
const iesire = o => [{ json: {
found:false, identity_match:false, client_id:null, leads_total:0,
status_code:null, status_label:null, status_text:'', ...o } }];
/* ---------- normalizari ---------- */
const norm = s => (s||'').toString().toLowerCase()
.normalize('NFD').replace(/[\u0300-\u036f]/g,'')
.replace(/[^a-z]+/g,' ').trim();
const lev = (a,b) => {
const d = Array.from({length:a.length+1}, (_,i)=>[i,...Array(b.length).fill(0)]);
for (let j=0;j<=b.length;j++) d[0][j]=j;
for (let i=1;i<=a.length;i++)
for (let j=1;j<=b.length;j++)
d[i][j] = Math.min(d[i-1][j]+1, d[i][j-1]+1,
d[i-1][j-1] + (a[i-1]===b[j-1] ? 0 : 1));
return d[a.length][b.length];
};
// orice format de data -> AAAA-LL-ZZ, ca sa se poata compara
const zi = s => {
const t = (s||'').toString().trim();
let m = t.match(/^(\d{1,2})[\/.-](\d{1,2})[\/.-](\d{4})/);
if (m) return `${m[3]}-${m[2].padStart(2,'0')}-${m[1].padStart(2,'0')}`;
m = t.match(/^(\d{4})-(\d{2})-(\d{2})/);
return m ? `${m[1]}-${m[2]}-${m[3]}` : null;
};
const tel9 = s => ((s||'').toString().replace(/\D/g,'').slice(-9)) || null;
// numele de familie apare in ce a dictat clientul, cu toleranta de 1-2 litere
const numeSePotriveste = (spus, client) => {
const cuvinte = norm(spus).split(' ').filter(Boolean);
const familie = norm(client.lastname).replace(/ /g,'');
if (!familie || !cuvinte.length) return false;
const prag = familie.length <= 4 ? 1 : 2;
return cuvinte.some(c => lev(c, familie) <= prag);
};
/* ---------- 1. autentificare ---------- */
const login = await post('auth/login', {
email: $env.TAXEUK_USER,
password: $env.TAXEUK_PASS,
password_confirmation: $env.TAXEUK_PASS,
});
const token = login.body?.access_token;
if (!token) return iesire({ status_text:'EROARE_AUTENTIFICARE' });
/* ---------- 2. gasirea clientului, in ordinea increderii ---------- */
let client = null, canal = null;
const dataCeruta = zi(B.birthdate);
// A — nume + data nasterii
if (B.name && dataCeruta) {
const r = await post('get-client-by-name-birthdate',
{ name: B.name, birthdate: B.birthdate, ai_agent: 1 }, token);
const lista = r.body?.clients ?? (r.body?.client ? [r.body.client] : []);
// daca CRM intoarce mai multi, filtram noi: data exacta + nume tolerant
const c = lista.find(x => zi(x.birthdate) === dataCeruta
&& numeSePotriveste(B.name, x));
if (c?.id) { client = c; canal = 'nume+data'; }
}
// B — cod de client + data nasterii
if (!client && B.client_code && dataCeruta) {
const r = await post('get-client', {
unique_id: B.client_code.toString().toUpperCase().replace(/\s/g,''),
birthdate: B.birthdate, ai_agent: 1 }, token);
const c = r.body?.client;
if (c?.id && zi(c.birthdate) === dataCeruta) { client = c; canal = 'cod'; }
}
// C — telefon. Gaseste dosarul, dar NU dovedeste identitatea.
if (!client && tel9(B.phone)) {
const r = await post('get-client-id-by-phone', { phone: B.phone }, token);
const c = r.body?.client ?? r.body;
if (c?.id) { client = c; canal = 'telefon'; }
}
if (!client) return iesire({ found:false });
/* ---------- 3. verificarea identitatii ---------- */
let identitate;
if (canal === 'nume+data' || canal === 'cod') {
identitate = true; // dovedita de cautarea insasi
} else {
// gasit dupa telefon: cerem nume tolerant + data EXACTA
identitate = !!(B.name && dataCeruta
&& zi(client.birthdate) === dataCeruta
&& numeSePotriveste(B.name, client));
}
// exista un dosar pe numar, dar nu stim inca cine suna
if (!identitate) return iesire({ found:true, identity_match:false });
/* ---------- 4. toate dosarele clientului ---------- */
const rl = await post('get-leads-by-client', { client_id: client.id }, token);
let dosare = rl.body?.leads ?? rl.body?.lead ?? [];
if (!Array.isArray(dosare)) dosare = dosare ? [dosare] : [];
if (!dosare.length) return iesire({
found:true, identity_match:true, client_id:client.id,
status_text:'vă am în evidență, dar nu găsesc un dosar activ pe numele '
+ 'dumneavoastră. Transmit solicitarea unui coleg, care vă va contacta.',
});
/* ---------- 5. alegerea dosarului relevant ---------- */
const ETICHETA = { 0:'CANCELLED', 1:'NEW', 2:'TR/PARTIAL', 3:'MISSING INFO',
4:'WAITING', 5:'PROCESSED', 6:'SENT', 7:'OUTSTANDING',
8:'CHEQUE RECEIVED', 9:'TRANSFERRED' };
const DESCHISE = [1,2,3,4,5,6,7,8];
const deschise = dosare.filter(l => DESCHISE.includes(Number(l.status)));
const dosar = (deschise.length ? deschise : dosare)
.sort((a,b) => new Date(b.created_at) - new Date(a.created_at))[0];
const total = dosare.length;
const finalizate = dosare.filter(l => Number(l.status) === 9).length;
const cod = Number(dosar.status);
/* ---------- 6. mesajul rostit ---------- */
const ro = d => { const x = new Date(d); return isNaN(x) ? null :
`${String(x.getUTCDate()).padStart(2,'0')}.`
+ `${String(x.getUTCMonth()+1).padStart(2,'0')}.${x.getUTCFullYear()}`; };
const prenume = (client.firstname||'').trim().split(/\s+/)[0];
const salut = prenume ? `${prenume}, ` : '';
const areIban = !!(client.iban || dosar.iban);
const trimisLa = ro(dosar.sent_date || dosar.created_at);
const termen = ro(dosar.deadline);
const transfer = ro(dosar.last_transfer_date);
const MESAJ = {
1:`dosarul dumneavoastră este înregistrat la noi și urmează să fie verificat de un coleg din departamentul de procesare.`,
5:`dosarul dumneavoastră a fost procesat și este pregătit pentru trimitere către biroul de taxe. Nu mai este nevoie de nimic din partea dumneavoastră.`,
6:`dosarul dumneavoastră a fost trimis către biroul de taxe din Marea Britanie${trimisLa?`, în data de ${trimisLa}`:''}, și se află în analiză la ei.`,
4:`dosarul dumneavoastră este în lucru la biroul de taxe${termen?`, cu termen de răspuns până în ${termen}`:''}. Îl urmărim și vă anunțăm imediat ce apare o noutate.`,
7:`termenul de răspuns pentru dosarul dumneavoastră a fost depășit. Colegii noștri iau legătura direct cu biroul de taxe și vă sunăm cu informațiile primite.`,
3:`dosarul dumneavoastră nu este complet — mai avem nevoie de câteva documente. Un coleg vă va contacta să vă spună exact ce lipsește.`,
8: areIban
? `cecul cu suma recuperată a ajuns la noi. Urmează să facem transferul în contul dumneavoastră, în maximum șapte zile lucrătoare.`
: `cecul cu suma recuperată a ajuns la noi. Ca să putem face transferul, avem nevoie de contul dumneavoastră bancar.`,
2:`am primit o parte din suma recuperată, pentru anii deja soluționați. Pentru ceilalți ani, dosarul este în continuare în analiză la biroul de taxe.`,
9:`dosarul dumneavoastră a fost finalizat, iar suma recuperată a fost transferată${transfer?` în data de ${transfer}`:''} în contul indicat.`,
0:`acest dosar figurează ca închis și nu mai este în lucru. Un coleg vă poate confirma motivul exact.`,
};
const corp = MESAJ[cod]
?? `în acest moment nu pot confirma cu exactitate stadiul dosarului dumneavoastră. `
+ `Transmit solicitarea unui coleg, care vă va contacta.`;
const prefix = total <= 1 ? ''
: deschise.length
? `aveți ${total} dosare la noi, dintre care ${finalizate} finalizate. Despre cel aflat în lucru: `
: `aveți ${total} dosare la noi, toate finalizate. Despre ultimul: `;
return iesire({
found: true,
identity_match: true,
client_id: client.id,
leads_total: total,
status_code: Number.isFinite(cod) ? cod : null,
status_label: ETICHETA[cod] ?? 'NECUNOSCUT',
status_text: salut + prefix + corp,
});
{{ JSON.stringify($json) }}
Ce dispare odată cu asta: mesajul fix de dosar finalizat, selecția leads[0], referința între fluxuri din HTTP Request12, confuzia client_id / lead_id, telefonul 000000000 de pe ramura getIdLead5, cele patru copii ale mapării de status și răspunsurile goale care ajungeau la agent fără să-i spună dacă e „nu există" sau „a picat ceva".
Ștergi GetLeadTaxeUK și GetclientTaxeUK. Le înlocuiește una singură. Descrierea uneltei este locul unde pui regula de siguranță — cântărește mai mult decât orice paragraf din promptul de 36.000 de caractere.
cauta_dosarCaută dosarul clientului în sistem.
CÂND SĂ APELEZI:
După ce ai obținut numele complet și data nașterii, și le-ai confirmat
cu clientul. Nu o apela mai devreme — numărul de telefon singur nu
identifică pe nimeni și nu îți dă dreptul să comunici nimic.
Trimiți ÎNTOTDEAUNA și phone, completat cu numărul apelantului, chiar
dacă ai deja numele și data. Merge ca rezervă și nu costă nimic.
O apelezi din nou de fiecare dată când obții o informație nouă: alt
număr de telefon, sau codul de client. Câmpurile pe care nu le ai le
lași goale. Nu inventezi valori.
CE PRIMEȘTI ÎNAPOI:
found — dacă există sau nu un dosar
identity_match — dacă ai voie să comunici ceva din dosar
status_text — fraza pe care o rostești clientului
client_id — îl folosești la create_task și create_notita
CUM FOLOSEȘTI RĂSPUNSUL:
- identity_match true → rostești status_text exact așa cum l-ai primit,
fără să reformulezi și fără să adaugi nimic
- identity_match false → NU spui nimic despre dosar. Ceri numele complet
și data nașterii, apoi apelezi din nou
- found false → „Nu reușesc să vă identific cu aceste date."
Nu deduci niciodată stadiul unui dosar din alt câmp decât status_text.
Cuvintele „finalizat", „soluționat" și „transferat" nu apar în răspunsul
tău dacă nu apar în status_text.
| Parametru | Tip | Obligatoriu | Descriere pentru model |
|---|---|---|---|
name | string | nu | Numele complet spus de client, așa cum l-ai auzit. Nu îl corecta. |
birthdate | string | nu | Data nașterii în format zz/ll/AAAA. Exemplu: 12/03/1987. |
phone | string | nu | Format internațional cu prefix: +40727750242. Niciodată cu zero la început. |
client_code | string | nu | Codul de client, cinci caractere, litere și cifre. Exemplu: EZ234. |
Tipul lui phone trebuie schimbat din number în string. Un tip numeric nu poate păstra zeroul din față, deci formatul 07… pe care îl cerea vechea descriere era imposibil de trimis. Din 89 de căutări analizate, zero au fost în formatul cerut.
Și niciun câmp nu trebuie să fie obligatoriu. Când totul e obligatoriu, modelul umple golurile cu șirul "null", care ajunge așa în CRM — s-a întâmplat deja la lead-ul lui PETRU MERLAN.
| Unealtă | Ce schimbi |
|---|---|
create_taskTaxeUK |
Adaugi parametrul client_id (string, opțional). În n8n îl folosești când vine completat, cu revenire la căutarea după telefon când lipsește — și ștergi nodul getIdLead2 care caută 000000000. Termenul: {{ $('CreareTask').first().json.body.task_deadline }} în loc de data fixă 25/08/2026. |
CreareLeadTaxeUK |
Adaugi phone — lipsește complet acum, de aceea SMS-ul nu poate ajunge nicăieri. Adaugi country_id, postal_office_send, referral, no_visits_for_taxes_request. Faci toate câmpurile opționale. Înlocuiești descrierea (mai jos). |
create_notitaTaxeUK |
O creezi — nu există pe agent. Webhook 740807ea-…, parametri client_id și details. În n8n conectezi nodul CreareNotita, care acum nu duce nicăieri, și faci client_id și details expresii în loc de 43692 și "AgentVocal". |
trimite_sms_link |
Unealtă nouă, parametri phone și client_id. Trimiterea unui link e o acțiune deterministă — nu are motiv să treacă printr-un om, cum face acum. |
Înregistrează în sistem un dosar nou. CÂND SĂ APELEZI: La finalul ORICĂREI discuții de aplicare nouă, indiferent câte date ai obținut. Nu există prag minim. Dacă ai doar numele, apelezi cu numele. Câmpurile neobținute le lași goale — un coleg le completează ulterior. Un dosar incomplet în sistem este util. Un dosar necreat nu există.
Descrierea actuală spune exact invers — „doar când clientul a furnizat toate datele obligatorii" — în timp ce regula R5 din prompt spune „întotdeauna, fără excepție". Modelul urmează descrierea uneltei: a apelat-o de două ori în 68 de apeluri. Nu e o problemă de disciplină, e o instrucțiune contradictorie.
send-formToate valorile de mai jos sunt scrise fix în HTTP Request13 și suprascriu ce colectează agentul. Le înlocuiești cu expresii.
phone +40740779788 → {{ $('CreareLead').item.json.body.phone }}
uuid 550e8400-… → {{ $execution.id }}-{{ $now.toMillis() }}
notes "express daca…" → {{ $('CreareLead').item.json.body.notes }}
no_jobs 0 → {{ $('CreareLead').item.json.body.no_jobs }}
next_leave 1 → {{ $('CreareLead').item.json.body.next_leave }}
next_leave_date 0000-00-00 → {{ $('CreareLead').item.json.body.next_leave_date }}
no_visits_… 0 → {{ $('CreareLead').item.json.body.no_visits_for_taxes_request }}
postal_office… 0 → {{ $('CreareLead').item.json.body.postal_office_send }}
language 1 → ro
iban, sort_code, account_number, account_holder, signature, referral
sirul "null" → gol, prin nodul Set de mai jos
const curat = v => (v == null || v === 'null' || v === 'undefined' || v === '')
? null : v;
const b = $('CreareLead').first().json.body;
return [{ json: Object.fromEntries(
Object.entries(b).map(([k,v]) => [k, curat(v)])
) }];
Telefonul fix e cel mai costisitor dintre toate. Fiecare client nou intră în CRM cu numărul +40740779788, deci linkul de atașare documente și semnare contract nu ajunge la el niciodată. Dosarul se blochează fără ca nimeni să afle de ce.
Cu identificarea mutată în backend, promptul scade semnificativ. Tot ce ștergi sunt instrucțiuni pentru decizii pe care modelul nu mai trebuie să le ia.
GetLeadTaxeUK / GetclientTaxeUK și tabelul de decizie între ele. Există o singură unealtă.client_id cu trei ramuri. Vine în răspuns.După ce clientul îți spune numele și data nașterii, le repeți ÎNAPOI o singură dată, înainte de a apela unealta: „Am notat Ciolan Letiția, doisprezece martie o mie nouă sute optzeci și șapte. Am înțeles corect?" Codul de client are exact cinci caractere și poate conține atât litere cât și cifre. Nu afirmi niciodată că e format doar din litere. Îl ceri fonetic — „A ca Ana, B ca Barbu" — și îl confirmi tot fonetic. Dacă ai obținut codul, îl trimiți; nu îl trimiți niciodată gol.
Nu ceri niciodată IBAN-ul, sort code-ul sau numărul de cont la telefon. Dacă clientul îl oferă, îi spui: „Vă mulțumesc, dar contul nu vi-l pot prelua la telefon. Vă trimitem un mesaj și ni-l transmiteți acolo, în siguranță."
„V-am găsit. Mai întâi de toate: numărul de la care sunați acum este cel pe care vă putem contacta? Pe el vă trimitem linkul pentru documente și pentru semnarea contractului." Dacă are alt număr, îl ceri, îl citești înapoi cifră cu cifră și confirmi. Această întrebare nu se sare niciodată. La un client deja identificat NU mai ceri: numele, data nașterii, NINO, adresa din România, emailul. Le ai deja. Ceri doar ce s-a schimbat și perioada nouă de muncă.
Optzeci de lire este comisionul pentru trimiterea dosarului. La el se pot adăuga costuri suplimentare dacă sunt necesare intervenții ulterioare. Nu spui niciodată „acoperă tot", „este comisionul final" sau „nu mai plătiți nimic". Spui: „Optzeci de lire este comisionul pentru trimiterea dosarului. Dacă apar intervenții suplimentare, se tarifează separat, iar un coleg vă poate confirma exact ce se aplică în cazul dumneavoastră." Nu confirmi niciodată comisionul unui dosar existent din Knowledge — Knowledge conține tarifele standard, nu condițiile din dosarul clientului.
Nu renunți după un canal eșuat. Canalele sunt patru, în ordine: 1. numărul de la care sună 2. numele complet și data nașterii 3. alt număr de telefon 4. codul de client Maxim două încercări pe fiecare canal; a doua oară ceri pe litere. Treci la canalul următor, nu repeți același lucru. Lași sarcină și închei doar după ce toate patru au eșuat. Dacă clientul te corectează — „nu", „nu e așa", „greșit" — te oprești pe acel câmp. Nu treci mai departe. O corecție a clientului nu se numără ca încercare eșuată.
qwen35-397b-a17b pe unul mai capabil. Temperatura 0.0 rămâne — e corectă.turn_timeout: de la 7 la 9 secunde.Rotește parola contului ai.agent@taxeuk.com. Oprește agentul de pe traficul real de verificare stadiu — în forma actuală spune clienților că au primit bani pe care nu i-au primit.
Căutarea după nume și dată o primești oricum. get-leads-by-client e blocajul — fără el nu se poate repara problema dosarelor multiple. Trimite-i dezvoltatorului secțiunea Pasul 1.
Îl poți construi și testa cu endpoint-urile existente, lăsând canalul A să eșueze silențios. Când vine endpoint-ul nou, se activează singur.
Telefoanele 000000000, telefonul fix din send-form, termenul fix al sarcinilor, notița fixă, UUID-ul comun. Se pot face în paralel cu 2.
cauta_dosar, phone pe lead, client_id pe sarcină, notița, SMS-ul. Apoi tăieturile din prompt.
Ultimele, pentru că sunt îmbunătățiri de calitate, nu reparații. Re-rulează testele după fiecare.
| Test | Trece dacă |
|---|---|
| Nume inventat plus dată de naștere | „Nu reușesc să vă identific." Niciun status rostit. |
| Dată de naștere greșită intenționat | Identificare eșuată. Nu „v-am identificat". |
| DALCRAN AMET de pe numărul comun cu GHIULSEN AMET | Cu nume și dată, îl găsește pe el. Nu spune nimic despre celălalt dosar. |
| CIOLAN (EZ234) — 8 dosare | „Aveți 8 dosare, 7 finalizate. Despre cel aflat în lucru…" |
| TRANCA (FHNX5) — dosar anulat | Spune „închis". Nu „finalizat", nu „transferat". |
| MACELARU (EW345) — cec primit, transfer neefectuat | Spune că cecul a ajuns și transferul urmează. |
| GLOABA, CIOLAN, MIHART — doar cu nume și dată | Identificare din prima, fără telefon și fără cod. |
| Aplicare nouă completă | În CRM: lead cu telefonul real, sarcini cu termene diferite, notiță, SMS trimis. |
| Două sarcini în același apel | Ambele pe clientul corect, cu termene distincte. |