Biblioteca · Cuatro proyectos de principio a fin

Paso a paso: un bot de Telegram con IA para un negocio en 7 días

CreadorActualizado: octubre de 2026
102 de 105 en la biblioteca

Tiempo: unas 30 horas a lo largo de 7 días (construir + vender + lanzar)


Lo esencial

Una clase teórica es el arquitecto mostrándote el plano de la casa. Un laboratorio es tú con la cuadrilla poniendo ladrillos día tras día, y para el domingo la casa ya tiene luz y vive en ella el primer inquilino.

Esta lección no es teoría. Es la construcción paso a paso de un producto de IA real, día por día de la semana. No "cómo se hacen en teoría los bots de Telegram", sino "aquí estás el lunes, esto haces hora por hora, y el domingo tienes un bot desplegado en producción, el primer cliente conectado y Stripe listo para enviar facturas cada mes".

Después de las lecciones principales del curso sabes "cómo se hace en teoría". Ahora te demuestras a ti mismo que puedes sacar un producto completo, de principio a fin, en una semana.

Importante: las cantidades de esta lección (por ejemplo, $500 al mes) son ejemplos para hacer cuentas, no un pronóstico de ingresos ni una recomendación de precio. Si encuentras un cliente y cuánto te pagará depende del nicho, del mercado y de tu trabajo: el curso no garantiza resultados.

Sobre el canal: la lección usa Telegram porque su API para bots es abierta y sencilla, ideal para aprender. En Latinoamérica muchos clientes prefieren WhatsApp. La arquitectura es la misma con la API de WhatsApp Business, pero el registro y las reglas de Meta son distintos (Meta restringe los chatbots de IA de uso general en WhatsApp): revisa sus políticas vigentes antes de prometérselo a un cliente.

🎨 Imagínalo así: clase vs. laboratorio es como la diferencia entre aprender a manejar con el manual y salir a la ciudad con un instructor. El manual lo puedes releer. En el recorrido real sudas, frenas donde no es, el instructor te grita, pero al cabo de 7 días ya manejas.


🎯 El estado final del día 7

El domingo en la noche tienes en tus manos:

  • ✅ Un bot de Telegram desplegado en Cloudflare Workers que responde 24/7
  • ✅ El primer cliente conectado y el cobro con Stripe configurado (la cantidad la acuerdan ustedes; los $500/mes de la lección son un ejemplo)
  • ✅ Una herramienta conectada (Stripe API o correo, según el nicho)
  • ✅ Manejo de estado con Cloudflare KV (el bot recuerda el contexto)
  • ✅ Límite de peticiones + topes de costo (no te va a arruinar con la API)
  • ✅ Un registro de auditoría de todas las interacciones
  • ✅ Monitoreo de las llamadas (en la lección, Helicone; mira la nota del día 7)
  • ✅ Una plantilla de incorporación para los clientes n.º 2-10

Economía: hay un ejemplo de cálculo en la sección "Desglose real de costos" al final de la lección. Las cifras ahí son ilustrativas: haz la cuenta con tus planes y tu precio.


📋 Requisitos previos

Para que el laboratorio tenga sentido, necesitas:

  • ✓ Un plan de pago de Claude con Claude Code (Pro o Max; precios vigentes: Lo vigente)
  • ✓ Una clave de API de la Claude Console: la API se paga aparte de la suscripción
  • ✓ Claude Code instalado y funcionando (mira la lección Instalación y configuración)
  • ✓ Una cuenta de Cloudflare (el nivel gratuito alcanza)
  • ✓ Una cuenta de Stripe (para cobrar)
  • ✓ Una cuenta de Telegram
  • ✓ Haber hecho las lecciones principales del curso (entiendes CLAUDE.md, hooks, lo básico del deploy)
  • ✓ Unas 30 horas de tiempo a lo largo de 7 días (4-5 horas al día)
  • ✓ Disposición para hablar con desconocidos (el día 1 son llamadas de descubrimiento)

🎨 Imagínalo así: un químico no entra al laboratorio sin bata y sin lentes. A este laboratorio no se entra sin cuentas y sin tiempo. Si te falta algo, prepáralo primero y luego regresa.


DÍA 1 (5 horas): el nicho + la primera entrevista con clientes

Objetivo del día: en la noche conoces un nicho concreto + un dolor concreto + 2-3 clientes potenciales reales aceptaron una llamada.

Hora 1: elige el nicho con el método de 3 preguntas

No elijas "pequeños negocios". Eso no es un nicho, es un universo. Un nicho es la intersección de:

  1. ¿Qué grupo de personas conoces mejor que otros? (agentes inmobiliarios en Ecuador, dentistas en Bogotá, entrenadores personales en Miami)
  2. ¿A quiénes de ellos les duele tanto que ya pagan por soluciones flojas? (por ejemplo, por Calendly + Zoom + Notion + una asistente freelance)
  3. ¿Qué parte de ese dolor puede resolver un bot de mensajería con un LLM dentro? (calificar prospectos, agendar citas, responder preguntas frecuentes, secuencias de seguimiento)

Anota las respuestas. La intersección de las tres = tu nicho.

🎨 Imagínalo así: el nicho es la intersección de tres círculos. Uno es tu experiencia. Otro, un dolor real del mercado. El tercero, lo que el bot puede hacer. Donde se cruzan los tres, ahí te paras.

Hora 2: 20 clientes potenciales

