Pular para o conteúdo

Taly com voz no site — plano do funil /talk

Status: canônico · Escopo: Experiência do funil /talk em tactflow.io — o que existe hoje, o plano da Taly com voz e o que fica para depois · Atualizado em: 2026-08-11

Este documento registra o plano do funil do site, decidido em conversa em 11/08/2026. A §1 é levantamento verificado no código — o que a tela promete hoje e o que ela entrega. Da §3 em diante é plano: nada disso está construído, e a ordem da §6 é sugestão de sequência, não compromisso de data. Escopo continua sendo DIRECTION.md; se este doc contradiz aquele, aquele ganha.

Isto não reabre o “wedge web-booking” descartado em DIRECTION.md §5. Web continua superfície secundária do produto. O que este plano trata é o funil de venda do site — como um lead chega até a call de setup —, não o canal que o cliente usa para atender o cliente dele.

Levantado em 11/08/2026, lendo o código e a config de produção. A coluna da direita é o estado antes da fase 0.

Promessa na telaO que acontecia de fatoHoje
Hero “Talk to it” — único CTA da páginaAbre um formulário de seis passos. Nada fala: não existe cliente Vapi Web no site, o VoiceDriver de funnel.ts só existe em comentário, e POST /talk/voice-session responde 503 no stub e 501 no liveRótulo passou a ser “Run your numbers”, que é o que o funil faz. Volta a ser “Talk to it” com a #111
“Book a call”Não agenda. Grava o lead e manda e-mail com link para uma página externa. Em produção PUBLIC_BOOKING_URL está vazio, então o e-mail degrada para “reply with two times that work this week” — ping-pong manual com o fundadorInalterado — é a fase 1
“Not now — just email me the breakdown”Manda por e-mail exatamente a conta que já está na tela, e não grava nada: nem o endereço, nem o consentimentoFica de pé até a #113 dar o substituto: é a única saída de baixo compromisso que existe, e tirá-la antes da Taly deixaria o funil sem nenhuma
Caixa “occasional Tactflow updates. Unsubscribe in one click”Não existe lista, não existe registro de consentimento e nenhum e-mail carrega link de descadastroA promessa de descadastro saiu. O consentimento para updates fica: nasce desmarcado e só vira true por clique. A frase volta com a #113
GET /talk/availability e POST /talk/bookNinguém no site chama. O stub devolve horários falsos e o live responde 501. Contrato órfão desde que o agendamento virou página externaRemovidos

Das quatro coisas que o funil oferecia ao visitante, uma fazia o que diz — e era a menos útil: o e-mail que devolve o número que a pessoa está lendo naquele instante.

O detalhe da não-persistência está documentado no handler, em api/handlers/talk.ts, e o mapa de quem grava o quê está em talk-contract.md.

Os cinco itens acima têm a mesma forma: a interface promete uma capacidade que o backend não tem. É a mesma raiz das correções #41 (a tela confirmava e-mail que ninguém mandava), #95 (a tela parou de prometer e-mail) e #96 (o e-mail citava números para quem nunca viu número nenhum).

O defeito só aparece testando à mão, e às vezes só em produção: lint, typecheck, boundaries e docs:lint não sabem o que a tela está prometendo. Nenhum gate liga um CTA à capacidade de que ele depende, então promessa e entrega divergem sem nada ficar vermelho.

Consequência para o plano: um funil novo volta a divergir se essa lacuna continuar.

Metade da guarda já entrou: api-worker/src/journey/journey.ts declara a jornada como dado tipado e o journey.test.ts ao lado chama os handlers reais, reprovando a CI quando o mapa e o comportamento discordam. O que ele ainda não alcança é o marketing-web — nada liga a cópia da tela à capacidade que ela promete, e era exatamente ali que as cinco promessas da §1 moravam.

