Registro de chamadas

Se você optar por fornecer registro de chamadas na sua integração, é fundamental registrar as chamadas entre clientes e gerentes, vinculando cada chamada à entidade correspondente (contato, empresa ou lead), de acordo com o algoritmo de registro de chamadas (ou a escolha do gerente).

📘

Nesta documentação, abordaremos a implementação de serviços VoIP com o Kommo via webhook, que é uma das opções possíveis para esse tipo de integração. De qualquer forma, você pode usar uma lógica diferente, conforme o serviço VoIP que estiver integrando.

Fluxo de registro de chamadas

Quando uma chamada entre um cliente e um gerente termina, os seguintes processos podem ocorrer:

  1. O serviço VoIP envia um webhook informando o fim da chamada. O backend da integração deve então encontrar a entidade apropriada (lead, contato ou empresa) ou criar um Lead de Entrada caso não exista nenhuma entidade vinculada ao número na Kommo.

  2. Se existir um contato com aquele número de telefone, você pode adicionar uma Nota de Chamada com todos os dados necessários, vinculando-a ao contato via API.

  3. Se a chamada foi encerrada dentro da Kommo, é possível exibir uma janela modal para o gestor inserir informações adicionais de resultado da chamada . Ao salvar os dados, eles são enviados para o backend da integração VoIP, que então cria a Nota de Chamada na entidade, junto com a gravação enviada pelo webhook.

O esquema a seguir representa um fluxo de exemplo de integração VoIP em funcionamento. Ao receber uma chamada, um modal aparece e a sessão começa. Ao finalizar, a chamada é registrada com a nota e a gravação na entidade apropriada. Nem chamadas duplicadas nem leads ignorados devem ser enviados ao Kommo pela integração. No exemplo abaixo, o recurso de ligação de dentro do Kommo está ativado, e tanto a gravação quanto o webhook são enviados pelo serviço VoIP.

O conjunto de etapas não é obrigatório, mas o essencial é manter a sincronização entre elas para garantir que todas — ou parte delas — afetem apenas uma única entidade. As requisições podem chegar simultaneamente ou vir apenas do modal ou do sistema VoIP (via webhook). Também é importante notar que a API da Kommo não gerencia a identidade da chamada.

👍

Em qualquer cenário, consideramos o registro de chamadas essencial, independentemente de virem primeiro à partir do webhook ou da janela de resultaod da chamada.

Uma das possíveis soluções pra tratar das requisições assíncronas, é registrar na base de dados as informações sobre a nota já adicionada, evitando duplicação. Utilizar um servidor de fila externa pra armazenar as tarefas também é recomendado. A aplicação principal adiciona uma tarefa na fila com o nome do handler. O servidor de fila é configurado pra reconhecer os handlers e workers correspondentes de acordo com a propriedade da tarefa. Ele oferece a possibilidade de atrasar a execução de uma tarefa sob condições específicas. Recomendamos o uso de Beanstalkd, Apache Kafka ou RabbitMQ como servidores de fila por conta de sua performance, confiabilidade e simplicidade

Nós implementamos o caso de uso na seguinte hieraquia:

Todas as chamadas devem ser tomadas pelo serviço VoIP e armazenadas no repositório de chamadas dentro da base de dados da integração. Uma função para selecionar uma chamada pelos seus identificadores deve ser implementada.

public static function getByCallIdAndKommoAccountId(
    string $callId,
    int $kommoAccountId,
): VoipCalls {
    // modelo de tabela de banco de dados voip_calls
    return VoipCalls::query() 
        ->where('call_id', '=', $callId)
        ->where('kommo_account_id', '=', $kommoAccountId)
        ->first();
}

Uma tarefa é criada para registrar cada chamada. Enviamos as tarefas para a fila, e então pegamos elas da fila usando os workers. O construtor da tarefa é assim:

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

O AddCallUseCase correspondente recebe a tarefa e a executa. A chamada é adicionada à cona Kommo usando a API de registro de chamadas. Esse caso de uso vai ser executado sempre que soubermos de uma nova chamada, seja via webhook ou pela janela 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');
    }

    // Call our client API to create call event
    $kommoCall = $this->kommoApiClient->calls()->addOne($call);

    // Create an incoming lead if no entity was found by number
    if ($voipCall->getDirection() === CallType::INBOUND) {
        //call our client API to create incoming lead
        $incomingLead = $this->unsortedService->makeUnsorted(
            $call,
            $this->OAuthConfig->getWidgetCode(),
            $voipCall->getResponsibleUserId()
        );
        $incomingLeadUid = $incomingLead->getUid();
        $voipCall->setUnsortedUid($incomingLeadUid)->save();
    }
    $task->setSuccess(true);
}

O AddCallWorker Receve a tarefa da fila, contendo o ID da conta Kommo e o ID de chamada (Call ID), e então busca pela chamada na base de dados da integração. Se a chamada já foi adicionada — não é necessário adicionar novamente.

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('Create call event error');
    }
}

Nota de chamada

O registro de chamadas é feito criando eventos dentro das entidades correspondentes, categorizadas em tipos de eventos específicos para chamadas realizadas ou recebidas. Se o sistema VoIP oferecer suporte a gravação de chamadas, a interface do usuário pode exibir uma URL e um reprodutor de áudio para ouvir a gravação. Para que o reprodutor funcione corretamente, o servidor que hospeda a gravação deve incluir o cabeçalho HTTP Accept-Ranges: bytes na resposta. A ausência desse cabeçalho pode resultar no mau funcionamento da navegação pela faixa de áudio (por exemplo, avançar ou retroceder) durante a reprodução.

O registro é feito usando o endpoint POST /api/v4/calls. Ele busca automaticamente entidades pelo número de telefone e vincula a chamada conforme o algoritmo definido. Se não existir uma entidade para o numero, a chamada não será vinculada. Uma documentação detalhada acerca do algoritmo de vinculação da chamada está disponível na especificação do método. Se não existir qualquer entidade correspondente ao número de telefone fornecido no banco de dados do sistema, o registro da chamada não será vinculado a nenhuma entidade.

O sistema oferece múltiplas opções para exibir informações de chamda, dependendo da direção da chamada (realizada ou recebida)