Sari la conținut
megapromotingHai să discutăm

Expertise · Integrations and automations

Get your systems talking to each other, without anyone retyping the same data.

We connect the booking system, the store, the CRM, the spreadsheets and the messaging channels, so that an agent or a scheduled rule can read and write in them in real time. The keys stay on the server, the tools have written limits, and every integration is also tested for the case where the other system is down.

Construit dejaIntegrări proprii, scrise și rulate, măsurate azi în cod: conectorul pentru sistemul de programări Altegio (946 de linii, cu șase unelte expuse agentului), conectorul de CRM amoCRM (2.888 de linii în platforma de mesagerie, plus 12 funcții de server în platforma vocală), Bitrix24 (903 linii, plus 4 funcții), Google Sheets (615 linii, plus 10 funcții și un cont de serviciu dedicat), catalogul viu peste magazin (serviciu de platformă, 318 linii), notificări de lead pe Telegram (1.776 de linii), SMS prin Infobip cu limite anti-abuz, automatizări programate (1.088 de linii) și canalul de chat al unei platforme locale de anunțuri (2.459 de linii). În execuție, agentul poate chema 15 tipuri de unelte interne, pe lângă unelte prin webhook și unelte cu cod propriu rulat în izolare.

O integrare nu e un logo pe o pagină. E răspunsul la o întrebare foarte concretă: poate sistemul acesta să fie citit și scris în timp real, de cine, cu ce drepturi, și ce se întâmplă când nu răspunde? De aceea prima noastră întrebare la orice integrare nu e „cu ce API”, ci „ce poartă publică are”. Uneori răspunsul schimbă complet costul lucrării: magazinele pe WooCommerce, de pildă, expun public un API de magazin care nu cere nicio cheie de consumator, deci un client nou se conectează doar cu adresa site-ului lui — nu cu credențiale pe care trebuie să le genereze, să le trimită și apoi să le rotească.

În platforma noastră de mesagerie, o unealtă pe care agentul o poate chema în timpul conversației are unul din trei tipuri: apel către un webhook, cod propriu rulat izolat, sau o unealtă internă a platformei. Uneltele interne sunt cele pe care le preferăm, pentru că țin cheile în afara bazei de date: astăzi sunt cincisprezece, de la citirea unei pagini și scrierea într-o foaie de calcul, până la disponibilitatea și crearea unei programări, trimiterea unui SMS și interogarea catalogului de produse.

Diferența dintre o integrare care ține și una care se rupe stă aproape întotdeauna în lucruri mărunte, aflate pe teren. Sistemul de programări nu întoarce nimic dacă întrebi orele unui specialist pentru un serviciu pe care nu îl face — serviciul și specialistul călătoresc mereu împreună. Același sistem răspunde cu refuz dacă cererea vine cu antetul implicit al bibliotecii HTTP, deci fiecare cerere trebuie să se identifice explicit. Un magazin poate raporta preț zero pentru produsele cu variante, iar o căutare pe cuvinte goale întoarce tot catalogul. Sunt zece asemenea detalii per integrare, și niciunul nu e în documentație.

Peste integrări stau automatizările: reguli care se execută în timp, nu la cererea cuiva. La noi sunt programate cu un planificator în proces — reluarea conversațiilor rămase fără răspuns, primul mesaj către un contact nou, punerea pe pauză și repornirea agentului după programul de lucru — fiecare cu contoare proprii, ca să se vadă câte rulări au fost, câte mesaje au plecat, câte au fost sărite și de ce. O automatizare fără contor e o automatizare despre care nu poți spune dacă funcționează.

What it covers

The work, piece by piece

Întâi verificăm ce poartă publică are sistemul

