Genere una campaña de avisos salientes para facilitar las llamadas automatizadas.

Utilice la API de Control de Llamadas 3CX para automatizar campañas de llamadas salientes. Conecte una lista de números telefónicos a un IVR, reproduzca un mensaje automatizado, y transfiera llamadas a diferentes destinos basados en selecciones de menú IVR. A diferencia de la marcación saliente tradicional, este método proporciona flexibilidad en la mensajería, el enrutamiento de llamadas y la integración con CRM o bases de datos. Siga leyendo para saber más y cómo empezar.

Cuándo Utilizar el Script de Campaña de Anuncios de Salida

Script de Campaña de Anuncios de Salida

La cancelación de un vuelo es un ejemplo perfecto. Una aerolínea puede avisar a los pasajeros mediante un mensaje grabado y ofrecerles opciones de menú para que se pongan en contacto con el servicio de asistencia.

Este es un caso de uso básico, pero puede ampliarse. Puede crear un IVR personalizado con manejo de entrada DTMF y control de flujo de audio para campañas salientes.

Consulte el repositorio GitHub oficial de 3CX para ver más ejemplos.

Administración de Llamadas e Integración API

Genere un IVR 3CX, agréguelo al Acceso API de Control de Llamadas, y selecciónelo de la lista de Extensiones.

Inicio de Llamada

La interfaz de usuario del cliente utiliza un área de texto simple para introducir una lista de números separados por comas.

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

Una estructura de Colas procesa las llamadas una a una. Las llamadas no contestadas u ocupadas pueden ponerse en cola para ser remarcadas.

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

Lógica de Llamada

La siguiente función recupera el primer número de la Cola e inicia el procesamiento.

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

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

Antes de marcar, el sistema comprueba la conexión con el PBX y se asegura de que la extensión de origen no está en 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;
    }

//…

Hacer una Llamada

La llamada se realiza utilizando el primer dispositivo disponible.

Puede encontrar la lista de dispositivos disponibles para un DN específico dentro del Estado del Control de Llamadas.

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,
      );
//…

El método makeCallFromDevice utiliza este 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',
        },
      },
    );
  }

Manejo de Errores

Si el PBX acepta la solicitud, se almacena el ID de llamada. En caso contrario, se registra un error.

  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,
        });
      }
//…

Aquí se manejan los errores entre la aplicación y el PBX:

    //...
    } 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,
        });
      }
    }

Manejo de los Eventos de los Participantes

Una conexión WebSocket realiza el seguimiento del estado del IVR, inicia nuevas llamadas y administra a los participantes.

Puede encontrar más detalles sobre la estructura de eventos WebSocket y otros aspectos relacionados en esta guía.

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);
//...

Cuando se produce una actualización, la aplicación obtiene y almacena nuevos datos.

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 utilizar esta URL para solicitar la entidad actualizada y realizar una actualización de estado incremental para nuestra aplicación (comprobar Solicitud de Actualización DN):

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

Cuando se retira un participante, la campaña continúa.

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 utilizar este controlador de eventos dentro del receptor de eventos WebSocket.

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

Más Scripts de Flujo de Llamadas Disponibles

Tenemos una colección de scripts de flujo de llamadas en nuestro sitio web. Revíselos y vea cómo puede automatizar 3CX para que se ajuste a sus necesidades.

Manténgase Informado

Síganos en X y LinkedIn para mantenerse al tanto de las actualizaciones y únase a nosotros en el Foro 3CX para compartirnos lo que piensa acerca de las últimas funcionalidades.