Skip to content

Respuestas y Manejo de Errores

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"
}
}

CampoTipoDescripción
successbooleanIndica si la operación fue exitosa
statusCodenumberCódigo HTTP de la respuesta
messagestringMensaje descriptivo del resultado
dataobjectDatos del cálculo de nómina
metadataobjectInformación sobre la petición
CampoTipoDescripción
periodoobjectPeriodo calculado (mes y año)
escenariostringEscenario utilizado (mejor/peor/promedio)
nivelCalculostringNivel de cálculo (empleado/proyecto)
porcentajeNovedadesnumberPorcentaje de novedades configurado
resultadosobjectResultados detallados del cálculo
notastringNota informativa sobre el escenario
CampoTipoDescripción
nivelCalculostringNivel utilizado
cantidadEmpleadosProcesadosnumberTotal de empleados calculados
estadisticasNovedadesobjectEstadísticas sobre novedades aplicadas
detalleEmpleadosarrayArray con el detalle de cada empleado
totalesGeneralesobjectTotales consolidados de todos los empleados
totalesPorProyectoarray|nullTotales por proyecto (solo si nivelCalculo: "proyecto")

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": { ... }
}
"devengos": {
"salarioBasico": 3000000,
"auxilioTransporte": 200000,
"horasExtras": 234567.50,
"recargos": 125000,
"bonificaciones": 500000,
"otrosIngresos": 0,
"baseIbc": 3000000,
"totalDevengos": 4059567.50
}
CampoDescripción
salarioBasicoSalario base proporcional a días trabajados
auxilioTransporteAuxilio de transporte si aplica
horasExtrasTotal pagado por horas extras
recargosTotal pagado por recargos nocturnos/festivos
bonificacionesTotal de bonificaciones
baseIbcBase para calcular IBC (70% si es salario integral)
totalDevengosSuma de todos los devengos
"deducciones": {
"salud": 120000,
"pension": 120000,
"totalDeducciones": 240000,
"retencionFuente": 45000,
"detalleRetencion": {
"retencion": 45000,
"porcentajeAplicado": 4,
"baseGravable": 3739567.50,
"baseEnUvt": 79.45
}
}
CampoDescripción
saludAporte empleado a salud (4%)
pensionAporte empleado a pensión (4%)
retencionFuenteRetención en la fuente según tabla UVT
totalDeduccionesSuma total de deducciones
detalleRetencionDetalle del cálculo de retención
"aportesEmpleador": {
"salud": 255000,
"pension": 360000,
"arl": 15660,
"sena": 60000,
"icbf": 90000,
"cajaCompensacion": 120000,
"totalAportesEmpleador": 900660
}
CampoDescripciónPorcentaje
saludAporte empleador a salud8.5%
pensionAporte empleador a pensión12%
arlSeguro de riesgos laborales0.522% - 6.96%
senaAporte al SENA2%
icbfAporte al ICBF3%
cajaCompensacionAporte a caja de compensación4%
totalAportesEmpleadorSuma de todos los aportes~28-30%
"provisiones": {
"cesantias": 266560,
"interesesCesantias": 2665.60,
"primaServicios": 266560,
"vacaciones": 125000,
"totalProvisiones": 660785.60
}
CampoDescripciónPorcentaje
cesantiasProvisión mensual de cesantías8.33%
interesesCesantiasIntereses sobre cesantías1% mensual
primaServiciosProvisión de prima de servicios8.33%
vacacionesProvisión de vacaciones4.17%
totalProvisionesSuma de todas las provisiones~22%

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
}
}
"totales": {
"nominaNeta": 3819567.50,
"costoEmpleador": 900660,
"totalProvisiones": 660785.60,
"costoTotalRecursoHumano": 5381013.10
}
CampoDescripciónFórmula
nominaNetaValor a pagar al empleadoDevengos - Deducciones
costoEmpleadorAportes patronalesSuma de aportes
totalProvisionesProvisiones laboralesSuma de provisiones
costoTotalRecursoHumanoCosto total mensualNómina + Aportes + Provisiones

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
}
}
CampoDescripción
porcentajeConfiguradoPorcentaje enviado en el request
porcentajeRealPorcentaje efectivo aplicado
empleadosConNovedadesCantidad de empleados que tienen alguna novedad
tiposNovedadesAplicadasContador de cada tipo de novedad aplicada

"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

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": { ... }
}
]

Todas las respuestas de error siguen esta estructura:

{
"success": false,
"statusCode": 400,
"error": {
"message": "Errores de validación en los datos enviados",
"details": [ ... ]
}
}

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

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 Bearer en 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 timestamp de la petición
  • Reportar el error con los datos del request (sin el API Key)

CódigoTipoSignificadoAcción
200ÉxitoCálculo realizado correctamenteProcesar datos
400ErrorDatos inválidos o faltantesRevisar validaciones
403ErrorAPI Key inválidoVerificar autenticación
500ErrorError interno del servidorReintentar o contactar soporte

<?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";
}
}
}
}
<?php
try {
$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";
}
}
<?php
function 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);

<?php
if (!isset($respuesta['success']) || !$respuesta['success']) {
// Manejar error
throw new Exception($respuesta['error']['message'] ?? 'Error desconocido');
}
<?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);
<?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");
}

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 hora
redis_setex($cacheKey, 3600, json_encode($respuesta));
return $respuesta;

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


  • Agregado parámetro porcentajeNovedades para escenario promedio
  • Nuevas estadísticas de novedades en la respuesta
  • Mejorado cálculo de retención en la fuente
  • Campo nota informativa según escenario
  • Contador de empleados con novedades por proyecto
  • 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.