Respuestas y Manejo de Errores
Estructura de Respuesta Exitosa
Section titled “Estructura de Respuesta Exitosa”Todas las respuestas exitosas del endpoint tienen el código HTTP 200 OK y siguen esta estructura:
{ "success": true, "statusCode": 200, "message": "Cálculo de nómina realizado exitosamente", "data": { "periodo": { ... }, "escenario": "...", "nivelCalculo": "...", "porcentajeNovedades": 0, "resultados": { ... }, "nota": "..." }, "metadata": { "tiempoProcesamientoMs": 3.14, "timestamp": "2025-01-28 10:30:45", "versionApi": "v1.1" }}Campos de la Respuesta
Section titled “Campos de la Respuesta”Nivel Superior
Section titled “Nivel Superior”| Campo | Tipo | Descripción |
|---|---|---|
success | boolean | Indica si la operación fue exitosa |
statusCode | number | Código HTTP de la respuesta |
message | string | Mensaje descriptivo del resultado |
data | object | Datos del cálculo de nómina |
metadata | object | Información sobre la petición |
Objeto data
Section titled “Objeto data”| Campo | Tipo | Descripción |
|---|---|---|
periodo | object | Periodo calculado (mes y año) |
escenario | string | Escenario utilizado (mejor/peor/promedio) |
nivelCalculo | string | Nivel de cálculo (empleado/proyecto) |
porcentajeNovedades | number | Porcentaje de novedades configurado |
resultados | object | Resultados detallados del cálculo |
nota | string | Nota informativa sobre el escenario |
Objeto resultados
Section titled “Objeto resultados”| Campo | Tipo | Descripción |
|---|---|---|
nivelCalculo | string | Nivel utilizado |
cantidadEmpleadosProcesados | number | Total de empleados calculados |
estadisticasNovedades | object | Estadísticas sobre novedades aplicadas |
detalleEmpleados | array | Array con el detalle de cada empleado |
totalesGenerales | object | Totales consolidados de todos los empleados |
totalesPorProyecto | array|null | Totales por proyecto (solo si nivelCalculo: "proyecto") |
Detalle por Empleado
Section titled “Detalle por Empleado”Cada objeto en detalleEmpleados contiene:
{ "idEmpleado": "EMP001", "idProyecto": "PROY001", "periodo": { "mes": 1, "anio": 2025 }, "escenario": "promedio", "tipoSalario": "no_integral", "devengos": { ... }, "deducciones": { ... }, "aportesEmpleador": { ... }, "provisiones": { ... }, "novedades": { ... }, "ibcAjustado": 3000000, "totales": { ... }}Objeto devengos
Section titled “Objeto devengos”"devengos": { "salarioBasico": 3000000, "auxilioTransporte": 200000, "horasExtras": 234567.50, "recargos": 125000, "bonificaciones": 500000, "otrosIngresos": 0, "baseIbc": 3000000, "totalDevengos": 4059567.50}| Campo | Descripción |
|---|---|
salarioBasico | Salario base proporcional a días trabajados |
auxilioTransporte | Auxilio de transporte si aplica |
horasExtras | Total pagado por horas extras |
recargos | Total pagado por recargos nocturnos/festivos |
bonificaciones | Total de bonificaciones |
baseIbc | Base para calcular IBC (70% si es salario integral) |
totalDevengos | Suma de todos los devengos |
Objeto deducciones
Section titled “Objeto deducciones”"deducciones": { "salud": 120000, "pension": 120000, "totalDeducciones": 240000, "retencionFuente": 45000, "detalleRetencion": { "retencion": 45000, "porcentajeAplicado": 4, "baseGravable": 3739567.50, "baseEnUvt": 79.45 }}| Campo | Descripción |
|---|---|
salud | Aporte empleado a salud (4%) |
pension | Aporte empleado a pensión (4%) |
retencionFuente | Retención en la fuente según tabla UVT |
totalDeducciones | Suma total de deducciones |
detalleRetencion | Detalle del cálculo de retención |
Objeto aportesEmpleador
Section titled “Objeto aportesEmpleador”"aportesEmpleador": { "salud": 255000, "pension": 360000, "arl": 15660, "sena": 60000, "icbf": 90000, "cajaCompensacion": 120000, "totalAportesEmpleador": 900660}| Campo | Descripción | Porcentaje |
|---|---|---|
salud | Aporte empleador a salud | 8.5% |
pension | Aporte empleador a pensión | 12% |
arl | Seguro de riesgos laborales | 0.522% - 6.96% |
sena | Aporte al SENA | 2% |
icbf | Aporte al ICBF | 3% |
cajaCompensacion | Aporte a caja de compensación | 4% |
totalAportesEmpleador | Suma de todos los aportes | ~28-30% |
Objeto provisiones
Section titled “Objeto provisiones”"provisiones": { "cesantias": 266560, "interesesCesantias": 2665.60, "primaServicios": 266560, "vacaciones": 125000, "totalProvisiones": 660785.60}| Campo | Descripción | Porcentaje |
|---|---|---|
cesantias | Provisión mensual de cesantías | 8.33% |
interesesCesantias | Intereses sobre cesantías | 1% mensual |
primaServicios | Provisión de prima de servicios | 8.33% |
vacaciones | Provisión de vacaciones | 4.17% |
totalProvisiones | Suma de todas las provisiones | ~22% |
Objeto novedades
Section titled “Objeto novedades”Contiene las novedades aplicadas al empleado. Puede incluir cualquier combinación de:
"novedades": { "horasExtras": { "total": 234567.50, "detalle": [ { "tipo": "diurnas_ordinarias", "cantidad": 15, "valorHora": 12500, "total": 234375 } ] }, "recargos": { "total": 125000, "detalle": [ ... ] }, "bonificaciones": { "total": 800000, "totalSalarial": 500000, "totalNoSalarial": 300000, "detalle": [ ... ] }, "incapacidad": { "dias": 5, "tipo": "eps", "pagos": { ... }, "diasReducidos": 5, "reduccionIBC": 500000 }, "licencia": { "tipo": "no_remunerada", "dias": 3, "reduccionSalario": 300000 }, "suspension": { "dias": 2, "tipo": "disciplinaria", "reduccionSalario": 200000, "reduccionAuxilio": 13333.33, "reduccionTotal": 213333.33 }}Objeto totales
Section titled “Objeto totales”"totales": { "nominaNeta": 3819567.50, "costoEmpleador": 900660, "totalProvisiones": 660785.60, "costoTotalRecursoHumano": 5381013.10}| Campo | Descripción | Fórmula |
|---|---|---|
nominaNeta | Valor a pagar al empleado | Devengos - Deducciones |
costoEmpleador | Aportes patronales | Suma de aportes |
totalProvisiones | Provisiones laborales | Suma de provisiones |
costoTotalRecursoHumano | Costo total mensual | Nómina + Aportes + Provisiones |
Estadísticas de Novedades
Section titled “Estadísticas de Novedades”El objeto estadisticasNovedades proporciona información sobre la aplicación de novedades:
"estadisticasNovedades": { "escenario": "promedio", "porcentajeConfigurado": 30, "totalEmpleados": 10, "empleadosConNovedades": 3, "empleadosSinNovedades": 7, "porcentajeReal": 30, "tiposNovedadesAplicadas": { "horasExtras": 2, "bonificaciones": 2, "recargos": 1, "incapacidad": 1 }}| Campo | Descripción |
|---|---|
porcentajeConfigurado | Porcentaje enviado en el request |
porcentajeReal | Porcentaje efectivo aplicado |
empleadosConNovedades | Cantidad de empleados que tienen alguna novedad |
tiposNovedadesAplicadas | Contador de cada tipo de novedad aplicada |
Totales Generales
Section titled “Totales Generales”"totalesGenerales": { "totalSalariosBasicos": 15000000, "totalAuxilioTransporte": 600000, "totalDevengos": 18234567.50, "totalDeducciones": 1240000, "totalAportesEmpleador": 4503300, "totalProvisiones": 3303928, "totalNominaNeta": 16994567.50, "totalCostoEmpleador": 4503300, "totalCostoRecursoHumano": 24801795.50}Estos totales suman todos los empleados procesados y son útiles para:
- Proyecciones presupuestarias mensuales
- Comparación entre escenarios
- Análisis de costos totales de nómina
Totales por Proyecto
Section titled “Totales por Proyecto”Solo disponible cuando nivelCalculo: "proyecto". Agrupa resultados por cada idProyecto:
"totalesPorProyecto": [ { "idProyecto": "PROYECTO_ALPHA", "cantidadEmpleados": 5, "empleadosConNovedades": 2, "totales": { "totalSalariosBasicos": 15000000, "totalAuxilioTransporte": 400000, "totalDevengos": 17500000, "totalDeducciones": 1200000, "totalAportesEmpleador": 4200000, "totalProvisiones": 3100000, "totalNominaNeta": 16300000, "totalCostoEmpleador": 4200000, "totalCostoRecursoHumano": 23600000 } }, { "idProyecto": "PROYECTO_BETA", "cantidadEmpleados": 3, "empleadosConNovedades": 1, "totales": { ... } }]Respuestas de Error
Section titled “Respuestas de Error”Todas las respuestas de error siguen esta estructura:
{ "success": false, "statusCode": 400, "error": { "message": "Errores de validación en los datos enviados", "details": [ ... ] }}Códigos de Error HTTP
Section titled “Códigos de Error HTTP”400 Bad Request - Errores de Validación
Section titled “400 Bad Request - Errores de Validación”Ocurre cuando los datos enviados no cumplen las validaciones.
Ejemplo:
{ "success": false, "statusCode": 400, "error": { "message": "Errores de validación en los datos enviados", "details": [ "El campo \"periodo\" es obligatorio", "Perfil #1: El campo \"salarioBasico\" debe ser un número mayor a 0", "Perfil #2: El salario básico no puede ser menor al SMMLV proporcional a la jornada" ] }}Causas comunes:
- Falta algún campo obligatorio (
periodo,escenario,nivelCalculo,perfiles) - El salario es menor al SMMLV
- Los días contratados están fuera del rango 1-30
- Formato de JSON inválido
- Suma de días de novedades excede 30 días
- Horas extras exceden límites legales
Solución:
- Revisar el detalle en
error.details[] - Corregir los valores según las validaciones descritas
- Verificar que el JSON esté bien formado
403 Forbidden - Error de Autenticación
Section titled “403 Forbidden - Error de Autenticación”Ocurre cuando el API Key es inválido o falta.
Ejemplo:
{ "success": false, "status": 403, "error": { "message": "Acceso denegado. Tu no tienes los permisos necesarios para acceder a este recurso." }}Causas:
- No se envió el header
X-Api-Key - El API Key es incorrecto
- El API Key expiró o fue revocado
- Falta el prefijo
Beareren el header
Solución:
- Verificar que el header sea:
X-Api-Key: Bearer {tu_api_key} - Contactar al líder técnico si el API Key no funciona
- Solicitar un nuevo API Key si el actual expiró
500 Internal Server Error - Error del Servidor
Section titled “500 Internal Server Error - Error del Servidor”Ocurre cuando hay un error interno en el servidor.
Ejemplo:
{ "success": false, "statusCode": 500, "error": { "message": "Error interno al procesar la solicitud", "details": "Division by zero in calculation service", "trace": "..." }}Causas:
- Error no manejado en el servidor
- Problema con la base de datos
- Timeout en procesamiento interno
- Bug en el código del servidor
Solución:
- Reintentar la petición después de unos segundos
- Si persiste, contactar a soporte técnico con el
timestampde la petición - Reportar el error con los datos del request (sin el API Key)
Códigos de Respuesta Resumen
Section titled “Códigos de Respuesta Resumen”| Código | Tipo | Significado | Acción |
|---|---|---|---|
| 200 | Éxito | Cálculo realizado correctamente | Procesar datos |
| 400 | Error | Datos inválidos o faltantes | Revisar validaciones |
| 403 | Error | API Key inválido | Verificar autenticación |
| 500 | Error | Error interno del servidor | Reintentar o contactar soporte |
Ejemplos de Manejo de Respuesta
Section titled “Ejemplos de Manejo de Respuesta”Procesamiento de Respuesta Exitosa
Section titled “Procesamiento de Respuesta Exitosa”<?php$respuesta = calcularNomina($requestData);
if ($respuesta['success']) { $data = $respuesta['data'];
// Extraer información relevante $tiempoMs = $respuesta['metadata']['tiempoProcesamientoMs']; $escenario = $data['escenario']; $costoTotal = $data['resultados']['totalesGenerales']['totalCostoRecursoHumano'];
echo Cálculo exitoso en {$tiempoMs}ms\n"; echo "Escenario: {$escenario}\n"; echo "Costo Total RH: $" . number_format($costoTotal, 2) . "\n";
// Procesar cada empleado foreach ($data['resultados']['detalleEmpleados'] as $empleado) { echo "\nEmpleado: {$empleado['idEmpleado']}\n"; echo "Costo: $" . number_format($empleado['totales']['costoTotalRecursoHumano'], 2) . "\n";
// Verificar si tiene novedades if (!empty($empleado['novedades'])) { echo "⚠️ Este empleado tiene novedades:\n"; foreach (array_keys($empleado['novedades']) as $tipo) { echo " - " . ucfirst($tipo) . "\n"; } } }}Manejo de Errores de Validación
Section titled “Manejo de Errores de Validación”<?phptry { $respuesta = calcularNomina($requestData);} catch (Exception $e) { $mensaje = $e->getMessage();
if (strpos($mensaje, 'HTTP 400') !== false) { // Extraer detalles de validación del mensaje echo Errores de validación:\n";
// En una implementación real, parsearías la respuesta JSON // para obtener el array de errores $errorResponse = json_decode($e->getMessage(), true);
if (isset($errorResponse['error']['details'])) { foreach ($errorResponse['error']['details'] as $error) { echo " • $error\n"; } }
echo "\n💡 Verifica los datos y reintenta.\n"; }}Logging de Peticiones
Section titled “Logging de Peticiones”<?phpfunction logPeticionNomina($requestData, $respuesta, $tiempoInicio){ $tiempoTotal = (microtime(true) - $tiempoInicio) * 1000;
$log = [ 'timestamp' => date('Y-m-d H:i:s'), 'escenario' => $requestData['escenario'], 'nivelCalculo' => $requestData['nivelCalculo'], 'cantidadPerfiles' => count($requestData['perfiles']), 'tiempoTotalMs' => $tiempoTotal, 'exito' => $respuesta['success'] ?? false ];
if ($respuesta['success']) { $log['tiempoProcesamientoMs'] = $respuesta['metadata']['tiempoProcesamientoMs']; $log['costoTotal'] = $respuesta['data']['resultados']['totalesGenerales']['totalCostoRecursoHumano']; } else { $log['error'] = $respuesta['error']['message']; }
// Guardar en log file o base de datos error_log(json_encode($log), 3, '/var/log/nomina_api.log');}
// Uso$tiempoInicio = microtime(true);$respuesta = calcularNomina($requestData);logPeticionNomina($requestData, $respuesta, $tiempoInicio);Mejores Prácticas
Section titled “Mejores Prácticas”1. Siempre Valida el Campo success
Section titled “1. Siempre Valida el Campo success”<?phpif (!isset($respuesta['success']) || !$respuesta['success']) { // Manejar error throw new Exception($respuesta['error']['message'] ?? 'Error desconocido');}2. Implementa Timeouts Apropiados
Section titled “2. Implementa Timeouts Apropiados”<?php// Para 1-100 perfiles: 15 segundos// Para 101-500 perfiles: 30 segundos// Para 501-1000 perfiles: 45 segundos
$cantidadPerfiles = count($requestData['perfiles']);
if ($cantidadPerfiles <= 100) { $timeout = 15;} elseif ($cantidadPerfiles <= 500) { $timeout = 30;} else { $timeout = 45;}
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);3. Monitorea el Rendimiento
Section titled “3. Monitorea el Rendimiento”<?php$tiempoRespuesta = $respuesta['metadata']['tiempoProcesamientoMs'];
if ($tiempoRespuesta > 5000) { // Más de 5 segundos, considerar optimización error_log("Petición lenta: {$tiempoRespuesta}ms para " . count($requestData['perfiles']) . " perfiles");}4. Cachea Resultados Estables
Section titled “4. Cachea Resultados Estables”Si calculas los mismos escenarios repetidamente, usa caché:
<?php$cacheKey = md5(json_encode($requestData));$cached = redis_get($cacheKey);
if ($cached !== null) { return json_decode($cached, true);}
$respuesta = calcularNomina($requestData);
// Cachear por 1 horaredis_setex($cacheKey, 3600, json_encode($respuesta));
return $respuesta;Soporte y Contacto
Section titled “Soporte y Contacto”Si encuentras problemas no documentados o necesitas ayuda adicional:
📧 Email de soporte: hola@dataemunah.com
🔧 Líder técnico: Consulta con tu líder técnico asignado
📚 Documentación: Revisa siempre la última versión de esta documentación
Changelog de la API
Section titled “Changelog de la API”v1.1 (Enero 2025)
Section titled “v1.1 (Enero 2025)”- Agregado parámetro
porcentajeNovedadespara escenario promedio - Nuevas estadísticas de novedades en la respuesta
- Mejorado cálculo de retención en la fuente
- Campo
notainformativa según escenario - Contador de empleados con novedades por proyecto
v1.0 (Enero 2025)
Section titled “v1.0 (Enero 2025)”- Lanzamiento inicial
- Soporte para 3 escenarios (mejor, peor, promedio)
- Cálculo por empleado y por proyecto
- Soporte completo de novedades laborales
- Porcentajes personalizables
¡Eso es todo! Ya tienes toda la información necesaria para integrar el endpoint de simulación de nómina en tus sistemas.