> ## Documentation Index
> Fetch the complete documentation index at: https://docs.switchbord.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Testes de lançamento da campanha Futura

> Plano de teste operacional para audiências tipadas, materialização de destinatários, templates personalizados, respostas de botão, opt-out de consentimento e acompanhamento manual antes de uma campanha Futura entrar em produção.

# Testes de lançamento da campanha Futura

Use este runbook antes de lançar uma campanha Futura no WhatsApp. O fluxo depende de definições de segmento tipadas, um snapshot materializado de destinatários, personalização de template, respostas de botão normalizadas, tratamento de consentimento e acompanhamento manual com equipe dedicada para respostas de interessados.

<Note>
  O pacote de lançamento durável BORD-433/BORD-441 está em `planning/campaigns/futura-launch-runbook.md`, `planning/campaigns/futura-dry-run-fixtures.json`, `planning/futura/futura-strategy-templates-compliance.md` e `planning/campaigns/bord-431-campaign-journey-scope note.md`.
</Note>

<Warning>
  A resposta de campanha `start_journey` do BORD-431 não está implementada para o lançamento MVP. Os metadados de rota de produção da Futura devem usar apenas ações de tag/atributo de contato/conversa aberta/opt-out, com acompanhamento humano manual para contatos interessados.
</Warning>

<Warning>
  Não use esta checklist para enviar a uma audiência de produção até que a campanha tenha um snapshot de destinatários congelado, um template aprovado e um caminho explícito de opt-out.
</Warning>

## Escopo

O fluxo de campanha recém-mesclado suporta:

* definições de audiência com direcionamento `all`, `tags` ou `segment`;
* filtros de campo customizado tipados para audiências de segmento;
* materialização de destinatários em `campaign_recipients` antes do disparo;
* vínculo de template por nome ou ID de template aprovado, locale e variáveis de corpo;
* IDs de resposta de botão normalizados para roteamento de campanha e journey;
* eventos de opt-out via botão de campanha gravados no ledger de consentimento;
* ações de rota determinísticas para tags de contato, atributos de contato, conversas abertas e opt-outs.

O lançamento MVP explicitamente não inclui a execução de `start_journey` da resposta de campanha BORD-431.

## Contrato de metadados da campanha

Os rascunhos de campanha Futura devem carregar os metadados de campanha v1 junto com as colunas legadas de campanha. Os metadados são o contrato de go-live que os operadores revisam antes da materialização.

```json theme={null}
{
  "audienceDefinition": {
    "kind": "tags",
    "tagIds": ["tag-vip"],
    "tagLabels": ["Futura"],
    "snapshotAt": "2026-04-27T07:00:00.000Z"
  },
  "templateBinding": {
    "templateName": "futura_repeat_guest_check_it",
    "locale": "it",
    "variables": [
      {
        "component": "body",
        "index": 0,
        "source": "contact.first_name",
        "fallback": "cliente"
      }
    ]
  },
  "responseRoutes": {
    "futura_not_booked": {
      "label": "Non ho ancora prenotato",
      "actions": [
        { "type": "tag_contact", "tagLabel": "futura_not_booked" },
        { "type": "open_conversation", "queue": "sales" },
        { "type": "set_contact_attribute", "attribute": "futura_response", "value": "not_booked" }
      ]
    },
    "futura_already_booked": {
      "label": "Ho già prenotato",
      "actions": [
        { "type": "tag_contact", "tagLabel": "futura_already_booked" },
        { "type": "set_contact_attribute", "attribute": "futura_response", "value": "already_booked" }
      ]
    },
    "marketing_opt_out": {
      "label": "Non voglio ricevere messaggi",
      "actions": [{ "type": "record_opt_out", "reason": "marketing_button" }]
    }
  },
  "launchSafety": {
    "requireDryRun": true,
    "maxRecipients": 500,
    "requireOptOutFooter": true
  }
}
```

## Checklist de pré-voo

