🔔 Webhooks - Zeus Colecta API
Documentación completa para integrar webhooks en tu plataforma. Recibe notificaciones en tiempo real cuando el estado de una visita cambia.
Los webhooks son notificaciones HTTP en tiempo real que enviamos a tu servidor cuando algo importante sucede en Zeus Colecta. En lugar de consultar constantemente (polling), nosotros te notificamos automáticamente.
Introducción
Los webhooks son la forma más eficiente de integrar Zeus Colecta en tu plataforma.
¿Por Qué Usar Webhooks?
| Ventaja | Descripción |
|---|---|
| ⚡ Tiempo real | Actualizaciones en menos de 1 segundo |
| 💰 Eficiente | No necesitas polling constante |
| 🔒 Seguro | Autenticación con Bearer token JWT |
| 📊 Completo | Incluye toda la información de la visita |
| 🔄 Confiable | Reintentos automáticos en caso de error |
Estados que Generan Webhooks
Solo dos cambios de estado disparan webhooks:
Visita entregada exitosamente
No se pudo entregar (cliente ausente, rechazó, etc.)
⚡ Configuración Rápida (5 pasos)
Tendrás webhooks funcionando en 5 minutos siguiendo estos pasos.
Paso 1️⃣ : Endpoint HTTPS
Tu servidor debe tener un endpoint HTTPS que acepte POST:
https://tu-api.com/webhooks/estado-cambios
Paso 2️⃣ : Validar Token
Recibirás un header de autenticación:
Authorization: Bearer {JWT_TOKEN}
Paso 3️⃣ : Procesar JSON
El payload incluye toda la información de la visita
Paso 4️⃣ : Responder con 200
Confirma la recepción con HTTP 200-204
Paso 5️⃣ : Registrar URL
Comunícanos tu endpoint para habilitarlo
Ejemplo Mínimo (Python)
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_TOKEN = "tu_token_aqui"
@app.route('/webhooks/estado-cambios', methods=['POST'])
def handle_webhook():
# Validar token
token = request.headers.get('Authorization', '').replace('Bearer ', '')
if token != WEBHOOK_TOKEN:
return jsonify({"error": "No autorizado"}), 401
# Procesar
data = request.get_json()
visita = data['data']
if visita['estado_nuevo'] == 'completada':
print(f"✅ Visita {visita['visita_id']} completada")
return jsonify({"status": "ok"}), 200
if __name__ == '__main__':
app.run(port=5000)
Testing Rápido
Opción 1: Webhook.site (Recomendado - Sin código)
- Ve a https://webhook.site
- Obtén tu URL pública
- Comunícanos la URL
- Verifica en tiempo real
Opción 2: ngrok (Para servidor local)
ngrok http 5000
# Compartir: https://abc123.ngrok.io/webhooks/estado-cambios
📋 Especificación Técnica
Headers HTTP
Cada webhook se envía con estos headers:
POST /webhooks/estado-cambios HTTP/1.1
Host: tu-api.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Payload JSON Completo
{
"event": "visita_estado_cambio",
"timestamp": "2026-06-01T14:30:45.123456",
"data": {
"visita_id": 12345,
"tracking_id": "REF-001-2026",
"titulo": "Entrega Cliente ABC",
"direccion": "Calle Principal 123",
"estado_anterior": "pendiente",
"estado_nuevo": "completada",
"numero_seguimientos": ["330005000000000431"],
"conductor": {
"id": 5,
"nombre": "Juan Pérez García"
},
"ruta": {
"id": 1,
"nombre": "Ruta Centro Lima"
},
"contacto": {
"nombre": "María García López",
"telefono": "+51 987 654 321",
"email": "maria@example.com"
},
"ubicacion": {
"latitud": "-12.0456",
"longitud": "-77.0456"
},
"check_in": {
"fecha_hora": "2026-06-01T10:30:00",
"latitud": "-12.0456",
"longitud": "-77.0456"
},
"check_out": {
"fecha_hora": "2026-06-01T10:45:00",
"latitud": "-12.0457",
"longitud": "-77.0457"
},
"fotos": {
"dni": "https://storage.example.com/dni-001.jpg",
"evidencia": "https://storage.example.com/evidencia-001.jpg"
}
}
}
Códigos de Respuesta HTTP
| Código | Significado | Acción |
|---|---|---|
| 200 | OK - Procesado | Registramos éxito inmediatamente |
| 201 | Created | Registramos éxito inmediatamente |
| 204 | No Content | Registramos éxito inmediatamente |
| 4xx | Error cliente | No se reintenta |
| 5xx | Error servidor | Se reintenta hasta 3 veces |
Reintentos y Backoff
Si tu servidor responde con error 5xx:
- Primer intento: Inmediato
- Segundo intento: Después de ~5 segundos
- Tercer intento: Después de ~25 segundos
- Abandono: Si todos fallan, se registra el error
Deduplicación (Idempotencia)
Para evitar procesar el mismo webhook dos veces, usa esta clave única:
unique_key = f"{visita_id}_{timestamp}"
# En tu base de datos
CREATE UNIQUE INDEX idx_visita_timestamp
ON webhook_events(visita_id, timestamp);
💻 Ejemplos de Código
Python (Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_TOKEN = "tu_token_aqui"
@app.route('/webhooks/estado-cambios', methods=['POST'])
def handle_webhook():
token = request.headers.get('Authorization', '').replace('Bearer ', '')
if token != WEBHOOK_TOKEN:
return jsonify({"error": "Token inválido"}), 401
data = request.get_json()
visita = data['data']
if visita['estado_nuevo'] == 'completada':
print(f"✅ Visita {visita['visita_id']} completada")
actualizar_en_bd(visita)
return jsonify({"status": "ok"}), 200
Node.js (Express)
const express = require('express');
const app = express();
app.use(express.json());
const WEBHOOK_TOKEN = "tu_token_aqui";
app.post('/webhooks/estado-cambios', (req, res) => {
const token = (req.headers.authorization || '').replace('Bearer ', '');
if (token !== WEBHOOK_TOKEN) {
return res.status(401).json({ error: 'Token inválido' });
}
const { data } = req.body;
console.log(`Visita ${data.visita_id}: ${data.estado_nuevo}`);
res.status(200).json({ status: 'ok' });
});
app.listen(3000);
PHP
<?php
$WEBHOOK_TOKEN = "tu_token_aqui";
$token = str_replace('Bearer ', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
if ($token !== $WEBHOOK_TOKEN) {
http_response_code(401);
echo json_encode(['error' => 'Token inválido']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$visita = $data['data'];
if ($visita['estado_nuevo'] === 'completada') {
error_log("Visita {$visita['visita_id']} completada");
}
http_response_code(200);
echo json_encode(['status' => 'ok']);
?>
Go
package main
import (
"encoding/json"
"log"
"net/http"
"strings"
)
const WEBHOOK_TOKEN = "tu_token_aqui"
func handleWebhook(w http.ResponseWriter, r *http.Request) {
token := strings.Replace(r.Header.Get("Authorization"), "Bearer ", "", 1)
if token != WEBHOOK_TOKEN {
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]string{"error": "Token inválido"})
return
}
var payload map[string]interface{}
json.NewDecoder(r.Body).Decode(&payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func main() {
http.HandleFunc("/webhooks/estado-cambios", handleWebhook)
log.Println("Escuchando en :5000")
http.ListenAndServe(":5000", nil)
}
🔧 Troubleshooting & FAQs
Problema: "Rechazo mi webhook"
- Verifica que tu endpoint sea HTTPS (no HTTP)
- Asegúrate de responder con 200-204
- Valida que el token sea correcto
- Verifica que procese en menos de 15 segundos
Problema: "No recibo webhooks"
- Verifica que tu URL sea pública e HTTPS
- Asegúrate de que el firewall no bloquee
- Revisa los logs en nuestro dashboard
- Prueba con Webhook.site
¿Qué pasa si mi servidor está caído?
Reintentamos automáticamente 3 veces con backoff exponencial. Si sigue fallando, se registra en nuestros logs.
Preguntas Frecuentes
¿Qué estados generan webhooks?
Solo completada y falso_flete. Otros estados se ignoran.
¿Cuánto tarda en llegar un webhook?
Típicamente menos de 1 segundo desde que ocurre el cambio.
¿Se garantiza la entrega?
Reintentamos 3 veces. Es tu responsabilidad implementar idempotencia.
¿Cómo evito procesar el mismo webhook 2 veces?
Usa visita_id + timestamp como clave única en tu BD.
¿El token es secreto?
Sí. No lo publiques en GitHub. Trata como una contraseña.
¿Puedo cambiar el token o URL?
Sí. Comunícate con nuestro equipo técnico para rotarlo de forma segura.
✅ Checklist de Implementación
- Endpoint HTTPS configurado
- Token validado en header Authorization
- Payload JSON parseado correctamente
- Respuesta HTTP 200-204 enviada
- Datos guardados en base de datos
- Idempotencia implementada (visita_id + timestamp)
- Logs configurados para auditoría
- Probado en ambiente de desarrollo
- URL comunicada a nuestro equipo técnico
📞 Soporte & Contacto
¿Preguntas o problemas? Contáctanos:
api@homedeliverype.com
+51 987 654 321
developers.homedeliverype.com
dashboard.homedeliverype.com