Crie uma campanha de prompt de saída para facilitar as chamadas automatizadas.

Use a API do 3CX Call Control para automatizar campanhas de chamadas de saída. Conecte uma lista de números de telefone a um IVR (URA), reproduza uma mensagem automatizada e transfira chamadas para diferentes destinos com base nas seleções do menu URA. Ao contrário da discagem de saída tradicional, esse método oferece flexibilidade em mensagens, encaminhamento de chamadas e integração com CRM ou banco de dados. Continue lendo para saber mais e como começar.

Quando Usar o Script de Campanha de Prompts de Saída

Script de Campanha de Prompts de Saída

O cancelamento de um voo é um exemplo perfeito. Uma companhia aérea pode notificar os passageiros por meio de uma mensagem gravada e oferecer opções de menu para conectá-los ao suporte.

Esse é um caso de uso básico, mas pode ser ampliado. Você pode criar uma URA personalizada com tratamento de entrada DTMF e controle de fluxo de áudio para campanhas de saída.

Consulte o repositório Oficial do 3CX no GitHub para obter mais exemplos

Configuração de Tratamento de Chamadas e Integração de API

Crie uma URa 3CX, adicione-a ao Call Control API Access e selecione-o na lista Extensions (Extensões).

Início da Chamada

A interface do usuário do cliente usa uma área de texto simples para inserir uma lista de números separados por vírgula.

const destinations = source
.split(‘,’)
.map((num) => num.trim())
.filter(Boolean);

Uma estrutura de fila processa as chamadas uma a uma. As chamadas não atendidas ou ocupadas podem ser enfileiradas para rediscagem.

destinations.forEach((destNumber) => this.callQueue.enqueue(destNumber));

Lógica de Chamada

A função abaixo recupera o primeiro número da fila e inicia o processamento.

public async makeCallsToDst() {
if (this.callQueue.isEmpty()) return;

const destNumber = this.callQueue.dequeue();
// …

Antes de discar, o sistema verifica a conexão do PABX e garante que o ramal de origem não esteja em uso.

if (!this.sourceDn || !this.externalApiSvc.connected) {
if (destNumber)
this.failedCalls.push({
callerId: destNumber,
reason: NO_SOURCE_OR_DISCONNECTED,
});
return;
}

const participants = this.getParticipantsOfDn(this.sourceDn);

if (participants && participants.size > 0) {
if (destNumber)
this.failedCalls.push({
callerId: destNumber,
reason: CAMPAIGN_SOURCE_BUSY,
});
return;
}

//…

Como Fazer Uma Chamada

A chamada é feita usando o primeiro dispositivo disponível.

É possível encontrar a lista de dispositivos disponíveis para um DN específico dentro do Estado do Controle de Chamadas.

try {
const source = this.fullInfo?.callcontrol.get(this.sourceDn);
const device: DNDevice | undefined = source?.devices?.values().next().value;
if (!device?.device_id) {
throw new BadRequest(‘Devices not found’);
}
const response = await this.externalApiSvc.makeCallFromDevice(
this.sourceDn,
encodeURIComponent(device.device_id),
destNumber,
);
//…

O método makeCallFromDevice usa esse endpoint:

public makeCallFromDevice(source: string, deviceId: string, dest: string) {

const url = ‘/callcontrol’ + `/${source}` + ‘/devices’ + `/${deviceId}` + ‘/makecall’;

return this.fetch!.post(
url,
{
destination: dest,
},
{
headers: {
‘Content-Type’: ‘application/json; charset=utf-8’,
},
},
);
}

Tratamento de Erros

Se o PABX aceitar a solicitação, a ID da chamada será armazenada. Caso contrário, um erro será registrado.

if (response.data.result?.id) {
this.incomingCallsParticipants.set(response.data.result.id, response.data.result);
} else {
this.failedCalls.push({
callerId: destNumber!,
reason: response?.data?.reasontext || UNKNOWN_CALL_ERROR,
});
}
//…

Os erros entre a aplicação e o PABX são tratados aqui:

//…
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
this.failedCalls.push({
callerId: destNumber!,
reason: error.response?.data.reasontext || UNKNOWN_CALL_ERROR,
});
} else {
this.failedCalls.push({
callerId: destNumber!,
reason: UNKNOWN_CALL_ERROR,
});
}
}

Tratamento de Eventos do Participante

Uma conexão WebSocket rastreia o status da URA, inicia novas chamadas e gerencia os participantes.

Você pode encontrar mais detalhes sobre a estrutura de eventos do WebSocket e outros aspectos relacionados neste guia.

private wsEventHandler = (json: string) => {
try {
const wsEvent: WSEvent = JSON.parse(json);
if (!this.externalApiSvc.connected || !wsEvent?.event?.entity) {
return;
}
const { dn, type } = determineOperation(wsEvent.event.entity);
//…

Quando ocorre uma atualização, o aplicativo busca e armazena novos dados.

case EventType.Upset:
{
this.externalApiSvc
.requestUpdatedEntityFromWebhookEvent(wsEvent)
.then((res) => {
const data = res.data;
set(this.fullInfo, wsEvent.event.entity, data); // update local state
if (dn === this.sourceDn) {
if (type === PARTICIPANT_TYPE_UPDATE) {
/**
* handle here update of participants
*/
}
}
})
.catch((err) => {
if (axios.isAxiosError(err)) {
console.error(`AXIOS ERROR code: ${err.response?.status}`);
} else console.error(‘Unknown error’, err);
});
}
break;

Podemos usar esse URL para solicitar a entidade atualizada e executar uma atualização de estado incremental para nosso aplicativo (verifique DN Update Request):

public requestUpdatedEntityFromWebhookEvent(ws: WSEvent) {
return this.fetch.get(ws.event.entity);
}

Quando um participante é removido, a campanha continua.

case EventType.Remove: {
const removed = set(this.fullInfo, wsEvent.event.entity, undefined);
if (dn === this.sourceDn) { // update related to our campaign handler
if (type === PARTICIPANT_TYPE_UPDATE) {// update related to call participant
/**
* handle here removed participants
*/
if (removed?.id) {
//…
if (!participants || participants?.size < 1) { // Handler is free
this.makeCallsToDst(); // continue with campaign
}
}
}
}
}

Podemos usar esse manipulador de eventos no ouvinte de eventos do WebSocket.

ws.on(‘message’, (buffer) => {
const message = decoder.decode(buffer as Buffer);
wsEventHandler(message);
});

Mais Scripts de Fluxo de Chamadas Disponíveis

Temos uma coleção de scripts de fluxo de chamadas em nosso site. Dê uma olhada neles e veja como você pode automatizar o 3CX para atender às suas necessidades.