# Tasks: Webhook VM112 → Ops Platform

**Input**: [spec.md](./spec.md) · [plan.md](./plan.md) · [contracts/webhook-onboard.md](./contracts/webhook-onboard.md)

**Prerequisites**: plan.md ✅ · spec.md ✅ · research.md ✅ · data-model.md ✅

## Format: `[ID] [P?] [Story] Description`

---

## Phase 1: Setup

**Purpose**: Preparar paths e configuração partilhada

- [ ] T001 Confirmar VM122 API healthy: `curl http://10.10.10.122:8080/health`
- [ ] T002 Confirmar VM112 portal healthy: `curl http://10.10.10.112:8090/api/onboarding/health`
- [ ] T003 [P] Documentar secret actual em ambos `.env` (VM122 `WEBHOOK_SECRET`, VM112 `OPS_WEBHOOK_SECRET` a criar)
- [ ] T004 [P] Criar `scripts/verify-webhook.sh` em `ligbox-ops-platform/scripts/`

**Checkpoint**: Ambos serviços acessíveis na LAN

---

## Phase 2: Foundational — Ops Receptor (VM122)

**Purpose**: Idempotência e tickets melhorados no receptor existente

**⚠️ CRITICAL**: Completar antes do emissor portal

- [ ] T005 [US1] Adicionar função `_is_duplicate_event(conn, event, session_id, domain)` em `api/app/main.py` (VM122 deploy path)
- [ ] T006 [US1] Em `webhook_onboard`: skip INSERT ticket se duplicado; sempre INSERT webhook_events
- [ ] T007 [US1] Melhorar subject ticket: `[{event}] {domain} — {email}` quando `data.email` presente
- [ ] T008 [P] [US1] Rebuild e restart: `docker-compose -f docker-compose.mvp.yml up -d --build api` na VM122
- [ ] T009 [US1] Testar idempotência com curl duplicado (quickstart §2)

**Checkpoint**: Receptor pronto para receber do portal

---

## Phase 3: User Story 1 — Ticket automático ao criar conta (P1) 🎯 MVP

**Goal**: Portal emite `account.created` → ticket no Ops Desk

- [ ] T010 [P] [US1] Adicionar campos `ops_webhook_url`, `ops_webhook_secret`, `ops_webhook_enabled` em `backend/app/config.py` (obsidian-infra path)
- [ ] T011 [US1] Criar `backend/app/services/ops_webhook.py` com `emit_event()` + retry 3x (1s, 3s, 9s) + httpx timeout 5s
- [ ] T012 [US1] Em `ops_webhook.py`: log warn via `activity_log` em falha; nunca raise
- [ ] T013 [US1] Em `backend/app/routers/onboarding.py` `create_account`: obter `session_id` de `request.state` e chamar `ops_webhook.emit_event("account.created", ...)` via `BackgroundTasks`
- [ ] T014 [US1] Payload `data`: `email`, `account_verified`, `needs_review`, `dns_mode`, `mail_aliases`
- [ ] T015 [US1] Adicionar `.env` entries em VM112: `OPS_WEBHOOK_URL=http://10.10.10.122:8080/api/v1/webhooks/onboard`
- [ ] T016 [US1] Deploy portal VM112 (rsync + restart backend)
- [ ] T017 [US1] E2E: onboarding teste → verificar ticket em `GET /api/v1/desk/tickets`

**Checkpoint**: MVP US1 completo e testável

---

## Phase 4: User Story 2 — Rastreio de sessão (P2)

**Goal**: Eventos correlacionados por `session_id`

- [ ] T018 [US2] Garantir `session_id` sempre enviado em `emit_event` (fallback `""` aceite pelo Ops)
- [ ] T019 [US2] Adicionar endpoint `GET /api/v1/webhooks/events?session_id=` no Ops (opcional, consulta audit log)
- [ ] T020 [US2] Testar 2 eventos mesma sessão — ambos em `webhook_events`

**Checkpoint**: Sessão rastreável no Ops

---

## Phase 5: User Story 3 — Falha não bloqueia onboarding (P2)

**Goal**: Portal resiliente com Ops offline

- [ ] T021 [US3] Teste: parar API VM122, completar onboarding, confirmar resposta 200 ao cliente
- [ ] T022 [US3] Verificar activity log portal contém warn de falha webhook
- [ ] T023 [US3] Confirmar `OPS_WEBHOOK_ENABLED=false` desactiva envio sem erro

**Checkpoint**: Resiliência validada

---

## Phase 6: User Story 4 — Eventos funil P3 (opcional)

**Goal**: Visibilidade pipeline completo

- [ ] T024 [P] [US4] Hook `domain.validated` em endpoint validate-domain do portal
- [ ] T025 [P] [US4] Hook `dns.applied` em cloudflare apply
- [ ] T026 [US4] Hook `onboarding.completed` / `onboarding.failed` nos pontos finais
- [ ] T027 [US4] Ops: eventos não-ticket só INSERT webhook_events

**Checkpoint**: Funil completo visível no Ops

---

## Phase 7: Polish & Docs

- [ ] T028 [P] Actualizar `README.md` do ligbox-ops-platform com link para spec
- [ ] T029 [P] Rotacionar `WEBHOOK_SECRET` para valor produção (não dev default)
- [ ] T030 Executar `scripts/verify-security.sh` na VM122 pós-deploy
- [ ] T031 Actualizar inventário VMs / backlog Obsidian com INT-2 concluído

---

## Dependencies

```text
Phase 1 → Phase 2 → Phase 3 (MVP)
                 → Phase 4, 5 (paralelo após Phase 3)
                 → Phase 6 (opcional)
                 → Phase 7
```

## MVP Scope (mínimo entregável)

**T001–T017** — US1 completa: portal emite, ops recebe, ticket criado, idempotente.