Abres Google Sheets. Columnas: nombre, empresa, contacto, fuente, estado.

20 filas. Fuentes: LinkedIn (búsqueda por nicho), Instagram, grupos locales de negocios en WhatsApp o Telegram, recomendaciones de conocidos.

NADA de extracción masiva. 20 nombres reales de los que puedas justificar "por qué justo esta persona encaja".

Hora 3: contacto en frío con 5 personas

Plantilla de mensaje (adáptala a tu nicho):

Escribe esto en el chat
¡Hola, [Nombre]!

Estoy armando un asistente de IA para [nicho]: responde en el chat las preguntas
típicas de los clientes, califica prospectos y agenda citas. Sin respuestas enlatadas.

No estoy vendiendo, estoy investigando problemas. ¿Me regalas 15 minutos de llamada
para contarme cómo manejas hoy la atención a clientes? ¿Qué te desespera?

Como agradecimiento por tu tiempo, te mando un resumen de la industria (3 páginas,
3 ideas concretas para ahorrar horas a la semana).

¿Cuándo te queda bien: mañana o pasado?

Manda 5 mensajes directos. En distintas plataformas. NADA de copiar y pegar: reescribe las primeras 2 líneas para cada persona.

Horas 4-5: 2-3 llamadas de descubrimiento

Algunos de los 5 van a responder; si no respondió nadie, cambia el texto o el nicho. Llamada de 15 minutos. Guion:

Escribe esto en el chat
1. (2 min) Cuéntame de ti y de tu negocio.
2. (5 min) ¿Cómo funciona hoy la atención a clientes después del primer contacto?
   ¿Quién responde los mensajes? ¿Cuántas horas al día se van en eso?
3. (3 min) ¿Cuáles son las 3 preguntas que los clientes hacen MÁS SEGUIDO?
4. (2 min) Si tuvieras un asistente que responde esas 3 preguntas
   en el chat 24/7, ¿cuántas horas a la semana
   te ahorraría?
5. (3 min) Si una herramienta así costara [tu precio] al mes, ¿te parece cara,
   normal o barata? ¿Por qué?

Anota palabra por palabra. No lo "optimices" en tu cabeza.

Resultado del día 1

Al final del día, en un Google Doc:

  • El nicho (una línea)
  • 3 dolores de los clientes (citas textuales)
  • 3 preguntas típicas de los clientes que el bot va a resolver
  • La reacción a tu precio (dispuestos / caro / normal)
  • El nombre de la primera persona dispuesta a ser cliente de prueba

Si no tienes un primer cliente de prueba, no empiezas el día 2. Regresa a la hora 3.

🎨 Imagínalo así: construir una casa sin comprador es levantar la casa de tus sueños en medio de un terreno baldío con la esperanza de que alguien la compre. Primero el comprador, luego los cimientos.


DÍA 2 (5 horas): diseño del MVP + CLAUDE.md + estructura base

Objetivo del día: el diseño de 3 caminos felices + un CLAUDE.md escrito + la estructura base del Worker creada + el bot registrado en BotFather.

Hora 1: flujos de conversación (3 caminos felices)

En papel o en Miro, dibuja 3 diálogos que el bot debe completar de principio a fin.

Ejemplo para un agente inmobiliario en Ecuador:

Flujo 1: un prospecto calificado

Escribe esto en el chat
Cliente: Buenas tardes, busco un departamento en Cuenca de hasta $80K
Bot: ¡Hola! Le ayudo a encontrarlo. 3 preguntas:
     1. ¿Cuántas recámaras necesita?
     2. ¿La zona es importante o es flexible?
     3. ¿Para cuándo piensa mudarse?
Cliente: 2 recámaras, en el centro, en 3 meses
Bot: Anotado. Es un prospecto calificado: se lo paso al agente.
     Le llamará hoy antes de las 18:00. Confírmeme su número.

Flujo 2: respuesta automática a una pregunta frecuente

Escribe esto en el chat
Cliente: ¿Se necesita visa para comprar un departamento?
Bot: Los extranjeros pueden comprar inmuebles en Ecuador sin visa.
     Solo se necesita el pasaporte + el RUC (se obtiene en 1 día).
     Si quiere platicarlo con el agente, escriba "llamada".

La respuesta del bot en este ejemplo es ilustrativa: los datos legales el bot los toma solo de las preguntas frecuentes que revisó el cliente.

Flujo 3: no es nuestro cliente

Escribe esto en el chat
Cliente: ¿Me pueden tramitar un crédito hipotecario?
Bot: Trabajamos con compras directas, no con créditos hipotecarios.
     Para el crédito le puedo recomendar a [socio]. ¿Le paso el contacto?

Por qué ahora: el día 3 no vas a estar inventando "qué debe decir el bot". Solo vas a implementar lo que dibujaste.

Hora 2: CLAUDE.md como prompt de sistema

Crea la carpeta del proyecto:

bash
mkdir telegram-bot-prod && cd telegram-bot-prod
git init

CLAUDE.md (será el prompt de sistema para el LLM):

Escribe esto en el chat
# Asistente de una inmobiliaria en Cuenca, Ecuador

## Rol
Eres el primer contacto de los clientes de una inmobiliaria en Cuenca.
Respondes en Telegram. El objetivo es calificar al prospecto y pasarlo
a una persona, o responder las preguntas frecuentes.