Pentru magazinele pe WooCommerce folosim API-ul public de magazin, care nu cere cheie de consumator: clientul se conectează doar cu adresa site-ului. E o alegere care are consecințe reale, nu doar estetice — am înlocuit cu ea unelte scrise de mână care țineau chei de magazin în clar în baza de date. Când sistemul nu are poartă publică, trecem la autentificare, dar atunci cheia devine o piesă care se administrează, nu una care se uită într-un rând de bază de date.

Cheile rămân pe server, nu în unealtă și nu în pagină

Unealta de SMS e internă platformei tocmai pentru că mediul în care rulează codul propriu al unei unelte nu are acces la variabilele de mediu — dacă trimiterea s-ar face din cod de unealtă, cheia ar trebui scrisă în baza de date, iar acea cheie poate trimite mesaje pe cheltuiala tuturor. La fel la sistemul de programări: jetonul de partener e unul singur, ținut în mediul serverului, în timp ce clientul își pune doar identificatorul companiei lui.

Agentul cheamă unealta în timpul conversației, cu parametri și termen

Fiecare unealtă are o descriere scrisă pentru model, parametri tipizați cu ce e obligatoriu și ce nu, și un termen propriu de execuție — de la 15 secunde pentru o listă de servicii până la 30 pentru o căutare de disponibilitate. Termenul per unealtă nu e un detaliu: fără el, o integrare lentă blochează conversația, iar omul de la celălalt capăt aude tăcere.

Programări reale în sistemul salonului sau al clinicii

Șase unelte peste Altegio: lista serviciilor cu prețurile reale, specialiștii, disponibilitatea completă, crearea programării, anularea și modificarea. Disponibilitatea nu întoarce doar „liber/ocupat”: întoarce ce specialiști fac serviciul cerut și primele ore libere pentru fiecare, ordonate după cel mai devreme, tocmai ca agentul să propună o alternativă concretă în loc să spună „nu e disponibil”.

Catalogul magazinului, citit viu

Prețul și disponibilitatea se citesc din magazin la momentul întrebării, nu dintr-o copie învechită. Catalogul se încarcă paginat, până la 30 de pagini de câte 100 de produse, se ține într-o memorie temporară de zece minute pe site și se normalizează într-o singură formă, citită fără modificări de toți consumatorii — inclusiv de carusela de produse din mesagerie, care are nevoie de preț numeric, și de widgetul din pagină, care are nevoie de titlu.

CRM-uri, foi de calcul și canale de mesaje

amoCRM și Bitrix24 cu OAuth complet, reîmprospătare de jeton și verificare de stare. Google Sheets printr-un cont de serviciu dedicat, separat de restul credențialelor, cu scriere declanșată de agent. Telegram pentru notificări de lead. SMS prin Infobip. Plus canalul de chat al unei platforme locale de anunțuri, conectat cu jeton de reîmprospătare propriu — genul de integrare care nu există în niciun catalog internațional și care contează foarte mult local.

Automatizări care rulează în timp, cu contoare

Reluarea conversațiilor rămase fără răspuns, primul mesaj către un contact nou, punerea pe pauză și repornirea agentului după program. Fiecare rulare incrementează contoare vizibile: câte rulări, câte mesaje trimise, câte sărite pentru că agentul era oprit, câte pentru că nu avea configurație, câte erori. Fără ele, singurul mod de a afla că o automatizare a murit e reclamația unui client.

Limite anti-abuz, pentru că decizia o ia un model

O unealtă chemată de un model lingvistic are nevoie de margini pe care modelul nu le poate depăși. La SMS: în modul „către firmă”, destinatarul vine din configurație, niciodată de la model; în modul „către client”, numărul e normalizat și validat. Peste asta, plafon zilnic per agent, pauză de un minut către același număr și limită de lungime a mesajului. Sunt garduri scrise în cod, nu instrucțiuni în prompt — un prompt se poate ocoli, un gard nu.

Filtre la ieșire, pentru datele care nu au voie să iasă

