> ## 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.

# Test di go-live della campagna Futura

> Piano di test operativo per audience tipizzate, materializzazione dei destinatari, template personalizzati, risposte a bottone, opt-out del consenso e follow-up manuale prima che una campagna Futura vada in produzione.

# Test di go-live della campagna Futura

Usa questo runbook prima di lanciare una campagna WhatsApp Futura. Il flusso dipende da definizioni di segmento tipizzate, uno snapshot materializzato dei destinatari, personalizzazione dei template, risposte a bottone normalizzate, gestione del consenso e follow-up manuale con personale dedicato per le risposte interessate.

<Note>
  Il pacchetto di lancio durevole BORD-433/BORD-441 vive in `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>
  La risposta di campagna BORD-431 `start_journey` non è implementata per il lancio MVP. I metadati delle route di produzione Futura devono usare solo azioni tag/attributo contatto/conversazione aperta/opt-out, con follow-up umano manuale per i contatti interessati.
</Warning>

<Warning>
  Non usare questa checklist per inviare a un'audience di produzione finché la campagna non ha uno snapshot dei destinatari congelato, un template approvato e un percorso di opt-out esplicito.
</Warning>

## Ambito

Il flusso di campagna appena mergiato supporta:

* definizioni di audience con targeting `all`, `tags`, o `segment`;
* filtri tipizzati su campi personalizzati per le audience di tipo segment;
* materializzazione dei destinatari in `campaign_recipients` prima del dispatch;
* binding del template tramite nome del template approvato o ID template, locale e variabili del corpo;
* ID di risposta a bottone normalizzati per l'instradamento di campagna e journey;
* eventi di opt-out da bottone di campagna scritti nel registro dei consensi;
* azioni di route deterministiche per tag contatto, attributi contatto, conversazioni aperte e opt-out.

Il lancio MVP esplicitamente non include l'esecuzione della risposta di campagna BORD-431 `start_journey`.

## Contratto dei metadati di campagna

Le bozze di campagna Futura dovrebbero portare i metadati di campagna v1 insieme alle colonne di campagna legacy. I metadati sono il contratto di go-live che gli operatori rivedono prima della materializzazione.

```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 di preflight

<Steps>
  <Step title="Confermare la fonte dell'audience">
    Scegli `tags` per la lista iniziale Futura o `segment` quando la campagna dipende da attributi del contatto. Evita `all` a meno che il workspace non abbia esplicitamente approvato un invio a lista completa.
  </Step>

  <Step title="Validare i filtri di segmento tipizzati">
    Per le audience `segment`, controlla ogni filtro prima della materializzazione. I campi supportati sono `tags`, `name`, `phone` e `custom_fields`. I campi personalizzati usano la sintassi `key=value`; i confronti `gt` e `lt` devono confrontare valori numerici o di data, sia dalle definizioni dei campi personalizzati sia da valori che vengono interpretati come numeri o date in stile ISO.
  </Step>

  <Step title="Anteprima del conteggio del segmento">
    Visualizza in anteprima il segmento nell'app o tramite lo stesso percorso backend usato da `previewSegment`. Conferma il conteggio e un campione di contatti rispetto al brief della campagna.
  </Step>

  <Step title="Verificare il consenso prima dello snapshot">
    Il materializer salta i contatti il cui `consent_state` è `opted_out` o il cui `subscriber_status` legacy è `unsubscribed`. Conferma il conteggio previsto degli opt-out con l'owner della campagna prima di procedere.
  </Step>

  <Step title="Verificare il binding del template">
    Conferma che il template WhatsApp sia approvato in Meta e in Switchbord, che il `locale` corrisponda al codice lingua approvato, che ogni variabile del corpo abbia una fonte più un fallback, che le value proposition nel corpo usino il grassetto WhatsApp, e che la quick reply di opt-out sia visibile come `Stop promozioni` o `Non scrivermi` piuttosto che `STOP`.
  </Step>

  <Step title="Congelare le route e i limiti di sicurezza">
    Conferma che `responseRoutes` usi solo gli ID di bottone supportati: `futura_not_booked`, `futura_already_booked` e `marketing_opt_out`. Conferma che le azioni di route non includano `start_journey` per il lancio MVP. Conferma che `launchSafety.maxRecipients` sia pari o inferiore alla dimensione di invio approvata.
  </Step>
</Steps>

## Materializzazione dei destinatari

La materializzazione trasforma la definizione dell'audience in righe durevoli di `campaign_recipients`. Considera questo passaggio come lo snapshot dell'audience per l'invio.

Comportamento previsto:

* legge la `audienceDefinition` della campagna dai metadati v1;
* risolve le audience `all`, tag o segment all'interno del workspace;
* deduplica i contatti per ID;
* salta i contatti opted-out o unsubscribed;
* inserisce nuovi destinatari `queued` con una chiave univoca `campaign_id,contact_id`;
* aggiorna i metadati della campagna con `materialization.materializedCount`, `skippedOptOutCount`, `duplicateCount` e `materializedAt`.

Dopo la materializzazione, verifica:

* `materializedCount` corrisponde al conteggio approvato dopo gli skip per consenso;
* `skippedOptOutCount` è spiegabile e diverso da zero solo quando previsto;
* una seconda esecuzione di materializzazione segnala duplicati invece di creare destinatari duplicati;
* non sono state create righe `campaign_recipients` per contatti esterni al workspace;
* non esistono righe per contatti di test noti come opted-out.

## Controlli di dispatch e personalizzazione

Il dispatch della campagna elabora solo i destinatari in stato `queued` mentre la campagna è `running`. Invia in batch e riaccoda `campaign.dispatch` finché non rimangono destinatari in coda.

Per ogni destinatario di test, verifica:

* esiste o viene creata una conversazione nel canale previsto;
* una riga `messages` in uscita è accodata con `send_mode: template` e `author_label: Campaign`;
* `payload.templateName` è il nome del template collegato (bound);
* `payload.templateLocale` è presente quando è configurato un locale;
* `payload.templateComponents` include i parametri del corpo nell'ordine dell'indice;
* `contact.first_name` viene risolto prima da `contacts.metadata.first_name`, poi dal primo token di `contacts.name`, poi dal fallback configurato;
* l'anteprima del corpo del messaggio renderizzato sostituisce `{{1}}`, `{{2}}` e i placeholder successivi in modo coerente con i componenti del template;
* i destinatari opted-out incontrati durante il dispatch vengono segnati come falliti con `error_code: skipped_opt_out` e non vengono inviati.

<Note>
  La materializzazione dei destinatari salta in anticipo gli opt-out noti, ma il dispatch riverifica il consenso, così i contatti che fanno opt-out tra la materializzazione e l'invio vengono comunque bloccati.
</Note>

## Normalizzazione delle risposte a bottone

L'elaborazione dei webhook in entrata normalizza entrambi i formati di bottone WhatsApp in un singolo ID di bottone prima della gestione della route:

* bottoni di risposta legacy: `message.button.payload`;
* bottoni interattivi: `message.interactive.button_reply.id`;
* risposte a lista interattiva: `message.interactive.list_reply.id`.

Testa tutti i bottoni di campagna usando i loro ID di payload esatti:

| ID Bottone              | Risultato atteso                                                                                                |
| ----------------------- | --------------------------------------------------------------------------------------------------------------- |
| `futura_not_booked`     | Il contatto può essere taggato, segnato come non prenotato, e la conversazione aperta per il follow-up vendite. |
| `futura_already_booked` | Il contatto può essere taggato/segnato come già prenotato senza opt-out o avvio automatico di journey.          |
| `marketing_opt_out`     | Viene registrato un evento di opt-out del consenso e il contatto viene segnato come unsubscribed.               |

Rifiuta definizioni di route che usano etichette dei bottoni, titoli tradotti, spazi o varianti di maiuscole/minuscole come chiavi. Le route devono usare ID normalizzati, non testo di visualizzazione.

## Test di opt-out del consenso

Per il bottone `marketing_opt_out`:

1. Invia un messaggio di campagna di test a un contatto seed che ha dato il consenso.
2. Rispondi toccando il bottone di opt-out, non digitando una parola chiave STOP.
3. Conferma che venga scritta una riga `consent_events` con `event_type: opted_out`, `source: campaign_button`, `actor: system`, e metadati contenenti il `buttonId` e l'ID del messaggio del provider.
4. Conferma che il `subscriber_status` del contatto venga aggiornato a `unsubscribed` e che il contatto non sia più eleggibile per successive materializzazioni di campagna.
5. Conferma che i tentativi di dispatch successivi saltino il contatto prima di creare un invio Meta.

La gestione dell'opt-out tramite parola chiave si applica ancora per i messaggi testuali in stile STOP/START, ma l'opt-out da bottone di campagna deve funzionare senza il parsing delle parole chiave.

## Follow-up manuale invece di journey avviate dalla campagna

L'esecuzione della risposta di campagna BORD-431 `start_journey` è scope-notata (esclusa) dal lancio MVP. Un'azione di route con `type: "start_journey"` è accettata dallo schema di dominio ma non viene eseguita come percorso di attach/avvio da campagna a journey. Gli operatori non devono fare affidamento su di essa per il follow-up del lancio Futura.

Usa questo percorso di follow-up manuale:

* `futura_not_booked` tagga il contatto, registra `futura_response=not_booked`, e apre/instrada la conversazione verso le vendite;
* un operatore umano rivede la conversazione e invia manualmente la prossima risposta approvata;
* `futura_already_booked` tagga o segna il contatto come già prenotato e non richiede alcuna journey automatica;
* `marketing_opt_out` registra l'opt-out del consenso e non deve mai attivare un follow-up di marketing.

La successiva PR di follow-up BORD-431 deve aggiungere l'avvio/attach idempotente della journey con il contesto di campagna prima che questo runbook possa fare affidamento su avvii automatici di journey.

## Checklist operativa di go-live

Prima del lancio in produzione:

* [ ] I metadati di campagna validano contro lo schema v1.
* [ ] L'anteprima del segmento o il conteggio dei tag corrisponde all'audience approvata.
* [ ] I filtri tipizzati su campi personalizzati sono stati testati con almeno un contatto seed corrispondente e uno non corrispondente.
* [ ] I conteggi della materializzazione dei destinatari sono stati registrati nelle note di lancio.
* [ ] Il nome del template approvato, il locale e i fallback delle variabili del corpo sono stati verificati.
* [ ] I payload dei componenti del template sono stati ispezionati per almeno due contatti seed.
* [ ] Gli ID dei bottoni sono stati testati da WhatsApp, non solo da payload di webhook simulati (mock).
* [ ] `marketing_opt_out` ha creato un evento nel registro dei consensi di tipo `campaign_button`.
* [ ] La materializzazione e il dispatch post-opt-out hanno entrambi saltato il contatto seed.
* [ ] È stato assegnato personale per il follow-up manuale di `futura_not_booked` e delle eventuali risposte opzionali `futura_already_booked`.
* [ ] I metadati delle route di produzione non contengono azioni `start_journey`.
* [ ] I job outbox `campaign.dispatch` e `message.dispatch` si sono svuotati senza fallimenti inaspettati.
* [ ] I conteggi di delivery, letto, fallito e saltato sono stati confrontati con le metriche di dettaglio della campagna.
* [ ] L'owner dell'incidente e il punto di decisione per rollback/pausa sono documentati.

## Criteri di rollback e pausa

Metti in pausa la campagna e ferma l'elaborazione della coda se accade una di queste condizioni:

* la materializzazione include contatti fuori dall'audience approvata;
* i contatti opted-out ricevono messaggi di campagna;
* le risposte a bottone vengono registrate come etichette di visualizzazione invece di ID normalizzati;
* la pianificazione del lancio dipende dall'avvio automatico da campagna a journey prima che BORD-431 sia implementato;
* Meta restituisce errori di policy o template sostenuti nel tempo per il template Futura;
* i fallimenti di delivery superano la soglia concordata dall'owner della campagna.

Quando metti in pausa, registra l'ID della campagna, l'ID del workspace, i conteggi della materializzazione, i conteggi correnti dei destinatari in coda/accettati/falliti e gli ID rappresentativi dei job outbox nelle note dell'incidente.