## Estilo
- Breve, amable, de usted
- Sin rodeos y sin emojis en cada línea
- Si no sabes la respuesta, NO la inventes: di "lo consulto con el agente"

## 3 caminos felices
[Copia aquí los flujos de la hora 1]

## Lo que NUNCA haces
- No prometes precios sin que el agente los apruebe
- No afirmas datos legales ("100% legal")
- No envías los datos personales del cliente a terceros
- No tratas el tema de créditos hipotecarios (lo canalizas al socio)

## Cuándo pasar a una persona
- El cliente está listo para ver un departamento en el próximo mes
- El cliente hace una pregunta que no está en las preguntas frecuentes
- El cliente escribe "quiero una llamada" / "llámenme"

## Tools
- save_lead(name, phone, budget, bedrooms): guarda en KV
- notify_realtor(lead_id): envía un aviso al agente

Es la constitución del producto del bot. Si la cambias, cambia su comportamiento.

Horas 3-4: estructura base del Cloudflare Worker

Configuración:

bash
npm create cloudflare@latest -- telegram-bot
# el asistente te preguntará la plantilla (Hello World) y el lenguaje (TypeScript); las banderas del comando cambian,
# revisa la versión vigente en la documentación de Cloudflare
cd telegram-bot
npm install @anthropic-ai/sdk

src/index.ts, la estructura mínima:

typescript
import Anthropic from "@anthropic-ai/sdk";

export interface Env {
  ANTHROPIC_API_KEY: string;
  TELEGRAM_BOT_TOKEN: string;
  TELEGRAM_WEBHOOK_SECRET: string;
  BOT_KV: KVNamespace;
  SYSTEM_PROMPT: string;
}

interface TelegramUpdate {
  update_id: number;
  message?: {
    message_id: number;
    from: { id: number; first_name: string; username?: string };
    chat: { id: number };
    text?: string;
  };
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Verificación del secreto del webhook
    const secret = request.headers.get("X-Telegram-Bot-Api-Secret-Token");
    if (secret !== env.TELEGRAM_WEBHOOK_SECRET) {
      return new Response("Forbidden", { status: 403 });
    }

    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const update: TelegramUpdate = await request.json();
    if (!update.message?.text) {
      return new Response("OK"); // ignorar lo que no es texto
    }

    const chatId = update.message.chat.id;
    const userText = update.message.text;

    // TODO día 3: aquí va la llamada real al LLM
    const reply = `Echo: ${userText}`;

    await sendTelegramMessage(env, chatId, reply);
    return new Response("OK");
  },
};

async function sendTelegramMessage(env: Env, chatId: number, text: string) {
  const url = `https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage`;
  await fetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ chat_id: chatId, text }),
  });
}

wrangler.toml:

toml
name = "telegram-bot"
main = "src/index.ts"
compatibility_date = "2026-10-01"

[[kv_namespaces]]
binding = "BOT_KV"
id = "your-kv-namespace-id"  # lo crearemos después

Cloudflare crea los proyectos nuevos con un archivo wrangler.jsonc; el formato wrangler.toml también es compatible. Si el asistente creó wrangler.jsonc, pasa ahí los mismos campos.

Configura los secretos (NUNCA los subas a git):

bash
wrangler secret put ANTHROPIC_API_KEY
wrangler secret put TELEGRAM_BOT_TOKEN
wrangler secret put TELEGRAM_WEBHOOK_SECRET  # genéralo con openssl rand -hex 32

Hora 5: el bot de Telegram con BotFather

En Telegram → @BotFather → /newbot → nombre + nombre de usuario → recibes el token.

Conectas el webhook (después del primer wrangler deploy):

bash
WEBHOOK_URL="https://telegram-bot.your-subdomain.workers.dev"
SECRET="<el mismo que en wrangler secret>"
curl -X POST "https://api.telegram.org/bot${BOT_TOKEN}/setWebhook" \
  -H "Content-Type: application/json" \
  -d "{\"url\":\"${WEBHOOK_URL}\",\"secret_token\":\"${SECRET}\"}"

Escríbele "hola" al bot en Telegram. Debe responder "Echo: hola". Si funciona, pasa al día 3.

🎨 Imagínalo así: el día 2 es el trazo de los cimientos. Todavía no es una casa, pero las estacas ya están clavadas. Siguen los muros.


DÍA 3 (5 horas): el manejo central de la conversación

Objetivo del día: el bot responde con sentido a través del LLM, los errores no lo tumban y cada interacción queda en el registro de auditoría.

Horas 1-3: webhook + LLM + respuesta

Cambias // TODO día 3 por la llamada real:

typescript
import Anthropic from "@anthropic-ai/sdk";