Când o integrare întoarce mai mult decât ar trebui să știe clientul, filtrăm la ieșire, nu sperăm că agentul se abține. Un exemplu real: pentru un client din transport, politica interzice ca agentul să dicteze numărul de telefon al șoferului, dar API-ul întorcea acele numere în răspuns. Am scris un filtru care parcurge recursiv rezultatul uneltei și elimină câmpurile de contact ale șoferului, păstrând numerele dispeceratului. Filtrul e aplicat exact acolo unde e nevoie, nu global, ca să nu ascundă altceva din greșeală.

What it looks like

The path, step by step.

01

Inventarul: ce sisteme, ce porți, ce drepturi

Ce sisteme sunt în joc, ce expune fiecare, cine are dreptul să creeze credențiale și cine le rotește. La fiecare, întrebarea de bază: se poate citi fără autentificare? Dacă da, integrarea devine mult mai ieftină și mult mai ușor de repetat la al doilea client.

02

Conectorul: unelte declarate, chei pe server, termene

Fiecare acțiune devine o unealtă cu nume, descriere scrisă pentru model, parametri tipizați și termen propriu. Cheile rămân în mediul serverului. La final, agentul nu „știe să facă” ceva difuz: are un set finit de acțiuni, fiecare cu marginile ei.

03

Testul pe cazul care eșuează, nu doar pe cel care merge

Un conector se testează pe cererea corectă, pe cererea incompletă, pe sistemul care răspunde încet și pe sistemul care nu răspunde deloc. Ce spune agentul când integrarea cade e o decizie de proiectare, nu o eroare pe care o descoperă clientul: preferăm un mesaj cinstit și o preluare de către om, în locul unei improvizații plauzibile.

04

Rezistența la instabilitatea celuilalt sistem

Reîncercare cu pauze crescătoare, dar numai pe erorile care au sens să fie reîncercate — o limitare de rată sau o eroare de server, nu o cerere greșită, care va fi greșită și a doua oară. Iar când problema e la nivelul rețelei celuilalt, tratamentul e altul; exemplul de mai jos e exact un asemenea caz.

05

Predarea: ce se schimbă când furnizorul se schimbă

Notăm ce se strică dacă celălalt sistem își schimbă API-ul, ce credențiale expiră și când, ce trebuie reînnoit manual. O integrare fără această listă e o datorie ascunsă: funcționează perfect până în ziua în care nu mai funcționează și nimeni nu știe de ce.

Un centru. În jur, ce intră în el — pe trasee separate.CENTRUL SE SPRIJINĂ PE CE E ÎN JURUL LUI
Sistemele externe din jurul conversației: programări, catalogul magazinului, CRM, foi de calcul, SMS și canale de mesaje — fiecare citit la cerere, cu cheile rămase pe server.

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 credențialele
Cheile de furnizor ale platformei stau în mediul serverului, nu în baza de date și niciodată în pagina clientului. Credențialele proprii ale unui client — acolo unde integrarea le cere — se stochează criptate. Deosebirea contează: o cheie de platformă compromisă afectează pe toată lumea, o credențială de client afectează un cont; le tratăm diferit pentru că riscul e diferit.
Ce se stochează din sistemele externe și ce nu
Catalogul de produse nu se copiază: se citește la cerere și se ține într-o memorie temporară de zece minute, ca zece întrebări consecutive despre același magazin să nu însemne zece parcurgeri complete. Programările nu se dublează la noi — sursa de adevăr rămâne sistemul salonului. Ce păstrăm e urma conversației și rezultatul acțiunii, nu o replică a bazei celuilalt sistem.
Ce ajunge la modelul lingvistic
Rezultatul unei unelte intră în conversație, deci intră și în contextul modelului. De aceea filtrarea se face pe rezultat, înainte de a fi dat modelului, nu în instrucțiuni. Câmpurile pe care clientul nu are voie să le audă se elimină la sursă; ce a fost eliminat nu poate fi dictat, indiferent cum e întrebat agentul.
Cine poate lega o integrare
Conectarea unei integrări e o operațiune autentificată, legată de contul afacerii, iar uneltele rezultate se atașează unui agent anume — nu tuturor. Un agent are exact uneltele de care are nevoie pentru rolul lui. E aceeași logică ca la accesul unui angajat: nu i se dau toate cheile pentru că e mai simplu.
Ce rămâne de reparat, spus deschis
Nu toate modulele de integrare din platforma noastră sunt la același nivel. Cele enumerate aici sunt scrise și rulate. Există și module începute, de câteva zeci de linii, care nu fac încă nimic util — nu le prezentăm ca disponibile și nu le punem în nicio ofertă. Dacă ai nevoie de un sistem care e la noi în stadiul acela, îl tratăm ca lucrare de construit, cu o verificare de compatibilitate înainte, nu ca pe o casetă de bifat.

