Apariencia
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:
| Pieza | Campo en api_users | Ejemplo |
|---|---|---|
| URL base | url_api | https://tu-dominio.com/api/ |
| Ruta por evento | Clave dentro del JSON api_endpoint | respuesta, 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):
| Webhook | URL resultante | Mé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 unaapiKeyregistrada, TSALVA la envía en el headerx-apikeyde cada webhook saliente. Si no tienesapiKey, la petición de aceptación se envía sin headers y la de cambio de estado solo conContent-Type: application/json. - Verificación en tu extremo: puedes validar ese header
x-apikeycontra 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:
requestApiusaaxiossin un timeout explícito. No hay un límite de 30 s definido en el código; aplica el comportamiento por defecto deaxios. 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). Responder200es 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
localhostno 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ónRecomendació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.