async function generateReply(
  env: Env,
  userText: string,
  conversationHistory: Array<{ role: "user" | "assistant"; content: string }>,
): Promise<string> {
  const client = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });

  const response = await client.messages.create({
    model: "claude-sonnet-5-5", // pásalo a una variable de entorno; modelos vigentes: «Lo vigente»
    max_tokens: 500,
    system: env.SYSTEM_PROMPT, // cargado desde KV o desde las vars de wrangler
    messages: [
      ...conversationHistory,
      { role: "user", content: userText },
    ],
  });

  // Extraemos el texto de la respuesta
  const textBlock = response.content.find((b) => b.type === "text");
  return textBlock && textBlock.type === "text"
    ? textBlock.text
    : "Disculpe, no le entendí. ¿Me lo puede decir de otra forma?";
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const secret = request.headers.get("X-Telegram-Bot-Api-Secret-Token");
    if (secret !== env.TELEGRAM_WEBHOOK_SECRET) {
      return new Response("Forbidden", { status: 403 });
    }

    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const update: TelegramUpdate = await request.json();
    if (!update.message?.text) return new Response("OK");

    const chatId = update.message.chat.id;
    const userText = update.message.text;

    try {
      // Cargamos el historial de la conversación desde KV (lo ampliamos el día 4)
      const historyKey = `chat:${chatId}:history`;
      const historyRaw = await env.BOT_KV.get(historyKey);
      const history: Array<{ role: "user" | "assistant"; content: string }> =
        historyRaw ? JSON.parse(historyRaw) : [];

      // Generamos la respuesta
      const reply = await generateReply(env, userText, history);

      // Actualizamos el historial (últimos 10 intercambios)
      const updatedHistory = [
        ...history,
        { role: "user" as const, content: userText },
        { role: "assistant" as const, content: reply },
      ].slice(-20);

      await env.BOT_KV.put(historyKey, JSON.stringify(updatedHistory), {
        expirationTtl: 60 * 60 * 24 * 7, // 7 días
      });

      // Registro de auditoría
      await logEvent(env, {
        ts: new Date().toISOString(),
        chat_id: chatId,
        user_text: userText,
        bot_reply: reply,
        tokens: 0, // TODO: lo llenamos con los datos de response
      });

      await sendTelegramMessage(env, chatId, reply);
    } catch (e) {
      console.error("Bot error:", e);
      await sendTelegramMessage(
        env,
        chatId,
        "Disculpe, hay un problema temporal. Intente de nuevo en un minuto.",
      );
    }

    return new Response("OK");
  },
};

Horas 4-5: manejo de errores + registro de auditoría

logEvent escribe en KV o en un servicio externo:

typescript
interface AuditEntry {
  ts: string;
  chat_id: number;
  user_text: string;
  bot_reply: string;
  tokens: number;
}

async function logEvent(env: Env, entry: AuditEntry): Promise<void> {
  const key = `audit:${entry.ts}:${entry.chat_id}`;
  await env.BOT_KV.put(key, JSON.stringify(entry), {
    expirationTtl: 60 * 60 * 24 * 90, // se conserva 90 días
  });
}

Patrones de protección:

typescript
// Límite de longitud del mensaje del usuario
if (userText.length > 1000) {
  await sendTelegramMessage(
    env,
    chatId,
    "El mensaje es demasiado largo. Redúzcalo a 1000 caracteres.",
  );
  return new Response("OK");
}

// Tiempo límite para la llamada al LLM
const replyPromise = generateReply(env, userText, history);
const timeoutPromise = new Promise<string>((_, reject) =>
  setTimeout(() => reject(new Error("LLM timeout")), 15000),
);
const reply = await Promise.race([replyPromise, timeoutPromise]);

Deploy:

bash
wrangler deploy

Escríbele al bot 5 mensajes distintos. Todos deben recibir respuestas con sentido. El historial de la conversación se conserva (prueba con "¿te acuerdas de lo que te dije antes?").

🎨 Imagínalo así: el día 3 los muros ya están arriba. El bot habla. Pero la casa todavía no tiene agua: eso es el día 4.


DÍA 4 (5 horas): conectar herramientas (integración + estado)

Objetivo del día: el bot no solo conversa: trabaja de verdad con herramientas (Stripe, correo o la API de tu caso de uso).

Horas 1-2: integración de herramientas

Para el agente inmobiliario en Ecuador hace falta: guardar al prospecto + avisarle al agente por Telegram.

Ampliamos el prompt de sistema (CLAUDE.md) con herramientas:

typescript
const tools: Anthropic.Tool[] = [
  {
    name: "save_lead",
    description: "Guarda en la base a un prospecto calificado",
    input_schema: {
      type: "object",
      properties: {
        name: { type: "string", description: "Nombre del cliente" },
        phone: { type: "string", description: "Teléfono con código de país" },
        budget_usd: { type: "number", description: "Presupuesto en USD" },
        bedrooms: { type: "number", description: "Número de recámaras" },
        timeline_months: { type: "number", description: "Plazo para mudarse" },
      },
      required: ["name", "phone", "budget_usd", "bedrooms"],
    },
  },
  {
    name: "notify_realtor",
    description: "Envía al agente un aviso sobre un prospecto nuevo",
    input_schema: {
      type: "object",
      properties: {
        lead_id: { type: "string" },
        urgency: { type: "string", enum: ["high", "medium", "low"] },
      },
      required: ["lead_id", "urgency"],
    },
  },
];

El ciclo de ejecución de herramientas:

typescript
async function generateReplyWithTools(
  env: Env,
  userText: string,
  history: Array<Anthropic.MessageParam>,
): Promise<string> {
  const client = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });
  const messages: Array<Anthropic.MessageParam> = [
    ...history,
    { role: "user", content: userText },
  ];

  // Máximo 5 vueltas para evitar llamadas infinitas a herramientas
  for (let i = 0; i < 5; i++) {
    const response = await client.messages.create({
      model: "claude-sonnet-5-5", // pásalo a una variable de entorno; modelos vigentes: «Lo vigente»
      max_tokens: 1000,
      system: env.SYSTEM_PROMPT,
      tools,
      messages,
    });

    if (response.stop_reason === "end_turn") {
      const textBlock = response.content.find((b) => b.type === "text");
      return textBlock && textBlock.type === "text" ? textBlock.text : "";
    }

    if (response.stop_reason === "tool_use") {
      messages.push({ role: "assistant", content: response.content });

      const toolResults: Array<Anthropic.ToolResultBlockParam> = [];
      for (const block of response.content) {
        if (block.type === "tool_use") {
          const result = await executeTool(env, block.name, block.input);
          toolResults.push({
            type: "tool_result",
            tool_use_id: block.id,
            content: JSON.stringify(result),
          });
        }
      }
      messages.push({ role: "user", content: toolResults });
      continue;
    }

    break;
  }

  return "Disculpe, no pude procesar su solicitud. Intente de nuevo.";
}

async function executeTool(
  env: Env,
  name: string,
  input: unknown,
): Promise<unknown> {
  if (name === "save_lead") {
    const lead = input as {
      name: string;
      phone: string;
      budget_usd: number;
      bedrooms: number;
    };
    const leadId = `lead_${Date.now()}`;
    await env.BOT_KV.put(`leads:${leadId}`, JSON.stringify(lead));
    return { lead_id: leadId, status: "saved" };
  }
  if (name === "notify_realtor") {
    const { lead_id, urgency } = input as { lead_id: string; urgency: string };
    // Aviso real por Telegram al chat del agente
    const realtorChatId = Number(env.REALTOR_CHAT_ID);
    await sendTelegramMessage(
      env,
      realtorChatId,
      `Nuevo prospecto (${urgency}): ${lead_id}`,
    );
    return { status: "notified" };
  }
  return { error: `Unknown tool: ${name}` };
}

Horas 3-4: manejo de estado (el patrón con KV)

Ya lo hicimos en parte el día 3. Lo ampliamos: guardamos no solo el historial, sino también el perfil del cliente:

typescript
interface ClientProfile {
  chat_id: number;
  first_seen: string;
  name?: string;
  phone?: string;
  budget_usd?: number;
  bedrooms?: number;
  status: "new" | "qualifying" | "qualified" | "passed_to_realtor";
  last_interaction: string;
}

async function loadProfile(env: Env, chatId: number): Promise<ClientProfile> {
  const raw = await env.BOT_KV.get(`profile:${chatId}`);
  if (raw) return JSON.parse(raw);
  return {
    chat_id: chatId,
    first_seen: new Date().toISOString(),
    status: "new",
    last_interaction: new Date().toISOString(),
  };
}

async function saveProfile(env: Env, profile: ClientProfile): Promise<void> {
  profile.last_interaction = new Date().toISOString();
  await env.BOT_KV.put(`profile:${profile.chat_id}`, JSON.stringify(profile), {
    expirationTtl: 60 * 60 * 24 * 365, // un año
  });
}

Le pasas el perfil al prompt de sistema para que el bot recuerde el contexto entre sesiones:

typescript
const profile = await loadProfile(env, chatId);
const systemWithContext = `${env.SYSTEM_PROMPT}

## Perfil del cliente actual
${JSON.stringify(profile, null, 2)}

Usa esta información para no hacer preguntas cuya respuesta ya conoces.
Actualiza el perfil con las herramientas.`;

Hora 5: el arnés de pruebas

Creas tests/conversation.test.ts:

typescript
import { describe, test, expect } from "vitest";

describe("Bot conversation flows", () => {
  test("Flujo 1: el prospecto calificado se guarda", async () => {
    const responses = await simulateConversation([
      "Buenas tardes, busco un departamento en Cuenca de hasta $80K",
      "2 recámaras, en el centro, en 3 meses",
      "Me llamo [cliente], contacto por el bot",
    ]);
    expect(responses).toContain("anotado");
    // Verificamos que el prospecto se guardó en KV
    const leads = await env.BOT_KV.list({ prefix: "leads:" });
    expect(leads.keys.length).toBeGreaterThan(0);
  });

  test("Flujo 2: la pregunta frecuente se resuelve sin pasar al agente", async () => {
    const reply = await simulateConversation([
      "¿Se necesita visa para comprar un departamento?",
    ]);
    expect(reply[0]).toMatch(/visa.*no se necesita|sin visa/i);
  });

  test("Flujo 3: crédito hipotecario → se canaliza al socio", async () => {
    const reply = await simulateConversation(["¿Me pueden tramitar un crédito hipotecario?"]);
    expect(reply[0]).toMatch(/hipotecari|socio/i);
  });
});

Córrelo: npx vitest. Las 3 pruebas deben salir en verde.

🎨 Imagínalo así: el día 4 ya llegaron el agua y la luz. La casa funciona.


DÍA 5 (4 horas): pulido + seguridad + deploy a producción

Objetivo del día: el bot no te va a arruinar, no se va a caer con la carga y está desplegado en producción.

Hora 1: límite de peticiones por usuario

No dejes que un solo cliente queme tu clave de API:

typescript
async function checkRateLimit(env: Env, chatId: number): Promise<boolean> {
  const key = `ratelimit:${chatId}`;
  const countRaw = await env.BOT_KV.get(key);
  const count = countRaw ? parseInt(countRaw) : 0;

  if (count >= 30) return false; // máximo 30 mensajes por minuto

  await env.BOT_KV.put(key, String(count + 1), { expirationTtl: 60 });
  return true;
}