A case

The integration was failing intermittently, and it was not on our side

Situația

Un conector de CRM funcționa uneori și alteori nu, fără tipar la prima vedere. Cererile rămâneau agățate până depășeau limita de execuție a mediului de server, deci în jurnale apărea o depășire de timp — semnul care te trimite, greșit, să cauți lentoare în propriul cod.

Ce am construit

Cauza era în rețea, la celălalt capăt. Numele domeniului contului de CRM se rezolvă în mai multe adrese IP, iar o parte dintre nodurile clusterului erau nesănătoase. O cerere HTTP obișnuită alege o adresă și, dacă aceea nu răspunde, nu încearcă celelalte adrese întoarse de DNS — nu există trecere automată la următorul răspuns. Reparația a fost să nu mai folosim clientul HTTP implicit pentru acel domeniu: rezolvăm noi numele, deschidem conexiuni criptate direct către fiecare adresă în paralel, cu un termen scurt pe fiecare, și folosim prima care răspunde; dacă toate sunt reci într-o rundă, se reia.

Ce a ieșit

Căderile intermitente au încetat să fie o loterie. Mai important pentru client: diagnosticul a devenit spusibil într-o frază — „nodurile furnizorului sunt intermitent indisponibile, iar noi le ocolim” — în loc de „uneori nu merge”.

Ce nu spune cazul

E o reparație care compensează o problemă a altcuiva, și trebuie spus. Adaugă complexitate în codul nostru pentru un defect din infrastructura furnizorului; dacă acesta își repară clusterul, complexitatea rămâne la noi. O documentăm ca atare, ca să poată fi scoasă când nu mai e necesară.

Întrebări

What people ask us before they call

Ce sisteme ați conectat efectiv, nu teoretic?

Sistemul de programări Altegio, cu șase acțiuni chemate de agent. amoCRM și Bitrix24, cu OAuth complet și reîmprospătare de jeton. Google Sheets, cu cont de serviciu dedicat. Magazine pe WooCommerce, prin API-ul public de magazin. Telegram, pentru notificări. Infobip, pentru SMS. Canalul de chat al unei platforme locale de anunțuri. Plus canalele de mesagerie Meta și Telegram pentru conversații. Pentru orice alt sistem — inclusiv platforme de magazin pe care nu le-am dus până la capăt — răspunsul onest e: la cerere, după o verificare de compatibilitate, tratată ca lucrare, nu ca setare.

Magazinul meu trebuie să-mi genereze chei API?

Pentru WooCommerce, nu. Folosim API-ul public de magazin, care nu cere cheie de consumator — te conectezi cu adresa site-ului. E și mai sigur, nu doar mai simplu: am înlocuit cu această abordare unelte mai vechi care țineau chei de magazin scrise în clar în baza de date. Pentru alte platforme, depinde de ce expun; verificăm înainte să promitem.

Poate agentul să facă o programare reală, nu doar să spună că o face?

