Skip to content

Configuración de Webhooks

Esta guía describe cómo se configura el destino de los webhooks salientes de TSALVA (aceptación de oferta y cambios de estado) y qué hace realmente el código respecto a seguridad, timeouts y reintentos.

Fuente

Configuración leída de la tabla api_users (campos url_api, api_endpoint, apiKey, access_status, endpoints). El envío se realiza con requestApi (src/functions.js) desde src/v3/utils/helpers.js y src/v3/controller/users.controller.js.

Cómo se arma la URL de destino

TSALVA no tiene un único campo webhook_url. La URL de cada webhook saliente se compone de dos piezas de tu perfil de API:

PiezaCampo en api_usersEjemplo
URL baseurl_apihttps://tu-dominio.com/api/
Ruta por eventoClave dentro del JSON api_endpointrespuesta, actualizar

El campo api_endpoint es un JSON (se parsea con JSON.parse) con, al menos, estas claves usadas por el código:

json
{
  "respuesta": "aceptacion",
  "actualizar": "cambio-estado"
}

La URL final se construye por concatenación directa (url_api + ruta):

WebhookURL resultanteMétodo
Aceptación de oferta{url_api}{api_endpoint.respuesta}PUT
Cambio de estado{url_api}{api_endpoint.actualizar}PUT

Concatenación literal

url_api y la ruta se unen tal cual, sin normalizar separadores. Asegúrate de que url_api termine en / (o que la ruta lo incluya) para que la URL quede bien formada. Ejemplo: https://tu-dominio.com/api/ + aceptacion = https://tu-dominio.com/api/aceptacion.

Seguridad y verificación

Lo que el código realmente hace:

  • Header x-apikey (opcional): si tu perfil tiene una apiKey registrada, TSALVA la envía en el header x-apikey de cada webhook saliente. Si no tienes apiKey, la petición de aceptación se envía sin headers y la de cambio de estado solo con Content-Type: application/json.
  • Verificación en tu extremo: puedes validar ese header x-apikey contra el valor que registraste para autenticar que la petición proviene de TSALVA.

No hay firma HMAC

El código no firma el cuerpo (no hay HMAC/X-Signature). La única verificación disponible es comparar el header x-apikey. Usar HTTPS y una apiKey robusta es una recomendación, no algo que el código imponga.

Timeouts y reintentos

Comportamiento real (no inventado)

  • Timeout: requestApi usa axios sin un timeout explícito. No hay un límite de 30 s definido en el código; aplica el comportamiento por defecto de axios. Mantener tu endpoint rápido es una recomendación.
  • Reintentos: no existe lógica de reintentos. Cada webhook se envía en un único intento.
  • Webhook de aceptación: si tu endpoint no responde 200, TSALVA trata la asignación como rechazada (libera recursos y marca la oferta como inactiva). Responder 200 es obligatorio para confirmar.
  • Webhook de cambio de estado: si tu endpoint no responde 200, TSALVA solo registra el error; no revierte el servicio ni reintenta.

Cómo solicitar la configuración

El alta y la edición de estos campos (url_api, api_endpoint, apiKey) las realiza el equipo de TSALVA sobre tu usuario de API; no hay un endpoint público de auto-servicio para configurarlos.

Email: soporte@robpixels.com

Información a proporcionar:

Empresa:            [Nombre de tu empresa]
NIT:                [Tu NIT/documento]
Contacto técnico:   [Nombre y email]

URL base (url_api): https://tu-dominio.com/api/
Rutas (api_endpoint):
  - respuesta  (aceptación):     aceptacion
  - actualizar (cambio de estado): cambio-estado
apiKey:             [clave que TSALVA enviará en x-apikey]

Requisitos de tu endpoint

  • Debe ser accesible públicamente (una IP privada o localhost no funcionará).
  • Debe aceptar el método PUT con Content-Type: application/json.
  • Debe responder HTTP 200 para confirmar la recepción.
  • HTTPS es una recomendación de seguridad (el código no lo obliga).

Ejemplo de receptor (Node.js + Express)

javascript
const express = require('express');
const app = express();
app.use(express.json());

// Middleware opcional de verificación
const verificarApiKey = (req, res, next) => {
  if (req.headers['x-apikey'] !== process.env.TSALVA_API_KEY) {
    return res.status(401).json({ error: 'API Key inválida' });
  }
  next();
};

// Aceptación de oferta -> ruta "respuesta"
app.put('/api/aceptacion', verificarApiKey, (req, res) => {
  procesarAceptacion(req.body);
  res.status(200).json({ received: true }); // 200 obligatorio para confirmar
});

// Cambio de estado -> ruta "actualizar"
app.put('/api/cambio-estado', verificarApiKey, (req, res) => {
  procesarCambioEstado(req.body);
  res.status(200).json({ received: true });
});

app.listen(3000, () => console.log('Servidor webhook en el puerto 3000'));

Ejemplo de receptor (Python + Flask)

python
from flask import Flask, request, jsonify
import os

app = Flask(__name__)

def verificar_api_key():
    return request.headers.get('x-apikey') == os.environ.get('TSALVA_API_KEY')

@app.route('/api/aceptacion', methods=['PUT'])
def aceptacion():
    if not verificar_api_key():
        return jsonify({'error': 'API Key inválida'}), 401
    procesar_aceptacion(request.get_json())
    return jsonify({'received': True}), 200  # 200 obligatorio

@app.route('/api/cambio-estado', methods=['PUT'])
def cambio_estado():
    if not verificar_api_key():
        return jsonify({'error': 'API Key inválida'}), 401
    procesar_cambio_estado(request.get_json())
    return jsonify({'received': True}), 200

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

Pruebas locales con ngrok

bash
# Terminal 1: inicia tu aplicación
npm start

# Terminal 2: expón el puerto local
ngrok http 3000

# Usa la URL pública que ngrok te da como url_api al solicitar la configuración

Recomendación

Registra (log) todos los webhooks recibidos y procesa la lógica pesada de forma asíncrona, respondiendo 200 de inmediato. Como no hay reintentos, un 200 tardío que expire por timeout equivale a una notificación perdida.

Verifica el orden de eventos

No hay garantía de orden ni de entrega. Diseña tu integración para ser idempotente y para tolerar notificaciones fuera de secuencia, apoyándote en fechaCambioEstado del payload de cambio de estado.

Tsalva API - Documentación desarrollada por RobPixels