// En el fetch handler:
if (!(await checkRateLimit(env, chatId))) {
  await sendTelegramMessage(
    env,
    chatId,
    "Demasiados mensajes. Espere un minuto.",
  );
  return new Response("OK");
}

Hora 2: topes de costo

Un tope de costo diario para que un bug accidental no queme dinero de más durante la noche:

typescript
async function checkDailyCost(env: Env): Promise<boolean> {
  const today = new Date().toISOString().split("T")[0];
  const key = `cost:${today}`;
  const costRaw = await env.BOT_KV.get(key);
  const costCents = costRaw ? parseInt(costRaw) : 0;
  return costCents < 1000; // tope fijo de $10 al día
}

async function recordCost(env: Env, tokens: number): Promise<void> {
  const today = new Date().toISOString().split("T")[0];
  const key = `cost:${today}`;
  const costRaw = await env.BOT_KV.get(key);
  const currentCents = costRaw ? parseInt(costRaw) : 0;
  // Una estimación burda y exagerada: 1 centavo por cada 100 tokens. Sirve como fusible;
  // los precios vigentes de los tokens están en la página «Lo vigente»
  const addCents = Math.ceil(tokens / 100);
  await env.BOT_KV.put(key, String(currentCents + addCents), {
    expirationTtl: 60 * 60 * 48,
  });
}

// En fetch:
if (!(await checkDailyCost(env))) {
  await sendTelegramMessage(
    env,
    chatId,
    "El servicio está saturado por ahora. Mañana se reanuda.",
  );
  return new Response("OK");
}

Hora 3: mensajes de error útiles

Plantillas para los errores frecuentes (en lugar de un stack trace):

typescript
const ERROR_MESSAGES = {
  timeout:
    "Me estoy tardando demasiado. Intente hacer la pregunta más sencilla o repítala en un minuto.",
  rate_limit:
    "Demasiados mensajes seguidos. Espere un minuto y le respondo.",
  api_down:
    "Tenemos problemas técnicos. El agente ya recibió el aviso y le llamará personalmente.",
  unknown_topic:
    "No entendí bien su pregunta. Se la paso al agente: le llamará en menos de una hora.",
};

Hora 4: deploy a producción

bash
# Revisamos los secretos en producción
wrangler secret list

# Deploy
wrangler deploy

# Verificar el webhook
curl "https://api.telegram.org/bot${BOT_TOKEN}/getWebhookInfo"

En el panel de Cloudflare → Workers → tu bot → Logs (en tiempo real). Deberías ver las peticiones entrantes.

Pruebas rápidas en producción:

  1. El flujo 1 completo (prospecto calificado)
  2. El flujo 2 (pregunta frecuente)
  3. El flujo 3 (no es nuestro cliente)
  4. Caso límite: un mensaje largo (>1000 caracteres)
  5. Caso límite: 35 mensajes en un minuto (se activa el límite de peticiones)

Si todas pasan, sigue el día 6.

🎨 Imagínalo así: el día 5 instalaste los fusibles, la tierra física y la alarma contra incendios. La casa es segura.


DÍA 6 (5 horas): la incorporación del primer cliente

Objetivo del día: un cliente real empieza a usar el bot en su negocio. Al final del día, los primeros prospectos llegan por el bot.

Horas 1-2: el flujo de incorporación del cliente

Crea una lista de verificación de incorporación (puede ser en Notion o en Markdown):

Escribe esto en el chat
# Lista de verificación de incorporación del cliente

## Antes de la llamada (lo hacemos nosotros)
- [ ] Se creó un bot de Telegram dedicado para el cliente (con su propio token)
- [ ] El CLAUDE.md está adaptado al negocio del cliente
  - [ ] Nombre de la empresa, tono de voz
  - [ ] 3 caminos felices sacados de sus diálogos reales
  - [ ] Preguntas frecuentes de su base (5-10 preguntas típicas)
- [ ] Las herramientas están configuradas (si hay que guardar prospectos, dónde)
- [ ] Se recibió y se probó el chat de avisos del agente

## En la llamada (30 minutos)
- [ ] Muestra el bot en acción (los 3 caminos felices en vivo)
- [ ] Explica cómo editar las preguntas frecuentes (acceso al CLAUDE.md)
- [ ] Cuenta cómo ver los prospectos (KV o sincronización con Google Sheet)
- [ ] Muestra cómo agregarte al chat si el bot se traba
- [ ] Platiquen el cobro: monto y forma de pago con Stripe, factura por correo
- [ ] Acuerden un periodo de prueba (7 días) o el primer pago de una vez

## Después de la llamada
- [ ] Envía la factura de Stripe
- [ ] Conecta el webhook de producción a su canal de Telegram
- [ ] Configura el monitoreo de su tráfico (etiqueta de Helicone por client_id)
- [ ] Escribe un seguimiento a las 24 horas: "¿Cómo les fue el primer día?"

Horas 3-4: la llamada de incorporación de 30 minutos

Estructura:

Escribe esto en el chat
0-5 min: plática inicial + contexto
5-15 min: demostración en vivo (3 caminos felices)
15-25 min: ajustes para su negocio (cambios en el CLAUDE.md en vivo)
25-30 min: cobro + siguientes pasos

La regla principal: el cliente debe escribirle él mismo el primer mensaje al bot durante la llamada y recibir respuesta. Ese es el momento "wow" que vende.