O funil não está com uma peça faltando por acidente de escopo. Ele foi desenhado em volta da Taly, e o que foi para o ar é o formulário dela sem ela:

  • o passo intro é ela se apresentando (Hi — I'm Taly.), e não coleta nada de propósito;
  • a máquina de funnel.ts existe para “um funil só, dois drivers” — o clique do visitante e a voz da Taly chamando os mesmos métodos;
  • POST /talk/voice-session já está no contrato com a forma de uma sessão Vapi Web ({ assistant, publicKey });
  • o glossário já separa as duas figuras: Taly é o agente do site, que qualifica o visitante e vende a Tactflow, e é diferente do agente do telefone, que agenda serviço para o cliente do nosso cliente.

Então “Talk to it” no hero não é promessa exagerada — é a promessa certa, sem implementação atrás. O plano abaixo não é redesenho: é terminar o que o desenho já pedia.

E a calculadora, que hoje é o centro da página, não precisa provar nada sozinha. Ela é honesta (âncora conservadora de 15%, floor nos agendamentos, números que o lead confere de cabeça) e continua exatamente como está — muda quando ela aparece, não o que ela calcula.

A Taly atende por voz no site, responde dúvidas sobre a Tactflow dentro de limites, e agenda a call de setup na nossa agenda. Sem empresa fictícia, sem role-play: o visitante fala com a empresa que ele está avaliando, e a Tactflow atende o próprio telefone com o produto que vende.

O que ela faz:

  • se apresenta e conduz — é ela a anfitriã do funil, não um widget solto num canto;
  • responde o que o lead quer saber sobre como o produto funciona;
  • pede nome, e-mail e telefone quando for agendar — sempre digitados na tela, nunca ditados, que é a regra que já vale no funil (o STT erra exatamente nos caracteres que precisam estar exatos);
  • agenda a setup-call de 30 minutos na agenda da Tactflow;
  • nunca fala preço. A config já declara isso e a regra 2 do funnel.ts também: preço só nas calls humanas.

Por que no browser e não por telefone:

  1. o contrato já foi escrito para isso — { assistant, publicKey } é sessão Vapi Web, e o comentário do handler já diz que o caminho de erro (microfone negado, WebRTC bloqueado por rede corporativa) tem de existir de qualquer forma;
  2. a sessão do browser é a identidade, então acompanhar a conversa na tela e gravar o lead saem de graça — um telefone fora da aba exigiria casar a ligação por caller ID;
  3. no desktop, dialar exige trocar de aparelho, e o demo simplesmente não acontece.

O que sai de cena:

  • o POST /talk/breakdown-email no formato atual. A contrapartida por um endereço deixa de ser a conta que já está na tela e passa a ser a call agendada. Ou ele vira captura de verdade — gravando contato e consentimento — ou sai;
  • GET /talk/availability e POST /talk/book, órfãos desde que o agendamento virou página externa. Com a Taly agendando pelo toolset do agente, eles não voltam a ser necessários;
  • o PUBLIC_BOOKING_URL como caminho principal. Ele passa a ser fallback de quem não quer ou não pode falar.

A maior parte da configuração existe. A Tactflow já é tenant dela mesma:

Já existeOnde
Tenant, unit (Fort Worth, America/Chicago), contact capture, greetingconfig/tenants/tactflow.json
Offering setup-call, 30 min, sem preço nem depósitoconfig/verticals/tactflow.json
O limite de escopo: “answers questions about the product; it does not quote pricing”, disclosureLevel: brand_assistantconfig/verticals/tactflow.json
O objetivo da conversa, e a ordem de não repetir as quatro perguntas do funilconfig/verticals/tactflow.json
Tools de consultar disponibilidade e agendar (toolset booking), montagem do assistente, Google Calendar (freebusy + insert)build-assistant-response.ts e o caminho descrito em voice-runtime-map.md

O que falta:

  1. POST /talk/voice-session de verdade — hoje 501. Monta a config do assistente a partir do tenant tactflow e devolve { assistant, publicKey }.
  2. Vapi Web SDK no site, mais o caminho de erro: microfone negado, permissão revogada, WebRTC bloqueado. Todo passo continua concluível clicando — é regra da máquina do funil, e agora ela deixa de ser hipótese.
  3. Conexão de calendário do tenant tactflow ativa, para a setup-call cair numa agenda real em vez de virar registro solto.
  4. Base de conhecimento da Taly com limite explícito: nada de preço, nada de promessa de capacidade que o produto não tem. É a falha mais cara possível numa conversa de venda, e o repo já tem o hábito de tratar isso como regra e não como esperança.
  5. Teto de custo e de abuso. publicKey é chave pública de verdade, usável de qualquer origem. O Turnstile já está no /talk/voice-session; falta teto de minutos por sessão, por IP e por dia. Web call custa menos que ligação (sem perna de telefonia), não custa zero.
  6. Um momento de e-mail só, e ele confirma a call agendada — com contato e consentimento gravados, como o /talk/contact já faz.

Cada fase entrega valor sozinha e nenhuma deixa promessa quebrada na tela.

FaseIssueO que entraPor que nessa ordem
0 ✅#109Limpeza de honestidade: endpoints órfãos removidos, CTA do hero honesto, promessa de descadastro foraNão dependia de decidir nada do resto, e parava o sangramento de credibilidade
1#110POST /talk/voice-session live + calendário do tenant tactflowÉ o backend inteiro do plano; sem ele o site não tem o que chamar
2#111Vapi Web no site, com o caminho de erro e o fallback de cliquePrimeira vez que “Talk to it” cumpre o que diz
3#112Base de conhecimento da Taly, com o limite de preçoEla pode agendar antes de saber responder tudo; o inverso não é verdade
4#113E-mail único de confirmação, com contato e consentimento gravadosDepende de existir call agendada para confirmar
5#114Ligar a cópia da tela ao estado declarado na jornada — o mapa de journey.ts já prova rota e e-mailPode entrar em qualquer ponto; quanto mais cedo, menos retrabalho nas fases seguintes

Três coisas foram levantadas, fazem sentido, e não entram agora — com o motivo registrado para não voltarem como novidade:

  • Ouvir o agente do telefone. A ideia era a Taly oferecer o número da Arlington HVAC no meio da conversa, para o lead ligar e ouvir o que o cliente dele ouve. Não dá hoje: ela teria que soletrar o número por voz, e não existe nada na interface que mostre esse número na tela. É o espelho da regra que já vale para entrada — telefone e e-mail são sempre digitados porque o STT erra o caractere que precisa estar exato; na saída, o TTS tem o mesmo problema com dígito. Precisa de superfície visual antes, então fica como plano futuro.
  • Outbound “me ligue”. Não existe capacidade de outbound no repo — só o webhook de entrada. E um botão que liga para um número digitado na internet aberta é superfície de fraude de pedágio e de assédio, porque qualquer um digita o número de um terceiro.
  • Toolset lead. Para a Taly conduzir as quatro perguntas falando. resolveVapiTools só implementa booking e avisa alto ao cair de volta, então isso é opcional: ela vende e agenda sem conduzir a calculadora.
  • Quanto custa deixar a Taly atendendo, e qual é o teto por mês. É conversa de pricing.md, não de código.
  • Se o setor continua sendo perguntado no funil. O mercado de entrada é HVAC em DFW (market-entry-dfw-hvac.md), mas o funil aceita clínica, estúdio e serviços profissionais, e a máquina usa o setor para escolher a unidade de trabalho na manchete. A decisão de quem atende e por qual transporte, com as alternativas recusadas e o motivo de cada uma, está registrada em ADR 0004.