> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vaulterp.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks do Vault ERP: Notificações de Eventos em Tempo Real

> Configure webhooks no Vault ERP para receber notificações em tempo real quando vendas são fechadas, relógios entram em estoque ou consignações expiram.

Os webhooks do Vault ERP permitem que seus sistemas recebam notificações HTTP em tempo real sempre que eventos importantes ocorrem — sem necessidade de polling. Quando um relógio é vendido, uma consignação expira ou um cliente tem um match na lista de desejos, o Vault ERP envia imediatamente um payload JSON para o endpoint que você configurou.

## Eventos Disponíveis

| Evento | Descrição |
| - | - |
| `watch.created` | Novo relógio adicionado ao catálogo. |
| `watch.status_changed` | Status de um relógio foi alterado (ex.: `disponivel` → `reservado`). |
| `watch.authenticated` | Laudo de autenticação registrado para um relógio. |
| `sale.created` | Nova negociação aberta no pipeline. |
| `sale.status_changed` | Negociação movida para um novo estágio do pipeline. |
| `sale.closed_won` | Venda fechada com sucesso (`fechado_ganho`). |
| `consignment.expiring` | Prazo de uma consignação está dentro dos próximos 7 dias. |
| `consignment.expired` | Prazo de uma consignação foi ultrapassado. |
| `customer.wishlist_match` | Um relógio compatível com o desejo de um cliente entrou no estoque. |

## Configurando um Webhook

**1. Acesse as configurações de webhooks**

No menu lateral do Vault ERP, navegue até **Configurações → Integrações → Webhooks**.

**2. Adicione um novo webhook**

Clique em **"Adicionar Webhook"** para abrir o formulário de configuração.

**3. Informe a URL do seu endpoint**

Digite a URL do endpoint que receberá os eventos. A URL deve obrigatoriamente usar **HTTPS** — URLs HTTP não são aceitas por questões de segurança.

**4. Selecione os eventos**

Marque apenas os eventos que seu sistema precisa processar. Você pode alterar essa seleção a qualquer momento sem recriar o webhook.

**5. Salve e confirme o recebimento**

Clique em **"Salvar"**. O Vault ERP enviará imediatamente um evento de teste (`vault.ping`) para o endpoint configurado. Verifique nos logs do seu sistema se o payload foi recebido corretamente.

## POST /webhooks

Registra um novo endpoint de webhook programaticamente, sem precisar acessar o painel. Útil para provisionamento automatizado de integrações.

### Campos do Body

**url** `string` *required*

URL HTTPS do endpoint que receberá os eventos. URLs HTTP são rejeitadas.

**events** `array` *required*

Lista dos eventos que este webhook deve receber. Use `["*"]` para assinar todos os eventos disponíveis.

**description** `string`

Descrição opcional para identificar a finalidade deste webhook (ex.: `"Integração CRM externo"`).

### Exemplo de Requisição

```bash theme={null}
curl -X POST https://api.vault-erp.com/v1/webhooks \
  -H "Authorization: Bearer seu_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://meuservico.com.br/webhooks/vault",
    "events": ["sale.closed_won", "watch.created", "consignment.expiring"],
    "description": "Integração com CRM externo"
  }'
```

### Exemplo de Resposta

```json theme={null}
{
  "data": {
    "id": "wh_abc789",
    "url": "https://meuservico.com.br/webhooks/vault",
    "events": ["sale.closed_won", "watch.created", "consignment.expiring"],
    "description": "Integração com CRM externo",
    "secret": "whsec_x8kQm2pLr4nZvJtY9cFdHsEbWoG7uAi3",
    "active": true,
    "created_at": "2024-03-20T17:00:00Z"
  }
}
```

> [!WARNING]
>
> O campo `secret` é retornado **apenas uma vez**, no momento da criação. Armazene-o imediatamente em um gerenciador de segredos — ele é necessário para verificar a assinatura HMAC de cada evento recebido. Não será possível recuperá-lo depois.

***

## Estrutura do Payload

Todos os payloads de webhook seguem a mesma estrutura, independentemente do tipo de evento:

```json theme={null}
{
  "event": "sale.closed_won",
  "timestamp": "2024-03-15T18:45:00Z",
  "data": {
    "id": "sale_xyz456",
    "watch_id": "watch_abc123",
    "customer_id": "cust_def789",
    "final_price": 8100000,
    "closed_at": "2024-03-15T18:45:00Z"
  }
}
```

**event** `string`

Nome do evento que acionou a notificação (ex.: `sale.closed_won`, `watch.created`).

**timestamp** `string`

Data e hora em que o evento ocorreu no Vault ERP, em formato ISO 8601 UTC.

