Electronic Payroll DIAN
Issue Payroll Support Documents (NominaIndividual, TipoXML=102) and Adjustment Notes (NominaIndividualDeAjuste, TipoXML=103) per DIAN Resolution 000013/2021. Your system sends all data in one POST and the API generates XML, calculates CUNE (SHA-384), signs with XAdES-BES, zips and transmits to DIAN.
🔑 Company and environment by API Key: The endpoint derives the company and environment (production/sandbox) from the X-Api-Key header. DO NOT send id_empresa or ambiente in the body — they are redundant, ignored, and a security risk (a client with a test API Key should not be able to issue on behalf of another company).
⚠️ Important: The API does NOT require pre-existing employees, contracts or periods. Your payroll system sends all data in one POST and the API handles everything.
POST /v1/api/nomina/enviar
Generates XML, calculates CUNE (SHA-384), signs with XAdES-BES, zips, transmits to DIAN and persists. Returns CUNE, trackId, QR URL and DIAN response.
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| transmit | boolean | No | true=send to DIAN. false=only generate XML/CUNE (default: true) |
| empleado | object | Yes | Employee data (see empleado sub-table) |
| periodo | object | Yes | Pay period (see periodo sub-table) |
| devengados | array | Yes | Earned lines (see devengados sub-table) |
| deducciones | array | No | Deduction lines (see deducciones sub-table) |
| totales | object | No | Auto-calculated from devengados/deducciones if omitted |
| notas | array<string> | No | Free notes (NIE031) |
| novedad | boolean | No | Mark contractual novelty (default: false) |
| cune_nov | string | No | CUNE of previous document (required if novedad=true) |
| prefijo | string | No | Consecutive prefix (default: "NOM") |
| fecha_gen | string | No | YYYY-MM-DD (default: today) |
| hora_gen | string | No | HH:MM:SS-05:00 (default: current Colombia time) |
| periodo_nomina | string | No | 1=Regular, 2=Extraordinary (default: 1) |
| tipo_moneda | string | No | COP by default |
| trm | number | No | Exchange rate (only if tipo_moneda != COP) |
| lugar_generacion | object | No | Override { pais, departamento, municipio, idioma } — defaults to empresa |
| empleador | object | No | Employer override — defaults to empresa |
| pago | object | No | Payment method info |
| fechas_pagos | array<string> | No | Payment dates (YYYY-MM-DD) |
| redondeo | number | No | Rounding cents |
| novedades | array | No | Period novelties |
| sanciones | array | No | Disciplinary sanctions |
empleado
| Field | Type | Required | Description |
|---|---|---|---|
| tipo_documento | string | No | 13=CC, 22=CE, 41=Passport (default: 13) |
| numero_documento | string | Yes | Employee document number |
| primer_nombre | string | Yes | First name |
| otros_nombres | string | No | Middle names (if applicable) |
| primer_apellido | string | Yes | First surname |
| segundo_apellido | string | No | Second surname |
| tipo_trabajador | string | No | DIAN 5.5.3: 01=Employee, 02=Independent, 09=Apprentice (default: 01) |
| sub_tipo_trabajador | string | No | DIAN 5.5.4: 00=None, 01=Rural, 02=Urban (default: 00) |
| alto_riesgo_pension | boolean | No | Decree 2090/2003 |
| codigo_trabajador | string | No | Internal employee code |
| tipo_contrato | string | No | DIAN 5.5.2: 1=Fixed, 2=Indef, 3=Work, 4=Apprentice, 5=Services (default: 2) |
| lugar_trabajo_pais | string | No | ISO 3166-1 alpha-2 (default: CO) |
| lugar_trabajo_departamento | string | No | DANE department code |
| lugar_trabajo_municipio | string | No | DANE municipality code |
| lugar_trabajo_direccion | string | No | Workplace address |
periodo
| Field | Type | Required | Description |
|---|---|---|---|
| fecha_ingreso | string | No | YYYY-MM-DD |
| fecha_retiro | string | No | YYYY-MM-DD (opcional) |
| fecha_liquidacion_inicio | string | Yes | YYYY-MM-DD |
| fecha_liquidacion_fin | string | Yes | YYYY-MM-DD |
| tiempo_laborado_dias | integer | Yes | Days worked (>= 1) |
devengados[]
| Field | Type | Required | Description |
|---|---|---|---|
| seccion | string | Yes | BASIC, TRANSPORT, HED, HEN, HRD, HRN, SND, VACATIONS, BONUSES, etc. |
| descripcion | string | No | Free description |
| dias_trabajados | integer | No | Only for BASICO |
| sueldo_trabajado | number | No | Only for BASICO (if valor_total is omitted) |
| auxilio_transporte | number | No | Only for TRANSPORT (if valor_total is omitted) |
| valor_total | number | No | Total earned value. If omitted, uses sueldo_trabajado or auxilio_transporte |
deducciones[]
| Field | Type | Required | Description |
|---|---|---|---|
| seccion | string | Yes | HEALTH, PENSION, SINDICATE, SANCTION, LOAN, TAX_WITHHOLDING, etc. |
| descripcion | string | No | Free description |
| porcentaje | number | No | Percentage (e.g. 4 for health) |
| base_calculo | number | No | Calculation base (IBC) |
| valor_total | number | Yes | Total deduction value |
Example
{
"transmit": true,
"empleado": {
"tipo_documento": "13",
"numero_documento": "1234567890",
"primer_nombre": "JUAN",
"otros_nombres": "CARLOS",
"primer_apellido": "PEREZ",
"segundo_apellido": "GOMEZ",
"tipo_trabajador": "01",
"sub_tipo_trabajador": "00",
"alto_riesgo_pension": false,
"lugar_trabajo_pais": "CO",
"lugar_trabajo_departamento": "76",
"lugar_trabajo_municipio": "76001",
"lugar_trabajo_direccion": "Calle 1 #2-3",
"tipo_contrato": "2"
},
"periodo": {
"fecha_ingreso": "2026-01-15",
"fecha_liquidacion_inicio": "2026-07-01",
"fecha_liquidacion_fin": "2026-07-31",
"tiempo_laborado_dias": 30
},
"devengados": [
{
"seccion": "BASICO",
"dias_trabajados": 30,
"sueldo_trabajado": 1300000,
"valor_total": 1300000,
"descripcion": "Salario basico julio"
},
{
"seccion": "TRANSPORTE",
"auxilio_transporte": 162000,
"valor_total": 162000,
"descripcion": "Auxilio de transporte"
}
],
"deducciones": [
{
"seccion": "SALUD",
"porcentaje": 4,
"base_calculo": 1300000,
"valor_total": 52000,
"descripcion": "Aporte salud EPS"
},
{
"seccion": "FONDO_PENSION",
"porcentaje": 4,
"base_calculo": 1300000,
"valor_total": 52000,
"descripcion": "Aporte pension AFP"
}
],
"notas": [
"Liquidacion de julio 2026"
]
}Code Examples
curl -X POST https://api.jcflow.com.co/v1/api/nomina/enviar \
-H "X-Api-Key: sk_test_tu_api_key_aqui" \
-H "Content-Type: application/json" \
-d '{"transmit":true,"empleado":{"tipo_documento":"13","numero_documento":"1234567890","primer_nombre":"JUAN","otros_nombres":"CARLOS","primer_apellido":"PEREZ","segundo_apellido":"GOMEZ","tipo_trabajador":"01","sub_tipo_trabajador":"00","alto_riesgo_pension":false,"lugar_trabajo_pais":"CO","lugar_trabajo_departamento":"76","lugar_trabajo_municipio":"76001","lugar_trabajo_direccion":"Calle 1 #2-3","tipo_contrato":"2"},"periodo":{"fecha_ingreso":"2026-01-15","fecha_liquidacion_inicio":"2026-07-01","fecha_liquidacion_fin":"2026-07-31","tiempo_laborado_dias":30},"devengados":[{"seccion":"BASICO","dias_trabajados":30,"sueldo_trabajado":1300000,"valor_total":1300000,"descripcion":"Salario basico julio"},{"seccion":"TRANSPORTE","auxilio_transporte":162000,"valor_total":162000,"descripcion":"Auxilio de transporte"}],"deducciones":[{"seccion":"SALUD","porcentaje":4,"base_calculo":1300000,"valor_total":52000,"descripcion":"Aporte salud EPS"},{"seccion":"FONDO_PENSION","porcentaje":4,"base_calculo":1300000,"valor_total":52000,"descripcion":"Aporte pension AFP"}],"notas":["Liquidacion de julio 2026"]}'Success Response
{
"success": true,
"message": "Nómina Electrónica procesada exitosamente",
"data": {
"id": 1,
"cune": "A1B2C3D4E5F6...",
"cune_scheme": "CUNE-SHA384",
"qr_url": "https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=A1B2C3D4...",
"trackId": "uuid-track-id",
"transmission": {
"isValid": true,
"statusCode": "00",
"statusDescription": "Procesado Correctamente",
"errors": []
},
"totales": { "devengados": 1462000, "deducciones": 104000, "comprobante": 1358000 },
"ambiente": 2,
"xml": "<NominaIndividual>...</NominaIndividual>"
}
}POST /v1/api/nomina/ajuste
Creates an Adjustment Note (NominaIndividualDeAjuste, TipoXML=103) that REPLACES or DELETES a previous payroll referenced by CUNE. The API looks up the original by CUNE (not by id) and generates the adjustment XML.
| Field | Type | Required | Description |
|---|---|---|---|
| cune_referencia | string | Yes | CUNE of original payroll to replace/delete |
| empleado | object | Yes | Employee data |
| tipo_nota | integer | No | 1=Replace, 2=Delete (DIAN 5.5.8, default: 1) |
| numero_referencia | string | No | Original payroll number (default: stored consecutivo) |
| fecha_referencia | string | No | YYYY-MM-DD (default: stored fecha_gen) |
| transmit | boolean | No | true=send to DIAN (default: true) |
| prefijo | string | No | Consecutive prefix (default: "NOM") |
| fecha_gen | string | No | YYYY-MM-DD (default: today) |
| hora_gen | string | No | HH:MM:SS-05:00 (default: current Colombia time) |
| periodo_nomina | string | No | 1=Regular, 2=Extraordinary (default: uses original) |
| lugar_generacion | object | No | Generation location override |
| empleador | object | No | Employer override |
| periodo | object | No | Pay period (default: uses original) |
| devengados | array | No | Corrected earned lines (default: empty) |
| deducciones | array | No | Corrected deduction lines (default: empty) |
| totales | object | No | Totals (default: zeros) |
| notas | array<string> | No | Free notes |
GET /v1/api/nomina/estado/:trackId
Queries the current payroll status via DIAN GetStatus and updates it in the DB. trackId is in the URL; id_empresa is not sent.
GET /v1/api/nomina/listar
Lists the company payrolls with pagination and optional filters. The company is determined by the API Key.
| Field | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page (default 1) |
| limit | integer | No | Items per page (default 20) |
| tipo | string | No | INDIVIDUAL | AJUSTE |
| estado | string | No | enviada | aceptada | rechazada | error | borrador |
| fecha_desde | string | No | YYYY-MM-DD |
| fecha_hasta | string | No | YYYY-MM-DD |
GET /v1/api/nomina/:id
Returns the detail of a specific payroll, including earned and deduction lines.
GET /v1/api/nomina/:id/xml
Downloads the signed payroll XML (text/xml).
GET /v1/api/nomina/:id/zip
Downloads the ZIP containing the signed XML (application/zip).
💡 Tip: In the sandbox environment (Habilitación), DIAN accepts any correctly calculated CUNE. You can use an sk_test_ API Key without a real digital certificate during development.