Las ofertas que llegan a Saplic por un feed reciben las postulaciones en Saplic: el candidato no sale del sitio. Cada postulación se reenvía a la fuente:
https://.Los dos llevan los mismos datos. Solo se envían candidatos que postularon a una oferta de esa fuente.
POST a la URL registrada, con Content-Type: application/json; charset=utf-8.
| Cabecera | Qué lleva |
|---|---|
X-Saplic-Event |
application.created (una postulación) o test.ping (prueba). |
X-Saplic-Delivery |
Identificador de la entrega. Es el mismo en todos los reintentos de una postulación. |
X-Saplic-Timestamp |
Momento del envío, en segundos Unix. |
X-Saplic-Signature |
HMAC-SHA256(secreto, timestamp + "." + cuerpo), en hexadecimal en minúsculas. |
https. Saplic no sigue redirecciones: una respuesta 3xx cuenta como fallo.application.created{
"event": "application.created",
"delivery_id": "4f6c1d0a9b7e4c2f8a1d3e5b7c9f0a2b",
"created_at": "2026-10-07T15:04:05+00:00",
"source": "saplic",
"job": {
"reference": "ACME-2026-0412",
"title": "Marketing Coordinator",
"url": "https://acmefoods.example/careers/marketing-coordinator",
"saplic_url": "https://saplic.com/oferta.php?id=41234"
},
"application": {
"applied_at": "2026-10-07T15:03:58+00:00",
"answers": [
{ "question": "Do you have a valid work permit?", "answer": "Yes" }
]
},
"candidate": {
"name": "Ana López",
"email": "[email protected]",
"phone": "+503 70001234",
"country": "El Salvador",
"city": "San Salvador",
"experience": [
{ "title": "Marketing Analyst", "company": "Globex", "from": "2022-03", "to": "", "current": true }
],
"education": [
{ "title": "Marketing", "school": "Universidad Centroamericana", "level": "Universitario" }
],
"cv_url": "https://saplic.com/feed-cv.php?t=…",
"cv_url_expires_in_hours": 72
}
}
job.reference es el referencenumber que la fuente mandó en su feed. cv_url es null si el candidato no subió un archivo de CV.
test.ping{
"event": "test.ping",
"delivery_id": "…",
"created_at": "2026-10-07T15:04:05+00:00",
"source": "saplic",
"message": "Evento de prueba de Saplic. No corresponde a ninguna postulación."
}
Se manda desde el panel para comprobar la conexión y la firma. No lleva datos de ningún candidato.
El secreto es una cadena de 64 caracteres hexadecimales que Saplic le entrega a la fuente. Se usa tal cual, como texto (no hay que convertirla a bytes).
HMAC-SHA256(secreto, X-Saplic-Timestamp + "." + cuerpo) en hexadecimal.X-Saplic-Signature con una comparación de tiempo constante.X-Saplic-Timestamp tiene más de 5 minutos de diferencia con la hora del servidor.200 sin procesar) si X-Saplic-Delivery ya se recibió antes: es un reintento de algo ya procesado.Los pasos 4 y 5 son los que impiden que alguien que capture un envío lo vuelva a mandar.
PHP:
$cuerpo = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_SAPLIC_TIMESTAMP'] ?? '';
$firma = $_SERVER['HTTP_X_SAPLIC_SIGNATURE'] ?? '';
$ok = ctype_digit($ts)
&& abs(time() - (int)$ts) <= 300
&& hash_equals(hash_hmac('sha256', $ts . '.' . $cuerpo, $secreto), $firma);
if (!$ok) { http_response_code(401); exit; }
// … descartar si $_SERVER['HTTP_X_SAPLIC_DELIVERY'] ya se procesó …
http_response_code(200);
Python:
import hmac, hashlib, time
def firma_valida(secreto: str, timestamp: str, cuerpo: bytes, firma: str) -> bool:
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
return False
esperado = hmac.new(secreto.encode(), timestamp.encode() + b"." + cuerpo, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, firma)
Node.js:
const crypto = require('crypto');
function firmaValida(secreto, timestamp, cuerpo /* Buffer */, firma) {
if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const esperado = crypto.createHmac('sha256', secreto).update(timestamp + '.').update(cuerpo).digest('hex');
return esperado.length === firma.length && crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(firma));
}
Si el correo no sale o el webhook no responde 2xx en 10 segundos, Saplic reintenta a los 5 minutos, a los 30 minutos y a las 2 horas. Si el último reintento también falla, el envío queda en error y el equipo de Saplic recibe una alerta; la postulación sigue guardada y se puede reenviar a mano.
Cada reintento del webhook lleva un X-Saplic-Timestamp y una firma nuevos, y el mismo X-Saplic-Delivery.
El CV no va adjunto: va como enlace (cv_url en el webhook, un botón en el correo).
Conviene descargar el archivo al recibir la postulación y no guardar el enlace.
Asunto: Nueva postulación: {título de la oferta} — vía Saplic, en el idioma con el que se registró la fuente (español, inglés, portugués o francés). Lleva la referencia de la oferta, los datos del candidato (nombre, correo, teléfono, país, ciudad, experiencia, estudios y sus respuestas a las preguntas de la oferta) y el enlace al CV.
El secreto se puede regenerar desde Saplic en cualquier momento. El anterior deja de servir de inmediato, así que hay que actualizarlo del lado de la fuente en el mismo momento.