Validar archivo de proyectos
curl --request POST \
--url https://api.dev.impactu.colav.co/submit/project \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://api.dev.impactu.colav.co/submit/project"
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/project', 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/project",
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/project"
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/project")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.impactu.colav.co/submit/project")
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{
"data": {
"schema_version": "1.0",
"valid": true,
"total_rows": 12,
"total_projects": 10,
"errors_count": 0,
"warnings_count": 1,
"issues_truncated": false,
"issues": [
{
"severity": "warning",
"code": "recommended_missing",
"message": "Se recomienda diligenciar 'resumen'.",
"row": 3,
"column": "resumen",
"value": null
}
]
}
}
{
"data": {
"schema_version": "1.0",
"valid": false,
"total_rows": 5,
"total_projects": 5,
"errors_count": 2,
"warnings_count": 0,
"issues_truncated": false,
"issues": [
{
"severity": "error",
"code": "required",
"message": "El campo 'titulo' es obligatorio.",
"row": 2,
"column": "titulo",
"value": null
},
{
"severity": "error",
"code": "invalid_money",
"message": "Debe contener solo números, sin signos de moneda ni separadores de miles (ej. 50000000).",
"row": 4,
"column": "valor_total",
"value": "$ 5.000.000"
}
]
}
}
{
"data": {
"schema_version": "1.0",
"valid": false,
"total_rows": 5,
"total_projects": null,
"errors_count": 1,
"warnings_count": 0,
"issues_truncated": false,
"issues": [
{
"severity": "error",
"code": "missing_column",
"message": "Falta la columna obligatoria 'codigo_proyecto'.",
"row": null,
"column": "codigo_proyecto",
"value": null
}
]
}
}
{"error": "Debe enviar el archivo en el campo 'file' (multipart/form-data)."}
{
"error": "Extensión '.xls' no soportada. Formatos admitidos: .xlsx. Si el archivo es .xls, ábralo en Excel y guárdelo como .xlsx."
}
{"msg": "Token inválido o expirado", "success": false}
{"error": "El archivo supera el tamaño máximo de 10 MB."}
Submit
Validar archivo de proyectos
Valida un Excel de proyectos institucionales (formato v1.0) y devuelve un reporte detallado de errores y advertencias.
POST
/
submit
/
project
Validar archivo de proyectos
curl --request POST \
--url https://api.dev.impactu.colav.co/submit/project \
--header 'Content-Type: application/json' \
--data '{}'import requests
url = "https://api.dev.impactu.colav.co/submit/project"
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/project', 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/project",
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/project"
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/project")
.header("Content-Type", "application/json")
.body("{}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.dev.impactu.colav.co/submit/project")
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{
"data": {
"schema_version": "1.0",
"valid": true,
"total_rows": 12,
"total_projects": 10,
"errors_count": 0,
"warnings_count": 1,
"issues_truncated": false,
"issues": [
{
"severity": "warning",
"code": "recommended_missing",
"message": "Se recomienda diligenciar 'resumen'.",
"row": 3,
"column": "resumen",
"value": null
}
]
}
}
{
"data": {
"schema_version": "1.0",
"valid": false,
"total_rows": 5,
"total_projects": 5,
"errors_count": 2,
"warnings_count": 0,
"issues_truncated": false,
"issues": [
{
"severity": "error",
"code": "required",
"message": "El campo 'titulo' es obligatorio.",
"row": 2,
"column": "titulo",
"value": null
},
{
"severity": "error",
"code": "invalid_money",
"message": "Debe contener solo números, sin signos de moneda ni separadores de miles (ej. 50000000).",
"row": 4,
"column": "valor_total",
"value": "$ 5.000.000"
}
]
}
}
{
"data": {
"schema_version": "1.0",
"valid": false,
"total_rows": 5,
"total_projects": null,
"errors_count": 1,
"warnings_count": 0,
"issues_truncated": false,
"issues": [
{
"severity": "error",
"code": "missing_column",
"message": "Falta la columna obligatoria 'codigo_proyecto'.",
"row": null,
"column": "codigo_proyecto",
"value": null
}
]
}
}
{"error": "Debe enviar el archivo en el campo 'file' (multipart/form-data)."}
{
"error": "Extensión '.xls' no soportada. Formatos admitidos: .xlsx. Si el archivo es .xls, ábralo en Excel y guárdelo como .xlsx."
}
{"msg": "Token inválido o expirado", "success": false}
{"error": "El archivo supera el tamaño máximo de 10 MB."}
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. Este endpoint solo valida: no guarda el archivo.Cuerpo de la solicitud
file
requerido
Archivo Excel
.xlsx con los proyectos institucionales. Un .xls se rechaza
con un mensaje que pide guardarlo como .xlsx. Solo se lee la primera hoja,
con una fila por proyecto. Si un proyecto tiene varios investigadores o
fuentes de financiación, se separan con ; en la misma celda, o se repite la
fila con el mismo codigo_proyecto.Límites
| Límite | Valor | Respuesta |
|---|---|---|
| Tamaño del archivo | 10 MB | 413, sin leer el archivo |
Contenido descomprimido (un .xlsx es un zip) | 100 MB | 400 |
| Filas con datos | 20 000 | 400 |
Problemas listados en issues | 500 | 200, con issues_truncated: true y los conteos completos |
400 con el motivo.
Formato del archivo
La primera fila lleva los encabezados. Se reconocen aunque cambien las mayúsculas, las tildes o los espacios, o lleven el* con el que el documento del formato marca los obligatorios: Código Proyecto* es codigo_proyecto. Una columna que no es del formato se ignora con una advertencia, y una columna repetida es un error.
| Columna | Obligatoria | Descripción |
|---|---|---|
codigo_proyecto | ✅ | Identificador único del proyecto. No debe cambiar en futuras actualizaciones. |
titulo | ✅ | Nombre completo del proyecto. |
fecha_inicio | ✅ | Fecha de inicio: celda con formato de fecha, o texto DD/MM/YYYY. |
investigador_principal_id | ✅ | Número de documento del investigador principal, sin espacios ni separadores. Debe coincidir con el formato de Staff. |
resumen | Recomendada | Resumen o abstract del proyecto. Si falta la columna o el valor, se reporta una advertencia. |
objetivos | — | Objetivos generales o específicos. |
tipo_proyecto | — | Uno de: investigación, extensión, regalías, innovación, consultoría. |
estado | — | Uno de: propuesta, en ejecución, finalizado, suspendido. |
fecha_fin | — | Fecha de finalización, con el mismo formato que fecha_inicio. |
investigador_principal_institucion_id | — | Código ROR de la institución del investigador principal: 03bp5hc83 o https://ror.org/03bp5hc83. |
investigadores_ids | — | Documentos de los coinvestigadores, separados por ;. |
valor_total | — | Presupuesto total en COP: número entero, sin signos de moneda ni separadores de miles (ej. 50000000). |
valor_especie_total | — | Presupuesto en especie en COP, con el mismo formato. |
valor_fresco_total | — | Presupuesto en recursos frescos en COP, con el mismo formato. |
fuente_financiacion_ids | — | Códigos ROR o IDs de Wikidata (Q12345) de las entidades financiadoras, separados por ;. |
instituciones_participantes_ids | — | Códigos ROR o IDs de Wikidata de las instituciones aliadas, separados por ;. |
codigo_minciencias | — | Código del proyecto en MinCiencias / CvLAC / GrupLAC. |
codigo_bpin | — | Código BPIN para proyectos de regalías o inversión pública. |
convocatoria | — | Nombre o código de la convocatoria. |
regiones_ejecucion_id | — | Códigos GeoNames de departamentos o regiones (el número o su URL), separados por ;. |
unidad_academica_ids | — | Códigos de las unidades académicas responsables, separados por ;. Deben coincidir con el formato de Staff. |
grupos_id | — | Códigos Scienti de los grupos de investigación, separados por ;. |
productos_asociados_ids | — | Identificadores de productos derivados (DOI, Handle DSpace, código MinCiencias, OpenAlex ID), separados por ;. |
Reglas entre campos
| Regla | Severidad |
|---|---|
fecha_fin anterior a fecha_inicio | Error |
Proyecto finalizado sin fecha_fin (si la columna existe) | Advertencia |
Proyecto de regalías sin codigo_bpin | Advertencia |
El investigador principal también está en investigadores_ids | Advertencia |
valor_total distinto de valor_especie_total + valor_fresco_total (si los tres tienen valor) | Advertencia |
| Un valor repetido en una columna de lista | Advertencia |
Respuesta
object
Reporte de validación.
Mostrar propiedades
Mostrar propiedades
string
Versión del esquema de validación utilizado.
boolean
true si el archivo no tiene errores (puede tener advertencias).integer
Número de filas con datos.
integer | null
Número de proyectos únicos (por
codigo_proyecto). null si hay errores
de estructura, como una columna obligatoria faltante: en ese caso las
filas no se validan.integer
Número de errores encontrados.
integer
Número de advertencias encontradas.
boolean
true si hay más de 500 problemas: issues lista los primeros 500 y los
conteos incluyen todos.object[]
Lista de problemas, ordenados por fila (primero los del archivo, sin fila)
y luego por columna.
Mostrar propiedades de cada problema
Mostrar propiedades de cada problema
string
error o warning.string
Código del tipo de problema. Por ejemplo,
missing_column,
unknown_column, required, recommended_missing, invalid_date,
invalid_money, invalid_enum, invalid_date_range o
conflicting_values.string
Descripción del problema.
integer | null
Número de la fila en Excel.
null para los problemas del archivo
(columnas faltantes, hojas de más).string | null
Nombre de la columna afectada, si aplica.
string | null
Valor que causó el problema, si aplica.
{
"data": {
"schema_version": "1.0",
"valid": true,
"total_rows": 12,
"total_projects": 10,
"errors_count": 0,
"warnings_count": 1,
"issues_truncated": false,
"issues": [
{
"severity": "warning",
"code": "recommended_missing",
"message": "Se recomienda diligenciar 'resumen'.",
"row": 3,
"column": "resumen",
"value": null
}
]
}
}
{
"data": {
"schema_version": "1.0",
"valid": false,
"total_rows": 5,
"total_projects": 5,
"errors_count": 2,
"warnings_count": 0,
"issues_truncated": false,
"issues": [
{
"severity": "error",
"code": "required",
"message": "El campo 'titulo' es obligatorio.",
"row": 2,
"column": "titulo",
"value": null
},
{
"severity": "error",
"code": "invalid_money",
"message": "Debe contener solo números, sin signos de moneda ni separadores de miles (ej. 50000000).",
"row": 4,
"column": "valor_total",
"value": "$ 5.000.000"
}
]
}
}
{
"data": {
"schema_version": "1.0",
"valid": false,
"total_rows": 5,
"total_projects": null,
"errors_count": 1,
"warnings_count": 0,
"issues_truncated": false,
"issues": [
{
"severity": "error",
"code": "missing_column",
"message": "Falta la columna obligatoria 'codigo_proyecto'.",
"row": null,
"column": "codigo_proyecto",
"value": null
}
]
}
}
{"error": "Debe enviar el archivo en el campo 'file' (multipart/form-data)."}
{
"error": "Extensión '.xls' no soportada. Formatos admitidos: .xlsx. Si el archivo es .xls, ábralo en Excel y guárdelo como .xlsx."
}
{"msg": "Token inválido o expirado", "success": false}
{"error": "El archivo supera el tamaño máximo de 10 MB."}