Registro de llamadas

Si decides ofrecer registro de llamadas en tu integración, es fundamental registrar las llamadas entre clientes y gerentes y vincular cada llamada a la entidad correspondiente (contacto, compañía o lead) de acuerdo con el algoritmo de registro de llamadas (o según la elección del gerente).

📘

En esta documentación cubriremos la implementación de servicios VoIP con Kommo mediante webhook, que es una de las opciones posibles para este tipo de integraciones. De cualquier forma, puedes utilizar una lógica diferente según el servicio que vayas a integrar.

Flujo de registro de llamadas

Cuando finaliza una llamada entre un cliente y un gerente, pueden ocurrir las siguientes acciones principales:

  1. Cuando el servicio VoIP envía un webhook indicando que la llamada terminó, el backend de la integración debe buscar la entidad correspondiente (lead, contacto o compañía), o crear un Lead entrante si no existe ninguna entidad asociada al número telefónico en tu cuenta de Kommo.
  2. Si existe un contacto con ese número, puedes añadir una Nota de llamada con toda la información necesaria y vincularla al contacto mediante la API.
  3. Si la llamada se finaliza desde Kommo, puedes mostrar una ventana modal para que el gerente añada información adicional sobre el Resultado de llamada. Cuando el gerente completa los datos y guarda el resultado, esa información se envía al backend de la integración VoIP, que crea la Nota de llamada en la tarjeta de la entidad, junto con la grabación obtenida del webhook.

El siguiente esquema representa un flujo de ejemplo de una integración VoIP en acción. Al recibir una llamada, aparece una ventana modal y comienza la sesión de llamada. Cuando la llamada finaliza, se añade tanto la nota como la grabación de la llamada a la entidad correspondiente. Ni las llamadas duplicadas ni los leads entrantes ignorados deben enviarse a Kommo a través de tu integración. En el ejemplo siguiente, la función de realizar llamadas dentro de Kommo está activada, y tanto la grabación de la llamada como el envío del webhook son proporcionados por el servicio VoIP.

La presencia de estos pasos no es obligatoria. Sin embargo, el enfoque principal debe estar en sincronizar la gestión de las solicitudes para garantizar que todos los pasos — o cualquier subconjunto de ellos — afecten únicamente a una sola entidad. Las solicitudes pueden llegar simultáneamente o provenir únicamente de la ventana modal o del sistema VoIP (por ej.: mediante un webhook). También es importante tener en cuenta que la API de Kommo no gestiona la identidad de las llamadas.

👍

En cualquier caso, consideramos que los registros de llamadas son esenciales, independientemente de si provienen primero del webhook o de la ventana de resultado de llamada.

Una de las posibles soluciones para manejar estas solicitudes asíncronas es guardar en la base de datos la información sobre la nota añadida para controlar la duplicación, y utilizar un servidor de colas externo para almacenar las tareas. La aplicación que contiene la lógica de negocio envía una tarea a la cola con un nombre de handler específico. El servidor de colas se configura para conocer los handlers y workers según la propiedad de la tarea. Esto permite retrasar la ejecución de una tarea bajo ciertas condiciones. Recomendamos utilizar Beanstalkd, Apache Kafka o RabbitMQ como servidor de colas debido a su rendimiento, fiabilidad y simplicidad.

Implementamos el caso de uso en la siguiente jerarquía:

Todas las llamadas deben obtenerse desde el servicio VoIP y almacenarse en el repositorio de llamadas dentro de la base de datos de la integración. Debe implementarse una función para seleccionar una llamada por medio de sus identificadores.

static function getByCallIdAndKommoAccountId(
    string $callId,
    int $kommoAccountId,
): VoipCalls {
    // Modelo de la tabla voip_calls en la base de datos
    return VoipCalls::query() 
        ->where('call_id', '=', $callId)
        ->where('kommo_account_id', '=', $kommoAccountId)
        ->first();
}

Se crea una tarea para registrar cada llamada. Luego enviamos las tareas a la cola, y las obtenemos de la cola mediante workers. A continuación se muestra el constructor de la tarea:

public function __construct(
    private int $kommoAccountId,
    private string $callId,
);

El AddCallUseCase correspondiente toma la tarea y la ejecuta. La llamada se añade a la cuenta de Kommo utilizando la API de registro de llamadas. Este caso de uso se ejecuta una vez que conocemos una nueva llamada, ya sea a través del webhook o de la ventana modal.

public function handle(AddCallTask $task): void
{
    $voipCall = VoipCalls::getByCallIdAndKommoAccountId(
        $task->getCallId(),
        $task->getKommoAccountId()
    );

    $call = Call::fromModel($voipCall); // Call entity

    if ($record = $voipCall->getRecording() ?? '') {
            $record = sprintf(
                '%s/voip/%s/get_call_record/%s',
                $this->appConfig->getBaseUrl(),
                $task->getKommoAccountId(),
                $call->getCallId()
            );
    }
    $call->setRecordLink($record);

    if ($voipCall->getEntityId() !== null) {
        throw new WorkerException('Already created');
    }

    // Llamar a nuestra API de cliente para crear el evento de llamada
    $kommoCall = $this->kommoApiClient->calls()->addOne($call);

    // Crear un lead entrante si no se encontró ninguna entidad por número
    if ($voipCall->getDirection() === CallType::INBOUND) {
        // Llamar a nuestra API de cliente para crear el lead entrante
        $incomingLead = $this->unsortedService->makeUnsorted(
            $call,
            $this->OAuthConfig->getWidgetCode(),
            $voipCall->getResponsibleUserId()
        );
        $incomingLeadUid = $incomingLead->getUid();
        $voipCall->setUnsortedUid($incomingLeadUid)->save();
    }
    $task->setSuccess(true);
}

AddCallWorker toma una tarea de la cola con el ID de la cuenta de Kommo y el ID de la llamada, y busca la llamada en la base de datos de la integración. Si ya fue añadida, no es necesario volver a agregarla:

public function run(array $data, LoggerInterface $logger): void
{
    $taskData = $data['data'];
    $task = new AddCallTask(
        $taskData['account_id'],
        $taskData['call_id'],
    );
    $this->addCallUseCase->handle($task);
    if (!$task->isSuccess()) {
        throw BusinessLogicException::create('Erro ao criar evento de chamada');
    }
}

Nota de llamada

El registro de llamadas se realiza creando eventos dentro de la entidad correspondiente, clasificados bajo tipos de evento específicos para llamadas entrantes y salientes. Si el sistema VoIP admite la funcionalidad de grabación de llamadas, la interfaz de usuario puede mostrar una URL y un reproductor de audio para escuchar la grabación. Para que el reproductor funcione correctamente, el servidor que aloja la grabación debe incluir el encabezado HTTP Accept-Ranges: bytes en su respuesta. La ausencia de este encabezado puede afectar la capacidad de desplazarse dentro del audio (por ej.: avanzar o retroceder) durante la reproducción.

El registro de llamadas se realiza mediante el endpoint POST /api/v4/calls. Este endpoint busca automáticamente las entidades asociadas al número telefónico proporcionado y adjunta el registro de la llamada a la entidad correspondiente según un algoritmo definido. La documentación detallada sobre el algoritmo de vinculación de llamadas se encuentra en la especificación del método. Si no existe ninguna entidad en la base de datos del sistema que coincida con el número telefónico proporcionado, el registro de la llamada no se vinculará a ninguna entidad.

El sistema ofrece múltiples opciones para mostrar la información de la llamada, dependiendo de la dirección de la llamada (entrante o saliente).