API Reference v2.5.0

Documentación de la API de Wardian

Integra facturación electrónica, inventarios y más. Todas las peticiones se autentican con una API Key.

  • Producción: https://wardian.com.co/dist/api/
  • Sandbox / pruebas: https://sandbox.wardian.com.co/dist/api/
  • Base URL (este entorno): //wardian.com.co/dist/api/
  • Formato: JSON
  • Autenticación: header X-API-Key (la key es distinta por entorno)

Autenticación

Envía tu API Key en el header X-API-Key en cada petición. Si falta o es inválida, la API responde 401 Unauthorized.

X-API-Key: wrd_tu_api_key_aqui

1 Generar tu API Key

En tu panel, ve a Software → Documentación → API Keys y presiona Generar API Key. Cópiala y guárdala; solo se muestra completa al crearla.

Ir a generar mi API Key

2 Primer request

Toda petición lleva el header X-API-Key. Para POST, envía el cuerpo en JSON con Content-Type: application/json. Las respuestas son JSON.

GET (listar)
const res = await fetch("//wardian.com.co/dist/api/products/data_list.php", {
  headers: { "X-API-Key": "wrd_tu_api_key_aqui" }
});
const data = await res.json();
POST (crear)
const res = await fetch("//wardian.com.co/dist/api/third/create.php", {
  method: "POST",
  headers: {
    "X-API-Key": "wrd_tu_api_key_aqui",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ /* campos del endpoint */ })
});
const data = await res.json();

Códigos de error

CódigoSignificado
200OK
400Petición inválida (faltan datos)
401API Key inválida o ausente
404Recurso no encontrado
500Error interno

Relaciones (catálogos)

Varios campos de productos, terceros y de la factura estándar no son valores libres: referencian catálogos (IDs de la DIAN / DANE). Obtén los valores válidos antes de crear.

GET//wardian.com.co/dist/api/references/tables.php?table={catalogo}

Catálogos globales (datos de referencia, públicos). Usa table=all para traerlos todos.

Campo que lo usatable=Descripción
unit_measure_idunidadesUnidades de medida DIAN
standard_code_idcodigos_estandarCódigos estándar de adopción
tribute_idtributosTributos (IVA, INC, etc.)
identification_document_iddocumentos_identidadTipos de documento (CC, NIT…)
municipalities_codemunicipiosMunicipios (DANE)
country_codepaisesPaíses
—responsabilidades_iva, regimen_iva, autorretenciones, bancosOtros catálogos disponibles
GET (público, sin API Key)
const res = await fetch("//wardian.com.co/dist/api/references/tables.php?table=unidades");
const data = await res.json();
Response
{
  "success": true,
  "data": [
    { "id": 70, "code": "94", "name": "unidad" }
  ]
}

Catálogos con endpoint propio (requieren X-API-Key)

CampoEndpointNotas
categoria_idapi/settings/categories/list.phpCategorías del usuario
numbering_range_idapi/numbering_ranges/list.phpResoluciones / rangos de numeración
payment_method_codeapi/pos/list_payment_methods.phpMétodos de pago DIAN
tax_rate—Valor directo: 0, 5, 8, 19
La factura estándar (api/invoice/generate.php) usa estas mismas relaciones en su customer (identification_document_id, tribute_id, municipalities_code) y en cada ítem (unit_measure_id, standard_code_id, tax_rate), más numbering_range_id y payment_method_code.

Productos

Campos como unit_measure_id, standard_code_id, tax_rate, categoria_id y tribute_id referencian catálogos. Consulta Relaciones / Catálogos para obtener los valores válidos. Son las mismas relaciones que usa la factura estándar.
GET//wardian.com.co/dist/api/products/data_list.php

Lista los productos del usuario dueño de la API Key. Acepta paginación: ?pagina=1&search=texto.

Response
{
  "success": true,
  "data": [
    { "id": 123, "code_reference": "SKU-001", "name": "Camiseta", "price": "50000.00", "tax_rate": "19.00" }
  ]
}
POST//wardian.com.co/dist/api/products/create.php

Crea un producto. Se envía como multipart/form-data (permite imágenes en files[]).

CampoDescripción
typereqTipo de producto
code_referencereqCódigo / referencia único
namereqNombre del producto
pricereqPrecio de venta
unit_measure_idreqUnidad de medida (id DIAN)
standard_code_idreqCódigo estándar (id)
tax_rate, cost_price, wholesale_priceopcImpuesto, costo, precio mayorista
categoria_id, details, requiere_stock, stockopcCategoría, detalles, control de stock
contenido_presentacion, lote, fecha_vencimiento, cums_codigoopcPresentación, lote, vencimiento, CUMS
files[]opcImágenes del producto
Request (JSON)
{
  "type": "Producto",
  "code_reference": "SKU-001",
  "name": "Camiseta",
  "price": "50000",
  "unit_measure_id": "70",
  "standard_code_id": "1",
  "tax_rate": "19",
  "categoria_id": "5"
}
Response
{
  "status": "success",
  "message": "Producto registrado exitosamente",
  "data": { "producto_id": 123, "code_reference": "SKU-001", "name": "Camiseta" }
}
Para adjuntar imágenes usa multipart/form-data (campo files[]) en vez de JSON.
POST//wardian.com.co/dist/api/products/edit.php

Edita un producto existente. Mismos campos que crear, más:

CampoDescripción
idreqID del producto a editar
Request (JSON)
{ "id": "123", "name": "Camiseta Premium", "price": "60000" }
Response
{ "status": "success", "message": "Producto actualizado exitosamente" }

Terceros

GET//wardian.com.co/dist/api/third/data_list.php

Lista los terceros (clientes/proveedores) del usuario. Acepta ?pagina=1&search=texto.

Response
{
  "success": true,
  "data": [
    { "id": 45, "identification": "901234567", "company_name": "ACME SAS", "email": "info@acme.co" }
  ]
}
POST//wardian.com.co/dist/api/third/create.php

Crea un tercero.

CampoDescripción
typeSelectorreqTipo (Persona / Empresa)
identification_document_idreqTipo de documento (id DIAN)
identificationreqNúmero de identificación
address, email, phonereqDirección, correo, teléfono
tribute_idreqResponsabilidad tributaria (id)
dv, company_name, name, last_name, trade_nameopcDV, razón social, nombres, nombre comercial
ciiu, country_code, municipalities_codeopcCIIU, país, municipio (DANE)
Request (JSON)
{
  "typeSelector": "Empresa",
  "identification_document_id": "31",
  "identification": "901234567",
  "email": "info@acme.co",
  "phone": "3001234567",
  "address": "Calle 1 # 2-3",
  "tribute_id": "21"
}
Response
{
  "status": "success",
  "message": "Tercero creado exitosamente",
  "data": { "tercero_id": 45, "name": "ACME SAS", "identification": "901234567", "email": "info@acme.co", "phone": "3001234567" }
}
POST//wardian.com.co/dist/api/third/edit.php

Edita un tercero. Mismos campos que crear, más:

CampoDescripción
tercero_idreqID del tercero a editar
Request (JSON)
{ "tercero_id": "45", "email": "nuevo@acme.co", "phone": "3009999999" }
Response
{ "status": "success", "message": "Tercero actualizado exitosamente" }

Categorías

GET//wardian.com.co/dist/api/settings/categories/list.php

Lista las categorías de productos del usuario.

Response
{
  "success": true,
  "data": [
    { "id": 5, "nombre": "Ropa", "descripcion": "Prendas de vestir", "color": "#CFFF00", "activo": 1 }
  ]
}
POST//wardian.com.co/dist/api/settings/categories/create.php

Crea una categoría de productos.

CampoDescripción
nombrereqNombre de la categoría
descripcion, color, icono, activoopcDescripción, color, ícono, estado
configuracion_puc_activa, cuenta_ingreso_venta, cuenta_costo_venta, cuenta_inventario, cuenta_compra…opcCuentas contables PUC (si se activa la config)
Request (JSON)
{ "nombre": "Ropa", "descripcion": "Prendas de vestir", "color": "#CFFF00", "activo": "1" }
Response
{
  "status": "success",
  "message": "Categoría creada exitosamente",
  "data": { "categoria_id": 5, "nombre": "Ropa", "descripcion": "Prendas de vestir", "color": "#CFFF00" }
}
POST//wardian.com.co/dist/api/settings/categories/edit.php

Edita una categoría. Mismos campos que crear, más:

CampoDescripción
categoria_idreqID de la categoría a editar
Request (JSON)
{ "categoria_id": "5", "nombre": "Ropa y Calzado" }
Response
{ "status": "success", "message": "Categoría actualizada exitosamente" }

Crear factura estándar

Antes de facturar: crea/ten el tercero (obtienes su id) y los productos, y ten a mano el numbering_range_id (ver Catálogos). La factura se envía a la DIAN y responde con el número y el CUFE.
Requisito de la cuenta: tu cuenta (la dueña de la API Key) debe estar habilitada en la DIAN — con credenciales DIAN configuradas y un rango de numeración autorizado. Sin eso, la DIAN responde error. El entorno (pruebas/producción) lo determina la configuración DIAN de tu cuenta.
POST//wardian.com.co/dist/api/invoice/generate.php

Genera una factura electrónica de venta estándar (multipart/form-data). Responde JSON con el resultado DIAN.

Cabecera

CampoDescripción
customerreqID del tercero (de api/third)
numbering_range_idreqRango de numeración / resolución
payment_formreq1 Contado · 2 Crédito
payment_method_codereqMétodo de pago (catálogo)
observationopcNota / observación
payment_due_dateopcFecha de vencimiento (si crédito)
companyopcNombre del emisor (para el correo/PDF)
reference_code, cuenta_bancaria_id, cost_center_idopcReferencia, cuenta bancaria, centro de costo
currency_code, currency_value, currency_dateopcMoneda (por defecto COP)

Ítems — products[]

CampoDescripción
idreqID del producto (de api/products)
code_reference, namereqCódigo y nombre del ítem
quantity, pricereqCantidad y precio unitario
tax_ratereq% IVA: 0, 5, 8, 19
unit_measure_id, standard_code_id, tribute_idreqCatálogos (ver Relaciones)
discount_rate, is_excludedopc% descuento, excluido de IVA (0/1)
ret_fuente_rate, ret_iva_rate, ret_ica_rateopcRetenciones (si el cliente retiene)

Ejemplo

Request (JSON)
{
  "customer": "45",
  "numbering_range_id": "502",
  "payment_form": "1",
  "payment_due_date": "2026-07-02",
  "payment_method_code": "10",
  "observation": "Gracias por su compra",
  "products": [
    {
      "id": "123",
      "code_reference": "SKU-001",
      "name": "Camiseta",
      "quantity": "2",
      "price": "50000",
      "tax_rate": "19",
      "unit_measure_id": "70",
      "standard_code_id": "1",
      "tribute_id": "1",
      "discount_rate": "0",
      "is_excluded": "0"
    }
  ]
}
Response
{
  "status": "success",
  "message": "Factura creada exitosamente",
  "data": {
    "invoice_id": 47014,
    "bill_number": "SETP990000227",
    "cufe": "7b4a382a9a751cc88156a47f2eac24c7987353d1...",
    "code_reference": "INV6a46c29683f15fdc6"
  }
}
Importante: envía payment_due_date (una fecha, aun en contado) y en cada ítem discount_rate e is_excluded con valor (ej. 0). La DIAN rechaza campos vacíos/nulos con "Datos de la factura incompletos".

Crear factura por mandato

La facturación por mandato (operation_type 11) permite facturar por cuenta de terceros mandantes. Es idéntica a la factura estándar, pero cada ítem indica el mandante por el que se factura. La DIAN responde con el número y el CUFE.
Requisito de la cuenta: igual que la factura estándar — tu cuenta debe estar habilitada en la DIAN (credenciales configuradas y rango de numeración autorizado).
POST//wardian.com.co/dist/api/invoice/generate_mandate.php

Genera una factura electrónica de venta por mandato. Responde JSON con el resultado DIAN.

Cabecera

Mismos campos que la factura estándar (customer, numbering_range_id, payment_form, payment_method_code, observation, payment_due_date, company, moneda…). El operation_type se fija internamente en 11.

Ítems — products[]

Mismos campos que la factura estándar (id, code_reference, name, quantity, price, tax_rate, catálogos, retenciones…) más los datos del mandante por el que se factura ese ítem:

CampoDescripción
mandate_identificationreqIdentificación (NIT/CC) del tercero mandante. Si va vacío, el ítem se factura sin mandato.
mandate_identification_document_idreqTipo de documento del mandante (catálogo documentos_identidad)
mandate_dvopcDígito de verificación del mandante (si aplica)

Ejemplo

Request (JSON)
{
  "customer": "45",
  "numbering_range_id": "502",
  "payment_form": "1",
  "payment_due_date": "2026-07-02",
  "payment_method_code": "10",
  "observation": "Facturación por mandato",
  "products": [
    {
      "id": "123",
      "code_reference": "SKU-001",
      "name": "Canon de arrendamiento",
      "quantity": "1",
      "price": "1000000",
      "tax_rate": "0",
      "unit_measure_id": "70",
      "standard_code_id": "1",
      "tribute_id": "1",
      "discount_rate": "0",
      "is_excluded": "0",
      "mandate_identification": "901234567",
      "mandate_identification_document_id": "31",
      "mandate_dv": "8"
    }
  ]
}
Response
{
  "status": "success",
  "message": "Factura de mandato creada exitosamente",
  "data": {
    "invoice_id": 47021,
    "code_reference": "INV6a46c29683f15fdc6",
    "bill_number": "SETP990000228",
    "api_status": "Created",
    "cufe": "8c5b493b0b862dd99267b58a3fbd35d8098464e2...",
    "comprobante_id": 90312
  }
}
Importante: igual que la factura estándar, envía payment_due_date y en cada ítem discount_rate e is_excluded con valor. Para facturar por mandato, cada ítem debe incluir mandate_identification (y su mandate_identification_document_id) del mandante.

Crear tiquete POS

Emite un tiquete POS electrónico (documento Factura de Venta POS) a la DIAN. Mismo formato de request que la factura estándar; usa el rango de numeración POS de tu cuenta.
Requisito de la cuenta: tu cuenta debe estar habilitada en la DIAN y tener un rango activo de tipo Factura de Venta POS. Si no envías numbering_range_id, se detecta automáticamente el rango POS activo del titular.
POST//wardian.com.co/dist/api/invoice/generate_pos.php

Genera un tiquete POS electrónico. Responde JSON con el resultado DIAN.

Cabecera

