Blogtelefoniaarquitecturaconstruir-vs-comprar

Com hem muntat un marcador de trucades dins del CRM

Telnyx, credencials que caduquen en vuit hores i el topall de cost abans de repartir la clau. Les decisions reals d'una peça que podíem haver comprat.

26 d’agost del 2026 4 min

Per què no el vam comprar

Un comercial que truca des del CRM necessita tres coses: marcar sense sortir de la fitxa, que la trucada quedi registrada sola i que ningú hagi de copiar res a mà. Això ho venen fet: Aircall i companyia comencen als 30-40 € per usuari i mes, amb permanència.

Ho vam mirar i vam decidir construir-lo. No per estalviar la quota —amb un o dos usuaris no s'estalvia res—, sinó per una raó concreta: el que volem no és telefonia, és que la trucada acabi dins de l'historial del lead amb la durada i el resultat. Un producte de tercers t'ho dona a la seva base de dades, i llavors has de sincronitzar dos sistemes per sempre.

És exactament el criteri que expliquem a la pàgina d'ERPs: construeix només on ets diferent. Aquí el que ens diferencia no és marcar un número, és que la trucada sigui una fila més de l'historial.

L'arquitectura, en quatre peces

Telnyx com a operador. L'API és directa i el WebRTC funciona al navegador sense instal·lar res. El número és un +34 real, no un mòbil personal.

Una funció que emet credencials efímeres. El navegador no veu mai la clau de l'API. Quan el comercial obre el marcador, una edge function crea una credencial de Telnyx que caduca en vuit hores i lligada a la connexió d'aquell tenant. Si algú li roba el token del navegador, té vuit hores i un sol número, no el compte.

Un webhook signat. Telnyx envia els esdeveniments de la trucada amb una signatura Ed25519. Es verifica abans de mirar res del cos: sense signatura vàlida, 401 i fora. Un webhook obert és una porta oberta a la teva base de dades.

Una fila a `lead_activities`. Quan la trucada penja, l'esdeveniment call.hangup porta la durada, si s'ha despenjat i la causa. Va tot a l'historial del lead, al mateix lloc que els correus i els WhatsApp.

La decisió que més ens agrada: el topall abans de la credencial

La telefonia és de les poques coses que poden gastar diners soles. Un bucle, una tecla enganxada o algú provant coses, i a final de mes hi ha una factura.

El topall de minuts diaris no es comprova quan es truca: es comprova abans d'emetre la credencial. Si el tenant ja ha gastat els minuts del dia, la funció retorna un 429 i no crea res.

La diferència és important i és fàcil de passar per alt. Comprovar-ho a la trucada vol dir que la credencial ja existeix i que algú amb el token pot seguir trucant. Comprovar-ho abans vol dir que si no hi ha credencial, no hi ha cost. El límit deixa de ser una alerta i passa a ser una paret.

Val per a qualsevol cosa que gasti per ús: si el guard va després de repartir la clau, el guard no serveix.

La gravació, apagada

Es podia deixar encesa amb dues línies. Està apagada per defecte i és una decisió legal, no tècnica: gravar una trucada comercial a Espanya vol dir avisar, guardar el consentiment i tenir un període de conservació definit. Res d'això és una casella d'un formulari.

El codi la suporta —quan Telnyx avisa que la gravació està desada, l'URL s'afegeix a la fila de la trucada— però ha d'entrar per decisió, amb l'avís muntat, no per defecte.

Què costa de veritat

El desenvolupament van ser unes poques setmanes, encaixat dins d'un CRM que ja existia. El cost mensual és el consum de Telnyx més el número, sense quota per usuari ni permanència.

Contra 30-40 € per usuari i mes d'un producte comprat, l'estalvi comença a comptar quan hi ha equip. Amb una persona no compensa pel preu: compensa perquè la trucada acaba on ha d'acabar.

Què t'emportes d'això

Si estàs valorant una peça així, les tres preguntes que et diran si val la pena construir-la:

1. El valor és a la trucada o a on acaba? Si és a la trucada, compra.

2. El guard de cost va abans o després de repartir la clau? Si va després, no és un guard.

3. La part legal és una casella o una decisió? Si és una casella, algú se l'oblidarà.

Comparteix: