Enviar archivo de Staff
curl --request POST \
--url https://api.dev.impactu.colav.co/submit/staff \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://api.dev.impactu.colav.co/submit/staff"
payload = {}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({})
};
fetch('https://api.dev.impactu.colav.co/submit/staff', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.dev.impactu.colav.co/submit/staff",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.dev.impactu.colav.co/submit/staff"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.dev.impactu.colav.co/submit/staff")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.impactu.colav.co/submit/staff")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body{
"success": true,
"errors": 0,
"warnings": 3,
"duplicates": 0,
"normalized_unique_values_count": 2,
"pdf_base64": "JVBERi0xLjQKJ...",
"file_msg": "Archivo staff_example-institution-id_2026-10-15_14:32.xlsx guardado correctamente."
}
{ "success": false, "msg": "Archivo requerido" }
{
"success": false,
"errors": 2,
"warnings": 1,
"duplicates": 1,
"normalized_unique_values_count": 0,
"pdf_base64": "JVBERi0xLjQKJ..."
}
{ "success": false, "msg": "El archivo cargado está vacío. Verifique que contenga información." }
{"msg": "Token inválido o expirado", "success": false}
{
"success": false,
"errors": 1,
"duplicates": 0,
"msg": "El archivo enviado no cumple con el formato requerido de columnas",
"details": ["Columnas faltantes: orcid, cvlac, scholar"],
"pdf_base64": "JVBERi0xLjQKJ..."
}
Submit
Enviar archivo de Staff
Sube un Excel de talento humano (.xlsx), lo valida contra las reglas de negocio y devuelve un reporte de calidad en PDF.
POST
/
submit
/
staff
Enviar archivo de Staff
curl --request POST \
--url https://api.dev.impactu.colav.co/submit/staff \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://api.dev.impactu.colav.co/submit/staff"
payload = {}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({})
};
fetch('https://api.dev.impactu.colav.co/submit/staff', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.dev.impactu.colav.co/submit/staff",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.dev.impactu.colav.co/submit/staff"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.dev.impactu.colav.co/submit/staff")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.impactu.colav.co/submit/staff")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body{
"success": true,
"errors": 0,
"warnings": 3,
"duplicates": 0,
"normalized_unique_values_count": 2,
"pdf_base64": "JVBERi0xLjQKJ...",
"file_msg": "Archivo staff_example-institution-id_2026-10-15_14:32.xlsx guardado correctamente."
}
{ "success": false, "msg": "Archivo requerido" }
{
"success": false,
"errors": 2,
"warnings": 1,
"duplicates": 1,
"normalized_unique_values_count": 0,
"pdf_base64": "JVBERi0xLjQKJ..."
}
{ "success": false, "msg": "El archivo cargado está vacío. Verifique que contenga información." }
{"msg": "Token inválido o expirado", "success": false}
{
"success": false,
"errors": 1,
"duplicates": 0,
"msg": "El archivo enviado no cumple con el formato requerido de columnas",
"details": ["Columnas faltantes: orcid, cvlac, scholar"],
"pdf_base64": "JVBERi0xLjQKJ..."
}
Requiere sesión activa. El JWT se lee desde la cookie
access_token_cookie
y la solicitud debe llevar el encabezado X-CSRF-TOKEN. Se envía como
multipart/form-data.ORIGINAL_, y el normalizado. Si Drive falla, se guardan en el almacenamiento local del servidor.
Cuerpo de la solicitud
file
requerido
Archivo Excel
.xlsx con los datos de talento humano. Debe contener una fila
por persona vinculada. Otro formato, un archivo que no se puede leer o un
archivo sin datos responden 400.Formato del archivo
Todas las columnas de la tabla deben estar en el archivo, salvonombre_completo, que es opcional. Si falta alguna o hay una columna sin nombre con datos, responde 422. Los encabezados se reconocen sin importar mayúsculas, tildes ni espacios alrededor, y las columnas que no están en la tabla se ignoran.
| Columna | Valor obligatorio | Descripción |
|---|---|---|
tipo_documento | ✅ | Tipo de documento de identidad. |
identificacion | ✅ | Número de documento. |
primer_apellido | ✅* | Primer apellido. Requerido si no se envía nombre_completo. |
segundo_apellido | — | Segundo apellido. Puede estar vacío sin penalización. |
nombres | ✅* | Nombres. Requerido si no se envía nombre_completo. |
nombre_completo | — | Nombre completo. Si la columna está presente, reemplaza primer_apellido, segundo_apellido y nombres, que dejan de ser obligatorias. No puede contener números. |
orcid | — | ORCID. Se normaliza a https://orcid.org/0000-0000-0000-0000. |
cvlac | — | CvLAC. Se guarda el código, aunque venga la URL con cod_rh. |
scholar | — | Google Scholar. Se normaliza a https://scholar.google.com/citations?user=.... |
nivel_academico | — | Valor de catálogo. Fuera de catálogo → normalizado a desconocido. |
tipo_contrato | — | Valor de catálogo. Fuera de catálogo → normalizado a desconocido. |
jornada_laboral | — | Valor de catálogo. Fuera de catálogo → normalizado a desconocido. |
categoria_laboral | — | Valor de catálogo. Fuera de catálogo → normalizado a desconocido. |
sexo | — | Valor de catálogo. Fuera de catálogo → normalizado a desconocido. |
fecha_nacimiento | — | Fecha en formato DD/MM/YYYY. |
fecha_inicial_vinculacion | — | Fecha en formato DD/MM/YYYY. |
fecha_final_vinculacion | — | Fecha en formato DD/MM/YYYY. |
codigo_unidad_academica | ✅ | Código único de la unidad académica. |
unidad_academica | ✅ | Nombre de la unidad académica. |
codigo_subunidad_academica | — | Puede estar vacío sin penalización. |
subunidad_academica | — | Puede estar vacío sin penalización. |
Los campos de catálogo (
sexo, nivel_academico, etc.) no generan error si
su valor no pertenece al catálogo, pero se normalizan a desconocido y se
reportan como advertencia. Las fechas con alias de “sin información” (n/a,
no aplica, etc.) también se normalizan a desconocido.Respuesta
boolean
true si el archivo no tiene errores. Con errores, la respuesta es 400 con
el mismo reporte.integer
Número de errores encontrados en el archivo.
integer
Número de advertencias.
integer
Número de registros duplicados detectados.
integer
Número de valores distintos que se normalizaron (por ejemplo,
M → hombre).string | null
Reporte de calidad en formato PDF codificado en Base64. Incluye el resumen
de errores, advertencias y normalizaciones aplicadas.
null si no hay nada
que reportar.string
Solo con
200: resultado del guardado del archivo normalizado.string
Solo en los errores: motivo del rechazo. Si faltan columnas o hay columnas
sin nombre, la respuesta es
422 y details lista cada problema. Si el
archivo no se puede guardar, la respuesta es 500.{
"success": true,
"errors": 0,
"warnings": 3,
"duplicates": 0,
"normalized_unique_values_count": 2,
"pdf_base64": "JVBERi0xLjQKJ...",
"file_msg": "Archivo staff_example-institution-id_2026-10-15_14:32.xlsx guardado correctamente."
}
{ "success": false, "msg": "Archivo requerido" }
{
"success": false,
"errors": 2,
"warnings": 1,
"duplicates": 1,
"normalized_unique_values_count": 0,
"pdf_base64": "JVBERi0xLjQKJ..."
}
{ "success": false, "msg": "El archivo cargado está vacío. Verifique que contenga información." }
{"msg": "Token inválido o expirado", "success": false}
{
"success": false,
"errors": 1,
"duplicates": 0,
"msg": "El archivo enviado no cumple con el formato requerido de columnas",
"details": ["Columnas faltantes: orcid, cvlac, scholar"],
"pdf_base64": "JVBERi0xLjQKJ..."
}