CampoDescripción
customerreqID del tercero (de api/third)
payment_formreq1 Contado · 2 Crédito
payment_method_codeopcMétodo de pago DIAN (por defecto 10)
numbering_range_idopcRango POS. Si se omite, se auto-detecta el rango Factura de Venta POS activo
observationopcNota (por defecto "Tiquete POS Electrónico")
payment_due_date, municipality_id, tip_amount, seller_idopcVencimiento, municipio, propina, vendedor

Ítems — products[]

CampoDescripción
idreqID del producto (de api/products)
code_reference, namereqCódigo y nombre del ítem
quantity, price, discount_ratereqCantidad, precio unitario, % descuento
tax_ratereq% IVA: 0, 5, 8, 19
unit_measure_id, standard_code_id, tribute_idreqCatálogos (ver Relaciones)
is_excluded, requiere_stock, withholding_tax_rateopcExcluido de IVA (0/1), control de stock, retención

Ejemplo

Request (JSON)
{
  "customer": "8",
  "payment_form": "1",
  "payment_method_code": "10",
  "observation": "Venta POS",
  "products": [
    {
      "id": "72707",
      "code_reference": "ENVIO-SHOPIFY",
      "name": "Envío - Estándar",
      "quantity": "1",
      "price": "4000",
      "tax_rate": "0",
      "discount_rate": "0",
      "unit_measure_id": "70",
      "standard_code_id": "1",
      "tribute_id": "22",
      "is_excluded": "0",
      "requiere_stock": "0"
    }
  ]
}
Response
{
  "status": "success",
  "message": "Factura POS creada exitosamente",
  "data": {
    "invoice_id": 47016,
    "bill_number": "EPOS71",
    "cufe": "20764680cd9bd7b8d62c72442e012887c912cfc8...",
    "api_status": "Created",
    "dian_response": { "bill": { "number": "EPOS71", "cufe": "2076...", "total": 4000, "id": "347619492" } }
  }
}
El endpoint descuenta inventario de los ítems con requiere_stock=1 dentro de la transacción. Envía numbering_range_id explícito si manejas varios rangos POS por sucursal.

Crear factura RIPS (salud)

Emite una factura electrónica RIPS (sector salud — Registro Individual de Prestación de Servicios de Salud). La factura DIAN incluye los datos de salud (pacientes, servicios, CUPS) del paquete RIPS previamente validado en la plataforma.
Requisitos de la cuenta: habilitación DIAN (credenciales configuradas y rango de numeración autorizado) y un paquete RIPS validado (rips_id) creado desde el módulo de RIPS. Los datos clínicos (paciente/servicios) se toman de ese paquete, no se envían en el request.
POST//wardian.com.co/dist/api/invoice/generate_rips.php

Genera una factura electrónica RIPS. Responde JSON con el resultado DIAN.

Cabecera

CampoDescripción
rips_idreqID del paquete RIPS validado. De él se cargan pacientes y servicios de salud.
invoice_typereqDebe ser "rips"
customerreqID del tercero (pagador — normalmente la EPS/entidad)
numbering_range_idreqRango de numeración / resolución
payment_formreq1 Contado · 2 Crédito
payment_method_codereqMétodo de pago (catálogo)
payment_due_datereqFecha de vencimiento (una fecha, aun en contado)
companyreqNombre del emisor (requerido para la notificación por correo)
observationopcNota / observación
cuenta_bancaria_id, cost_center_id, monedaopcCuenta bancaria, centro de costo, divisa
Los ítems (products) y los datos clínicos (health_data) se derivan automáticamente del paquete rips_id. No es necesario enviarlos.

Ejemplo

Request (JSON)
{
  "rips_id": "30",
  "invoice_type": "rips",
  "customer": "8",
  "numbering_range_id": "100",
  "payment_form": "1",
  "payment_method_code": "10",
  "payment_due_date": "2026-07-02",
  "company": "NEXO CONTABLE",
  "observation": "Servicios de salud - paquete RIPS"
}
Response
{
  "status": "success",
  "message": "Factura creada exitosamente",
  "data": {
    "invoice_id": 47021,
    "code_reference": "INV6a46da95177b095bb",
    "bill_number": "SETP990000231",
    "api_status": "Created",
    "cufe": "ad3a694a73e1a211f585d259c329c160031dbe2f...",
    "dian_response": { "bill": { "number": "SETP990000231", "total": 7350000 } }
  }
}
Importante: (1) el rips_id debe estar en estado validado y pertenecer a tu cuenta; (2) la cuenta debe tener credenciales DIAN configuradas; (3) envía payment_due_date (la DIAN rechaza con "Datos de la factura incompletos" si falta) y company (requerido por la notificación por correo). Los ítems y datos clínicos salen del paquete RIPS.

El flujo FEV-RIPS

RIPS = Registro Individual de Prestación de Servicios de Salud. FEV-RIPS es la pareja del RIPS con la factura electrónica de venta (FEV) en salud.

El orden importa: primero existe el paquete RIPS en la plataforma (con su rips_id), después se factura. No hay un endpoint que reciba un RIPS crudo y lo facture en un solo paso.
1. POST api/rips_invoice/rips.php            → registra el RIPS  (paquete_id)
2. GET  api/rips_invoice/get_rips_for_invoice.php?rips_id=N   → arma datos de factura
3. POST api/invoice/generate_rips.php        → factura ante la DIAN (CUFE) + valida ante MinSalud (CUV)

CUV ≠ CUFE (el punto que más confunde)

  • CUFE — lo asigna la DIAN al timbrar la factura. Identifica la factura.
  • CUV — lo asigna MinSalud (MUV) al validar el RIPS. Identifica la validación.
El CUFE no es un campo del RIPS: viaja dentro del XML de la factura que acompaña al RIPS en la validación ante MinSalud. Dentro del RIPS, la factura se referencia por su número (campo raíz numFactura) — no por el CUFE.

Estados del paquete

estadoSignificado
pendienteValidó localmente; falta su factura. En producción los RIPS sin factura quedan aquí hasta facturarse.
validadoCUV real de MinSalud.
rechazadoEl MUV lo rechazó (ver mensaje_respuesta con resultadosValidacion).
errorFalló la llamada al validador.

Estructura del payload RIPS

Forma del payload que registras en Registrar paquete RIPS. Sigue la estructura oficial Res. 2275/2023. Es también lo que genera Wardian al facturar desde un evento clínico.

Raíz — sin factura vs. con factura

La raíz es excluyente: un paquete se reporta como nota sin factura o con factura, nunca ambos.

Sin factura (RS)
{
  "numDocumentoIdObligado": "900123456",
  "numFactura": null,
  "numNota": "00001234",
  "tipoNota": "RS",
  "usuarios": [ /* … */ ]
}
Con factura (tras la FEV)
{
  "numDocumentoIdObligado": "900123456",
  "numFactura": "SETP990000001",
  "numNota": null,
  "tipoNota": null,
  "usuarios": [ /* … */ ]
}
No armas el modo con factura a mano: se activa solo. Al pasar un cufe (o al facturar el paquete), la plataforma resuelve la FEV localmente y reescribe la raíz a numFactura = número de esa factura, con tipoNota/numNota = null. Para notas crédito/débito coexisten numFactura + tipoNota (NC/ND) + numNota.

Usuario

Cada entrada de usuarios[] es un paciente con sus servicios. Códigos de residencia según la guía oficial (país Colombia = 170).

{
  "consecutivo": 1,
  "tipoDocumentoIdentificacion": "CC",
  "numDocumentoIdentificacion": "1234567890",
  "fechaNacimiento": "1985-06-15",
  "codSexo": "M",
  "codPaisResidencia": "170",
  "codMunicipioResidencia": "11001",   // DANE — de pacientes.municipio_id (o el tercero)
  "codZonaTerritorialResidencia": "02", // 02 = urbana
  "incapacidad": "NO",                  // "SI" | "NO"
  "codPaisOrigen": "170",
  "tipoUsuario": "01",
  "servicios": { "consultas": [ … ], "procedimientos": [ … ] }
}
codMunicipioResidencia es null si el paciente no tiene municipio DANE cargado (columna pacientes.municipio_id, con respaldo al tercero enlazado). El validador oficial lo exige — cárgalo desde la ficha del paciente.

Servicios — consultas vs. procedimientos

Wardian clasifica cada servicio en su bloque oficial según el tipo de evento: Consulta/Control → consultas[]; el resto (incl. Procedimiento, Urgencia) → procedimientos[]. Difieren en algunos campos:

consultas[]
{
  "codPrestador": "800100123456",
  "fechaInicioAtencion": "2024-02-20 10:30:00",
  "numAutorizacion": "0000000",
  "codConsulta": "890201",
  "modalidadGrupoServicioTecSal": "01",
  "grupoServicios": "01",
  "codServicio": 890,
  "finalidadTecnologiaSalud": "11",
  "causaMotivoAtencion": "38",
  "codDiagnosticoPrincipal": "I10",
  "codDiagnosticoRelacionado1": null,
  "codDiagnosticoRelacionado2": null,
  "codDiagnosticoRelacionado3": null,
  "tipoDiagnosticoPrincipal": "02",
  "tipoDocumentoIdentificacion": "CC",
  "numDocumentoIdentificacion": "1234567890",
  "vrServicio": 50000,
  "conceptoRecaudo": "05",
  "valorPagoModerador": 0,
  "numFEVPagoModerador": null,
  "consecutivo": 1
}
procedimientos[]
{
  "codPrestador": "800100123456",
  "fechaInicioAtencion": "2024-02-20 14:00:00",
  "idMIPRES": null,
  "numAutorizacion": "0000000",
  "codProcedimiento": "880201",
  "modalidadGrupoServicioTecSal": "01",
  "grupoServicios": "01",
  "codServicio": 890,
  "viaIngresoServicioSalud": "01",
  "finalidadTecnologiaSalud": "11",
  "codDiagnosticoPrincipal": "I10",
  "codDiagnosticoRelacionado": null,
  "codComplicacion": null,
  "tipoDocumentoIdentificacion": "CC",
  "numDocumentoIdentificacion": "1234567890",
  "vrServicio": 500000,
  "conceptoRecaudo": "05",
  "valorPagoModerador": 0,
  "numFEVPagoModerador": null,
  "consecutivo": 1
}
Al pasar a con factura, la plataforma sella numFEVPagoModerador = número de la FEV en todo ítem con valorPagoModerador > 0, en cualquiera de los bloques.
Placeholders y brechas conocidas: causaMotivoAtencion (38) y tipoDiagnosticoPrincipal (02) son valores por defecto hasta que se capture el dato clínico real (sobreescribibles por cuenta vía rips_configuracion). Los bloques urgencias, hospitalizacion, medicamentos, otrosServicios y recienNacidos aún no se generan (requieren datos que el modelo clínico no almacena).
Nombre de campo (verificado vs. Anexo Técnico Res. 2275): el concepto de pago moderador es conceptoRecaudo (campos C18/P17), presente en consultas y procedimientos. El Anexo no define tipoPagoModerador — es un alias del mismo concepto. Como el MUV (nivel 1) valida con esquema estricto y rechaza claves desconocidas, se emite únicamente conceptoRecaudo.

Registrar paquete RIPS

Registra un paquete RIPS armado por tu sistema y lo envía al validador. Devuelve el paquete_id (= rips_id) que usarás para facturar. Aquí no se crea FEV.
POST//wardian.com.co/dist/api/rips_invoice/rips.php

Cuerpo

CampoDescripción
payloadreqEl JSON RIPS completo — ver Estructura del payload (numDocumentoIdObligado, usuarios[] con servicios.consultas/servicios.procedimientos, …).
usuario_id_ripsopcDueño del RIPS. Por defecto, quien envía. Un principal puede enviar por sus subusuarios.
cufeopcCUFE de una FEV ya emitida. Si se envía, el RIPS se convierte a modo con factura (la raíz numFactura toma el número de esa factura) y se valida así. El CUFE se resuelve localmente contra tus facturas; no se escribe dentro del RIPS.
Request (JSON)
{
  "usuario_id_rips": 29,
  "payload": {
    "numDocumentoIdObligado": "900123456",
    "numNota": "00001234",
    "tipoNota": "RS",
    "usuarios": [ { "...": "ver Estructura del payload" } ]
  }
}
Response
{
  "status": "ok",
  "cuv": "a1b2c3...",
  "paquete_id": 30,
  "usuario_id_rips": 29,
  "usuario_id_envio": 29
}
Un CUV que empieza con WARDIAN- es simulado (validador inalcanzable) y solo ocurre fuera de producción. La opción cufe requiere la estructura moderna usuarios[].

Datos para facturar

Arma, a partir de un paquete RIPS con CUV, los datos listos para crear la factura: ítems, datos de salud, rango de numeración sugerido y cliente (EPS) sugerido.
GET//wardian.com.co/dist/api/rips_invoice/get_rips_for_invoice.php?rips_id={id}

Solo devuelve paquetes con cuv no vacío.

Response (extracto)
{
  "status": "success",
  "data": {
    "rips_id": 30,
    "rips_cuv": "a1b2c3...",
    "rips_cufe": null,
    "numbering_range_id": 100,
    "invoice_type": "rips",
    "health_data": { "provider_code": "...", "patient": { "...": "" } },
    "items": [ { "code_references": "890201", "name": "Consulta", "price": 50000 } ]
  },
  "suggested_customer": { "id": 8, "name": "EPS ..." }
}
rips_cufe (tomado del payload_fev) es null hasta que el RIPS se factura. Pasa data a Crear factura RIPS.

Revalidar / con factura

Reenvía un paquete al validador. Útil para reemplazar un CUV simulado por uno real, o para promover un RIPS sin factura a con factura una vez emitida su FEV.
POST//wardian.com.co/dist/api/rips_invoice/regenerate.php

Cuerpo

CampoDescripción
rips_idreqID del paquete a revalidar.
cufeopcSi se envía, convierte el paquete a modo con factura (raíz numFactura = número de esa FEV, resuelta localmente) antes de reenviar. El payload convertido se persiste.
Request (JSON)
{ "rips_id": 30, "cufe": "ad3a694a73e1..." }
Response
{
  "status": "success",
  "message": "RIPS regenerado correctamente con CUV real",
  "cuv": "a1b2c3...",
  "estado": "validado",
  "is_simulated": false
}
404 si no existe una factura con ese CUFE en tu cuenta. Sin cufe, revalida el paquete tal cual (sin cambiar a con factura).

Requisitos antes de tu primer RIPS

Tres cosas tienen que existir en la cuenta antes de que Registrar paquete RIPS devuelva un CUV real. Si falta alguna, la llamada no falla: degrada en silencio, y eso es lo que más tiempo hace perder.