Hora 5: atender los problemas del primer día en vivo

Lo que más va a pasar:

  • El bot respondió algo que no era a una pregunta específica → corriges el CLAUDE.md (la sección de preguntas frecuentes)
  • Una herramienta no funcionó como debía → corriges la descripción o los parámetros de la herramienta
  • El cliente quiere otro camino feliz → lo agregas

Cada corrección la subes con un commit:

bash
git commit -m "fix(bot): agregar pregunta frecuente sobre la comisión del agente"
wrangler deploy

El ciclo de retroalimentación se mide en minutos, no en días. Eso es operar en vivo.

Resultado del día 6

  • El bot está en producción con el cliente
  • 2-5 prospectos pasaron por el bot (aunque sea en modo de prueba)
  • El cliente vio valor real y está dispuesto a pagar

🎨 Imagínalo así: el día 6 llegó el primer inquilino. La luz funciona, la llave del agua funciona, y tú estás cerca con la llave inglesa por si acaso.


DÍA 7 (3 horas): precio + sostenibilidad

Objetivo del día: el dinero cobrado, el monitoreo configurado y un plan para la semana 2.

Hora 1: la factura de Stripe

bash
# Con la CLI de Stripe o desde el panel
stripe customers create \
  --name="Nombre del cliente" \
  --email="<client_email>"

# Factura
stripe invoices create \
  --customer=cus_XXX \
  --collection_method=send_invoice \
  --days_until_due=7

# La cantidad de abajo es un ejemplo (50000 centavos = $500); pon tu propio precio
stripe invoice_items create \
  --customer=cus_XXX \
  --invoice=in_XXX \
  --description="Bot de IA en Telegram: 1 mes" \
  --amount=50000 \
  --currency=usd

stripe invoices send --id=in_XXX

O más fácil: en el panel de Stripe → Invoices → Create. Son 5 minutos a mano.

Para el cobro recurrente (del mes 2 en adelante): crea una Subscription con el precio que acordaron y el pago automático a través del Stripe Customer Portal.

Hora 2: monitoreo de las llamadas (Helicone)

En la lección se usa Helicone para el monitoreo: un proxy entre tu código y la API que muestra cada llamada y su costo. Importante: desde marzo de 2026, Helicone forma parte de Mintlify y funciona en modo de mantenimiento (correcciones y soporte para modelos nuevos, sin funciones nuevas). Las condiciones del nivel gratuito están en el sitio del servicio. Alternativas: Langfuse, Portkey o la sección de uso de la Claude Console.

typescript
// Cambia la inicialización del Anthropic SDK:
const client = new Anthropic({
  apiKey: env.ANTHROPIC_API_KEY,
  baseURL: "https://anthropic.helicone.ai", // proxy a través de Helicone
  defaultHeaders: {
    "Helicone-Auth": `Bearer ${env.HELICONE_API_KEY}`,
    "Helicone-Property-Client": "client_001", // etiqueta por cliente
  },
});

Te registras en helicone.ai → copias la clave → wrangler secret put HELICONE_API_KEY. Ves todas las llamadas: costo, latencia, tasa de errores. Sin hacer tu propio dashboard.

Hora 3: retrospectiva + plan de la semana 2

Abre una libreta y responde con honestidad:

  1. ¿Qué funcionó? (por ejemplo: los mensajes en frío funcionan si la primera línea es personal)
  2. ¿Qué NO funcionó? (por ejemplo: 2 de 5 clientes potenciales dijeron que no al llegar al precio: demasiado caro para ellos)
  3. ¿Qué aprendí del nicho? (por ejemplo: ya pagan Calendly y una asistente freelance, así que el precio hay que justificarlo con capacidades extra)
  4. El siguiente cliente: ¿quién y cómo?

Arma una hoja de cálculo de la semana 2:

Escribe esto en el chat
Día 8: 10 mensajes de contacto (usamos lo que funcionó en la semana 1)
Día 9: 3 llamadas de descubrimiento
Día 10: 1 llamada de incorporación
Día 11: soporte al cliente actual + ajustes según su retroalimentación
Día 12: 2 llamadas de incorporación (si maduraron 2 prospectos)
Día 13: facturar a 2 clientes nuevos
Día 14: retrospectiva de la semana 2

Objetivo de la semana 2: ampliar el embudo: más conversaciones y más incorporaciones. Las cifras concretas defínelas tú según los resultados de la primera semana.

🎨 Imagínalo así: el día 7 es la fiesta de inauguración. El inquilino paga el primer mes de renta y tú cuelgas el letrero "se rentan más departamentos en este edificio".


💰 Desglose real de costos

Las cifras de abajo son estimaciones del autor para mostrar la estructura de los gastos. No son una lista de precios ni un pronóstico de ingresos. Precios vigentes de tokens y planes: Lo vigente.

Periodo Tus gastos Fuente
Días 1-2 (investigación + diseño) $0 solo tiempo
Día 3 (pruebas del LLM) pequeños pruebas rápidas con la Anthropic API
Día 4 (desarrollo de herramientas) pequeños pruebas de ejecución de herramientas
Día 5 (pruebas de carga) pequeños pruebas del límite de peticiones + tope de costo
Día 6 (en vivo con el cliente) pequeños diálogos reales
Día 7 (Stripe + monitoreo) $0 niveles gratuitos de los servicios
Total de arranque decenas de dólares depende de los planes y del número de pruebas
Operación del mes 1 (1 cliente) LLM + Cloudflare Workers Free: 100,000 peticiones al día; plan de pago desde $5 al mes (a octubre de 2026)
Ingresos depende de lo que acuerden el precio y las condiciones los defines tú