Da, dacă sistemul de programări o permite. Creează programarea, o poate anula și modifica, și verifică disponibilitatea înainte. Un detaliu care se învață doar pe teren: în acel sistem, serviciul și specialistul călătoresc împreună — dacă ceri orele libere ale unui specialist pentru un serviciu pe care el nu îl face, primești zero rezultate, nu o eroare. De aceea unealta de disponibilitate întoarce toți specialiștii care fac serviciul și primele ore libere pentru fiecare, ca agentul să propună o alternativă concretă.

Cheile mele unde ajung?

Cheile de platformă rămân în mediul serverului, niciodată în baza de date și niciodată în pagina ta. Credențialele care sunt ale tale se stochează criptate. Regula pe care o aplicăm: dacă o cheie poate cheltui bani sau poate citi date pentru toți clienții, nu are ce căuta nicăieri unde ajunge cod care rulează la cererea unui model lingvistic.

Poate agentul să trimită SMS-uri către cine vrea el?

Nu, și e o restricție deliberată. În modul de notificare către firmă, destinatarul vine din configurație — modelul nu îl poate alege. În modul către client, numărul se normalizează și se validează. Peste asta există plafon zilnic per agent, o pauză de un minut către același număr și o limită de lungime a mesajului. Sunt garduri în cod, nu instrucțiuni în prompt; un prompt se poate ocoli prin conversație, un gard nu.

Ce se întâmplă când sistemul celuilalt cade sau răspunde încet?

Fiecare unealtă are termen propriu de execuție, astfel încât un sistem lent să nu blocheze conversația. Reîncercăm cu pauze crescătoare, dar numai pe erorile care merită reîncercate — o limitare de rată sau o eroare de server, nu o cerere greșită. Iar comportamentul agentului la eșec se scrie în scenariu: spune că nu poate verifica acum și predă discuția, în loc să inventeze un răspuns plauzibil.

Ce se întâmplă dacă furnizorul își schimbă API-ul?

Se strică ceva, și de aceea preferăm porțile oficiale în locul citirii paginilor. Uneltele care extrag informația din HTML-ul unui site se rup la orice schimbare de temă — le-am înlocuit deliberat cu o singură implementare peste API-ul oficial, exact ca să nu întreținem zeci de variante fragile. Când nu există alternativă, spunem că e o soluție fragilă și o tratăm ca atare.

Puteți face automatizări fără agent, doar reguli programate?

Da. Reluarea conversațiilor rămase fără răspuns, primul mesaj către un contact nou, oprirea și repornirea agentului după programul de lucru — sunt sarcini programate care rulează singure. Fiecare are contoare proprii pentru rulări, mesaje trimise, mesaje sărite și erori, ca să se poată răspunde la întrebarea „a rulat?” cu o cifră, nu cu o presupunere.

Cum vă asigurați că agentul nu spune ce nu trebuie, dintr-o integrare?

Filtrăm rezultatul înainte să ajungă la model, nu după. Cazul concret pe care l-am rezolvat: politica unui client interzicea ca agentul să dicteze numărul de telefon al șoferului, dar API-ul întorcea acele numere. Am scris un filtru care parcurge recursiv răspunsul uneltei și scoate câmpurile de contact ale șoferului, păstrând numerele dispeceratului, aplicat exact pe unealta respectivă. Ce nu ajunge la model nu poate fi rostit, indiferent cum e întrebat agentul.

