Skip to content

Implementación

Esta guía técnica te ayudará a implementar correctamente el endpoint para recibir webhooks de Quralo en tu sistema HIS.

Requerimientos del Endpoint

Tu endpoint debe cumplir con los siguientes requerimientos:

1. Método HTTP

  • POST: Todos los webhooks se envían mediante HTTP POST

2. Headers Requeridos

Tu endpoint debe validar los siguientes headers:

http
POST /webhook
Content-Type: application/json
Authorization: Bearer {tu-token-de-autenticacion}
X-Webhook-Event: {tipo-de-evento}
X-Webhook-Signature: {firma-hmac-sha256}

3. Estructura de la Respuesta

Tu endpoint debe responder con códigos HTTP apropiados:

  • 200: Evento procesado correctamente
  • 400: Solicitud malformada o tipo de evento no soportado
  • 401: Falla de autenticación o firma inválida
  • 422: Payload con campos faltantes o inválidos
  • 500: Error interno del servidor

Flujo de Validación

Implementa las validaciones en el siguiente orden:

1. Autenticación por Token

javascript
// Validar header Authorization
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
  return res.status(401).json({
    error: 'Unauthorized',
    message: 'Missing or invalid Authorization header'
  });
}

const token = authHeader.replace('Bearer ', '');
if (token !== YOUR_AUTH_TOKEN) {
  return res.status(401).json({
    error: 'Unauthorized',
    message: 'Invalid authentication token'
  });
}

2. Verificación de Firma HMAC

javascript
// Validar firma del webhook
const signature = req.headers['x-webhook-signature'];
if (!signature) {
  return res.status(400).json({
    error: 'Bad Request',
    message: 'Missing X-Webhook-Signature header'
  });
}

// Generar firma esperada
const crypto = require('crypto');
const expectedSignature = crypto
  .createHmac('sha256', YOUR_WEBHOOK_SECRET)
  .update(JSON.stringify(req.body))
  .digest('hex');

if (signature !== expectedSignature) {
  return res.status(401).json({
    error: 'Unauthorized',
    message: 'Invalid webhook signature'
  });
}

3. Validación del Tipo de Evento

javascript
const eventType = req.headers['x-webhook-event'];
const supportedEvents = ['medication_request.created', 'encounter.created'];

if (!supportedEvents.includes(eventType)) {
  return res.status(400).json({
    error: 'Bad Request',
    message: `Unsupported event type: ${eventType}`,
    supported_events: supportedEvents
  });
}

4. Validación del Payload

javascript
const payload = req.body;

// Validar estructura base
if (!payload.data) {
  return res.status(422).json({
    error: 'Unprocessable Entity',
    message: 'Missing data field in payload'
  });
}

// Validar campos específicos según el tipo de evento
switch (eventType) {
  case 'medication_request.created':
    if (!payload.data.prescription_id || !payload.data.patient_id) {
      return res.status(422).json({
        error: 'Unprocessable Entity',
        message: 'Missing required fields for medication_request.created',
        required_fields: ['prescription_id', 'patient_id']
      });
    }
    break;
    
  case 'encounter.created':
    if (!payload.data.encounter_id || !payload.data.patient_id) {
      return res.status(422).json({
        error: 'Unprocessable Entity',
        message: 'Missing required fields for encounter.created',
        required_fields: ['encounter_id', 'patient_id']
      });
    }
    break;
}

Procesamiento del Evento

Una vez validado el webhook, procesa el evento según su tipo:

javascript
switch (eventType) {
  case 'medication_request.created':
    await processMedicationRequest(payload.data);
    break;
    
  case 'encounter.created':
    await processEncounter(payload.data);
    break;
    
  default:
    console.log(`Unhandled event type: ${eventType}`);
}

// Responder con éxito
res.status(200).json({
  status: 'success',
  message: 'Webhook processed successfully'
});

Consideraciones Importantes

Idempotencia

  • Implementa verificación de duplicados usando el ID único del evento
  • Almacena los IDs de eventos procesados para evitar duplicados

Timeouts

  • Tu endpoint debe responder en menos de 30 segundos
  • Para procesamientos largos, responde inmediatamente y procesa de forma asíncrona

Logging

  • Registra todos los webhooks recibidos para auditoría
  • Incluye timestamp, tipo de evento, y resultado del procesamiento

Manejo de Errores

  • Implementa manejo robusto de errores
  • Los webhooks fallidos serán reintentados automáticamente

Estructura Completa del Endpoint

javascript
app.post('/webhook', async (req, res) => {
  try {
    // 1. Validar autenticación
    await validateAuthentication(req);
    
    // 2. Validar firma
    await validateSignature(req);
    
    // 3. Validar tipo de evento
    const eventType = validateEventType(req.headers['x-webhook-event']);
    
    // 4. Validar payload
    validatePayload(eventType, req.body);
    
    // 5. Procesar evento
    await processEvent(eventType, req.body.data);
    
    // 6. Responder con éxito
    res.status(200).json({
      status: 'success',
      message: 'Webhook processed successfully'
    });
    
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(error.statusCode || 500).json({
      error: error.name || 'Internal Server Error',
      message: error.message
    });
  }
});

Documentación de Quralo