Todos los proyectos

Personal · Automatización · IA · Proyecto personal · sep. 2026

Outbound AI: CRM comercial y automatización de emails

Definí el flujo de trabajo y las reglas de negocio de un CRM de prospección asistido por IA y lo construí con Claude Code.

ImportarInvestigarRedactarAprobarEnviarSeguimientoRespuesta los pasos sombreados los hace una persona
Diagrama

Contexto

Proyecto personal de septiembre de 2026. Quería hacer prospección B2B personalizada sin escribir cada email a mano y sin enviar emails masivos genéricos.

Problema

Los primeros emails personalizados requieren mucha investigación, y los seguimientos se olvidan o, peor aún, se envían a alguien que ya ha respondido.

Qué hice

  • Definí el flujo diario y las diez reglas que el sistema nunca debe romper (por ejemplo, un primer email nunca se envía automáticamente).
  • Decidí qué es automático y qué necesita a una persona.
  • Lo probé y lo uso con contactos reales. El código se escribió con Claude Code; no es experiencia profesional como ingeniero de software.

Solución

Investigación y redacción
Agentes de IA investigan cada empresa, encuentran un motivo contrastado para escribir, redactan el email y eliminan afirmaciones sin respaldo.
CRM y campañas
Contactos, empresas, fases del pipeline y campañas comparadas por tasa de cierre.
Seguimientos
Yo apruebo y envío cada primer email; los seguimientos se envían solos y se detienen en cuanto alguien responde.

Cómo funciona: sigue un ejemplo paso a paso (cliente ficticio)

Resultado

  • Aplicación funcional: backend en Python/FastAPI, interfaz en React/TypeScript y 18 archivos de tests automáticos.
  • En uso inicial: más de 400 contactos importados, primeros emails enviados y respuestas detectadas.

Evidencias

Ver código: Los controles antes de cada seguimiento

Extracto de backend/app/services/followup_engine.py. Cada seguimiento pasa estos controles, del más barato y decisivo al más costoso; la comprobación de respuesta se hace dos veces.

def _process_link(session, link, grace_days, at):
    """All gates for one follow-up, cheapest and most decisive first."""
    contact = session.get(Contact, link.contact_id)

    # 1. Block state and contactability.
    contactable, reason = contact_service.is_contactable(contact)
    if not contactable:
        sequence_service.stop(session, link, reason=reason)
        return outcome(False, True, reason)

    # 2. Has the prospect replied? Re-checked here, never assumed from earlier.
    if response_service.has_replied_since(session, contact.id):
        sequence_service.stop(session, link, reason="Prospect replied — follow-up cancelled")
        return outcome(False, True, "Prospect had already replied")

    # 3. The previous email must be a verified send.
    previous = _previous_sent_emails(session, link)
    if not previous:
        link.next_follow_up_at = utcnow() + timedelta(hours=6)
        return outcome(False, False, "No verified sent email in this thread yet")

    # … 4–7: grace period, sequence step, sender pinned to the original mailbox,
    #        daily capacity, reuse of a draft that survived a crash

    # 8. Last look before handing anything to Mail.
    if response_service.has_replied_since(session, contact.id):
        email.status = str(EmailStatus.CANCELLED)
        sequence_service.stop(session, link, reason="Prospect replied while the follow-up was being written")
        return outcome(False, True, "Reply arrived during generation")

    sent, error = mail_service.send_follow_up(session, email)

La base de datos de contactos se queda en mi ordenador y nunca se sube al repositorio. El repositorio es privado y está disponible si se solicita.

Herramientas

Claude Code · Python · FastAPI · SQLAlchemy · SQLite · React · TypeScript · Anthropic API · AppleScript · Git