Pe ce se sprijină fiecare afirmație de mai sus (18 surse)
  1. Trei tipuri de unealtă: webhook, cod rulat izolat, unealtă internă; execuția trece printr-un singur punct de intraretools.routes.js:375-390
  2. 15 tipuri de unelte interne: web_scrape, current_time, calculator, google_sheets_save, telegram_lead_notify, sms_send, șase unelte Altegio, trei unelte de catalogtools.routes.js:750-845
  3. Mărimea reală a modulelor de integrare, măsurată azi: amocrm 2.888 linii, trei9 2.459, telegram-leads 1.776, automations 1.088, altegio 946, bitrix 903, google-sheets 615, store-catalog 184 (find <modul> -name '*.js' | xargs wc -l):
  4. Altegio: jetonul de partener e o singură cheie de integrator ținută în mediul serverului, niciodată în baza de date per unealtă; clientul pune doar identificatorul companieialtegio.service.js:1-27
  5. Altegio, particularitate de teren: serviciul și specialistul călătoresc împreună — orele unui specialist pentru un serviciu pe care nu îl face întorc zero rezultate; iar filtrul de securitate al furnizorului răspunde cu 403 la antetul implicit, deci e obligatoriu un identificator propriu de client HTTPaltegio.service.js:16-19
  6. Șase unelte Altegio expuse agentului, cu termene proprii: servicii 15 s, disponibilitate 30 s, creare programare 20 s; disponibilitatea întoarce specialiștii care fac serviciul plus primele ore libere pentru fiecarealtegio.service.js:34-90
  7. Catalogul de magazin folosește API-ul public WooCommerce, care nu cere consumer_key/consumer_secret; a înlocuit 109 unelte scrise de mână, dintre care 28 de scrapere peste HTML și câteva cu chei de magazin scrise în clar în baza de datestoreCatalog.service.js:1-20
  8. Catalogul se încarcă paginat, până la 30 de pagini de câte 100 de produse, cu memorie temporară de 10 minute pe site, și se normalizează într-o formă unică citită de toți consumatoriistoreCatalog.service.js:13-20, 23-25, 56-70
  9. SMS-ul e unealtă internă tocmai pentru că mediul de execuție al codului de unealtă nu are acces la variabilele de mediu; garduri: destinatar din configurație în modul „către firmă”, plafon zilnic implicit 50, pauză de 60 s către același număr, lungime maximă 480 de caracteresms.service.js:1-30
  10. Google Sheets printr-un cont de serviciu dedicat, separat de restul credențialelor, cu scriere declanșată de agentgoogle-sheets.service.js:1-30
  11. Filtru la ieșire care elimină recursiv câmpurile de contact ale șoferului din rezultatul uneltei, păstrând numerele dispeceratului; aplicat punctual, nu globaltools.routes.js:352-370
  12. Automatizări programate cu planificator în proces, cu contoare pentru rulări, mesaje trimise, mesaje sărite și eroriautomations.service.js:1-30
  13. Canalul de chat al unei platforme locale de anunțuri: autentificare cu jeton de reîmprospătare, listare de fire, citire și trimitere de mesajetrei9.routes.js:1-40
  14. amoCRM: clusterul furnizorului întoarce mai multe adrese IP, unele nesănătoase; `fetch` nu trece la următorul răspuns DNS, deci cererea se agăța până la limita de execuție. Reparație: rezolvare DNS proprie și conexiuni TLS în paralel către fiecare adresă, cu termen scurt per adresă și mai multe rundeindex.ts:1-40
  15. 12 funcții de server pentru amoCRM, 4 pentru Bitrix24, 10 pentru Google Sheets în platforma vocală (ls -d amocrm-* = 12; bitrix24-* = 4; *sheet* = 10):
  16. Reîncercare cu pauze crescătoare, 3 încercări, doar pe 429 și 5xx — erorile permanente 4xx nu se reîncearcăserver.js:138-166
  17. Webhook-uri de intrare pentru n8n și pentru furnizorul de trimiteri, cu verificare de semnăturăserver.js:1399-1470
  18. Interpolarea variabilelor în uneltele de tip webhook acceptă trei formate și tratează variabila lipsă ca șir gol — o variabilă nedefinită rupea altfel adresa cereriitools.routes.js:393-425

Citările interne arată numele fișierului și linia. Calea completă rămâne în depozitul nostru; o putem parcurge împreună, la cerere.

What would you want to work better?

Tell us about your process. Together we decide what is worth building, what we can connect, and how we check the result.

Hai să discutăm