# Data Model: Webhook VM112 → Ops Platform ## Webhook Payload (request body) | Campo | Tipo | Obrigatório | Descrição | |-------|------|-------------|-----------| | `event` | string | sim | Tipo: `account.created`, `domain.validated`, `dns.applied`, `onboarding.completed`, `onboarding.failed` | | `domain` | string | sim* | Domínio normalizado lowercase (*opcional para alguns eventos futuros) | | `session_id` | string | não | ID sessão onboarding (`X-Onboarding-Session`) | | `data` | object | não | Metadados específicos do evento | ### `data` para `account.created` | Campo | Tipo | Descrição | |-------|------|-----------| | `email` | string | Email criado (`admin@dominio.com`) | | `account_verified` | boolean | Resultado `carbonio.account_exists` | | `needs_review` | boolean | Inverso de verified ou flag explícita | | `dns_mode` | string | Modo DNS indicado no onboarding | | `mail_aliases` | string[] | Aliases configurados (opcional) | ## Ops Platform — entidades existentes ### webhook_events | Coluna | Tipo | Notas | |--------|------|-------| | id | INTEGER PK | auto | | event_type | TEXT | ex: `account.created` | | source | TEXT | `vm112-onboard` | | payload | TEXT | JSON serializado | | created_at | TEXT ISO8601 UTC | | **Dedup key (lógica)**: `event_type` + `JSON_EXTRACT(payload,'$.session_id')` + `JSON_EXTRACT(payload,'$.domain')` ### tickets | Coluna | Tipo | Notas | |--------|------|-------| | id | INTEGER PK | auto | | tenant_id | INTEGER | 1 = VM112 | | subject | TEXT | `[account.created] dominio.com — email` | | status | TEXT | `open` default | | payload | TEXT | JSON do evento | | created_at | TEXT ISO8601 UTC | | ### tenants (pré-existente) | id | name | ip | role | |----|------|-----|------| | 1 | VM112 Ligbox Onboard | 10.10.10.112 | onboarding_portal | ## Portal — configuração (.env) | Variável | Exemplo | Descrição | |----------|---------|-----------| | `OPS_WEBHOOK_URL` | `http://10.10.10.122:8080/api/v1/webhooks/onboard` | Endpoint receptor | | `OPS_WEBHOOK_SECRET` | `(secret)` | Igual a `WEBHOOK_SECRET` em VM122 | | `OPS_WEBHOOK_ENABLED` | `true` | Kill switch sem redeploy | ## State transitions ```text onboarding.create_account → [portal] carbonio.create_account OK → [portal] ops_webhook.emit("account.created") [background] → [ops] validate secret → [ops] check dedup → [ops] INSERT webhook_events → [ops] INSERT tickets (se account.created e não duplicado) → [ops] LPUSH ops:events redis → [ops] return {"accepted": true} ``` ## Response ```json {"accepted": true, "event": "account.created"} ``` Erros: - `401` — secret inválido - `422` — payload inválido (pydantic)