**data** `object`

Objeto com os dados do recurso afetado. A estrutura varia de acordo com o tipo de evento — consulte os exemplos de cada evento abaixo.

### Exemplos de Payload por Evento

**watch.created**

```json theme={null}
{
  "event": "watch.created",
  "timestamp": "2024-03-20T10:15:00Z",
  "data": {
    "id": "watch_pq9r2s",
    "brand": "Patek Philippe",
    "model": "Nautilus",
    "reference": "5711/1A-010",
    "status": "disponivel",
    "asking_price": 22000000,
    "stock_type": "proprio"
  }
}
```

**sale.closed\_won**

```json theme={null}
{
  "event": "sale.closed_won",
  "timestamp": "2024-03-15T18:45:00Z",
  "data": {
    "id": "sale_xyz456",
    "watch_id": "watch_abc123",
    "customer_id": "cust_def789",
    "final_price": 8100000,
    "closed_at": "2024-03-15T18:45:00Z"
  }
}
```

**consignment.expiring**

```json theme={null}
{
  "event": "consignment.expiring",
  "timestamp": "2024-03-18T08:00:00Z",
  "data": {
    "watch_id": "watch_cons001",
    "consignee_id": "cust_mno456",
    "expiry_date": "2024-03-25",
    "days_remaining": 7
  }
}
```

**customer.wishlist\_match**

```json theme={null}
{
  "event": "customer.wishlist_match",
  "timestamp": "2024-03-20T10:15:00Z",
  "data": {
    "customer_id": "cust_def789",
    "wishlist_item_id": "wish_001",
    "watch_id": "watch_pq9r2s",
    "reference": "5711/1A-010",
    "asking_price": 22000000
  }
}
```

## Verificação de Assinatura

O Vault ERP assina cada requisição de webhook com **HMAC-SHA256** usando o segredo exclusivo do seu webhook. Você deve sempre verificar a assinatura antes de processar o payload — isso garante que a requisição realmente veio do Vault ERP e não foi adulterada.

A assinatura é enviada no header `X-Vault-Signature` no formato `sha256=<hash>`.

### Verificando em Node.js

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return `sha256=${expected}` === signature;
}

// Exemplo de uso em um servidor Express
app.post('/webhooks/vault', (req, res) => {
  const signature = req.headers['x-vault-signature'];
  const rawBody = req.rawBody; // use um middleware que preserve o body raw

  if (!verifyWebhook(rawBody, signature, process.env.VAULT_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Payload verificado — processe o evento com segurança
  const event = req.body;
  console.log(`Received event: ${event.event}`);

  res.status(200).send('OK');
});
```

### Verificando em Python

```python theme={null}
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    return f"sha256={expected}" == signature
```

> [!NOTE]
>
> Use a versão em bytes do payload (o body HTTP bruto, antes de qualquer parsing JSON) para calcular o HMAC. Fazer o hash de uma string JSON re-serializada pode gerar diferenças de formatação e causar falhas de verificação.

O segredo do seu webhook está disponível em **Configurações → Integrações → Webhooks** — clique no webhook desejado para exibi-lo.

## Retentativas

Se o seu endpoint retornar uma resposta com status fora da faixa `2xx` (ou não responder a tempo), o Vault ERP tentará entregar o evento novamente com **backoff exponencial**:

| Tentativa | Intervalo após a falha anterior |
| - | - |
| 1ª | 1 minuto |
| 2ª | 5 minutos |
| 3ª | 30 minutos |
| 4ª | 2 horas |
| 5ª | 8 horas |

Após 5 tentativas sem sucesso, o evento é marcado como **falho** e não será reenviado automaticamente. Você pode visualizar e reprocessar eventos falhos em **Configurações → Integrações → Webhooks → Logs de Eventos**.

> [!WARNING]
>
> Seu endpoint deve responder dentro de **5 segundos** — caso contrário, a entrega é considerada falha e entra na fila de retentativas. Para operações demoradas (gravação em banco de dados, chamadas a APIs externas, envio de e-mails), responda imediatamente com `200 OK` e processe o payload de forma assíncrona em background. Nunca faça processamento pesado de forma síncrona dentro do handler do webhook.

## Boas Práticas

> [!TIP]
>
> Projete seus handlers de webhook para serem **idempotentes** — ou seja, processar o mesmo evento múltiplas vezes deve ter o mesmo efeito que processá-lo uma vez. Em caso de falhas de rede ou retentativas, o mesmo evento pode ser entregue mais de uma vez. Use o campo `data.id` como chave de deduplicação no seu banco de dados para evitar registros duplicados.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.