1. NIT de la cuenta

Sale de mi_cuenta.identification y alimenta el campo raíz numDocumentoIdObligado. Debe cumplir ^[0-9]{8,15}$. Sin NIT el paquete no se construye — es el único de los tres que da un error explícito.

2. Prestador

Al menos una fila de prestador, que alimenta codPrestador en cada servicio.

CampoDescripción
codigo_habilitacionCódigo de habilitación REPS. Opcional, pero solo se usa si tiene exactamente 12 caracteres.
numero_idRespaldo: si no hay habilitación de 12 caracteres, se usa el NIT/cédula rellenado con ceros a la izquierda hasta 12.
es_profesional1 para profesional independiente (persona), 0 para organización. La tabla mezcla ambos a propósito.
Trampa medida: un valor como 15-100562 tiene 9 caracteres, así que no se usa y el sistema cae al NIT sin avisar. Si esperas ver tu código de habilitación en el payload y ves ceros a la izquierda, es esto.

3. Credenciales SISPRO

Se leen de rips_endpoints ligadas al prestador: client_id = número de documento, client_secret = clave, más tipo_documento y el indicador reps (desmárcalo si usas código IPSnoREPS).

Sin credenciales no hay CUV real. Fuera de producción la plataforma devuelve una respuesta simulada (is_simulated: true) para que puedas integrar; en producción el paquete se queda en pendiente. Nunca des por bueno un CUV sin mirar is_simulated.

Catálogos de referencia y sus límites

El payload referencia varios catálogos oficiales. El validador comprueba los códigos contra ellos, así que conviene saber qué hay cargado de verdad — porque no está todo.

Dos niveles

  • Oficial — rips_cups, rips_cie10, rips_cums, rips_servicios y los catálogos de dominio. Globales, sin columna de tenant.
  • Propios del prestador — clinical_cups, clinical_cums, clinical_diagnosticos, clinical_eps. Los das de alta tú; el validador los acepta igual.

Al buscar códigos se unen los dos, y ante un duplicado gana la fila oficial.

La carga oficial está incompleta. Medido el 2026-09-02: rips_cie10 cubre solo las letras A E I J K M N R — no tiene el capítulo F (trastornos mentales) ni el Z. rips_cups cubre solo los capítulos 01–35 (quirúrgicos) — no tiene el capítulo 89, es decir, ningún código de consulta. Un código CUPS o CIE-10 perfectamente válido a nivel nacional puede ser rechazado simplemente porque aún no está cargado.

Qué hacer mientras tanto

Da de alta los códigos que uses como códigos propios. El validador los acepta con el mismo peso que los oficiales, y quedan disponibles en los desplegables. Es la vía soportada, no un truco: el respaldo existe justamente porque la carga oficial se sabe incompleta.

Los códigos Z00–Z99 están prohibidos como diagnóstico principal por norma, independientemente de que estén cargados. Si tu atención "no tiene enfermedad", igual necesitas un diagnóstico válido — no vale un código Z.

Rechazos del validador

Un estado de rechazado viene de MinSalud, no de la plataforma. El detalle llega en mensaje_respuesta, dentro del arreglo resultadosValidacion.

Causas frecuentes

CausaQué revisar
Código CUPS o CIE-10 inexistentePuede ser real pero no estar cargado — ver Catálogos y sus límites. Regístralo como código propio.
Diagnóstico principal ZProhibido por norma. Usa un diagnóstico distinto de Z00–Z99.
Bloque de servicios vacíoservicios debe traer al menos una consultas[] o un procedimientos[].
Servicio que no coincide con el REPSEl codServicio reportado debe corresponder a lo que el prestador tiene inscrito en el REPS. Es la causa de rechazo más común.
Raíz mal formadaSin factura y con factura son excluyentes: o numFactura, o numNota+tipoNota. Nunca ambos.

Endpoints retirados

api/rips_invoice/batch_create.php responde 501 a propósito. Antes era un stub que no creaba nada; ahora falla de forma explícita para no engañar a integraciones que aún lo llamen. Crea las facturas una a una.

Adjuntar XML de factura (venta, POS, RIPS y mandato)

Adjunta el XML de una factura ya existente en tu cuenta — sirve para cualquier factura electrónica: venta estándar, tiquete POS, RIPS (salud) y mandato. El adjunto se procesa en el momento (síncrono): se valida, se guarda y queda asociado a la factura. Re-adjuntar el mismo número reemplaza el XML anterior.
POST//wardian.com.co/dist/api/invoice/attach_xml.php

Acepta multipart/form-data (archivo) o JSON (base64). La API Key determina el usuario: solo puedes adjuntar sobre tus facturas.

Campos

CampoDescripción
bill_numberreqNúmero de la factura de venta (debe existir en tu cuenta)
xmlopc*Archivo XML (multipart). Máx 5 MB
xml_base64opc*El XML codificado en base64 (JSON o form). Máx 5 MB

* Envía uno de los dos. El XML debe ser de factura (raíz Invoice o AttachedDocument); si trae CUFE (cbc:UUID) debe coincidir con el de la factura — así no se puede adjuntar el XML de otra.

Ejemplo real

XML de ejemplo por tipo de factura — todos son documentos reales tal como se enviaron a la DIAN:

TipoFacturaDescarga
EstándarSETP990000122 XML
POSEPOS84 XML
RIPS (salud)FEPI13 XML
MandatoFEG240 XML
Fragmento del XML
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" …>
    <cbc:ID>FEPI13</cbc:ID>
    <cbc:UUID schemeID="1" schemeName="CUFE-SHA384">4fa0ee0c14522b25d3218f3ce42605c8d31ebc157a5e5f2c…</cbc:UUID>
    <cbc:IssueDate>2026-08-31</cbc:IssueDate>
    …
Request (JSON) — el xml_base64 es el archivo completo en base64
{
  "bill_number": "FEPI13",
  "xml_base64": "PEludm9pY2UgeG1sbnM9InVybjpvYXNpczpuYW1lczpzcGVjaWZpY2F0aW9uOnVibDpzY2hl…"
}
Respuesta
{
  "success": true,
  "message": "XML adjuntado a la factura FEPI13.",
  "data": {
    "factura_id": 663033,
    "bill_number": "FEPI13",
    "cufe": "4fa0ee0c14522b25d3218f3ce42605c8d31ebc157a5e5f2cb7688a7f778d0e4631b2fbd3a428d7457e7f2f2b0f598501",
    "bytes": 19740,
    "adjuntado_en": "2026-08-31 16:10:00"
  }
}

Errores

HTTPMotivo
401Falta o es inválida la X-API-Key
404La factura bill_number no existe en tu cuenta
409El CUFE del XML no coincide con el de la factura
413 / 422XML muy grande / mal formado o de otro tipo de documento

Crear documento soporte

El documento soporte se emite en compras a proveedores no obligados a facturar. Antes: ten el tercero/proveedor (su id), los ítems y el numbering_range_id del rango de documento soporte. Se envía a la DIAN y responde con el número y el CUDS.
Requisito de la cuenta: la cuenta dueña de la API Key debe estar habilitada en la DIAN, con credenciales y un rango de numeración de documento soporte autorizado.
POST//wardian.com.co/dist/api/document_support/generate.php

Genera un documento soporte electrónico. Acepta application/x-www-form-urlencoded o multipart/form-data. Responde JSON con el resultado DIAN. El documento soporte no lleva IVA (compra a no obligado).

Cabecera

CampoDescripción
customerreqID del proveedor (tercero, de api/third)
numbering_range_idreqRango de numeración de documento soporte
payment_formreq1 Contado · 2 Crédito
payment_method_codereqMétodo de pago (catálogo)
issue_dateopcFecha de emisión (por defecto hoy)
payment_due_dateopcFecha de vencimiento (si crédito)
observationopcNota / observación
cuenta_bancaria_id, cuenta_cxp_codigo, cost_center_idopcCuenta bancaria (pago), CxP (crédito), centro de costo
save_draftopc1 = guardar como Borrador (no se envía a la DIAN)

Ítems — products[]

CampoDescripción
code_reference, namereqCódigo y nombre del ítem
quantity, pricereqCantidad y precio unitario
discount_ratereq% descuento (ej. 0)
unit_measure_id, standard_code_idreqCatálogos (ver Relaciones)
ret_fuente_rate, ret_ica_rate, withholding_config_idopcRetenciones (ReteFuente / ReteICA)

Ejemplo

Request (JSON)
{
  "customer": "48",
  "numbering_range_id": "100",
  "payment_form": "1",
  "payment_method_code": "47",
  "issue_date": "2026-07-14",
  "observation": "Compra a proveedor no obligado",
  "products": [
    {
      "code_reference": "SERV-01",
      "name": "Servicio de mantenimiento",
      "quantity": "1",
      "price": "500000",
      "discount_rate": "0",
      "unit_measure_id": "70",
      "standard_code_id": "1",
      "ret_fuente_rate": "4"
    }
  ]
}
Response (emitido)
{
  "status": "success",
  "success": true,
  "data": {
    "document_id": 618,
    "code_reference": "INV6a46c29683f15fdc6",
    "number": "DS-1",
    "cuds": "a1b2c3d4e5f6...",
    "api_status": "Created"
  }
}
Response (save_draft=1)
{
  "status": "success",
  "success": true,
  "message": "Borrador guardado",
  "data": { "document_id": 618, "estado": "Borrador" }
}
Con save_draft=1 el documento queda en Borrador (no va a la DIAN) para revisar/emitir después desde el panel. Un borrador se puede convertir en documento soporte emitido.

Qué es el RDA

El RDA (Resumen Digital de Atención) es un reporte distinto e independiente del RIPS. La Resolución 1888 de 2025 obliga a enviar, por cada atención, un documento HL7 FHIR R4 a la plataforma de interoperabilidad (IHCE) del Ministerio de Salud. Una misma atención puede deber ambos: RIPS y RDA.

El flujo

#PasoDónde
1Cargar las credenciales que emitió MinSalud (por prestador y por entorno)Ajustes → RDA (IHCE)
2Registrar el profesional con documento y registro RETHUSAjustes → Prestadores (es_profesional=1)
3Completar una atención clínica con diagnóstico CIE-10Historia clínica → Eventos
4Se construye el Bundle, se valida local y se envíaautomático si auto_enviar está activo, o POST manual
5IHCE devuelve el identificador del RDA, que se persisteHistoria clínica → RDA (IHCE)

Lo que hay que saber del contrato

AspectoValor
EstructuraBundle con type: "document". entry[0] debe ser la Composition, y solo puede haber una.
ReferenciasIds planos, sin # y sin fullUrl: "subject": {"reference": "CC-80189301"}. Personas usan TipoDoc-NumDoc; la IPS, su código de habilitación pelado.
Tipo de documentoComposition.type = LOINC 51845-6 (Outpatient Consult note) para el RDA ambulatorio.
SeccionesNueve obligatorias. Las que no tengan datos igual se envían, con emptyReason = nilknown.
Obligatorios en el BundleComposition, Patient, Practitioner y DocumentReference son 1..1. La Organization de la IPS es 0..1.
TransporteOAuth2 client_credentials contra Azure AD. Cada llamada lleva Authorization: Bearer, Ocp-Apim-Subscription-Key y Content-Type: application/fhir+json.
DuplicadosIHCE responde 409 si coinciden Encounter.subject + period + serviceProvider + participant. Wardian lo detecta localmente antes de enviar.
La URL base no es pública. MinSalud la emite junto con el client_id, el client_secret y la Ocp-Apim-Subscription-Key al asignar credenciales. Se solicitan por la Mesa de Servicios del micrositio IHCE.
Estado de las secciones. Hoy llevan datos reales Diagnósticos (11450-4, desde el CIE-10 del evento) y Pagadores (48768-6, si el paciente tiene código EAPB). Las otras siete viajan con emptyReason porque su perfil exige datos que el sistema aún no captura de forma codificada. El Bundle es estructuralmente válido en cualquier caso.
El CUPS del evento no tiene sección en el RDA ambulatorio. Procedimientos (47519-4) solo existe en los RDA de hospitalización y urgencias; en consulta externa el CUPS viaja en Encounter.serviceType.

Credenciales IHCE

Una credencial por tenant y por entorno: QA, preproducción y producción coexisten sin pisarse. Los secretos se guardan cifrados y nunca se devuelven completos — solo enmascarados.
POST//wardian.com.co/dist/api/settings/rda_credenciales/save.php

Cuerpo

CampoDescripción
entornoreqqa | preproduccion | produccion
base_urlreqLa URL que emitió MinSalud. Debe ser https://.
tenant_idreqTenant de Azure AD contra el que se pide el token.
client_idreq
client_secretreqObligatorio al crear. Al editar, envíalo vacío para conservar el guardado.
subscription_keyreqEl Ocp-Apim-Subscription-Key. Mismo criterio que el secreto.
scopereqScope OAuth2, normalmente api://…/.default.
prestador_idopcIPS a la que pertenece la credencial, si el tenant opera varias.
auto_enviaropc0 por defecto. Con 1, cada atención completada dispara el envío.
op_enviar_ambulatorioopcRuta de la operación. Configurable porque el Manual documenta menos operaciones que la guía FHIR.
Request (JSON)
{
  "entorno": "qa",
  "base_url": "https://<la-que-emitio-minsalud>",
  "tenant_id": "00000000-0000-0000-0000-000000000000",
  "client_id": "<client-id>",
  "client_secret": "<client-secret>",
  "subscription_key": "<ocp-apim-subscription-key>",
  "scope": "api://rda/.default",
  "auto_enviar": 0
}
POST .../rda_credenciales/test.php con {"entorno":"qa"} pide un token real a Azure AD. Un success confirma tenant/client/secret/scope, pero no valida la subscription key ni la URL base: eso se prueba en el primer envío.
GET .../rda_credenciales/list.php devuelve las credenciales del tenant con los secretos enmascarados, y POST .../delete.php con {"id":N} elimina una.

Previsualizar Bundle

