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

# Webhook handling

# Implementação de Webhooks

> Como receber notificações automáticas da ConnectVets Notes API

# Webhooks para Notificações Automáticas

Os webhooks eliminam a necessidade de fazer polling constante, notificando sua aplicação quando eventos importantes acontecem.

## Eventos Disponíveis

| Evento            | Quando ocorre                      |
| ----------------- | ---------------------------------- |
| `note.completed`  | Quando o processamento é concluído |
| `note.failed`     | Quando há erro no processamento    |
| `note.processing` | Quando o processamento inicia      |

## Estrutura do Payload

```json theme={null} theme={null}
{
  "event": "note.completed",
  "timestamp": "2024-02-14T18:25:43Z",
  "data": {
    "note_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "external_id": "CLIENTE_123"
  }
}
```

## Implementações

### Node.js + Express

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

const app = express();

// Middleware para webhook (raw body)
app.use('/webhook', express.raw({ type: 'application/json' }));

// Sua chave secreta do webhook (fornecida pela equipe ConnectVets)
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

function verifySignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature.replace('sha256=', ''), 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-connectvets-signature'];
  const payload = req.body;

  // Verificar assinatura
  if (!verifySignature(payload, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Signature verification failed');
  }

  const event = JSON.parse(payload);
  
  // Processar evento
  switch (event.event) {
    case 'note.completed':
      handleNoteCompleted(event.data);
      break;
    case 'note.failed':
      handleNoteFailed(event.data);
      break;
    case 'note.processing':
      handleNoteProcessing(event.data);
      break;
  }

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

async function handleNoteCompleted(data) {
  console.log(`Nota ${data.note_id} processada com sucesso`);
  
  // Buscar detalhes completos da nota
  const noteDetails = await fetchNoteDetails(data.note_id);
  
  // Atualizar seu sistema
  await updateLocalDatabase(noteDetails);
  
  // Notificar usuário
  await notifyUser(data.external_id, 'completed');
}

async function handleNoteFailed(data) {
  console.log(`Erro no processamento da nota ${data.note_id}`);
  
  // Registrar erro
  await logError(data);
  
  // Notificar sobre o erro
  await notifyUser(data.external_id, 'failed');
}

async function handleNoteProcessing(data) {
  console.log(`Processamento iniciado para nota ${data.note_id}`);
  
  // Atualizar status na interface
  await updateStatus(data.note_id, 'processing');
}

app.listen(3000, () => {
  console.log('Webhook listener rodando na porta 3000');
});
```

### Python + Flask

```python theme={null} theme={null}
from flask import Flask, request, abort
import hashlib
import hmac
import json
import os

app = Flask(__name__)

WEBHOOK_SECRET = os.getenv('WEBHOOK_SECRET')

def verify_signature(payload, signature, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    received_signature = signature.replace('sha256=', '')
    
    return hmac.compare_digest(expected_signature, received_signature)

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-ConnectVets-Signature')
    payload = request.get_data()
    
    # Verificar assinatura
    if not verify_signature(payload, signature, WEBHOOK_SECRET):
        abort(401)
    
    event = json.loads(payload)
    
    # Processar evento
    if event['event'] == 'note.completed':
        handle_note_completed(event['data'])
    elif event['event'] == 'note.failed':
        handle_note_failed(event['data'])
    elif event['event'] == 'note.processing':
        handle_note_processing(event['data'])
    
    return 'OK', 200

def handle_note_completed(data):
    print(f"Nota {data['note_id']} processada com sucesso")
    
    # Buscar detalhes da nota
    note_details = fetch_note_details(data['note_id'])
    
    # Atualizar banco de dados local
    update_local_database(note_details)
    
    # Enviar notificação
    notify_user(data.get('external_id'), 'completed')

def handle_note_failed(data):
    print(f"Erro no processamento da nota {data['note_id']}")
    
    # Registrar erro
    log_error(data)
    
    # Notificar sobre erro
    notify_user(data.get('external_id'), 'failed')

def handle_note_processing(data):
    print(f"Processamento iniciado para nota {data['note_id']}")
    
    # Atualizar status
    update_status(data['note_id'], 'processing')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3000)
```

### PHP

```php theme={null} theme={null}
<?php

// webhook.php

$webhookSecret = $_ENV['WEBHOOK_SECRET'];

function verifySignature($payload, $signature, $secret) {
    $expectedSignature = hash_hmac('sha256', $payload, $secret);
    $receivedSignature = str_replace('sha256=', '', $signature);
    
    return hash_equals($expectedSignature, $receivedSignature);
}

// Obter dados do webhook
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CONNECTVETS_SIGNATURE'] ?? '';

// Verificar assinatura
if (!verifySignature($payload, $signature, $webhookSecret)) {
    http_response_code(401);
    exit('Signature verification failed');
}

$event = json_decode($payload, true);

// Processar evento
switch ($event['event']) {
    case 'note.completed':
        handleNoteCompleted($event['data']);
        break;
    case 'note.failed':
        handleNoteFailed($event['data']);
        break;
    case 'note.processing':
        handleNoteProcessing($event['data']);
        break;
}

http_response_code(200);
echo 'OK';

function handleNoteCompleted($data) {
    error_log("Nota {$data['note_id']} processada com sucesso");
    
    // Buscar detalhes da nota
    $noteDetails = fetchNoteDetails($data['note_id']);
    
    // Atualizar banco de dados
    updateLocalDatabase($noteDetails);
    
    // Enviar notificação
    notifyUser($data['external_id'] ?? null, 'completed');
}

function handleNoteFailed($data) {
    error_log("Erro no processamento da nota {$data['note_id']}");
    
    // Registrar erro
    logError($data);
    
    // Notificar sobre erro
    notifyUser($data['external_id'] ?? null, 'failed');
}

function handleNoteProcessing($data) {
    error_log("Processamento iniciado para nota {$data['note_id']}");
    
    // Atualizar status
    updateStatus($data['note_id'], 'processing');
}
?>
```

## Configuração do Webhook

### Solicitar Configuração

Para configurar webhooks, entre em contato com nossa equipe via [WhatsApp (+55 31 8512-7147)](https://wa.me/553185127147) informando:

* **URL do endpoint**: `https://seudominio.com/webhook`
* **Eventos desejados**: `["note.completed", "note.failed", "note.processing"]`
* **Ambiente**: produção ou desenvolvimento

Nossa equipe configurará os webhooks e fornecerá a chave secreta para validação.

### Validar Webhook

```bash theme={null} theme={null}
# Testar se seu endpoint está funcionando
curl -X POST "https://seudominio.com/webhook" \
  -H "Content-Type: application/json" \
  -H "X-ConnectVets-Signature: sha256=test" \
  -d '{"event":"test","data":{}}'
```

## Segurança e Boas Práticas

### 1. Sempre Verificar Assinatura

```javascript theme={null} theme={null}
// ❌ NUNCA faça isso
app.post('/webhook', (req, res) => {
  const event = req.body;
  processEvent(event); // Sem verificação!
});

// ✅ SEMPRE faça isso
app.post('/webhook', (req, res) => {
  if (!verifySignature(req.body, req.headers['x-connectvets-signature'], SECRET)) {
    return res.status(401).send('Unauthorized');
  }
  processEvent(req.body);
});
```

### 2. Ser Idempotente

```javascript theme={null} theme={null}
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const event = JSON.parse(req.body);
  
  // Evitar processar o mesmo evento duas vezes
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed');
  }
  
  processEvent(event);
  processedEvents.add(event.id);
  
  res.status(200).send('OK');
});
```

### 3. Responder Rapidamente

```javascript theme={null} theme={null}
app.post('/webhook', (req, res) => {
  // Responder imediatamente
  res.status(200).send('OK');
  
  // Processar assincronamente
  setImmediate(() => {
    processEvent(JSON.parse(req.body));
  });
});
```

## Testando Webhooks

### Usando ngrok para Desenvolvimento

```bash theme={null} theme={null}
# Instalar ngrok
npm install -g ngrok

# Expor porta local
ngrok http 3000

# Usar URL do ngrok no webhook
# https://abc123.ngrok.io/webhook
```

### Logs de Debug

```javascript theme={null} theme={null}
app.post('/webhook', (req, res) => {
  console.log('Headers:', req.headers);
  console.log('Body:', req.body.toString());
  
  // ... resto do código
});
```

***

<CardGroup cols={2}>
  <Card title="Conceitos de Webhooks" icon="webhook" href="/notes/concepts/webhooks">
    Entenda como funcionam os webhooks
  </Card>

  <Card title="Integração Básica" icon="code" href="/notes/examples/basic-integration">
    Voltar ao exemplo de integração
  </Card>
</CardGroup>

<Warning>
  **Importante**: Sempre use HTTPS em produção e mantenha sua chave secreta segura.
</Warning>
