Apariencia
Aceptación de Oferta
La aceptación de una oferta tiene dos caras que conviene no confundir:
- Endpoint entrante
PUT /api/v3/aceptacion: lo invoca el proveedor/central (o su integración) para aceptar una oferta que TSALVA le distribuyó. Está protegido por API key (x-apikey). - Notificación saliente de aceptación: cuando TSALVA determina el mejor ofertamiento, envía un webhook
PUThacia la URL configurada del cliente generador de la oferta para informarle qué central y técnico la tomaron.
Fuente
Documentado contra el código real: src/v3/routes/offers.routes.js, src/v3/middlewares/apikey.validator.js, src/v3/controller/gestion.controller.js (acceptOffer) y src/v3/utils/helpers.js (processAcceptedOffer).
1. Endpoint entrante: aceptar una oferta
Petición
PUT [URL_API]/api/v3/aceptacionAutenticación
Este endpoint no usa Basic Auth. Usa API key por header:
x-apikey: <tu_api_key>
Content-Type: application/jsonLa clave se valida contra la tabla api_users. El middleware (apiKeyValidator) rechaza la petición si:
| Situación | HTTP | msg |
|---|---|---|
No se envía el header x-apikey | 401 | No se ha brindado la clave de acceso. |
| La clave no existe o hay más de una coincidencia | 401 | La clave de acceso no es correcta. |
El usuario de API está inactivo (access_status != 1) | 401 | La solicitud no puede ser completada, debido a que el estado del usuario de API es 'INACTIVO', comuniquese con el administrador del API. |
| El endpoint no está en los permitidos del usuario | 403 | La solicitud no puede ser completada, debido a que no tiene permiso de acceso a este endpoint, comuniquese con el administrador del API. |
| Error interno al validar | 500 | Error interno en la validación de la clave de acceso. |
Todas las respuestas de error del validador incluyen status: false y un campo timestamp (ISO 8601). La respuesta 403 además incluye allowedEndpoints y requestedEndpoint.
Cuerpo (body) esperado
json
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"dniPrestador": "900123456",
"dniTecnico": "12345678",
"tiempoLlegadaSitio": 25,
"latitudTecnico": "6.2400",
"longitudTecnico": "-75.5800"
}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
id | string (UUID) | Sí | UUID de la oferta que se acepta |
dniPrestador | string | Sí | NIT de la central/prestador que acepta |
dniTecnico | string | Sí | Documento del técnico que tomará el servicio |
tiempoLlegadaSitio | number | No | Minutos estimados de llegada al sitio |
latitudTecnico | string | No | Latitud actual del técnico (se registra en la auditoría) |
longitudTecnico | string | No | Longitud actual del técnico |
WARNING
Si dniPrestador coincide con el NIT del generador de la oferta, TSALVA asigna directamente al técnico propio de la central. En caso contrario, TSALVA reenvía internamente la aceptación al endpoint POST /api/v3/asignacion del generador.
Respuestas
Los mensajes van en el campo data (copiados textualmente del código):
| Escenario | HTTP | Cuerpo |
|---|---|---|
| Oferta aceptada (asignación confirmada) | 200 | { "status": true, "data": "Oferta aceptada" } |
| Aceptada, pendiente confirmación de pago del cliente | 200 | { "status": true, "data": "Se ha aceptado la oferta, cliente no ha confirmado el metodo de pago" } |
| Recurso propio: respuesta aceptada, espera confirmación | 200 | { "status": true, "data": "RESPUESTA ACEPTADA, ESPERA POR FAVOR A QUE SE CONFIRME LA ASIGNACIÓN DEL SERVICIO" } |
| La oferta ya había sido aceptada | 400 | { "status": false, "data": "Oferta ya aceptada" } |
| Técnico no encontrado | 404 | { "status": false, "data": "NO SE ENCONTRÓ EL TÉCNICO" } |
| No se encontró la API del generador | 404 | { "status": false, "data": "API GENERADOR NO ENCONTRADO" } |
| Oferta no encontrada (respaldo en BD) | 404 | { "status": false, "data": "Oferta no encontrada" } |
| Error al aceptar la oferta | 500 | { "status": false, "data": "Error al aceptar la oferta" } |
INFO
Cuando el reenvío interno a /api/v3/asignacion es rechazado, TSALVA responde 400 devolviendo el cuerpo tal cual lo entregó ese endpoint.
Ejemplo (cURL)
bash
curl -X PUT "[URL_API]/api/v3/aceptacion" \
-H "x-apikey: tu_api_key" \
-H "Content-Type: application/json" \
-d '{
"id": "123e4567-e89b-12d3-a456-426614174000",
"dniPrestador": "900123456",
"dniTecnico": "12345678",
"tiempoLlegadaSitio": 25,
"latitudTecnico": "6.2400",
"longitudTecnico": "-75.5800"
}'2. Notificación saliente de aceptación
Cuando TSALVA evalúa las respuestas de las centrales y elige la mejor oferta, notifica al cliente generador enviando una petición a su endpoint configurado.
Petición
PUT {url_api}{api_endpoint.respuesta}La URL de destino se compone de dos campos configurados en tu perfil de API: url_api (URL base) + la clave respuesta del JSON api_endpoint. Ver Configuración.
Headers
Content-Type: application/json
x-apikey: <tu_api_key> # solo si tienes una apiKey configuradaINFO
El header x-apikey se envía únicamente si tu perfil de API tiene una apiKey registrada. Si no la tienes, la petición se envía sin ese header.
Cuerpo (payload) enviado
json
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"idGestor": "TSALVA",
"dniGestor": "901668869",
"dniPrestador": "900123456",
"nombrePrestador": "Central Norte SAS",
"telefonoCentral": "+57 4 123-4567",
"latitudTecnico": "6.2400",
"longitudTecnico": "-75.5800",
"identificadorTecnico": "12345678",
"dniTecnico": "12345678",
"telefonoTecnico": "+57 300-123-4567",
"tarifaEstimadaServicio": "0",
"tiempoLlegadaSitio": 25
}| Campo | Tipo | Descripción |
|---|---|---|
id | string (UUID) | UUID de la oferta aceptada |
idGestor | string | Identificador del gestor TSALVA (TSALVA) |
dniGestor | string | Documento del gestor TSALVA (901668869) |
dniPrestador | string | NIT de la central que aceptó |
nombrePrestador | string | Razón social de la central |
telefonoCentral | string | Teléfono de la central |
latitudTecnico | string | Latitud del técnico asignado |
longitudTecnico | string | Longitud del técnico asignado |
identificadorTecnico | string | Identificador del técnico |
dniTecnico | string | Documento del técnico |
telefonoTecnico | string | Teléfono del técnico |
tarifaEstimadaServicio | string | Tarifa estimada. Actualmente se envía "0" |
tiempoLlegadaSitio | number | Minutos estimados de llegada |
Integraciones legacy
Para el NIT 890903279, algunos campos cambian de formato: idGestor es TSALV, dniGestor es A900445812, dniPrestador incluye el dígito de verificación, e identificadorTecnico/dniTecnico se prefijan con C.
Respuesta que TSALVA espera de tu endpoint
Importante — tu endpoint debe responder HTTP 200
Si tu endpoint responde con un código distinto de 200, TSALVA interpreta que rechazaste la asignación: libera los recursos, marca la oferta como inactiva y notifica a la central que el servicio no fue asignado. No hay reintentos automáticos: es un único intento.
El contenido del cuerpo de tu respuesta no se valida; solo importa el código HTTP 200.
Ejemplo de receptor (Node.js / Express)
javascript
const express = require('express');
const app = express();
app.use(express.json());
app.put('/webhook/aceptacion', (req, res) => {
// Verificación opcional del header x-apikey (si lo configuraste)
if (req.headers['x-apikey'] !== process.env.TSALVA_API_KEY) {
return res.status(401).json({ error: 'API Key inválida' });
}
const { id, nombrePrestador, dniTecnico, tiempoLlegadaSitio } = req.body;
console.log(`Oferta ${id} aceptada por ${nombrePrestador}, técnico ${dniTecnico}`);
// Responder 200 para CONFIRMAR la asignación
res.status(200).json({ received: true });
});
app.listen(3000);Ejemplo de receptor (Python / Flask)
python
from flask import Flask, request, jsonify
import os
app = Flask(__name__)
@app.route('/webhook/aceptacion', methods=['PUT'])
def aceptacion():
if request.headers.get('x-apikey') != os.environ.get('TSALVA_API_KEY'):
return jsonify({'error': 'API Key inválida'}), 401
data = request.get_json()
print(f"Oferta {data['id']} aceptada por {data['nombrePrestador']}")
# Responder 200 para CONFIRMAR la asignación
return jsonify({'received': True}), 200Siguiente paso
Después de la aceptación, recibirás notificaciones de Cambio de Estado a medida que avanza el servicio. Consulta también la Configuración de webhooks.