Construye y valida el Bundle de un evento sin enviarlo y sin crear registros. Útil para revisar la estructura antes de tener credenciales.
GET//wardian.com.co/dist/api/clinical_history/rda/preview.php?evento_id=4242
Response (200)
{
  "evento_id": 4242,
  "entorno": "qa",
  "valido": true,
  "errores": [],
  "bundle": {
    "resourceType": "Bundle",
    "type": "document",
    "identifier": { "value": "…uuid…" },
    "entry": [
      { "resource": { "resourceType": "Composition", "id": "Composition-0" } },
      { "resource": { "resourceType": "Patient", "id": "CC-1000200300" } },
      { "resource": { "resourceType": "Practitioner", "id": "CC-79123456" } },
      { "resource": { "resourceType": "Organization", "id": "230010255201" } },
      { "resource": { "resourceType": "Location", "id": "230010255201-01" } },
      { "resource": { "resourceType": "Encounter", "id": "Encounter-0" } },
      { "resource": { "resourceType": "DocumentReference", "id": "DocumentReference-0" } },
      { "resource": { "resourceType": "Condition", "id": "Condition-0" } }
    ]
  }
}
Responde 422 con un mensaje legible cuando faltan datos estructurales: sin diagnóstico CIE-10 (el Encounter.diagnosis es 1..4), sin profesional registrado, o sin código de habilitación en la IPS.

Enviar RDA

Construye, valida, persiste y envía. Si la validación local falla, no se toca la red: el paquete queda en borrador con los errores.
POST//wardian.com.co/dist/api/clinical_history/rda/send.php

Cuerpo

CampoDescripción
evento_idreqEvento clínico a reportar.
entornoopcqa por defecto.
forzaropcReenvía aunque el contenido sea idéntico a un envío ya aceptado.
confirmar_produccionopcObligatorio si entorno es produccion. Salvaguarda contra envíos accidentales.
Response (200 — aceptado)
{
  "estado": "aceptado",
  "paquete_id": 17,
  "http_code": 200,
  "rda_id": "RDA-000123",
  "mensaje": "RDA aceptado por IHCE.",
  "errores": []
}

Estados y códigos

EstadoHTTPSignificado
aceptado200IHCE devolvió el identificador del RDA.
duplicado200El encuentro ya se había reportado con el mismo contenido. No se reenvía.
enviado202Respondió 200 pero sin identificador reconocible.
borrador422No pasó la validación local. No se envió.
rechazado422IHCE devolvió 400. Los issue del OperationOutcome vienen en errores.
error502Fallo de red o HTTP inesperado. Reintentable.
dist/cron/rda_retry_pending.php reintenta solo error y enviado, con espera creciente (5/15/60/240 min) y techo de 5 intentos. Nunca reintenta rechazado ni borrador: son defectos de contenido y reintentarlos da el mismo 400.

Consultar envíos

GET//wardian.com.co/dist/api/clinical_history/rda/list.php?estado=aceptado&entorno=qa

Devuelve los paquetes del tenant, el conteo por estado y un bloque aptitud con lo que falta para que los envíos pasen la validación de registros de MinSalud (EVOL, RETHUS, REPS, DIVIPOLA).

Response (200, recortado)
{
  "paquetes": [
    { "id": 17, "estado": "aceptado", "entorno": "qa", "rda_id": "RDA-000123",
      "fecha_evento": "2026-07-20", "diagnostico_codigo": "J00", "intentos": 1 }
  ],
  "por_estado": { "aceptado": 12, "rechazado": 1 },
  "aptitud": {
    "pacientes_total": 340,
    "pacientes_sin_fecha_nac": 0,
    "pacientes_sin_sexo_biologico": 12,
    "profesionales": 3,
    "ips_con_habilitacion": 1,
    "eventos_completados": 512,
    "eventos_sin_diagnostico": 4,
    "credenciales": { "qa": "lista", "preproduccion": "sin configurar", "produccion": "sin configurar" }
  }
}
list.php no devuelve el Bundle: es información clínica y pesa. Para el detalle completo (Bundle enviado, OperationOutcome y bitácora) usa GET .../rda/data.php?id=17.

Nómina — cómo funciona

Las APIs de nómina cubren el ciclo laboral completo: colaboradores y contratos, liquidación mensual, radicación ante la DIAN, liquidaciones definitivas, prestaciones sociales, préstamos, dotación, PILA y control de asistencia.

Todas cuelgan de //wardian.com.co/dist/api/roster/ y se autentican con el mismo header X-API-Key que el resto de la API.

Tres diferencias con el resto de esta documentación. Léelas antes de integrar:

1. Los errores responden 200 — pero solo si tu API Key es válida

Hay que distinguir dos situaciones, porque se comportan de forma completamente distinta:

SituaciónRespuesta
API Key ausente o inválida302 — redirección al formulario de login, en HTML. No es JSON y no lleva status
API Key válida, petición incorrecta200 OK con el error en el cuerpo
{ "status": "error", "message": "Seleccione un colaborador" }
Esto rompe el manejo de errores ingenuo. Si tu cliente hace response.json() sin mirar antes, una key caducada o mal escrita no te dará 401: te dará un error de parseo sobre el HTML de la pantalla de login. Comprueba el código de estado antes de interpretar el cuerpo, y trata cualquier 3xx como fallo de autenticación.

Una vez dentro, ningún endpoint de nómina fija código de estado: los fallos de validación y de permisos llegan todos como 200. La tabla de códigos de error general no aplica aquí. Ramifica siempre por el campo status.

Las dos excepciones son api/roster/documentos/download.php (que sí devuelve 403/404/500 en texto plano, no JSON) y api/roster/asistencia/ip_location.php (que devuelve 502).

2. El cuerpo JSON solo funciona con API Key

Los endpoints de nómina leen $_POST. Cuando te autenticas con X-API-Key y envías Content-Type: application/json, la plataforma decodifica el cuerpo y lo expone como si fuera un formulario — así que puedes enviar JSON con normalidad. Es el modo recomendado.

Las únicas excepciones que además leen JSON por su cuenta son nomina/create.php, nomina/temporal_create.php y datos_ley_manage.php. Y hay tres que exigen multipart/form-data obligatoriamente porque reciben ficheros: nomina/import.php, pila/mark_assisted_bulk.php y los de subida de documentos.

3. Varios campos que parecen anidados son texto JSON

Algunos parámetros se envían como una cadena JSON dentro de un campo, no como estructura anidada: liquidacion/create.php → detalle_mensual, detalle_deducciones, vacaciones_periodos; dotacion/deliver.php → checklist; dotacion/plantillas/save.php → items; asistencia/apply_to_nomina.php → novedades; asistencia/send_link.php → ids.

Orden del ciclo mensual

Los endpoints no son independientes: cada paso depende de lo que escribió el anterior.

1. Contrato        create_collaborator.php (crear_contrato=1)  ó  contrato_regularizar.php
                   ↓  sin contrato el motor no resuelve salario, fechas ni periodo de pago

2. Cifras de ley   GET smmlv_data.php?year=2026
                   ↓  si responde estimado:true, escríbelas con datos_ley_manage.php

3. Previsualizar   nomina/calculate_batch.php
                   ↓  devuelve data[] con el cálculo por colaborador

4. Guardar         nomina/create.php   ← reenvía ese data[] como detalles[]
                   ↓  una sola nómina por (periodo, mes, año)

5. Completar       nomina/auto_liquidate.php
                   ↓  revisa status: puede ser "partial"

6. Contabilizar    generate_payroll_accounting.php        (opcional)
7. Aprobar         nomina/send_approval.php               (opcional)

8. Radicar         send_payroll_dian.php  ← una llamada POR COLABORADOR
                   ⚠ punto de no retorno: marca Pagada y debita caja
Endpoints que mueven dinero o envían mensajes reales. Trata cada llamada como definitiva: send_payroll_dian.php, send_payroll_adjustment.php, nomina/adjust.php, nomina/delete.php, nomina/create.php y nomina/auto_liquidate.php (ambos amortizan préstamos), nomina/import.php con pago_opcion=ahora, nomina/temporal_create.php con action=mark_paid, liquidacion/approve.php, loans/create.php (que por defecto desembolsa), pila/pay.php, y todos los que mandan correo o WhatsApp.

Colaboradores y contratos

Un colaborador sin contrato no puede liquidarse: el motor saca de ahí el salario, las fechas de vigencia y el periodo de pago. Crea el contrato junto al colaborador (crear_contrato=1) o regularízalo después.
GET//wardian.com.co/dist/api/roster/list_collaborator.php

Lista paginada de colaboradores.

CampoDescripción
pagina, searchopcPaginación y búsqueda por nombre o documento
estado, tipo, tipo_empleado, contratoopcFiltros
mostrar_inactivosopcIncluye los desactivados
GET//wardian.com.co/dist/api/roster/data.php?id={id}

Ficha completa de un colaborador.

POST//wardian.com.co/dist/api/roster/create_collaborator.php

Crea un colaborador y, opcionalmente, su contrato laboral.

CampoDescripción
id_type, identificationreqTipo y número de documento
first_name, last_namereqNombres. middle_name y second_last_name son opcionales
base_salaryreqSalario mensual, también en contratos quincenales
contract_type, contract_start_datereqTipo y fecha de inicio. contract_type=6 (prestación de servicios) queda fuera de la nómina
crear_contratoopc1 genera el contrato laboral en la misma llamada
periodo_pago, jornada, modalidad_trabajoopcMensual/Quincenal, jornada y modalidad
fecha_fin, fecha_fin_pactada, periodo_prueba_dias, descripcion_obraopcSegún el tipo de contrato
codigo_eps, codigo_afp, codigo_arl, codigo_ccf, codigo_cesantiasopcAdministradoras de seguridad social. Sin ellos no se puede generar la planilla PILA
arl_risk, cotizante_tipo, cotizante_subtipo, worker_type, worker_subtypeopcClasificación PILA
health_percentage, pension_percentageopcPorcentajes de cotización
bank, custom_bank, account_type, account_number, payment_methodopcDatos de pago. account_type: 1=Ahorros, 2=Corriente
integral_salary, es_colaborador_interno, bonificaciones_fiscalizadasopcCambian la matemática — ver el aviso de abajo
auxilio, position, cargo_detalle, sucursales, notasopcAuxilio pactado, cargo, sucursales y notas
email, phone, address, departamento, municipalityopcContacto. El email es necesario para el portal y los desprendibles
paga_autoretencion, medicina_prepagada, tipo_empleadoopcAutorretención (Art. 114-1 ET), medicina prepagada y tipo
Los tres interruptores que cambian lo que se paga:
· integral_salary — el salario integral ya incorpora cesantías, intereses y prima (Art. 132 CST). El IBC se calcula sobre el 70%, y en prestaciones solo procede compensar vacaciones.
· es_colaborador_interno — pone el auxilio de transporte en cero.
· bonificaciones_fiscalizadas — con valor 1, las bonificaciones entran en la base de prestaciones y liquidación y suman al IBC (Art. 127 CST). Con 0 quedan fuera.
POST//wardian.com.co/dist/api/roster/edit_collaborator.php

Mismos campos que crear, más id y los de retención en la fuente: retefuente_dependientes, retefuente_dependientes_pct, retefuente_vivienda, retefuente_medicina_prepagada, retefuente_afc, retefuente_aporte_vol_pension.

POST//wardian.com.co/dist/api/roster/contrato_regularizar.php

Crea el contrato mínimo para un colaborador que no tiene ninguno. Es el prerrequisito del motor de nómina.

CampoDescripción
collaborator_idreqColaborador
tipo_contrato, fecha_inicioreqTipo y fecha de inicio
salario_pactadoreqSiempre el importe mensual
fecha_terminacion, cargo, auxilio_pactado, es_salario_integral, periodo_pago, observacionesopcResto de condiciones
POST//wardian.com.co/dist/api/roster/toggle_active.php

Activa o desactiva un colaborador. Campos: collaborator_id req, activo req.

POST//wardian.com.co/dist/api/roster/upload_photo.php

multipart/form-data. Campos: collaborator_id req, fichero en foto req.

GET//wardian.com.co/dist/api/roster/salary_history.php

Histórico salarial. Campos: collaborator_id req, fecha_referencia y page opc.

GET//wardian.com.co/dist/api/roster/vacation_balance.php

Saldo de vacaciones. Campos: collaborator_id req, fecha_referencia opc.

Cifras de ley del año

GET//wardian.com.co/dist/api/roster/smmlv_data.php?year=2026

Devuelve el salario mínimo, el auxilio de transporte y la UVT del año.

Si la respuesta trae estimado: true, las cifras del año todavía no están cargadas y el sistema está proyectando. Cárgalas antes de liquidar.
POST//wardian.com.co/dist/api/roster/datos_ley_manage.php

Escribe las cifras legales del año. Acepta cuerpo JSON nativo.

Request
{
  "anio": 2026,
  "smmlv": 1623500,
  "auxilio": 200000,
  "uvt": 49799,
  "decreto_smmlv": "Decreto 2613 de 2025",
  "decreto_auxilio": "Decreto 2614 de 2025",
  "es_transitorio": 0,
  "observaciones": ""
}

Ciclo de la nómina

GET//wardian.com.co/dist/api/roster/nomina/list.php

Lista las nóminas. Filtros opcionales: year y collaborator_id.

Response
{
  "status": "success",
  "data": [
    {
      "id": "512", "periodo": "1", "mes": "8", "anio": "2026",
      "estado": "Borrador", "creado_en": "2026-08-30 11:02:41",
      "total_colaboradores": "13", "total_neto": "31450000.00",
      "periodo_texto": "Ago 2026", "periodo_tipo": "Mensual"
    }
  ],
  "count": 1
}
No tiene paginación: devuelve todas las nóminas del año filtrado. Filtra por year si llevas histórico.
total_colaboradores cuenta filas, incluidas las que aún están sin liquidar en cero — no es "colaboradores liquidados".
Los números llegan como cadenas.
POST//wardian.com.co/dist/api/roster/nomina/calculate.php

Calcula la nómina de un colaborador. No guarda nada.

CampoDescripción
collaborator_idreqColaborador
periodo_year, periodo_monthopcPor defecto, el mes en curso
periodoopc1 mensual · 2 días 1–15 · 3 día 16 al fin de mes. Por defecto 1
dias_trabajadosopcPor defecto, los días del periodo
otros_ingresos, otras_deduccionesopcImportes adicionales
Response (extracto)
{
  "status": "success",
  "data": {
    "collaborator_id": 48,
    "nombre_completo": "ANDRÉS FELIPE GÓMEZ RUIZ",
    "salario_base": 2500000, "auxilio_transporte": 200000, "aplica_auxilio": true,
    "dias_trabajados": 30, "dias_base": 30, "dias_periodo": 30,
    "periodo_pago": "Mensual",
    "salario_proporcional": 2500000, "auxilio_proporcional": 200000,
    "base_seguridad_social": 2500000,
    "salud_empleado": 100000, "pension_empleado": 100000, "fsp": 0,
    "prestamos_descontados": 150000,
    "detalle_prestamos": [
      { "id": "31", "tipo": "prestamo_empresa", "valor_descontado": 150000 }
    ],
    "total_devengado": 2700000, "total_deducido": 350000, "neto_a_pagar": 2350000,
    "smmlv": 1623500, "periodo_year": 2026, "periodo_month": 8
  }
}
Los días se recortan en silencio. Aunque envíes 30, si el contrato terminó el día 12 la respuesta trae 12, sin aviso. Compara siempre el dias_trabajados de la respuesta con el que mandaste.
El contrato manda sobre la ficha: se resuelve el contrato vigente a la fecha de fin del periodo, así que recalcular un mes antiguo usa el contrato de entonces.
El auxilio es el legal del año de la nómina, no el guardado en la ficha, y solo aplica si el salario no supera 2 SMMLV.
Las horas extra y bonificaciones solo aparecen si ya existe una nómina creada para ese mes; si no, salen en cero.
Leer los préstamos no los reserva. La amortización real ocurre al guardar.
POST//wardian.com.co/dist/api/roster/nomina/calculate_batch.php

