IA

Cómo armar un panel de leads de anuncios por cliente con Claude Code

La guía anterior sobre construir con IA muestra el contexto que necesita el agente. Esta es un proyecto cerrado: un panel interno que muestra, por cliente, cuántos leads de anuncios llegaron y cuántos se volvieron venta. La arquitectura es pequeña (webhook, base de datos, una página) y cabe en una tarde con Claude Code, siempre que dividas el trabajo en prompts verificables. El servidor MCP de ZapZap entra solo para consulta, en modo solo lectura, y quien recibe los eventos es tu código. ZapZap es una solución no oficial: WhatsApp puede limitar o bloquear números, y el panel debe mostrar el estado de cada instancia.

Publicado el · 4 min de lectura

Este artículo también está disponible en: Português · English

Qué vas a construir y la arquitectura

Cuatro piezas: un endpoint que recibe el webhook de las instancias, una tabla que vincula cada instancia con un cliente, una tabla de leads con estado y valor, y una página con la consulta por cliente. Los eventos vienen de ZapZap, la venta vuelve por tracking/conversion, y el panel solo lee tu base. Evita dejar que el agente de código consulte la API en cada acceso a la página.

Para la recepción segura, sigue la guía de webhooks idempotentes. Para la medición, mira medir CPL con la API de conversión. Aquí el foco es orquestar al agente.

Paso 1: instrucciones del proyecto y el MCP solo lectura

Empieza por el archivo de instrucciones del proyecto, con la URL base, los nombres de las variables y la regla de usar solo operaciones de la OpenAPI. Luego instala el servidor MCP. El 11/11/2026 el paquete zapzap-mcp no aparece en npm (npm view devolvió 404), así que la instalación es clonando el repositorio github.com/zapzapapi/zapzap-mcp y compilando. Reconfirma con npm view antes de usar npx.

Regístralo con ZAPZAP_READ_ONLY=true, que expone solo las herramientas de lectura (listar instancias, ver estado, consultar mensajes y webhooks). Así el agente consulta y no envía un mensaje por error. El servidor no recibe mensajes: la recepción es el webhook del paso siguiente.

Claude Code: project instructions

# CLAUDE.md of the dashboard project (instructions for the agent)
- Stack: Node.js + Express + SQLite (or Postgres) + one HTML page.
- ZapZap API: base https://api.zapzapapi.com/api/v1, headers x-api-key and x-api-secret,
  read from ZAPZAP_API_KEY and ZAPZAP_API_SECRET. Never print or commit them.
- Spec: https://zapzapapi.com/openapi/zapzap-openapi.pt.json  Summary: https://zapzapapi.com/llms.txt
- Use only operations present in the spec. If something does not exist, say so.
- The zapzap MCP server is READ ONLY (ZAPZAP_READ_ONLY=true). Never ask to send messages.
- Webhooks: validate X-ZapZap-Signature on the raw body, dedupe by event_id or delivery_id.

Install the MCP server read-only

# The zapzap-mcp package is not on npm yet (npm view zapzap-mcp returned 404 on 2026-11-11),
# so install from the repository. Check "npm view zapzap-mcp" again before using npx.
git clone https://github.com/zapzapapi/zapzap-mcp.git
cd zapzap-mcp && npm install && npm run build

# Register it in Claude Code in read-only mode (no send tools are exposed)
claude mcp add zapzap \
  -e ZAPZAP_API_KEY=your-api-key -e ZAPZAP_API_SECRET=your-api-secret -e ZAPZAP_READ_ONLY=true \
  -- node /full/path/to/zapzap-mcp/dist/src/index.js

Paso 2: el esquema de la base y la consulta de la página

Pide al agente el esquema de abajo, que mapea instance_id a un cliente (la clave que el evento siempre trae) y guarda el lead por teléfono. Mapear por el instance_id del sobre es más robusto que depender de campos extra. El metadata de la instancia sirve como verificación cruzada. La consulta del final alimenta la página.

SQL: minimal schema and page query

