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