La misma calculadora para varios colaboradores. Es el paso previo a guardar.

CampoDescripción
collaborator_idsreqArray de ids. No admite cadena JSON: envía un array real
periodo_year, periodo_month, periodo, dias_trabajadosopcSe aplican igual a todos
Response
{
  "status": "success",
  "data": [ /* un objeto por colaborador, idéntico al de calculate.php */ ],
  "errors": [ { "collaborator_id": 99, "message": "Colaborador no encontrado (ID: 99)" } ],
  "count": 12
}
status es "success" aunque fallen todos. Si los 13 colaboradores dan error, recibes data: [], count: 0 y 13 entradas en errors. Compara count con los ids que enviaste y revisa errors.
No acepta otros_ingresos ni otras_deducciones: para eso usa calculate.php uno a uno.
POST//wardian.com.co/dist/api/roster/nomina/create.php

Guarda la nómina. Acepta cuerpo JSON nativo. La cabecera nace en estado Borrador y las filas en Liquidada.

CampoDescripción
periodo_yearreqDebe ser 2020 o posterior
periodo_monthreq1 a 12
detallesreqArray no vacío. Reenvía aquí el data[] de calculate_batch.php
periodoopcPor defecto 1
centro_costo_id, observacionesopcCentro de costo y notas
Sub-campos de detalles[]
CampoDescripción
collaborator_idreqSi falta, se guarda una fila huérfana sin que la petición falle. Inclúyelo siempre
neto_a_pagarreqEs el importe que se paga. Se guarda tal cual, sin recalcular
salario_base, auxilio_transporteopcImportes mensuales; el sistema los prorratea
dias_trabajadosopcSe recorta por la vigencia del contrato
salud_empleado, pension_empleadoopcSe registran siempre, aunque sean cero
fsp, otras_deduccionesopcSolo se registran si son mayores que cero
detalle_prestamosopcArray con id y valor_descontado por préstamo. Sin esto no se amortiza nada
Request
{
  "periodo_year": 2026,
  "periodo_month": 8,
  "periodo": 1,
  "observaciones": "Nómina de agosto",
  "detalles": [
    {
      "collaborator_id": 48,
      "salario_base": 2500000,
      "auxilio_transporte": 200000,
      "dias_trabajados": 30,
      "salud_empleado": 100000,
      "pension_empleado": 100000,
      "fsp": 0,
      "prestamos_descontados": 150000,
      "detalle_prestamos": [ { "id": 31, "valor_descontado": 150000 } ],
      "otras_deducciones": 0,
      "neto_a_pagar": 2350000
    }
  ]
}
Response
{ "status": "success", "message": "Nómina guardada exitosamente", "data": { "nomina_id": 512 } }
El endpoint confía en tus cifras. Lo único que recalcula son los días. No comprueba que devengado − deducido = neto, así que una petición armada a mano puede pagar cualquier importe. Genera siempre los detalles[] desde calculate_batch.php.
Si envías prestamos_descontados pero omites detalle_prestamos, al colaborador se le descuenta el dinero pero el saldo del préstamo no baja.
Solo cabe una nómina por periodo, mes y año. Rehacerla exige eliminarla antes, y eso se bloquea si ya hay documentos vivos en la DIAN.
POST//wardian.com.co/dist/api/roster/nomina/auto_liquidate.php

Liquida con valores por defecto los colaboradores que quedaron pendientes en la nómina. Único campo: nomina_id req.

Response — tres desenlaces posibles
{ "status": "success", "message": "13 colaborador(es) liquidado(s) con datos por defecto.",
  "liquidados": 13, "errores": [] }

{ "status": "success", "message": "Todos los colaboradores ya están liquidados", "liquidados": 0 }

{ "status": "partial",
  "message": "11 colaborador(es) liquidado(s), pero 2 quedaron sin liquidar y NO se pueden enviar a la DIAN así.",
  "liquidados": 11, "errores": ["Colaborador #99: Colaborador no encontrado (ID: 99)"] }
Hay tres valores de status, no dos. Tratar todo lo que no sea "error" como éxito esconde filas sin liquidar que después se radican en cero ante la DIAN. Revisa liquidados y errores.
Este endpoint también amortiza préstamos.

Radicar ante la DIAN

POST//wardian.com.co/dist/api/roster/generate_payroll_accounting.php

Genera o regenera los asientos contables de la nómina. Campo: nomina_id req.

POST//wardian.com.co/dist/api/roster/send_payroll_dian.php

Radica el documento de nómina electrónica de un colaborador ante la DIAN.

CampoDescripción
nomina_idreqNómina
collaborator_idreqColaborador. Una llamada por persona
caja_idopcCuenta de la que sale el dinero
Punto de no retorno. Marca la fila como Pagada, debita la caja y obtiene el CUNE. A partir de aquí la nómina no se puede editar ni eliminar sin pasar antes por la nota de ajuste.
Radica también las filas con neto cero cuando tienen novedades: su guarda de "todo en cero" exige además cero días y cero base.
POST//wardian.com.co/dist/api/roster/nomina/adjust.php

Nota de ajuste, anulación o regeneración contable de una nómina ya radicada.

CampoDescripción
nomina_idreqNómina
actionreqnota_ajuste · anular · regenerar_contabilidad
pila_confirmedopc1 confirma el aviso sobre la planilla PILA vinculada
nota_ajuste reabre la nómina para corregirla. Anula los documentos en la DIAN, devuelve el dinero a la cuenta de origen, restaura los saldos de préstamos, anula los comprobantes contables, y deja la cabecera en Ajustada con las filas de nuevo en Liquidada. Después hay que volver a radicar cada colaborador con send_payroll_dian.php.
Las llamadas a la DIAN ocurren antes y fuera de la transacción local. Si la parte local falla después, recibirás "DIAN procesada pero error al revertir contabilidad": quedará anulado en la DIAN y sin revertir en la plataforma.
El éxito parcial también responde "success". Revisa el array resultados[] buscando entradas con error.
POST//wardian.com.co/dist/api/roster/send_payroll_adjustment.php

Envía la nota de ajuste a la DIAN. Campos: nomina_id, collaborator_id, cune_referencia.

No lo uses como camino para deshacer. Anula el documento en la DIAN pero no revierte nada en la plataforma: la fila se queda en Pagada y ya no se puede radicar. Para deshacer usa nomina/adjust.php con action=nota_ajuste.

Ajustar y eliminar

Eliminar una nómina revierte de verdad: devuelve el dinero, restaura los préstamos, anula los comprobantes y borra las provisiones. Por eso conviene previsualizar antes.

GET//wardian.com.co/dist/api/roster/nomina/preview_delete.php?nomina_id={id}

Simulacro: dice qué se deshará y qué no. No modifica nada.

Response
{
  "status": "success",
  "nomina": { "id": 512, "periodo": "2026-08 (periodo 1)", "estado": "Enviada",
              "colaboradores": 13, "total": 31450000 },
  "se_deshace": [
    { "concepto": "Devolución a caja/banco", "detalle": "Bancolombia Cta. Corriente", "valor": 31450000 },
    { "concepto": "Saldos de préstamos restaurados", "detalle": "3 pago(s)", "valor": 450000 },
    { "concepto": "Comprobantes contables eliminados", "detalle": "3 comprobante(s)", "valor": null }
  ],
  "no_se_deshace": [
    "5 comprobante(s) siguen vigentes en la DIAN. Debe enviar la Nota de Ajuste antes de poder eliminar.",
    "Ya se enviaron los desprendibles por correo a los colaboradores. Esos correos no se pueden retirar."
  ],
  "bloqueado": true
}
bloqueado: true significa que delete.php se va a negar. Ocurre cuando quedan documentos vivos en la DIAN o no se puede determinar de qué cuenta salió el dinero. Consúltalo antes de ofrecer un botón de eliminar.
POST//wardian.com.co/dist/api/roster/nomina/delete.php

Elimina la nómina revirtiendo todos sus efectos. Irreversible.

CampoDescripción
nomina_idreqNómina
motivoreqMínimo 10 caracteres. Queda en la auditoría
pila_confirmedopc1 confirma el aviso de la planilla PILA
Response
{
  "status": "success",
  "message": "Nómina NE-512 eliminada correctamente",
  "revertido": {
    "caja_devuelto": 31450000, "prestamos_restaurados": 3, "prestamos_valor": 450000,
    "comprobantes_anulados": 3, "pagos_programados_borrados": 1, "prestaciones_ajustadas": 13
  },
  "avisos": ["El préstamo #31 lo creó esta nómina pero ya tiene pagos de otras; se conserva."]
}
Respuestas de bloqueo
{ "status": "error", "code": "dian_pendiente",
  "message": "No se puede eliminar: 5 comprobante(s) siguen vigentes en la DIAN...",
  "dian": { "enviados": 13, "ajustados": 8, "pendientes": 5 } }

{ "status": "error", "code": "motivo_requerido",
  "message": "El motivo es demasiado corto; explique brevemente por qué se elimina." }

{ "status": "error", "code": "reversion_imposible",
  "message": "No se puede determinar de qué cuenta salió el dinero..." }

{ "status": "pila_confirm",
  "message": "Esta nómina tiene una planilla PILA en estado PAGADA.",
  "pila": { "planilla_id": "88", "estado": "pagada", "sent_to_suaporte": true } }
Es el único endpoint de nómina con comprobación de permisos: exige el permiso de eliminación sobre el módulo. Si recibes code: "reversion_imposible", no se borró nada — normalmente falta marcar una cuenta como predeterminada en Bancos.

Importar y pagos temporales

POST//wardian.com.co/dist/api/roster/nomina/import.php

Crea una nómina completa desde un Excel. Requiere multipart/form-data — este endpoint no acepta JSON.

CampoDescripción
archivoreqFichero .xlsx o .xls
anio, mes, periodoopcAño ≥ 2020, mes 1–12, periodo 1–3
pago_opcionopcahora · fecha · despues (por defecto)
cuenta_bancaria_idopcObligatoria con ahora y fecha
fecha_pagoopcObligatoria con fecha
Columnas del Excel

La fila 1 son los encabezados. Escríbelos sin tildes: se ignoran mayúsculas, espacios y asteriscos, pero no se normalizan los acentos.

ColumnaDescripción
identificacion, dias_trabajados, salario_basereqObligatorias. Sin ellas la importación se rechaza
auxilio_transporteopcSi se omite, se calcula el legal
horas_extra_diurnas (25%), horas_extra_nocturnas (75%), horas_extra_dom_diurnas (100%), horas_extra_dom_nocturnas (150%)opcEn horas
recargo_nocturno (35%), recargo_dom_diurno (80%), recargo_dom_nocturno (110%)opcEn horas. Son recargos, no multiplicadores
vacaciones_dias/_valor, incapacidad_dias/_valor, licencia_dias/_valoropcSi das días sin valor, se calcula
bonificaciones, otros_ingresosopcIngresos adicionales
deduc_salud_pct, deduc_pension_pctopcPor defecto 4 cada uno
deduc_fsp, deduc_retefuente, deduc_prestamos, deduc_otrosopcDeducciones
Response
{
  "status": "success",
  "message": "11 colaborador(es) importados en nómina #513",
  "data": { "nomina_id": 513, "processed": 11, "skipped": 2, "total_neto": 27340000 },
  "warnings": ["Fila 5: Identificación '9998887' no encontrada"]
}
La importación no amortiza préstamos: deduc_prestamos escribe una deducción, pero el saldo del préstamo no baja.
Las filas con identificación vacía se saltan sin aviso, así que las filas en blanco al final reducen el processed en silencio.
Solo se cargan colaboradores activos y que no sean de prestación de servicios.
Si la contabilización falla, ocurre después de guardar y el error no se reporta: recibirás éxito con la nómina creada y sin comprobantes.
POST//wardian.com.co/dist/api/roster/nomina/temporal_create.php

Pagos a personal temporal, al margen de la nómina electrónica (sin DIAN ni PILA). Un solo endpoint con cuatro operaciones según action. Acepta JSON.

actionCamposQué hace
create (por defecto)empleados req, periodo, mes, anio, observacionesCrea el pago temporal
mark_paidid req, caja_idMarca como pagado, contabiliza y debita la caja
deleteid reqElimina. Bloqueado si ya está pagado
get_detailid reqCabecera y detalle
Sub-campos de empleados[]

collaborator_id, dias_trabajados, salario_base, bonificaciones, deducciones, notas.

Request
{
  "action": "create",
  "periodo": "Mensual",
  "mes": 8, "anio": 2026,
  "observaciones": "Refuerzo temporada",
  "empleados": [
    { "collaborator_id": 77, "dias_trabajados": 12, "salario_base": 2000000,
      "bonificaciones": 0, "deducciones": 0, "notas": "" }
  ]
}
Aquí periodo es texto libre ("Mensual"), no el número 1/2/3 del resto de la API.
mark_paid es idempotente: repetirlo responde éxito sin volver a cobrar. Pero una vez pagado no hay vuelta atrás: no existe "despagar" y el borrado queda bloqueado.
collaborator_id no se valida contra la lista de colaboradores.
GET//wardian.com.co/dist/api/roster/nomina/temporal_list.php

Lista los pagos temporales. Campos: pagina, search (busca por id u observaciones, no por nombre). Página fija de 10. Estados: Pendiente y Pagado.

POST//wardian.com.co/dist/api/roster/nomina/send_approval.php

Envía un enlace de aprobación de la nómina por correo o WhatsApp.

