SEIF Docs

Sales funnel on WhatsApp

A team project can carry a funnel: the checkpoints a lead goes through, what is due at each one and the exact message for it. The team prepares the day, the founder approves in Your decisions, and the office sends what was approved from the project's WhatsApp line: spaced, in business hours, one batch at a time. Replies come back to the founder with the conversation and the checkpoints to move to. Everything is a project file, so the cloud, a desktop and a Claude Code session read the same funnel.

FileWhat it holds
funil/funil.yaml (or funnel.yaml)Checkpoints, deadlines, messages per checkpoint, rules, variables, checks, API actions, API reads
team/shared/leads.csvOne row per lead: checkpoint, last contact, next date, codes from connected systems
knowledge/sales/funil.mdThe messages: a ## section per checkpoint; each > quote block is one WhatsApp message
team/shared/contatos.csvEvery message in and out (written by the WhatsApp line); the funnel's memory
team/conversas/<lead>.mdThe same conversation, readable
team/shared/optout.csvNumbers that asked not to be contacted (column telefone): never written to
team/lotes/<file>.jsonFirst messages written in advance for a group of leads ([{ "lead", "texts" }])
connections/<api>.yamlkind: http-api: the connected system the funnel reads and (with approval) writes

The funnel file

regras:
  primeiras_mensagens_por_dia: 25      # first contacts planned per day
  intervalo_entre_envios_s: 75         # pause between first contacts (a number that sends too fast is flagged)
  intervalo_outros_s: 8                # pause between other messages
  horario_envio: "08:00-20:00"         # planned messages leave only in this window (the funnel's time zone)
  preparar_as: "08:00"                 # the office prepares the day at this time, on weekdays
  sincronizar_a_cada_min: 60           # how often the office reads the connected API
fuso: America/Sao_Paulo
nosso_nome: Missão CNH                 # how our messages appear in team/conversas

api:
  conexao: missao-cnh-api              # connections/missao-cnh-api.yaml

variaveis:                             # {nome} in a message → the lead's nome_curto, and so on
  nome: "{nome_curto}"
  nota: "{nota|virgula}"               # transforms: virgula, maiusculas, codigo, compacto
  link_convite:
    valor: "https://www.missaocnh.com.br/parceria?codigo={convite_codigo}"
    requer: convite_codigo             # without it the message shows `falta` and the check holds it
    falta: "[link do convite: aprove o convite na API]"

primeira_mensagem:                     # the first checkpoint's message, chosen per lead
  secao: Abordado primeira mensagem
  requer: { whatsapp: sim }
  variantes:
    - { se: { segmento: "!instrutor" }, usar: autoescola }
    - { se: { nota: "*" }, usar: instrutor com nota }
    - { usar: instrutor sem nota }

checagem:                              # what keeps a message from reaching the founder
  sem_emoji: true
  max_caracteres: 1200
  valores_permitidos: [0, 10, 20, 47, 57]   # R$ values the messages may state
  proibido:
    - { padrao: "garant\\w* (a )?aprova", motivo: promete aprovação }

acoes:                                 # writes to the connected API, each one an approval
  criar-convite:
    titulo: Criar o convite na Missão CNH
    operacao: criarConvite
    corpo: { rotulo: "{nome} · {cidade}-{uf}", codigo: "{nome_curto|codigo}-{uf}" }
    grava: { convite_codigo: codigo }  # lead column ← field of the answer
    feito_quando: convite_codigo       # already done when the lead has it

sincronizar:                           # reads (no approval) that move leads with evidence
  - se: convite_codigo
    ler: { operacao: interessados, consulta: { codigo: "{convite_codigo}" }, primeiro: itens }
    grava: { interessado_id: id }
    avanca:
      - { se: formulario_preenchido, de: [aceitou, convidado], para: pre_cadastro, porque: preencheu os dados }

checkpoints:
  - id: novo
    grupo: prospeccao                  # groups fora and saida are out of the funnel
    prazo: 0                           # business days without news until the next action is due
    depois_do_envio: abordado          # where the lead goes once its message left
  - id: abordado
    mensagem: abordado-lembrete        # the ## section of funil.md
    prazo: 3
    lembretes_max: 1                   # reminders without an answer, then…
    sem_resposta_depois: sem_resposta  # …the lead moves here
  - id: aceitou
    mensagem: aceitou-convite
    ao_responder: seguir               # a reply here is the expected one: no classification card
    acao_api: criar-convite            # the API write that comes before the message
    depois_do_envio: convidado
    depois_do_envio_requer: convite_codigo

A lead in the first checkpoint enters the plan only when it has a date (proxima_data, or Agendar a primeira mensagem on the board): the founder chooses whom to approach and when.

The day

  1. The plan. At preparar_as on weekdays (or Preparar o dia agora in Team → Funil) the office reads the API, then turns what is due into approvals: one per message (with every text, editable, and the number) and one per API write. Messages with an unfilled variable, an emoji, a price outside the table or a forbidden promise are held, with the reason. The founder gets one notification for the plan, not one per message.
  2. Approval. What the founder approves goes to the send queue, exactly as approved. Approving everything at once is safe: the office sends one batch at a time, waits intervalo_entre_envios_s between first contacts and keeps planned messages inside horario_envio.
  3. Just before sending the lead is checked again: a reply that arrived after the plan, a new checkpoint or an opt-out retires the message (it no longer fits). A batch cut in the middle is marked failed and never resent by itself: the first messages may already be on the person's phone.
  4. After sending the conversation is written, the last contact updated and the lead moved (depois_do_envio), or its reminders counted.
  5. Replies. A lead's message reaches the founder as a card with the conversation: answer it (each paragraph one message, sent through the queue), move the lead to another checkpoint, ask the team for a draft, or file it. A message the founder sends from the phone (an audio, a call follow-up) is recorded and answers the card. Moving a lead plans its next step at once (an invite to create, then the message with its link).
  6. The API. An approved action runs once, with an idempotency key, stores what it returns on the lead and plans the message that waited for it. Reads run every sincronizar_a_cada_min and move leads only with evidence.

WhatsApp lines

On app.seifprotocol.com the platform runs the Evolution server and gives each project a line (Conectar WhatsApp: scan the QR code with the phone). The project never holds the server's key: its processes ask the host to send, and the host sends only from that project's line. On a desktop the team sends from the WhatsApp connection chosen in Data & go live (your own Evolution server).

From a terminal

seif funnel --project . [--date 2026-10-26] --json shows the board and what is due on a day, from this copy (read only). seif cloud link, seif cloud attach <project id> and seif cloud sync keep this folder and the cloud project the same files (see cli.md).