-- Minimal schema for the per-client lead dashboard
CREATE TABLE clients (id text PRIMARY KEY, name text NOT NULL);
CREATE TABLE instances (instance_id text PRIMARY KEY, client_id text NOT NULL REFERENCES clients(id));
CREATE TABLE leads (
  id integer PRIMARY KEY AUTOINCREMENT,
  client_id text NOT NULL, instance_id text NOT NULL,
  phone text NOT NULL, first_message text, first_seen_at text NOT NULL,
  status text NOT NULL DEFAULT 'open',          -- open | qualified | scheduled | won
  sale_value real, UNIQUE (instance_id, phone));
CREATE TABLE webhook_events (
  instance_id text NOT NULL, dedupe_key text NOT NULL, received_at text NOT NULL,
  PRIMARY KEY (instance_id, dedupe_key));

-- The page query: leads by client in the last 30 days
SELECT c.name, COUNT(*) AS leads,
       SUM(l.status = 'won') AS sales, COALESCE(SUM(l.sale_value), 0) AS revenue
FROM leads l JOIN clients c ON c.id = l.client_id
WHERE l.first_seen_at >= date('now', '-30 days')
GROUP BY c.id ORDER BY leads DESC;

Paso 3: los prompts, uno a la vez, con criterio de listo

Prompt 1: "Crea el endpoint POST /webhook con express.raw, valida X-ZapZap-Signature (HMAC SHA-256 sobre t.cuerpo, tolerancia de 5 minutos), guarda la clave event_id o delivery_id en webhook_events y responde 200. Escribe pruebas para firma válida, vencida y alterada." Prompt 2: "Usa el MCP para listar las instancias y llenar la tabla instances; muéstrame lo que vas a guardar antes de guardar." Prompt 3: "Registra cada primer mensaje recibido como lead, único por instancia y teléfono. Antes de modelar, guarda el primer payload real y muéstramelo." Ese último pedido evita que el agente invente la forma del campo data.

Prompt 4: "Crea la página /clientes con la consulta, ordenada por leads, y una columna de estado de la instancia." Prompt 5: "Llama a POST /{instanceId}/tracking/conversion cuando yo marque una venta en el panel y trata 404 y 409 como está documentado." En cada paso, exige que el agente cite la operación de la OpenAPI usada.

Paso 4: revisión, seguridad y lo que viene después

Revisa tres cosas antes de publicar: secretos solo en variables de entorno, nunca en el repositorio ni en el prompt; texto de cliente final tratado como entrada no confiable (el MCP lee mensajes y pueden contener instrucciones); y un login en la página, porque muestra teléfonos. Mantén el modo solo lectura, y solo piensa en habilitar envío después de un tope de volumen y una lista permitida.

Evoluciona en orden: panel de salud de los números, luego reporte mensual (automatizar reportes), luego el enlace del portal del cliente. Para el contexto general, mira la página de agentes de IA y agencias con IA.

Resumen rápido

  • Un panel por cliente cabe en cuatro piezas: webhook, mapa instancia a cliente, tabla de leads y una página.
  • Instala el MCP clonando el repositorio (el paquete no estaba en npm el 11/11/2026) y usa ZAPZAP_READ_ONLY=true.
  • Mapea por el instance_id del sobre, y usa el metadata como verificación cruzada.
  • Pide al agente el primer payload real antes de modelar el campo data.
  • Revisa secretos, entrada no confiable y login antes de publicar.

Lee también

Preguntas frecuentes

¿Necesito el MCP para armar el panel?

No. El panel funciona solo con webhook y OpenAPI. El MCP ayuda al agente a consultar instancias y estado durante el desarrollo.

¿El MCP recibe los mensajes de los clientes?

No. Según el README del servidor, no recibe mensajes entrantes: configura el webhook para eso.

¿Por qué ZAPZAP_READ_ONLY=true?

Con él solo quedan expuestas las herramientas de lectura, y el agente no puede enviar mensajes ni alterar la instancia.

¿Qué base de datos usar?

Cualquier SQL. El esquema de la guía usa SQLite por simplicidad; para varios usuarios simultáneos, prefiere Postgres.

¿El panel sustituye el portal del cliente de ZapZap?

No. El portal es la pantalla que el cliente ve con su marca; el panel de la guía es interno de la agencia, con la visión de todos los clientes.

Empieza el panel con una instancia

Crea la cuenta, conecta un número y apunta el webhook a tu proyecto. R$15 por número activo al mes, sin cobro por mensaje. Al verificar el teléfono hay un crédito de bienvenida, sujeto a la campaña vigente.