CampoDescripción
nomina_idreqNómina
methodopcemail (por defecto) · zapgo · whatsapp_direct
emailopcSi se omite, se usa el de la cuenta
phoneopcObligatorio para los dos métodos de WhatsApp
Cada llamada envía un mensaje real a un tercero.
Llamar dos veces invalida el enlace anterior: solo vive el último token, con 7 días de vigencia.
El token se crea antes del envío: si el correo falla recibirás error, pero el enlace anterior ya quedó anulado.
El estado de la nómina no cambia aquí. Éxito no significa aprobada: eso ocurre cuando el destinatario abre el enlace.
whatsapp_direct no envía nada: solo devuelve una URL wa.me para que la abras tú.

Liquidaciones

Liquidación definitiva al terminar el contrato: cesantías, intereses, prima, vacaciones, indemnización y compensación de dotación.

Orden: calculate → create (borrador; ya reserva los abonos de préstamos) → approve (termina el contrato, consolida préstamos, compensa dotación y contabiliza). Para deshacer: preview_delete → delete o void.
GET//wardian.com.co/dist/api/roster/liquidacion/list.php

Lista las liquidaciones. Campos: collaborator_id, estado, year, page. Página fija de 20.

GET//wardian.com.co/dist/api/roster/liquidacion/data.php?id={id}

Detalle completo de una liquidación.

Los campos detalle_mensual, detalle_deducciones y vacaciones_periodos_detalle vuelven como cadenas de texto, no como objetos. Hay que decodificarlos.
POST//wardian.com.co/dist/api/roster/liquidacion/calculate.php

Calcula la liquidación de un colaborador. No guarda nada. Su respuesta es exactamente lo que espera create.php.

CampoDescripción
collaborator_idreqColaborador
fecha_liquidacionreqFecha de terminación
motivo_terminacionreqVer catálogos. Solo Despido sin justa causa genera indemnización
modo_nominaopccon_nomina (por defecto) descuenta los días que la nómina del mes ya pagó · sin_nomina paga el periodo completo
bonificaciones, otras_deduccionesopcImportes adicionales
nominas_vinculadasopcIds de nóminas a descontar. Si lo omites se toman todas las elegibles; si lo envías vacío, ninguna
modo_nomina decide si el mes se paga dos veces. Con sin_nomina se liquida el periodo entero: si esa nómina ya se pagó, estos importes la pagan por segunda vez. Ojo, el valor por defecto no es el mismo en calculate que en create — indícalo siempre de forma explícita.
POST//wardian.com.co/dist/api/roster/liquidacion/calculate_batch.php

Cálculo masivo. Campo collaborator_ids req (array real). No acepta nominas_vinculadas.

bonificaciones y otras_deducciones se aplican a CADA colaborador, no se reparten entre ellos. La respuesta lo avisa en avisos[].
POST//wardian.com.co/dist/api/roster/liquidacion/create.php

Guarda la liquidación en estado Borrador. Reenvía aquí la salida de calculate.php.

CampoDescripción
collaborator_idreqColaborador
contrato_laboral_idreqFormalmente opcional, pero omitirlo hace que al aprobar se terminen TODOS los contratos vigentes del colaborador
fecha_ingreso, fecha_liquidacion, motivo_terminacionopcSe guardan sin validar
~30 campos de importesopccesantias, intereses_cesantias, prima_s1, prima_s2, vacaciones, indemnizacion, dotacion_pendiente, salud_empleado, pension_empleado, fsp, total_devengado, total_deducido, neto_a_pagar… se guardan tal cual, sin recalcular ni verificar que cuadren
detalle_mensual, detalle_deducciones, vacaciones_periodosopcCadenas JSON, no estructuras anidadas
nominas_vinculadas_detalleopcSolo se lee con modo_nomina=con_nomina. Sub-campos: nomina_id, dias, dias_auxilio, total
Aquí ya se mueve dinero: por cada entrada de detalle_deducciones se registra un abono provisional y baja de inmediato el saldo del préstamo, para que la nómina del mes no descuente lo mismo dos veces.
Solo cabe una liquidación activa por colaborador; hay que anular la anterior para crear otra.
POST//wardian.com.co/dist/api/roster/liquidacion/approve.php

Aprueba la liquidación: termina el contrato, consolida préstamos, compensa dotación y contabiliza.

CampoDescripción
idreqDebe estar en Borrador
pago_opcionopcahora · fecha · despues (por defecto)
cuenta_bancaria_idopcNecesaria con ahora/fecha. Si falta, el pago se omite en silencio
fecha_pagoopcObligatoria con fecha
auto_pilaopc1 solo prevalida los códigos de seguridad social; no genera la planilla
Los errores de contabilidad no revierten la aprobación: la liquidación puede quedar Aprobada sin comprobante.
GET//wardian.com.co/dist/api/roster/liquidacion/preview_delete.php?id={id}

Simulacro de eliminación, igual que el de nómina: se_deshace[], no_se_deshace[] y bloqueado. No modifica nada.

POST//wardian.com.co/dist/api/roster/liquidacion/delete.php

Elimina revirtiendo todos los efectos. Campos: id req, motivo req (mínimo 10 caracteres). Requiere permiso de eliminación.

POST//wardian.com.co/dist/api/roster/liquidacion/void.php

Anula sin borrar: revierte todo pero conserva el registro en estado Anulada. Campos: id req, motivo opc. Requiere permiso de eliminación.

El contrato no se reactiva si el colaborador ya tiene otro vigente o posterior; la respuesta lo indica en avisos[].

Prestaciones sociales

Cesantías, intereses, prima por semestre y vacaciones compensadas.

Orden: calcular_prestacion → register_payment. La vista previa y la escritura usan la misma función, así que el importe que ves es exactamente el que se guarda.
GET//wardian.com.co/dist/api/roster/benefits/balance.php

Saldo causado, pagado y pendiente por concepto. Campos: collaborator_id opc, year opc.

Si omites collaborator_id cambia la forma de la respuesta: pasa a modo agregado y desaparecen salario_actual, fecha_ingreso, periodos_vacaciones y otras diez claves. En ese modo los contadores de días se suman entre personas y no significan nada.
Usa prima.s1 y prima.s2; los escalares sueltos de prima son heredados del modelo anual antiguo.
GET//wardian.com.co/dist/api/roster/benefits/calcular_prestacion.php

Previsualiza el pago de una prestación.

CampoDescripción
collaborator_idreqColaborador
conceptoreqVer catálogos
periodo_yearreqEntre 2000 y 2100
fecha_corteopcPor defecto la legal, pero nunca posterior a hoy
Response (extracto)
{
  "status": "success",
  "data": {
    "concepto": "cesantias", "concepto_label": "Cesantías",
    "dias": 120, "base_calculo": 2749095, "valor": 916365,
    "formula": "(2749095 × 120) / 360",
    "base_detalle": [
      { "concepto": "Salario mensual", "valor": 2500000 },
      { "concepto": "Auxilio de transporte", "valor": 249095 }
    ],
    "fecha_corte": "2026-04-30", "dias_base": 360,
    "ya_pagado": 0, "plazo_pago_legal": "2027-02-14",
    "norma": "Art. 249 CST y Ley 50 de 1990: se consignan al fondo a más tardar el 14 de febrero.",
    "avisos": []
  }
}
El divisor es siempre 360 (año comercial, Art. 134 CST) y no se puede cambiar desde la petición.
GET//wardian.com.co/dist/api/roster/benefits/calcular_prestacion_masivo.php

Lo mismo para toda la plantilla. Campos: concepto req, periodo_year req, fecha_corte opc. Devuelve elegibles[] y omitidos[] con su motivo. Puede tardar: cuenta ~1 segundo por colaborador.

POST//wardian.com.co/dist/api/roster/benefits/register_payment.php

Registra el pago. Campos: collaborator_id req, concepto req, periodo_year req, fecha_pago req, fecha_corte, fondo_cesantias, observaciones opc.

El importe no se acepta desde la petición: se recalcula en el servidor. Enviar valor no tiene efecto.
La clave de duplicado es concepto + año + semestre + fecha de corte, a propósito: así un retiro parcial de cesantías y la consignación de febrero del mismo año pueden convivir.
No genera asiento contable ni movimiento de caja: solo alimenta el saldo y la liquidación.
POST//wardian.com.co/dist/api/roster/benefits/register_payment_masivo.php

Registra el pago para toda la plantilla, en una sola transacción.

Si no se registró ninguno, la respuesta llega con status: "error" aunque la transacción se haya confirmado, y con data lleno. Ramifica por los contadores de data.
GET//wardian.com.co/dist/api/roster/benefits/list.php

Histórico de pagos. Campos: collaborator_id, concepto, year, page. Página fija de 20.

POST//wardian.com.co/dist/api/roster/benefits/delete.php

Borra un pago registrado. Campo: id req.

Irreversible y sin auditoría. Borrar un registro vuelve a inflar retroactivamente todos los saldos pendientes y los importes de "ya pagado" del motor de liquidación.
POST//wardian.com.co/dist/api/roster/benefits/recalculate_provisions.php

Recalcula las provisiones de un año. Campo: year opc.

Masivo, sin simulacro y sobrescribe las provisiones guardadas. Solo alcanza registros que ya tienen provisión: no crea los que faltan.

Préstamos y deducciones

Orden: plan_preview → create → payment. Previsualiza siempre: es el único que avisa si el plan supera el tope de 240 cuotas.
POST//wardian.com.co/dist/api/roster/loans/plan_preview.php

Calcula el plan de cuotas sin guardar nada.

CampoDescripción
monto_originalreqEnvía enteros: aquí los puntos decimales se eliminan
fecha_inicioreqPrimera cuota
modoopccuotas (por defecto) · fecha
cuota_mensual / numero_cuotasopcUno de los dos en modo cuotas. Si envías ambos, manda la cuota
fecha_finopcObligatoria en modo fecha
periodo_pagoopcmensual (por defecto) · quincenal
Response
{
  "status": "success",
  "data": {
    "numero_cuotas": 6, "cuota": 200000, "ultima_cuota": 200000,
    "total_plan": 1200000, "fecha_fin_estimada": "2026-07-15", "periodo_pago": "mensual",
    "cuotas": [
      { "numero_cuota": 1, "fecha_vencimiento": "2026-02-15",
        "valor": 200000, "valor_abonado": 0, "estado": "Pendiente" }
    ]
  }
}
ultima_cuota puede diferir de cuota: ahí se concentra el residuo para que el plan sume el monto exacto.
POST//wardian.com.co/dist/api/roster/loans/create.php

Registra un préstamo, libranza o embargo.

CampoDescripción
collaborator_id, monto_original, fecha_inicioreqDatos base
tiporeqprestamo_empresa · libranza · embargo
cuota_mensual / numero_cuotasopcAl menos uno
periodo_pago, descripcion, observacionesopcCondiciones
pago_opcionopcPor defecto ahora, a diferencia del resto de módulos
cuenta_bancaria_id, fecha_pagoopcPara el desembolso
Cuidado con el valor por defecto de pago_opcion. Omitirlo y enviar cuenta_bancaria_id contabiliza un desembolso real de caja. Solo prestamo_empresa genera asientos; libranzas y embargos no.
Si el plan supera 240 cuotas, el préstamo se crea igualmente pero sin plan, y el error no se reporta. Previsualiza antes.
POST//wardian.com.co/dist/api/roster/loans/payment.php

Registra un abono. Campos: prestamo_id req, valor req (no puede exceder el saldo), fecha_pago, observaciones, nomina_id opc.

Solo admite préstamos en estado Activo. Uno que una liquidación dejó en Cancelado se rechaza a propósito. No existe endpoint para anular un abono.
GET//wardian.com.co/dist/api/roster/loans/list.php

Lista préstamos. Campos: collaborator_id, tipo, estado, page. Trae alerta_pago (proximo/vencido) y dias_para_pago con signo.

Esta consulta también escribe: marca como vencidas las cuotas que ya pasaron de fecha.
GET//wardian.com.co/dist/api/roster/loans/payments_list.php?prestamo_id={id}

Abonos de un préstamo. En los descontados por liquidación, periodo, mes y anio vienen vacíos.

POST//wardian.com.co/dist/api/roster/loans/delete.php

Elimina el préstamo y sus abonos. Campo: id req.

Bloqueo referencial
{
  "status": "error", "code": "prestamo_referenciado",
  "message": "No se puede eliminar el préstamo: tiene abonos ligados a liquidación #42 (Aprobada)...",
  "bloqueos": ["liquidación #42 (Aprobada), 300.000 descontados", "2 abono(s) descontados en nómina"]
}
El bloqueo solo cubre abonos ligados a una nómina o liquidación. Un abono manual sin nómina asociada no bloquea y se destruye en silencio. Eliminar el préstamo tampoco revierte los asientos del desembolso.

Dotación

Entregas de dotación por cuatrimestre (Art. 230-235 CST).

Orden: generate → checklist_get → deliver → send_email (opcional). pending alimenta la compensación de la liquidación.
POST//wardian.com.co/dist/api/roster/dotacion/generate.php

Programa las entregas del año. Campos: year opc, collaborator_id opc.

Elegibilidad automática: salario hasta 2 SMMLV y al menos 90 días de antigüedad a la fecha programada. El valor_estimado es el 5% del salario: una cifra de referencia, no un coste real.
Es idempotente, pero re-ejecutarlo tras un aumento de sueldo no refresca los valores ya creados. Los colaboradores sin fecha de ingreso se saltan sin contarse.
GET//wardian.com.co/dist/api/roster/dotacion/list.php

Entregas del año. Campos: collaborator_id, year (siempre se aplica: por defecto el año actual), estado. Incluye un resumen por estado.

GET//wardian.com.co/dist/api/roster/dotacion/pending.php?collaborator_id={id}

Entregas pendientes de todos los años, con total_compensacion. Es lo que se envía como dotacion_pendiente a la liquidación.

GET//wardian.com.co/dist/api/roster/dotacion/checklist_get.php

Ítems de una entrega o de una plantilla. Campos: entrega_id, plantilla_id opc.

La forma de la respuesta cambia según de dónde salgan los ítems. Los de una entrega traen id; los de una plantilla traen requiere_talla y no traen ids. Usa el campo source (entrega/plantilla/empty) para distinguirlos.
POST//wardian.com.co/dist/api/roster/dotacion/deliver.php

Marca la entrega como realizada. Campos: id req (debe estar Pendiente), fecha_entrega, descripcion, observaciones, checklist (cadena JSON), pago_opcion, cuenta_bancaria_id, fecha_pago.

Sub-campos de checklist[]: item_nombre req, categoria, cantidad, talla, entregado, observaciones.