<Steps>
  <Step title="Confirmar a fonte da audiência">
    Escolha `tags` para a lista inicial da Futura ou `segment` quando a campanha depender de atributos de contato. Evite `all` a menos que o workspace tenha aprovado explicitamente um envio para a lista completa.
  </Step>

  <Step title="Validar os filtros de segmento tipados">
    Para audiências `segment`, verifique todos os filtros antes da materialização. Os campos suportados são `tags`, `name`, `phone` e `custom_fields`. Campos customizados usam a sintaxe `key=value`; comparações `gt` e `lt` devem comparar valores numéricos ou de data, seja a partir de definições de campo customizado ou de valores que possam ser interpretados como números ou datas em formato ISO.
  </Step>

  <Step title="Pré-visualizar a contagem do segmento">
    Pré-visualize o segmento no app ou pelo mesmo caminho de backend usado por `previewSegment`. Confirme a contagem e uma amostra de contatos em relação ao briefing da campanha.
  </Step>

  <Step title="Verificar o consentimento antes de gerar o snapshot">
    O materializador ignora contatos cujo `consent_state` seja `opted_out` ou cujo `subscriber_status` legado seja `unsubscribed`. Confirme a contagem esperada de opt-outs com o responsável pela campanha antes de continuar.
  </Step>

  <Step title="Verificar o vínculo do template">
    Confirme que o template do WhatsApp está aprovado na Meta e no Switchbord, que o `locale` corresponde ao código de idioma aprovado, que toda variável de corpo tem uma origem e um fallback, que as proposições de valor no corpo usam negrito do WhatsApp e que a resposta rápida de opt-out aparece como `Stop promozioni` ou `Non scrivermi`, e não como `STOP`.
  </Step>

  <Step title="Congelar rotas e limites de segurança">
    Confirme que `responseRoutes` usa apenas os IDs de botão suportados: `futura_not_booked`, `futura_already_booked` e `marketing_opt_out`. Confirme que as ações de rota não incluem `start_journey` para o lançamento MVP. Confirme que `launchSafety.maxRecipients` está no valor aprovado para o envio ou abaixo dele.
  </Step>
</Steps>

## Materializar destinatários

A materialização transforma a definição de audiência em linhas duráveis em `campaign_recipients`. Trate isso como o snapshot da audiência para o envio.

Comportamento esperado:

* lê o `audienceDefinition` da campanha a partir dos metadados v1;
* resolve audiências `all`, de tag ou de segmento dentro do workspace;
* remove duplicidade de contatos por ID;
* ignora contatos com opt-out ou não inscritos;
* insere novos destinatários `queued` com uma chave única `campaign_id,contact_id`;
* atualiza os metadados da campanha com `materialization.materializedCount`, `skippedOptOutCount`, `duplicateCount` e `materializedAt`.

Após a materialização, verifique:

* `materializedCount` corresponde à contagem aprovada após os pulos de consentimento;
* `skippedOptOutCount` é explicável e diferente de zero apenas quando esperado;
* uma segunda execução de materialização reporta duplicados em vez de criar destinatários duplicados;
* nenhuma linha de `campaign_recipients` foi criada para contatos fora do workspace;
* não existem linhas para contatos de teste conhecidos com opt-out.

## Verificações de disparo e personalização

O disparo da campanha processa apenas destinatários no status `queued` enquanto a campanha está `running`. O envio ocorre em lotes e reenfileira `campaign.dispatch` até que não restem destinatários em fila.

Para cada destinatário de teste, verifique:

* existe ou é criada uma conversa no canal esperado;
* uma linha de `messages` de saída é enfileirada com `send_mode: template` e `author_label: Campaign`;
* `payload.templateName` é o nome do template vinculado;
* `payload.templateLocale` está presente quando um locale é configurado;
* `payload.templateComponents` inclui os parâmetros de corpo na ordem dos índices;
* `contact.first_name` é resolvido primeiro a partir de `contacts.metadata.first_name`, depois do primeiro token de `contacts.name`, e só então do fallback configurado;
* a pré-visualização da mensagem renderizada substitui `{{1}}`, `{{2}}` e placeholders subsequentes de forma consistente com os componentes do template;
* destinatários com opt-out encontrados no disparo são marcados como falhos com `error_code: skipped_opt_out` e não são enviados.

<Note>
  A materialização de destinatários ignora opt-outs conhecidos de antemão, mas o disparo reverifica o consentimento, de forma que contatos que optam por saída entre a materialização e o envio ainda são bloqueados.
</Note>

## Normalização de respostas de botão

O processamento do webhook de entrada normaliza os dois formatos de botão do WhatsApp em um único ID de botão antes do tratamento de rota:

* botões de resposta legados: `message.button.payload`;
* botões interativos: `message.interactive.button_reply.id`;
* respostas de lista interativa: `message.interactive.list_reply.id`.

Teste todos os botões de campanha usando seus IDs de payload exatos:

| ID do botão             | Resultado esperado                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `futura_not_booked`     | O contato pode ser marcado com tag, marcado como não reservado, e a conversa aberta para acompanhamento de vendas. |
| `futura_already_booked` | O contato pode ser marcado com tag/já reservado sem opt-out ou início automático de journey.                       |
| `marketing_opt_out`     | Um evento de opt-out de consentimento é registrado e o contato é marcado como não inscrito.                        |

Rejeite definições de rota que usem rótulos de botão, títulos traduzidos, espaços ou variações de maiúsculas/minúsculas como chaves. As rotas devem usar IDs normalizados, não texto de exibição.

## Teste de opt-out de consentimento

Para o botão `marketing_opt_out`:

1. Envie uma mensagem de teste de campanha para um contato de seed que tenha consentido.
2. Responda tocando no botão de opt-out, não digitando uma palavra-chave STOP.
3. Confirme que uma linha em `consent_events` é gravada com `event_type: opted_out`, `source: campaign_button`, `actor: system` e metadados contendo o `buttonId` e o ID da mensagem do provedor.
4. Confirme que o `subscriber_status` do contato é atualizado para `unsubscribed` e que o contato não é mais elegível para materializações subsequentes de campanha.
5. Confirme que tentativas de disparo posteriores ignoram o contato antes de criar um envio via Meta.

O tratamento de opt-out por palavra-chave ainda se aplica para mensagens de texto no estilo STOP/START, mas o opt-out via botão de campanha deve funcionar sem análise de palavra-chave.

## Acompanhamento manual em vez de journeys iniciadas por campanha

A execução de `start_journey` da resposta de campanha BORD-431 está fora do escopo do lançamento MVP. Uma ação de rota com `type: "start_journey"` é aceita pelo schema de domínio, mas não é executada como um caminho de anexação/início de campanha para journey. Os operadores não devem depender disso para o acompanhamento do lançamento da Futura.

Use este caminho de acompanhamento manual:

* `futura_not_booked` marca o contato com tag, registra `futura_response=not_booked` e abre/roteia a conversa para vendas;
* um operador humano revisa a conversa e envia manualmente a próxima resposta aprovada;
* `futura_already_booked` marca ou identifica o contato como já reservado e não requer journey automática;
* `marketing_opt_out` registra o opt-out de consentimento e nunca deve acionar um acompanhamento de marketing.

O PR de acompanhamento posterior do BORD-431 deve adicionar início/anexação de journey idempotente com contexto de campanha antes que este runbook possa depender de inícios de journey automatizados.

## Checklist operacional de go-live

Antes do lançamento em produção:

* [ ] Os metadados da campanha validam contra o schema v1.
* [ ] A pré-visualização do segmento ou a contagem de tags corresponde à audiência aprovada.
* [ ] Os filtros de campo customizado tipados foram testados com pelo menos um contato de seed correspondente e um não correspondente.
* [ ] As contagens de materialização de destinatários foram registradas nas notas de lançamento.
* [ ] O nome do template aprovado, o locale e os fallbacks de variável de corpo foram verificados.
* [ ] Os payloads de componente de template foram inspecionados para pelo menos dois contatos de seed.
* [ ] Os IDs de botão foram testados a partir do WhatsApp, não apenas de payloads de webhook mockados.
* [ ] `marketing_opt_out` criou um evento no ledger de consentimento `campaign_button`.
* [ ] A materialização e o disparo pós-opt-out ambos ignoraram o contato de seed.
* [ ] A equipe de acompanhamento manual está designada para `futura_not_booked` e quaisquer respostas opcionais de `futura_already_booked`.
* [ ] Os metadados de rota de produção não contêm ações `start_journey`.
* [ ] Os jobs de outbox `campaign.dispatch` e `message.dispatch` foram esvaziados sem falhas inesperadas.
* [ ] As contagens de entrega, leitura, falha e pulo foram comparadas com as métricas de detalhe da campanha.
* [ ] O responsável pelo incidente e o ponto de decisão de rollback/pausa estão documentados.

## Critérios de rollback e pausa

Pause a campanha e interrompa o processamento da fila se qualquer um destes ocorrer:

* a materialização inclui contatos fora da audiência aprovada;
* contatos com opt-out recebem mensagens de campanha;
* respostas de botão são registradas como rótulos de exibição em vez de IDs normalizados;
* o planejamento de lançamento depende de início automático de campanha para journey antes de o BORD-431 estar implementado;
* a Meta retorna erros sustentados de política ou de template para o template Futura;
* as falhas de entrega excedem o limite acordado pelo responsável pela campanha.

Ao pausar, registre o ID da campanha, o ID do workspace, as contagens de materialização, as contagens atuais de destinatários em fila/aceitos/falhos e os IDs representativos dos jobs de outbox nas notas do incidente.
