Raphael Serafim· Publicado el 10 de septiembre de 2026· 10 min de lectura

Aprovisionar WhatsApp para 100 clientes por API: crear, activar, suspender y cortar

Si dar de alta la cuenta de WhatsApp de un cliente nuevo depende de que alguien abra un panel, el proceso se traba en el décimo. Cómo atar el ciclo de vida de la instancia a tu facturación: crear en el alta, suspender en la mora y cortar en la baja.

Ver como Markdown

La pregunta que separa una integración de un producto: ¿qué pasa cuando entra el cliente número 11?

Si la respuesta implica que alguien abra un panel, llene un formulario y copie una clave a tu sistema, el proceso se traba — y el costo por cliente crece con el número de clientes, que es justo lo contrario de lo que necesita una agencia.

Aprovisionamiento en el alta

El modelo que escala ata la instancia al contrato:

javascript
async function activarCliente(clienteId) {
  const cliente = await db.clientes.buscar(clienteId);

  // 1. crea la instancia
  const inst = await wameAdmin.crearInstancia({
    nombre: `cliente-${cliente.id}`,
    // Un nombre legible ayuda el día que tengas que auditar la
    // factura consolidada y descubrir de quién es cada línea.
  });

  // 2. guarda la clave junto al cliente
  await db.clientes.actualizar(clienteId, {
    wame_instancia_id: inst.id,
    wame_key: cifrar(inst.key),
    canal_estado: 'esperando_conexion',
  });

  // 3. apunta el webhook a la URL DE ESTE cliente
  await wameAdmin.configurarWebhook(inst.key, {
    allowWebhook: true,
    webhookFormat: 'meta',
    webhookMessage: `https://tusistema.com/webhook/wame/${cliente.slug}/${cliente.webhookSecret}`,
  });

  return inst;
}

El cliente termina el alta en tu sistema y la instancia ya existe. Nadie abre el panel de nadie.

Una URL de webhook por cliente, con secreto propio. No uses una URL única para todos: además de tener que descubrir de quién es cada evento, un secreto filtrado comprometería toda la base. Es el mismo razonamiento de webhook en producción.

Conectar el número del cliente

Dos puertas, y la elección es por cliente:

API oficial — inicio de sesión seguro por la propia Meta, dentro de tu flujo. El número queda en el Business Manager del cliente, y él nunca escribe credenciales en tu sistema.

API no oficial — código QR. Tú lo pides y lo muestras en tu pantalla:

javascript
const { qr } = await wa.instance.connect();
// devuelve el QR para que el front de TU producto lo renderice

El cliente lo escanea dentro de tu sistema, con tu marca. No aparece ninguna pantalla de un tercero.

Un cliente chico empieza hoy con el código QR; el que necesita contrato va a la oficial. El comparativo entre ambas ayuda a decidir, y cambiar después no reescribe tu código.

Los estados del contrato se vuelven estados de la instancia

El error clásico es tratarlos como cosas separadas. Lo correcto es que mande el contrato:

ContratoInstanciaLlamada
ActivoActiva
En moraSuspendidadesactivar
PagóActivaactivar
Dado de bajaEliminada (tras gracia)eliminar
Prueba vencidaSuspendidadesactivar
javascript
async function aplicarEstado(clienteId, nuevoEstado) {
  const c = await db.clientes.buscar(clienteId);
  if (!c.wame_key) return;

  switch (nuevoEstado) {
    case 'en_mora':
    case 'prueba_vencida':
      // Suspender, no eliminar: la reactivación después del pago
      // tiene que ser inmediata, y eliminar perdería la conexión.
      await wameAdmin.desactivar(c.wame_key);
      break;

    case 'activo':
      await wameAdmin.activar(c.wame_key);
      break;

    case 'baja':
      // Periodo de gracia antes de eliminar. Las bajas por error
      // ocurren, y eliminar es irreversible.
      await agendar('eliminar_instancia', { clienteId }, { enDias: 30 });
      break;
  }

  await db.clientes.actualizar(clienteId, { canal_estado: nuevoEstado });
}

Suspender en lugar de eliminar es la decisión que más dolores de cabeza evita. La mora suele ser temporal; la eliminación no.

La reconciliación que se paga sola

El estado divergente es inevitable: una llamada falla, un webhook se pierde, alguien cambia el contrato directo en la base. El resultado es siempre el mismo — una instancia activa de un cliente que ya se fue, apareciendo en tu factura.