Es de un solo sentido: no hay forma de deshacer la entrega.
Enviar checklist vacío no toca los ítems; enviarlo con contenido los borra y reemplaza.
POST//wardian.com.co/dist/api/roster/dotacion/send_email.php

Envía el acta al colaborador para firma digital. Campos: id req, email opc.

Envía siempre a la dirección real, también en entorno de pruebas. Cada llamada genera un enlace nuevo con 30 días de vigencia, y los anteriores siguen sirviendo. No es idempotente.
POST//wardian.com.co/dist/api/roster/dotacion/delete.php

Elimina una entrega. Campo: id req.

No comprueba el estado: borra igual una entrega ya realizada o compensada. Borrar una compensada rompe la reversión de la liquidación que la compensó.

Plantillas de dotación

GET//wardian.com.co/dist/api/roster/dotacion/plantillas/list.php

Campo: solo_activas=1 opc. Aquí la clave del ítem es nombre (en checklist_get el mismo dato se llama item_nombre).

POST//wardian.com.co/dist/api/roster/dotacion/plantillas/save.php

Crea o actualiza. Campos: id (0 = crear), nombre req, descripcion, activa, items (cadena JSON: nombre, categoria, requiere_talla, cantidad_default).

items es reemplazo total, no fusión: omitirlo al actualizar borra todos los ítems y la respuesta sigue diciendo que se guardó correctamente.
POST//wardian.com.co/dist/api/roster/dotacion/plantillas/delete.php

Campo: id req.

Expediente del colaborador

Incapacidades

POST//wardian.com.co/dist/api/roster/incapacidades/create.php

Registra una incapacidad y la sincroniza con la nómina abierta del periodo.

CampoDescripción
collaborator_idreqColaborador
tiporeqComún o Laboral. Con tilde: escribirlo sin ella lo hace pagar al 100% como si fuera laboral
fecha_inicio, fecha_finreqLos días se calculan solos, ambos extremos incluidos
diagnostico, entidad_responsable, numero_incapacidad, estadoopcDatos del soporte
valor_reconocidoopcSolo documental: el valor que entra en nómina lo calcula el servidor
documento_soporteopcFichero, máx. 10 MB (multipart/form-data)
Response
{
  "status": "success",
  "message": "Incapacidad registrada correctamente y sincronizada con nómina del período",
  "data": { "id": "12", "dias": 5, "synced_nomina_id": "88" }
}
Cálculo legal (Art. 227 CST): Común días 1–90 a dos tercios del salario diario, días 91 en adelante a la mitad, siempre con el mínimo del salario mínimo diario. Laboral al 100% (ARL).
Si synced_nomina_id viene vacío, no había nómina abierta y la incapacidad no descuenta todavía.
GET//wardian.com.co/dist/api/roster/incapacidades/list.php?collaborator_id={id}

Lista las incapacidades del colaborador.

POST//wardian.com.co/dist/api/roster/incapacidades/update.php

Actualización parcial: solo se escriben los campos presentes. Campo id req.

Al editar, el valor se recalcula sin incluir las horas extra, a diferencia del alta. Editar una incapacidad puede por tanto bajar el importe que se había calculado al crearla.
POST//wardian.com.co/dist/api/roster/incapacidades/delete.php

Campo: id req.

Borra también la novedad asociada aunque la nómina ya esté liquidada, alterando en silencio los insumos de una nómina cerrada.

Memorandos

POST//wardian.com.co/dist/api/roster/memorandos/create.php

Crea un memorando y envía el enlace de firma al colaborador.

CampoDescripción
collaborator_id, asunto, fechareqDatos base
tiporeqLlamado de atención · Descargo · Suspensión · Acta de compromiso · Felicitación · Otro
suspension_inicio, suspension_finopcSolo se leen con tipo=Suspensión; en el resto se descartan sin avisar
descripcion, documento_soporteopcTexto y adjunto (máx. 10 MB)
Solo Suspensión afecta a la nómina: genera una licencia no remunerada que quita esos días del pago y de la causación de prestaciones.
En entorno de pruebas el correo no llega al colaborador, aunque la respuesta diga que se envió.
GET//wardian.com.co/dist/api/roster/memorandos/list.php?collaborator_id={id}

Lista los memorandos.

POST//wardian.com.co/dist/api/roster/memorandos/delete.php

Campo: id req.

El enlace de firma sobrevive al borrado y sigue siendo válido hasta caducar, apuntando a un memorando que ya no existe.
GET//wardian.com.co/dist/api/roster/memorandos/get_template.php

Plantilla de la carta. Campo tipo opc. El campo source indica si es propia (custom) o la interna (default). Marcadores disponibles: {{nombre_empresa}}, {{nit_empresa}}, {{nombre_colaborador}}, {{identificacion}}, {{cargo}}, {{tipo}}, {{asunto}}, {{descripcion}}, {{fecha}}.

POST//wardian.com.co/dist/api/roster/memorandos/save_template.php

Campos: contenido_template req, template_id, nombre, tipo_memorando opc.

Un template_id inexistente responde éxito sin guardar nada. Y tipo_memorando solo se puede fijar al crear: en las actualizaciones se ignora.

Documentos

POST//wardian.com.co/dist/api/roster/documentos/upload.php

multipart/form-data. Campos: collaborator_id req, tipo_documento req (cedula, eps, arl, pension, ccf, examen_medico, otro), fichero en archivo req, más observaciones y fecha_documento opc.

Máximo 10 MB. Formatos: PDF, JPEG, PNG, WebP y Word.

GET//wardian.com.co/dist/api/roster/documentos/list.php?collaborator_id={id}

Lista los documentos. No enlaces a ruta_archivo directamente: usa download.php.

GET//wardian.com.co/dist/api/roster/documentos/download.php?id={id}

Descarga el fichero.

Este endpoint no devuelve JSON. Transmite el fichero, y en caso de error responde 403, 404 o 500 con el mensaje en texto plano. Es la excepción a la regla de que todo llega como 200.
POST//wardian.com.co/dist/api/roster/documentos/delete.php

Campo: id req. Irreversible.

Certificado de ingresos y retenciones (Formulario 220)

GET//wardian.com.co/dist/api/roster/certificates/cert220.php?action={accion}
actionCamposDevuelve
collaborators—Colaboradores con nómina
years—Años con nómina
historyanioCertificados ya generados
generatecollaborator_id req, anioUn PDF, no JSON
batch_generateanio — requiere POSTSolo registra historial
batch_generate no produce ningún PDF pese a su nombre: solo deja constancia en el historial. Para obtener los documentos hay que llamar a generate colaborador por colaborador.
generate escribe en el historial antes de renderizar: descargar un certificado modifica la auditoría.

PILA / Seguridad social

Generación, validación y pago de la planilla integrada de liquidación de aportes, contra el operador SuAporte.

La secuencia es obligatoria. Cada paso depende de un dato que escribió el anterior; saltárselo devuelve un error local, no uno del operador.
1. generate            → escribe el archivo plano       estado: generada
2. validate            → obtiene código y número         estado: validada
3. inconsistencies     → necesita el CÓDIGO
   autocorrect         → necesita el CÓDIGO              estado: corregida
4. totals              → necesita el NÚMERO
   payment_url         → necesita el NÚMERO
   mark_assisted       → necesita el NÚMERO, escribe el PIN
5. pay                 → necesita el PIN                 estado: pagada
   query_payment       → necesita el PIN
6. reverse_payment     → exige estado 'pagada'           estado: aprobada
Ojo: suporte_codigo_planilla y suaporte_numero_planilla son campos distintos, y cada grupo de endpoints usa uno u otro. Nada en la API pone el estado aprobada en el camino de ida: solo lo escribe reverse_payment.
POST//wardian.com.co/dist/api/roster/pila/generate.php

Genera el archivo plano (Resolución 2388 de 2016).

CampoDescripción
periodo_year, periodo_monthopcPor defecto el mes en curso. Año entre 2020 y 2099
nomina_idsopcNóminas a consolidar. Todas deben ser del mismo mes
tipo_planillaopcPor defecto E
Error típico: códigos incompletos
{
  "status": "error",
  "message": "Hay 2 colaborador(es) sin códigos de SS completos. Sincronice BDUA para EPS/AFP.",
  "data": { "incomplete": [
    { "id": 90, "nombre": "Juan Diaz", "identification": "79123456",
      "missing": ["EPS", "AFP"], "bdua_last_sync": null }
  ]}
}
No se llama a SuAporte en este paso, y el archivo no se guarda en disco: queda en la plataforma y se descarga con download.php.
No se puede generar una segunda planilla del periodo mientras exista otra que no esté en error.
POST//wardian.com.co/dist/api/roster/pila/generate_from_nomina.php

Genera la planilla a partir de una nómina. Campos: nomina_id req, tipo_planilla opc.

Cuatro desenlaces posibles, no dos: ya vinculada (status: "info"), vinculada a una existente, generada, o status: "needs_selection" cuando la nómina es quincenal y hay otras del mismo mes. En ese caso hay que reenviar a generate.php con las nomina_ids elegidas.
POST//wardian.com.co/dist/api/roster/pila/generate_from_liquidacion.php

Planilla de retiro. Campo: liquidacion_id req (debe estar Aprobada). Los días se topan en 30 por norma (Decreto 1990 de 2016).

POST//wardian.com.co/dist/api/roster/pila/validate.php

Valida el archivo contra SuAporte. Campo: planilla_id req. Devuelve codigo_planilla, numero_planilla, contadores e inconsistencias[].

POST//wardian.com.co/dist/api/roster/pila/autocorrect.php

Corrección automática. Campo: planilla_id req.

Deja la planilla en corregida, pero la nómina vinculada sigue reportando validada.
GET//wardian.com.co/dist/api/roster/pila/inconsistencies.php

Inconsistencias paginadas. Campos: planilla_id req, page, limit (máx. 500).

GET//wardian.com.co/dist/api/roster/pila/totals.php?planilla_id={id}

Totales de la planilla según el operador. Requiere el número. Esta consulta guarda los totales, así que también escribe.

POST//wardian.com.co/dist/api/roster/pila/mark_assisted.php

Marca la planilla como asistida y obtiene el PIN. Campos: planilla_id req, causal opc.

No cambia el estado de la planilla, solo guarda el PIN.
POST//wardian.com.co/dist/api/roster/pila/pay.php

Paga la planilla. Campo: planilla_id req (debe tener PIN).

El importe lo calcula la plataforma sumando las cotizaciones del detalle; no se envía. Además del pago, genera el comprobante contable de egreso y descuenta el saldo del banco. Si la contabilización falla, el error no se reporta.
GET//wardian.com.co/dist/api/roster/pila/payment_url.php?planilla_id={id}

URL de pago por PSE. También guarda la URL obtenida.

GET//wardian.com.co/dist/api/roster/pila/query_payment.php?planilla_id={id}

Consulta el estado del pago.

estado_planilla y pago_estado son los valores locales; payment_info viene del operador. Pueden discrepar.
POST//wardian.com.co/dist/api/roster/pila/reverse_payment.php

Reversa el pago. Campo: planilla_id req (estado exactamente pagada).

No revierte el comprobante contable que creó el pago, ni actualiza el estado en la nómina vinculada: ambos hay que corregirlos aparte.
GET//wardian.com.co/dist/api/roster/pila/list.php

Lista planillas. Campos: periodo_year, periodo_month, estado, page, limit (máx. 100).

GET//wardian.com.co/dist/api/roster/pila/data.php?planilla_id={id}

Cabecera y detalle por colaborador, con IBC y cotizaciones.

GET//wardian.com.co/dist/api/roster/pila/download.php?planilla_id={id}

Descarga el archivo plano. No devuelve JSON: transmite el fichero de texto.

GET//wardian.com.co/dist/api/roster/pila/list_nominas_for_pila.php

Nóminas consolidables del periodo. Campos: periodo_year, periodo_month. Incluye is_complete_month.

GET//wardian.com.co/dist/api/roster/pila/sync_status.php

Estado de sincronización. Campos: module req (nomina, prestaciones o liquidacion), más nomina_id, liquidacion_id, year, month según el módulo.

Marcación asistida masiva

POST//wardian.com.co/dist/api/roster/pila/mark_assisted_bulk.php

Solo multipart/form-data — este endpoint no acepta JSON ni con API Key. Fichero Excel en archivo req. Devuelve un id_archivo que hay que consultar después.

GET//wardian.com.co/dist/api/roster/pila/mark_assisted_status.php?id_archivo={uuid}

Estado del proceso masivo.

Afiliaciones (BDUA / RUAF)

POST//wardian.com.co/dist/api/roster/pila/bdua_lookup.php

Consulta la afiliación de un colaborador y actualiza su EPS y AFP. Campo: collaborator_id req.

Si el registro no reporta afiliación vigente, no se borra la entidad ya seleccionada a mano. Los estados posibles son confirmado, sin_afiliacion y no_registrado. El campo message se compone dinámicamente: no lo analices, usa estado_eps y estado_afp.
GET//wardian.com.co/dist/api/roster/pila/bdua_query.php

Consulta por documento, sin tocar ninguna ficha. Campos: tipo_documento req, numero_documento req.

POST//wardian.com.co/dist/api/roster/pila/bdua_bulk.php

Sincroniza toda la plantilla. Sin parámetros.

Tarda un segundo por colaborador: con 100 personas la petición bloquea ~100 segundos. Dimensiona el tiempo de espera de tu cliente.
GET//wardian.com.co/dist/api/roster/pila/bdua_auto_sync.php

Consulta si toca sincronizar (GET) o la ejecuta (POST con force opcional).

Configuración y catálogos

GET//wardian.com.co/dist/api/roster/pila/config_get.php

Configuración de SuAporte, con el PIN enmascarado.

Si no hay configuración, responde status: "success" con data: null. Contémplalo.
POST//wardian.com.co/dist/api/roster/pila/config_save.php

Campos: tipo_documento req, numero_documento req, pin y clave_secreta (obligatorios la primera vez; en blanco conservan el valor), aportante_tipo, codigo_arl, codigo_ccf, bdua_auto_sync_days opc.

Guardar la configuración invalida la sesión activa con SuAporte: la siguiente llamada tiene que volver a autenticarse. No lo hagas a mitad de un flujo.
POST//wardian.com.co/dist/api/roster/pila/config_test.php

Prueba la conexión. Sin parámetros. El campo authorized devuelve siempre false y no debe interpretarse.

GET//wardian.com.co/dist/api/roster/pila/administradoras_manage.php

GET lista el catálogo (filtro tipo: EPS, AFP, ARL, CCF, CES). POST admite action: add, edit o toggle.

