TaxeUK · ghid de implementare

Fluxul reparat

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.

IdeeaDe ce se simplifică atât de mult

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.

RegulaAsimetria care decide totul

„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 faci în fiecare caz

Ce ai trimisCe a întors CRM-ulRăspuns către agentCe spune agentul
doar telefonnimic found:false „Nu găsesc un dosar pe numărul de la care sunați." → cere numele și data nașterii
doar telefonun client found:true
identity_match:false
Nimic despre dosar. „Îmi spuneți vă rog numele complet și data nașterii?"
doar telefonmai mulți clienți found:true
identity_match:false
Identic. Fluxul nu alege niciodată singur între titulari.
nume + datăun client identity_match:true
status_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:true
identity_match:false
Cere codul de client ca departajare
telefon găsise pe X,
nume + dată dau pe Y
Y identity_match:true
pe 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.

Scara conversației

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.

Ordinea dinăuntrul nodului

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.

Un nod Code, nu ramuri cu If

Tentația firească în n8n e să ramifici cu If sau Switch după ce parametri ai. Nu o face aici, din două motive concrete.

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.

Pasul 1Ce ceri de la dezvoltatorul CRM

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.

A · căutare după nume și dată de naștere — cel anunțat
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.

B · toate dosarele unui client — lipsește acum
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.

Pasul 2Nodul unic din n8n

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

Înainte de a lipi codul

Code node — „Cautare si identificare" · Run Once for All Items
/**
 * 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,
});
Respond to Webhook — responseBody
{{ 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".

Pasul 3Uneltele din ElevenLabs

Ș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.

Unealtă nouă — cauta_dosar

descriere
Caută 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.
ParametruTipObligatoriuDescriere pentru model
namestringnuNumele complet spus de client, așa cum l-ai auzit. Nu îl corecta.
birthdatestringnuData nașterii în format zz/ll/AAAA. Exemplu: 12/03/1987.
phonestringnuFormat internațional cu prefix: +40727750242. Niciodată cu zero la început.
client_codestringnuCodul 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.

Corecții la uneltele existente

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 phonelipseș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.
CreareLeadTaxeUK — descriere nouă
Î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.

Pasul 4Nodul send-form

Toate valorile de mai jos sunt scrise fix în HTTP Request13 și suprascriu ce colectează agentul. Le înlocuiești cu expresii.

HTTP Request13 — acum → corect
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
nod Set înainte de send-form — curăță șirurile „null"
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.

Pasul 5Ce tai și ce adaugi în prompt

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.

De șters

De adăugat

confirmarea înapoi — la Pasul 2, înainte de apelul uneltei
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.
contul bancar — înlocuiește blocul de cerere
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ță."
clientul existent — prima întrebare după identificare
„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ă.
comisionul — înlocuiește în Knowledge
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.
pragul de abandon — înlocuiește regula actuală
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ă.

Setări agent

OrdineaÎn ce succesiune faci lucrurile

0

Astăzi, înainte de orice

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.

30 min
1

Cere cele două endpoint-uri

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.

depinde de CRM
2

Nodul unic în n8n

Î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.

~4 ore
3

Valorile de test din producție

Telefoanele 000000000, telefonul fix din send-form, termenul fix al sarcinilor, notița fixă, UUID-ul comun. Se pot face în paralel cu 2.

~3 ore
4

Uneltele și promptul

cauta_dosar, phone pe lead, client_id pe sarcină, notița, SMS-ul. Apoi tăieturile din prompt.

~5 ore
5

Lista ASR, modelul, Knowledge

Ultimele, pentru că sunt îmbunătățiri de calitate, nu reparații. Re-rulează testele după fiecare.

~4 ore

Cele nouă teste care trebuie să treacă

TestTrece dacă
Nume inventat plus dată de naștere„Nu reușesc să vă identific." Niciun status rostit.
Dată de naștere greșită intenționatIdentificare eșuată. Nu „v-am identificat".
DALCRAN AMET de pe numărul comun cu GHIULSEN AMETCu 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 anulatSpune „închis". Nu „finalizat", nu „transferat".
MACELARU (EW345) — cec primit, transfer neefectuatSpune 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 apelAmbele pe clientul corect, cu termene distincte.