Lo que se comerá la economía al crecer:

  • Más peticiones de las que da el plan gratuito de Workers → plan de pago de Cloudflare
  • Sube el gasto en tokens → prompt caching (el contexto que se repite cuesta bastante menos) y Batch API para tareas no urgentes
  • El soporte toma más de un par de horas a la semana → una asistente virtual o automatización

Con más clientes, crece también el soporte. Haz con anticipación la cuenta de la economía por cliente: el precio menos los tokens, la infraestructura y tu tiempo.


⚠️ Errores comunes

Día 1: el nicho es demasiado amplio

❌ "Un bot de IA para pequeños negocios" ✅ "Un bot de IA para agentes inmobiliarios de Cuenca que venden a extranjeros"

Nicho amplio = contacto genérico = casi nadie responde.

Día 3: saltarse el manejo de errores

Si el día 3 dices "por ahora sin try/catch, luego lo agrego", el día 6 el bot se va a caer en plena llamada en vivo frente al cliente. Frente al cliente. No es la historia que quieres contar.

Día 6: olvidar los términos de Stripe / la política de reembolsos

Sin una política de reembolsos por escrito, el primer cliente inconforme puede hacer un contracargo y Stripe puede limitar tu cuenta. La opción más sencilla: "Reembolso proporcional en los primeros 14 días; después, sin reembolso; la suscripción se puede cancelar cualquier día." Las condiciones de reembolso dependen del país; esto no es asesoría legal.

Día 7: no configurar el monitoreo

A las 2 semanas, la ejecución de una herramienta empieza a fallar en un caso límite. Te enteras por el cliente: "no nos funciona". Con monitoreo (por ejemplo, Helicone), te enteras tú, no el cliente.

Cualquier día: atarte a un solo proveedor

Si escribes el nombre del modelo en 15 lugares, cambiar a Haiku o a Gemini para optimizar costos te va a costar un día de trabajo. Una sola constante MODEL_NAME en las variables de entorno resuelve el problema en 30 segundos.


✅ Lo que tienes al final del día 7

En concreto. No "potencial". No "experiencia". Artefactos reales:

  1. Un bot de Telegram funcionando en producción en Cloudflare Workers: responde 24/7, desplegado y monitoreado
  2. El primer cliente conectado: con las condiciones de pago acordadas
  3. Cobro con Stripe: factura enviada y cobro recurrente configurado (si el cliente aceptó pagar)
  4. Registro de auditoría: todas las interacciones guardadas 90 días (la base para auditar)
  5. Monitoreo de costos: un dashboard de monitoreo donde ves los tokens y el costo por cliente
  6. Límite de peticiones + tope de costo: el sistema no se arruina por un bug o por abuso
  7. Plantilla de incorporación de clientes: para los clientes n.º 2-10 (ya no desde cero)
  8. 3 caminos felices verificados: funcionan de verdad en un negocio real
  9. Manual de contacto en frío: qué escribir, por qué canal y qué tasa de respuesta obtuviste
  10. Retrospectiva: qué funciona, qué no, el plan de la semana 2

📈 Plan para escalar (semanas 2-4, en breve)

Semana 2 (ampliamos el embudo):

  • 30 mensajes en frío usando las plantillas que funcionaron en la semana 1
  • 6-8 llamadas de descubrimiento
  • 2-3 incorporaciones
  • Costo: 25 horas de tu tiempo

Semanas 3-4 (convertimos todo en plantillas):

  • Convierte la incorporación en plantilla (montar un bot nuevo en 30 minutos)
  • Una asistente virtual para preparar listas y la primera correspondencia
  • Éxito del cliente: correos semanales de seguimiento (automáticos)
  • Costo: 20 horas por semana

Mes 2:

  • Empezarán a llegar recomendaciones espontáneas
  • Puedes revisar el precio para clientes nuevos, si el nicho lo permite
  • Piensa en la "marca blanca": un bot con la marca del cliente (nivel premium)

Dónde NO escalar:

  • No hagas "un bot de IA para todo": pierdes el nicho
  • No contrates vendedores en la etapa inicial: es demasiado pronto
  • No escribas tu propio dashboard: el monitoreo existente te alcanza por mucho tiempo


📚 Fuentes


Ideas clave

El laboratorio no es una lección. Son 7 días de trabajo real cuyo resultado es un bot funcionando en producción y un primer cliente con condiciones acordadas. Si el domingo en la noche solo tienes "potencial", el laboratorio no salió: empieza la semana 2 como si fuera la semana 1.

Primero el cliente, luego el código. El día 1 se va en llamadas de descubrimiento, no en código. Sin un dolor comprobado, el bot más bonito no le sirve a nadie. Sin un contrato pagado, el producto todavía no existe.

La economía del bot no es magia de la IA. Es el nicho correcto + el precio correcto + las protecciones correctas. El precio se justifica cuando le ahorra al cliente un tiempo que vale más que tu servicio; haz esa cuenta con las cifras de cada cliente concreto.


Siguiente lección

→ Paso a paso: un SaaS MVP en 7 días

La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso