# Research: 004-onboard-funnel-events

**Date**: 2026-06-08

## Decisões

### R1 — Agregação funil: query vs tabela cache

**Decisão**: MVP query sobre `webhook_events` (JSON `payload.session_id`).

**Rationale**: YAGNI — volume baixo (~50 eventos/dia). Tabela `onboard_sessions` só se performance degradar.

### R2 — Ordem de fases

**Decisão**: Último evento conhecido define `current_stage`; ordem fixa enum.

**Rationale**: Eventos podem chegar fora de ordem (retries); timestamp resolve timeline, enum resolve funil.

### R3 — onboarding.started vs domain.validated

**Decisão**: Ambos em `validate-domain` — `started` só se primeira visita da sessão (Redis key `ops:started:{session_id}` TTL 48h no portal ou check activity log).

**Alternativa rejeitada**: Só `domain.validated` — perde métrica de conversão início.

### R4 — Ticket timeline storage

**Decisão**: Query join events por `session_id` extraído de `tickets.payload` — sem nova tabela MVP.

### R5 — infra.synced timing

**Decisão**: Emitir após `infrastructure.provision` success block em create_account, antes de `onboarding.completed`.

**Nota**: Se infra falhar (warn only), emitir `infra.synced` com `data.success=false` ou omitir — spec: omitir se falhou, `onboarding.completed` inclui `infra_status` em data.

## Hook points confirmados (VM112 onboarding.py)

| Linha ~ | Função | Evento |
|---------|--------|--------|
| 130 | validate_domain | started + domain.validated |
| 388 | apply_cloudflare_dns | dns.applied |
| 524 | create_account | account.created (existente) |
| 519 | create_account infra OK | infra.synced |
| 515 | create_account final | onboarding.completed |
| 462 | create_account except | onboarding.failed |

## Referências

- `specs/001-webhook-vm112-integration/spec.md` User Story 4 (P3)
- `specs/001-webhook-vm112-integration/plan.md` Phase D
- CHAT BRUTO VM122 — expectativa dashboard vs MVP