javascript
// corre todos los días de madrugada
async function reconciliar() {
  const clientes = await db.clientes.conInstancia();
  const instancias = await wameAdmin.listarInstancias();
  const porClave = new Map(instancias.map((i) => [i.key, i]));

  const divergencias = [];

  for (const c of clientes) {
    const inst = porClave.get(descifrar(c.wame_key));

    if (!inst) {
      divergencias.push({ cliente: c.id, problema: 'instancia_desaparecida' });
      continue;
    }

    const deberiaEstarActiva = c.canal_estado === 'activo';
    if (inst.activa !== deberiaEstarActiva) {
      divergencias.push({
        cliente: c.id,
        problema: 'estado_divergente',
        contrato: c.canal_estado,
        instancia: inst.activa ? 'activa' : 'inactiva',
      });
      await aplicarEstado(c.id, c.canal_estado);   // corrige
    }
  }

  // Huérfanas: están en la factura y no le pertenecen a nadie
  const clavesConocidas = new Set(clientes.map((c) => descifrar(c.wame_key)));
  for (const i of instancias) {
    if (!clavesConocidas.has(i.key)) {
      divergencias.push({ problema: 'instancia_huerfana', key: i.key });
    }
  }

  if (divergencias.length) await avisarEquipo(divergencias);
}

Una instancia huérfana es dinero que se va todos los meses en silencio. Media hora de código que se paga en la primera factura.

Monitorear la salud de todas

Con 100 clientes, no te enteras de que la instancia se cayó porque el cliente llame:

javascript
async function verificarSalud() {
  const activos = await db.clientes.activos();

  for (const c of activos) {
    const info = await wa(c).instance.info();

    if (!info.conectada) {
      await registrarIncidente(c.id, 'desconectada');
      // Avísale al cliente ANTES de que lo note. Cambia por completo
      // la conversación: de "tu sistema está roto" a "vimos que se
      // cayó y ya lo estamos resolviendo".
      await notificarCliente(c, 'canal_desconectado');
    }
  }
}

En la API no oficial esto importa más: la sesión puede caerse sola. La conexión sin celular elimina la causa más común.

Seguridad del multi-tenant

Cifra la clave de la instancia en la base. Permite enviar mensajes en nombre del número del cliente. Una filtración de base no puede convertirse en una filtración de canal.

Nunca expongas la clave en el front. Toda llamada sale de tu servidor. Una clave en el bundle es una clave pública.

Aísla por cliente en cada consulta. El WHERE cliente_id = ? olvidado es el bug que manda el mensaje de un cliente a la base de otro.

Un secreto de webhook por instancia, como en el ejemplo de arriba.

Conclusión

El aprovisionamiento por API es lo que hace que el décimo cliente cueste lo mismo que el segundo. Sin él, cada venta nueva agrega trabajo manual, y la operación encuentra un techo que no es comercial: es de proceso.

Cuatro piezas lo resuelven: crear en el alta, mapear el estado del contrato al estado de la instancia, reconciliar todos los días y monitorear la salud. Ninguna es difícil; la que más se olvida es la reconciliación, y es justamente la que aparece en la factura.

¿Listo para automatizar tu WhatsApp?

Crea tu cuenta gratis y empieza a enviar mensajes por la API en minutos.

Empezar gratis

Preguntas frecuentes

¿Se pueden crear instancias de WhatsApp por API, sin panel?+

Sí. Crear, activar, desactivar y eliminar son llamadas de API. Eso permite que el aprovisionamiento ocurra dentro de tu propio onboarding: el cliente termina el alta en tu sistema y la instancia ya existe, sin que nadie abra el panel de un tercero.

¿Cómo ato la instancia al ciclo de vida del cliente?+

Tratando el estado del contrato como la fuente de la verdad y la instancia como consecuencia. Contrato activo, instancia activa; contrato en mora, instancia suspendida; contrato dado de baja, instancia eliminada tras un periodo de gracia. Un job diario reconcilia ambos estados y corrige divergencias.

¿El cliente final tiene que saber que hay un proveedor detrás?+

No. Toda la gestión ocurre por API dentro de tu producto, y el cobro es el tuyo. El cliente ve WhatsApp funcionando en el sistema que le entregaste y conecta su número con un inicio de sesión de la propia Meta o con un código QR.

¿Qué pasa con la instancia si el cliente no paga?+

Eso lo decides tú, y por eso importa el control por API. El patrón que funciona es suspender en lugar de eliminar: la instancia deja de enviar, pero el historial y la conexión se mantienen, así que la reactivación tras el pago es inmediata.

¿Cómo evito pagar por instancias de clientes que ya se fueron?+

Con reconciliación automática. La causa más común de desperdicio es la baja registrada en el contrato y la instancia olvidada activa. Un job diario que compara ambos lados y reporta divergencias lo resuelve — y se paga solo en la primera factura.

Sigue leyendo