Al editar, el tipo y el código son inmutables. Los códigos CES* (cesantías) son internos: PILA no reporta cesantías y no aparecen en ningún listado oficial.
GET//wardian.com.co/dist/api/roster/pila/aportante_get.php

Datos de la empresa como aportante en SuAporte. aportante_create.php y aportante_update.php los crean y actualizan (campo tipo_aportante: pyme por defecto, independiente o corporate).

Asistencia y horarios

Marcación

POST//wardian.com.co/dist/api/roster/asistencia/clock.php

Registra entrada, salida o descanso. Tiene dos formas de autenticarse, y cada una pide cosas distintas.

Con qr_tokenCon API Key / sesión
Identificaciónidentification (el collaborator_id se ignora a propósito)collaborator_id o identification
Obligatorio ademásnadamotivo siempre, y pin si la empresa lo configuró
Auditoríanosiempre — se trata como marcación manual
CampoDescripción
tipoopcentrada (por defecto) · salida · descanso_inicio · descanso_fin. Es el único enum validado
latitud, longitudopcObligatorias si la empresa tiene alguna geocerca activa
motivoopcObligatorio por la vía autenticada. Entre 5 y 255 caracteres
pinopcClave de marcación manual, si está configurada
modoopcmanual_completo para registrar un día pasado; exige fecha y al menos una hora
face_snapshot, firmaopcImágenes en base64
tipo_marcacion, verificacion_tipo, observacionesopcEtiquetas libres
Los descansos no abren ni cierran la jornada: usar salida para el almuerzo partiría el día en dos y descuadraría el cómputo.
entrada siempre crea una jornada nueva: varias sesiones por día son intencionales.
El objeto estado de la respuesta es la situación después de marcar, para repintar los botones sin otra llamada.
verificacion_tipo es una etiqueta autodeclarada: el cotejo facial ocurre en el navegador, no en el servidor.
GET//wardian.com.co/dist/api/roster/asistencia/qr_token.php

Genera el código QR de marcación. Sin collaborator_id devuelve el de empresa (el que se proyecta en la entrada, sirve para toda la plantilla); con él, uno personal.

Response
{
  "status": "success",
  "data": {
    "token": "eyJ1aWQiOjcsInRzIjoxNzg3NTA0OTMxLCJub25jZSI6...",
    "url": "https://wardian.com.co/dist/roster/asistencia_qr.php?token=...",
    "scope": "empresa", "expires_in": 480, "refresh_in": 40
  }
}
Lee expires_in y refresh_in de la respuesta; no los fijes en tu código. Cambian según el alcance y la configuración: por defecto 480 segundos de vigencia para el de empresa y 3600 para los enlaces personales, refrescando cada 40. Refrescar no es caducar: un código que desaparece de pantalla sigue valiendo el resto de su ventana.
GET//wardian.com.co/dist/api/roster/asistencia/qr_permanente.php

Código fijo para el cartel impreso. GET lo consulta; POST con accion=rotar lo cambia — y eso invalida todos los carteles ya impresos. Rotar exige ser administrador.

GET//wardian.com.co/dist/api/roster/asistencia/list.php

Marcaciones del periodo. Campos: collaborator_id, fecha_desde, fecha_hasta opc (por defecto, el mes en curso). Incluye descansos[], horas_brutas y horas_trabajadas (ya con descansos descontados).

Los turnos que cruzan medianoche devuelven horas negativas: es una limitación conocida y este endpoint no la corrige.
GET//wardian.com.co/dist/api/roster/asistencia/report.php

Resumen por colaborador: dias_marcados, total_horas, dias_sin_salida.

Aquí total_horas es bruto: no descuenta descansos, al contrario que list.php. Los dos endpoints discreparán para quien tome descansos. Y dias_marcados cuenta registros, no fechas distintas.
POST//wardian.com.co/dist/api/roster/asistencia/edit.php

Corrige una marcación. Campos: id req, motivo req, pin si aplica, más hora_entrada, hora_salida y observaciones opc. Es actualización parcial: lo que omitas se conserva.

POST//wardian.com.co/dist/api/roster/asistencia/delete.php

Elimina una marcación y sus descansos. Campos: id req, motivo req, pin si aplica.

Si no encontró nada que borrar responde igualmente status: "success" con el mensaje "No encontrada". Comprueba el mensaje.
POST//wardian.com.co/dist/api/roster/asistencia/set_manual_pin.php

Configura la clave de marcación manual. Campos: pin req (4–20 caracteres; vacío la desactiva), pin_actual req si ya existe una — también para desactivarla. Solo administradores.

Del reloj a la nómina

POST//wardian.com.co/dist/api/roster/asistencia/calculate_extras.php

Calcula horas extra y recargos comparando las marcaciones con el horario. Solo propone, no escribe. Campos: fecha_desde req, fecha_hasta req, collaborator_id opc.

Response (extracto)
{
  "status": "success",
  "data": [
    { "collaborator_id": 88, "nombre": "Ana Perez",
      "novedades": [
        { "fecha": "2026-08-14", "tipo": "HORA EXTRA DIURNA",
          "factor": 25, "horas": 1.5, "valor": 18750, "es_recargo": false }
      ],
      "resumen": { "dias_trabajados": 21, "horas_extra": 6.5 },
      "origen_horario": "asignado" }
  ],
  "sin_horario": [ { "collaborator_id": 91, "nombre": "Luis Gomez" } ],
  "horario_inferido": [ { "collaborator_id": 90, "nombre": "Juan Diaz" } ]
}
sin_horario[] son los que tienen marcaciones pero nada calculable; horario_inferido[] se calcularon contra un horario deducido, no asignado: revísalos.
El valor ya incorpora el factor. No lo vuelvas a aplicar.
POST//wardian.com.co/dist/api/roster/asistencia/apply_to_nomina.php

Aplica esas novedades a la nómina. Campos: nomina_id req, novedades req (cadena JSON: el data[] de calculate_extras).

Reaplicar reemplaza, no duplica: las novedades que vinieron de asistencia se borran y se reinsertan. Las escritas a mano nunca se tocan. Después se recalculan salud, pensión y FSP, porque las horas extra suben la base de cotización.
Los ítems malformados se saltan sin avisar: compara el número de inserciones que devuelve con lo que enviaste.
Un colaborador con la lista de novedades vacía no es un caso neutro: limpia las suyas.
POST//wardian.com.co/dist/api/roster/asistencia/dias_sugeridos.php

Sugiere los días a pagar según las ausencias reales. Campos: nomina_id req, collaborator_id req. Solo lectura.

Ramifica siempre por data.aplica: cuando vale false la respuesta solo trae ese campo y un motivo. No resta domingos, festivos ni días ya justificados por vacaciones o incapacidad.

Geocercas

POST//wardian.com.co/dist/api/roster/asistencia/geofences.php

Un endpoint con varias operaciones según action: list, create, update, delete, toggle.

CampoDescripción
nombrereqEn create y update
poligonoreqMínimo 3 puntos. Cada punto es [latitud, longitud] — al revés que GeoJSON
collaborator_idsopcA quién aplica. Omitirlo en update borra todas las asignaciones
color, idopcColor y, en update/delete/toggle, el id
Request
{
  "action": "create",
  "nombre": "Sede Norte",
  "poligono": [[4.7010,-74.0460],[4.7015,-74.0440],[4.6995,-74.0445]],
  "collaborator_ids": [88, 90, 91]
}
Crear la primera geocerca cambia el contrato de clock.php para toda la empresa: a partir de ahí toda marcación exige coordenadas y se rechaza fuera de los polígonos. Un polígono mal formado no da error: simplemente nadie podrá marcar.
create devuelve el id nuevo en la raíz de la respuesta, no dentro de data.

Envío de enlaces y biometría

POST//wardian.com.co/dist/api/roster/asistencia/send_link.php

Envía el enlace de marcación por correo o WhatsApp. action: destinatarios (simulacro), enviar, marcar_enviado.

Campos: ids req en enviar (cadena JSON, máximo 25 por llamada), canales (email y/o whatsapp), momento, alcance, collaborator_id.

El envío cuesta ~1,5 segundos por destinatario, así que el lote de 25 es obligatorio: una plantilla de 100 personas son 4 llamadas seguidas.
POST//wardian.com.co/dist/api/roster/asistencia/envio_config.php

Configura el envío automático. Campos: envio_auto, envio_canal_email, envio_canal_whatsapp, envio_salida, envio_anticipacion_min (5 a 50 minutos). Solo administradores. No tiene consulta GET.

POST//wardian.com.co/dist/api/roster/asistencia/save_face_descriptor.php

Enrola el patrón facial. Campos: collaborator_id req, descriptor req (cadena JSON con al menos 64 números), source_photo opc. Uno por colaborador: volver a enrolar sobrescribe.

GET//wardian.com.co/dist/api/roster/asistencia/list_bio_status.php

Estado de enrolamiento facial y foto por colaborador. Sin parámetros.

Horarios

GET//wardian.com.co/dist/api/roster/horarios/list.php

Horarios con su detalle diario y descansos. Campo solo_activos=1 opc. Los días van de 1 (lunes) a 7 (domingo).

POST//wardian.com.co/dist/api/roster/horarios/save.php

Crea o actualiza un horario. Campos: id (0 = crear), nombre req, descripcion, detalle (cadena JSON).

Sub-campos de detalle[]: dia_semana req, hora_entrada, hora_salida, es_descanso, almuerzo_inicio, almuerzo_fin, descansos[] (con inicio, fin, nombre).

Es reemplazo total, no parche: al actualizar se borra el horario semanal entero y se reinserta desde detalle. Omitir detalle deja el horario vacío.
Las horas semanales se recalculan solas: el valor que envíes se descarta.
POST//wardian.com.co/dist/api/roster/horarios/delete.php

Campo: id req. Se bloquea si el horario está asignado a contratos vigentes.

Aprobaciones y portal del empleado

Los endpoints de approvals/ no funcionan con API Key. Verifican la sesión antes de resolver la clave, así que una petición autenticada solo con X-API-Key recibe una redirección al login con cuerpo vacío — y un response.json() fallará al analizar el HTML. Son exclusivos de sesión de navegador. A diferencia del resto de nómina, sí devuelven códigos HTTP reales (401, 405, 500).
GET//wardian.com.co/dist/api/roster/approvals/list.php

Solicitudes del portal. Campos: type req (vacation, incapacidad, permission), status opc (Pendiente por defecto, Aprobada, Rechazada, Cancelada, all).

Un status inválido no da error: cae en silencio a Pendiente.
GET//wardian.com.co/dist/api/roster/approvals/detail.php

Campos: request_type req y request_id req. Ojo: aquí el parámetro se llama request_type, mientras que en list.php es type.

POST//wardian.com.co/dist/api/roster/approvals/approve.php

Aprueba la solicitud y la sincroniza con la nómina abierta. Campos: request_type req, request_id req, observaciones opc.

status: "success" no garantiza que la novedad se creara ni que saliera el correo: esos bloques fallan en silencio. Comprueba data.synced_nomina_id.
Solo se pueden aprobar solicitudes en estado Pendiente. Los permisos no generan novedad de nómina.
POST//wardian.com.co/dist/api/roster/approvals/reject.php

Campos: request_type req, request_id req, observaciones req (obligatorias al rechazar). No tiene ningún efecto sobre la nómina.

POST//wardian.com.co/dist/api/roster/portal/send_access.php

Envía el acceso al portal del empleado. Campos: collaborator_id req (o la palabra __all__), method opc (email por defecto, whatsapp).

Este sí acepta API Key. El mensaje no lleva ninguna credencial: solo la dirección del portal, donde el empleado pide un código de un solo uso.
Con method=whatsapp y __all__ solo se prepara un mensaje, no una difusión.
status es "error" si no se envió ningún correo, aunque los contadores de data vengan completos. Ramifica por ellos.

Catálogos y estados

Estados de la nómina

EstadoCuándo
BorradorAl crearla. Es el estado inicial
EnviadaTras generarla (desprendibles y contabilidad ejecutados)
Aprobada / RechazadaCuando alguien resuelve el enlace de aprobación
AjustadaTras una nota de ajuste; la nómina vuelve a ser corregible

Por colaborador, dentro de la nómina: Activo (sin liquidar) → Liquidada → Pagada o Nota ajuste. Los dos últimos son terminales: bloquean el recálculo.

El estado no basta para saber si una nómina es editable: un documento vivo en la DIAN manda por encima. Consulta nomina/preview_delete.php.

Motivos de terminación (liquidación)

Valor a enviarSignificado
RenunciaRenuncia voluntaria
Despido justa causaDespido con justa causa
Despido sin justa causaDespido sin justa causa — el único que genera indemnización (Art. 64 CST)
Mutuo acuerdoMutuo acuerdo
Fin contratoTerminación de contrato a término fijo
Fin obraTerminación de obra o labor
Periodo de pruebaTerminación en periodo de prueba (sin indemnización, Art. 80 CST)

Conceptos de prestaciones

ValorConceptoCorte legalPlazo de pago
cesantiasCesantías31 de diciembre14 de febrero siguiente
intereses_cesantiasIntereses sobre cesantías31 de diciembre31 de enero siguiente
prima_s1Prima, primer semestre30 de junio30 de junio
prima_s2Prima, segundo semestre31 de diciembre20 de diciembre
vacaciones_compensadasVacaciones compensadasa la fecha—

Otros enumerados

ÁmbitoValores
Préstamos — tipoprestamo_empresa · libranza · embargo. Al liquidar cobran en ese orden inverso: primero el embargo
Préstamos — estadoActivo (el único que admite abonos) · Pagado · Cancelado
Préstamos — periodo_pagomensual · quincenal
Dotación — estadosPendiente · Entregada (sin vuelta atrás) · Compensada
Dotación — cuatrimestres1 (30 abr) · 2 (31 ago) · 3 (20 dic)
Liquidación — estadoBorrador · Aprobada · Pagada · Anulada
PILA — estadogenerada · validada · corregida · aprobada · pagada · error
PILA — administradorasEPS · AFP · ARL · CCF · CES
Incapacidades — tipoComún · Laboral (con tilde)
Memorandos — tipoLlamado de atención · Descargo · Suspensión · Acta de compromiso · Felicitación · Otro
Documentos — tipo_documentocedula · eps · arl · pension · ccf · examen_medico · otro
Asistencia — tipoentrada · salida · descanso_inicio · descanso_fin
Nómina — periodo1 mensual · 2 primera quincena · 3 segunda quincena
Cuentas bancarias — account_type1 Ahorros · 2 Corriente
Opciones de pagoahora · fecha · despues