Manual de integración

Descargar los XML de sus facturas recibidas y enviadas de la DIAN

API REST para consultar de forma masiva las facturas de compra y de venta registradas en el catálogo de la DIAN, y descargar sus XML —con NIT del emisor, totales e IVA discriminado— desde su propio sistema.

Versión 1.0 Protocolo REST · JSON Ambiente Producción
01

Qué hace este servicio

El servicio consulta el catálogo de la DIAN y devuelve los documentos electrónicos de un rango de fechas, con toda su información estructurada y, si se solicita, el XML original firmado de cada documento.

Se resuelve en dos etapas, que la API encadena automáticamente:

  1. Listado. Obtiene del catálogo los CUFE de todos los documentos del rango, con su emisor, folio, fecha, estado y valor.
  2. Consulta. Por cada CUFE pregunta a la DIAN y devuelve el XML original del documento.
Novedad · septiembre de 2026

La segunda etapa ya no exige certificado digital. El endpoint /api/xml-sin-certificado encadena las dos con el solo enlace AuthToken de la empresa, y entrega además las líneas de detalle y los impuestos por línea. La vía anterior, que sí pide el .p12, sigue vigente y sin cambios.

Importante

La información proviene directamente de la DIAN en cada consulta. El servicio no mantiene una copia de los documentos: actúa como pasarela, no como repositorio. Vea la sección Almacenamiento.

02

Antes de empezar

Para consumir la API necesita tener a mano lo siguiente:

ElementoPara qué sirveQuién lo entrega
URL baseDirección del servicioÁgil Factura
API Key
X-Api-Key
Autenticar los endpoints de listado y de XML por CUFEÁgil Factura
Token de empresa
Authorization: Bearer
Autenticar el flujo completo usando el certificado ya configuradoÁgil Factura
Enlace AuthToken de la DIANAutorizar la consulta del catálogo. Cambia en cada extracciónLa DIAN, por correo
Certificado .p12 y su contraseñaSolo si usará el endpoint de XML por CUFE enviando el certificadoSu entidad certificadora
Seguridad

La API Key y el token de empresa son credenciales: consúmalas desde su servidor, nunca desde el navegador ni desde una aplicación móvil, y no las publique en repositorios. Si sospecha que se expusieron, solicite su rotación.

03

El token de la DIAN

Es el punto que más dudas genera, así que conviene entenderlo bien. La DIAN envía por correo un enlace de autorización con esta forma:

https://catalogo-vpfe.dian.gov.co/User/AuthToken?pk=10910094|53031455&rk=901234567&token=6b84e2c2-9530-4e02-8bc0-647da438d804

Ese enlace completo es lo que se envía en el campo url de cada petición.

Tres reglas que debe respetar

ReglaDetalle
CaducaTiene vigencia limitada. Genere uno nuevo justo antes de cada extracción; no lo guarde para reutilizarlo al día siguiente.
Debe ser del mismo ambienteUn token de habilitación no sirve para consultar documentos de producción, y viceversa.
Va completoCópielo íntegro, incluidos pk, rk y token. Si recorta algún parámetro, la consulta falla.
Sobre el carácter «|»

El enlace de la DIAN contiene una barra vertical en el parámetro pk, que no es un carácter válido en una URL. El servicio la codifica automáticamente, así que puede pegar el enlace tal como lo recibió. Si su cliente HTTP valida URLs por su cuenta, envíela como %7C.

04

Autenticación

Hay dos formas, según el endpoint que use.

A · API Key para volumen

X-Api-Key: <api_key>

Es la que usan los dos endpoints del flujo principal. Una sola clave sirve para todas las empresas que usted maneje: el certificado de cada una viaja dentro de la petición, así que no hay nada que registrar previamente.

Como el certificado va en el cuerpo, use siempre HTTPS y consuma la API desde su servidor, nunca desde el navegador.

La clave es suya y personal: identifica a su organización y es la que atribuye el consumo que se le factura (ver Consumo). No la comparta con terceros.

B · Token de empresa

Authorization: Bearer <token_de_empresa>

Alternativa para los endpoints de extracción completa. El servicio firma con el certificado que la empresa ya tiene configurado, de modo que usted no lo envía — pero requiere dar de alta cada empresa con su certificado, lo que no resulta práctico cuando son muchas.

05

Compras y ventas

El campo tipo determina qué documentos se extraen:

ValorQué traeConsideraciones
Received Compras — documentos que le emitieron a su empresa Se valida que su empresa sea el adquiriente. Son los únicos sobre los que pueden registrarse eventos RADIAN.
Sent Ventas — documentos que su empresa emitió Solo para consulta y reportes. No aplican eventos RADIAN.

Para obtener ambos debe hacer dos extracciones separadas, una por cada valor. El resto de los parámetros es idéntico.

Tipos de documento

El listado incluye todo lo que la DIAN registre en el rango: facturas electrónicas, notas crédito y débito, documentos equivalentes POS y títulos valor. Filtre por el campo tipoDoc si solo le interesan algunos.

06

Endpoints

Con cuál empezar

Si lo que quiere es extraer XML de forma masiva, el primero le basta: resuelve listado y XML en una sola llamada y no necesita certificado digital. Los dos siguientes son la vía anterior, que sigue vigente y sí lo pide. Los tres usan la misma API Key y no requieren registrar ninguna empresa. Los demás son alternativas para casos puntuales.

POST /api/xml-sin-certificado X-Api-Key

Recorre el catálogo en un rango de fechas y, por cada documento, descarga además su XML firmado y lo interpreta. No requiere certificado digital: le basta el enlace AuthToken de la empresa. Devuelve lo del listado más las líneas de detalle y los impuestos por línea, que el listado no tiene.

{
  "url":        "https://catalogo-vpfe.dian.gov.co/User/AuthToken?...",
  "tipo":       "Received",        // Received compras · Sent ventas
  "fechaDesde": "2026-08-01",
  "fechaHasta": "2026-08-31",
  "limite":     10                  // opcional: acota cuántos se descargan
}
{
  "success":   true,
  "total":     161,
  "ok":        155,               // facturas con XML interpretado
  "no_aplica": 6,                 // acuses RADIAN u otros: NO son errores
  "errores":   0,
  "parcial":   false,
  "conteo": {
    "pedidos": 161, "entregados": 161, "de_cache": 0,
    "reintentos": 1, "bloqueos": 1, "sin_xml": 0, "fallidos": 0
  },
  "data": [{
    "TipoDocumento":      "Invoice",
    "Numero de Factura":  "AFE8052025",
    "CUFE":              "09acc105d8dd1e61...",
    "NIT del Emisor":    "860013771",
    "Sub Total":         553800,
    "Impuestos Cabecera": 105222,
    "Total a Pagar":     659022,
    "TaxBreakdown": [{ "code": "01", "name": "IVA", "percent": 19,
                       "taxable_amount": 553800, "amount": 105222 }],
    "LineItems": [{
      "Nombre del Producto": "Domo Circular resq Negro - CJ x 50 un",
      "Referencia": "D15251099", "Cantidad": 5,
      "Precio Unitario": 83000, "Total": 415000,
      "taxes": [{ "code": "01", "percent": 19, "amount": 78850 }]
    }],
    "Estado extracción": "ok"
  }]
}

conteo es el parte de la corrida y conviene revisarlo: de_cache son los que ya estaban guardados y no se volvieron a pedir, bloqueos las veces que la DIAN frenó el ritmo y reintentos las que se recuperaron. Si fallidos no es cero, la extracción está incompleta aunque success sea true: cada documento trae su propio Estado extracción.

Si el rango no cabe

Por encima de 400 documentos responde 422 sin haber descargado nada —no se gasta cupo del portal— y devuelve en rangos_sugeridos los tramos de fechas en que conviene partirlo, calculados con los documentos que hay realmente en cada día. Ver la sección 09.

POST /api/cufes X-Api-Key

Devuelve únicamente el listado del catálogo, sin consultar cada documento. Es mucho más rápido y sirve para saber qué hay antes de decidir qué extraer.

{
  "success": true,
  "total":   6,
  "data": [{
    "cufe":          "1ac814c2df59984f...",
    "tipoDoc":       "01",
    "folio":         "DPOS73771",
    "fechaEmision":  "26-07-2026",
    "nitEmisor":     "901772356",
    "nombreEmisor":  "GRUPO RC S.A.S.",
    "estado":        "Aprobado",
    "EstadoRadian":  "No Aplica",
    "valor":         "$ 246.850",
    "valorNumerico": 246850
  }]
}

valor viene con formato de moneda para mostrarlo tal cual; valorNumerico trae el mismo importe como número, para que no tenga que interpretar el texto al hacer cálculos o comparaciones. Puede llegar con decimales.

POST /api/ubl2.1/xml/by-cufes X-Api-Key

Entrega el XML original firmado de una lista de CUFE, en base64. Hasta 500 documentos por llamada. No requiere que su empresa esté registrada en el servicio: el certificado viaja en la petición.

{
  "certificate": "<contenido del .p12 en base64>",
  "password":    "<contraseña del certificado>",
  "ambiente":    1,                 // 1 producción · 2 habilitación
  "cufes":       [ "1669c097...", "18a20b3d..." ]
}
{
  "success": true, "total": 2, "ok": 2,
  "data": [{
    "cufe":      "1669c097...",
    "success":   true,
    "xmlBase64": "PD94bWwgdmVyc2lvbj0iMS4wIi..."
  }]
}

Alternativas

Estas tres usan token de empresa, lo que exige que cada empresa esté registrada en el servicio con su certificado. Sirven cuando se opera una sola empresa, no cuando son muchas.

POST /api/extractor/company Bearer

Inicia una extracción completa. Responde de inmediato con un identificador de trabajo, sin esperar a que termine el proceso. Es el endpoint principal.

{
  "tipo":       "Received",
  "fechaDesde": "2026-07-01",
  "fechaHasta": "2026-07-31",
  "url":        "https://catalogo-vpfe.dian.gov.co/User/AuthToken?..."
}
{
  "success":   true,
  "jobId":     "19e1f865-8d68-4b93-8f1a-b162eb27ca08",
  "statusUrl": "https://.../api/extractor/company/jobs/19e1f865-..."
}
GET /api/extractor/company/jobs/{jobId} Bearer

Consulta el avance y, al finalizar, entrega el resultado. Repita la llamada cada 15–20 segundos hasta que status sea done.

{
  "status":    "running",      // running · done · error
  "stage":     "enriching",
  "total":     6,
  "processed": 5,
  "log":       [ "..." ],
  "data":      [ /* documentos, al terminar */ ]
}
POST /api/extractor/company/stream Bearer

Hace todo el proceso en una sola llamada y responde al terminar. no recomendado

Mantiene la conexión abierta durante todo el proceso, por lo que el servidor web la corta con un error 504 en cuanto hay unos pocos documentos. Úselo solo para pruebas rápidas con rangos mínimos; para todo lo demás use el flujo asíncrono.

07

El XML que recibe

El campo xmlBase64 contiene el documento UBL 2.1 firmado, tal como lo tiene la DIAN. No es un extracto ni una conversión: es el archivo original, con validez legal, que usted procesa y conserva.

Qué trae

  • La firma digital XAdES del emisor y la validación de la DIAN.
  • Las extensiones DIAN (sts:DianExtensions) con el CUFE, la resolución de numeración y el rango autorizado.
  • Emisor y adquiriente completos, con identificación, régimen y ubicación.
  • Totales, desglose de impuestos, retenciones y el detalle de todos los renglones.
  • Notas, medios de pago, descuentos y cargos.

Cómo obtenerlo

Decodifique el base64 y guarde el resultado como archivo .xml:

# PHP
file_put_contents("$cufe.xml", base64_decode($doc['xmlBase64']));

# Python
open(f"{cufe}.xml", "wb").write(base64.b64decode(doc["xmlBase64"]))

El contenido decodificado empieza siempre por <?xml version="1.0"...><Invoice ...> (o CreditNote / DebitNote según el tipo de documento).

Nota

Junto al XML, la respuesta incluye algunos campos ya extraídos (CUFE, emisor, totales) que sirven para identificar el documento sin abrirlo. Si su sistema procesa el XML directamente, puede ignorarlos.

08

Flujo recomendado

Una llamada por empresa y período, sin certificado, sin trabajos asíncronos y sin consultas de avance.

  1. Obtener un enlace AuthToken nuevo de la DIAN para esa empresa.
  2. POST /api/xml-sin-certificado con el rango de fechas → devuelve cada documento con su XML ya interpretado.
  3. Revisar conteo: si fallidos no es cero, reintentar ese período.
  4. Guardar lo recibido y causarlo en su sistema.
  5. Repetir con tipo: "Sent" si también necesita las ventas.
# todo en una llamada, sin certificado
curl -X POST https://<url-base>/api/xml-sin-certificado \
  -H "X-Api-Key: <api_key>" -H "Content-Type: application/json" \
  -d '{"tipo":"Received","fechaDesde":"2026-08-01","fechaHasta":"2026-08-31","url":"<authtoken>"}'
Parta por meses

El tope es de 400 documentos por llamada. Un mes por empresa casi siempre cabe, es fácil de reintentar y encaja con el cierre contable. Si un mes se pasa, la respuesta le dice en qué tramos partirlo.

La vía anterior, con certificado

Sigue vigente y sin cambios. Son dos llamadas, y la segunda exige el .p12 de la empresa. Tiene sentido cuando ya dispone de los CUFEs por otro medio, o cuando necesita un caudal que no dependa del ritmo del portal de la DIAN.

# 1 · listar
curl -X POST https://<url-base>/api/cufes \
  -H "X-Api-Key: <api_key>" -H "Content-Type: application/json" \
  -d '{"tipo":"Received","fechaDesde":"2026-07-01","fechaHasta":"2026-07-31","url":"<authtoken>"}'

# 2 · traer los XML (en lotes)
curl -X POST https://<url-base>/api/ubl2.1/xml/by-cufes \
  -H "X-Api-Key: <api_key>" -H "Content-Type: application/json" \
  -d '{"certificate":"<p12_base64>","password":"<clave>","ambiente":1,"cufes":["..."]}'

Cuando son muchas empresas

El esquema escala sin configuración adicional, y esa es su principal ventaja:

  • Una sola API Key para toda la operación, sin importar cuántas empresas maneje.
  • Por la vía sin certificado no hay certificados que gestionar, que es lo que más pesa cuando son muchas empresas: ni pedirlos, ni custodiarlos, ni renovarlos al vencer.
  • Si usa la vía con certificado, los .p12 se quedan de su lado: no hay que cargarlos ni registrarlos en el servicio, viajan en cada petición.
  • Lo único imprescindible por empresa es su enlace AuthToken.
El punto que más trabajo cuesta

El enlace AuthToken es uno por empresa y caduca. Con muchas empresas, conseguir los enlaces vigentes es la parte más pesada del proceso — conviene planificarla antes de automatizar el resto.

09

Límites y rendimiento

AspectoLímiteQué significa
Documentos por llamada400Tope de /api/xml-sin-certificado. Si el rango los supera, se rechaza antes de descargar nada y se sugiere cómo partirlo.
CUFEs por llamada500Tope de /api/ubl2.1/xml/by-cufes. Es el máximo que acepta, no el recomendado — vea abajo.
Llamadas totalessin topePuede paginar tantos lotes como necesite.
Rango de fechassin topeUn rango amplio simplemente tarda más en listar.

El límite real es el tiempo, no la cantidad

El servicio consulta la DIAN una vez por cada documento, de forma secuencial. Un lote de 500 CUFEs son 500 consultas encadenadas, y puede tardar más de lo que el servidor web permite mantener abierta una petición.

Tamaño de lote sugerido

Use lotes de 100 a 150 CUFEs. Para afinarlo a su servidor, envíe un lote de 10 y mida cuánto tarda: divida ese tiempo entre 10 y aplique lote = 300 s ÷ segundos_por_documento. Lotes más pequeños además son más fáciles de reintentar cuando algo falla.

La vía sin certificado va al ritmo del portal

/api/xml-sin-certificado no consulta el servicio web de la DIAN sino su portal, y ese portal limita por dirección IP. Medido sobre 300 documentos:

Pausa entre peticionesAciertoDocumentos/min
sin pausa63 %—
600 ms95 %59
1.000 ms100 %44

Ir más deprisa no entrega más documentos: las peticiones rechazadas también gastan cupo. El servicio mantiene el paso de una por segundo y reintenta lo que la DIAN frene, así que no hay nada que ajustar del lado del cliente.

El cupo es del servidor, no de la empresa

Ese límite lo comparten todas las empresas que salgan por la misma IP. Extraer varias en paralelo no multiplica el caudal, solo lo reparte: el techo de un servidor son unos 2.600 documentos por hora en total. Para subirlo hay que repartir las extracciones entre varias direcciones de salida.

Tiempos de referencia

OperaciónTiempoPor qué
Listar un período~1 minCasi todo es abrir el navegador y autenticarse contra el portal; el listado en sí se resuelve en pocas consultas.
Un XML por CUFE~2 sConsulta directa al servicio web, sin navegador.
Listado y XML, sin certificado~1,4 s/docRitmo impuesto por el portal. Un mes de 160 documentos son unos 4 minutos; 400, unos 9.
Repetir un rango ya extraídoinmediatoLos XML descargados quedan guardados y no se vuelven a pedir.
Configuración del servidor

El listado puede rozar o superar el minuto, así que el servidor web debe permitirlo. Con los valores por defecto (60 s) la petición se corta con un error 504. Solicite a su proveedor que los tiempos de espera estén en 600 segundos: sobra margen a propósito, porque un rango amplio o un día lento del portal de la DIAN pueden alargar la consulta.

Recomendaciones

  • Extraiga por períodos mensuales y por empresa. Es más fácil de reintentar que un rango de un año.
  • Programe el proceso en tareas de fondo o nocturnas, nunca dentro de una petición de usuario.
  • No vuelva a pedir lo que ya tiene. Guarde por CUFE y consulte solo lo nuevo.
  • Compare ok contra total en cada respuesta y reintente solo los CUFEs que fallaron.
  • Procese los lotes en serie, no en paralelo, para no saturar el servicio ni la DIAN.
10

Almacenamiento de los XML

Punto clave

El servicio no archiva los documentos. Los consulta a la DIAN, se los entrega en la respuesta y no conserva copia. Si necesita los XML para efectos contables o de auditoría, guardarlos es responsabilidad de su sistema.

Al recibir un xmlBase64, decodifíquelo y persístalo. Una convención simple:

almacenamiento/xml/<NIT>/<AAAA-MM>/<CUFE>.xml

Guarde también el CUFE en su base de datos como identificador único del documento: le sirve para no volver a descargarlo y para cualquier consulta posterior.

Ventaja del diseño

Que el servicio no almacene nada significa que su información no queda duplicada en un tercero, y que siempre recibe el documento tal como lo tiene la DIAN en ese momento.

11

Errores

CódigoSignificadoQué hacer
401Credencial ausente o incorrectaRevise el encabezado Authorization o X-Api-Key.
422Datos inválidos, o empresa sin certificado configuradoVerifique el formato de fechas (AAAA-MM-DD) y que el enlace de la DIAN esté completo.
422
con rangos_sugeridos
El rango supera los 400 documentos de /api/xml-sin-certificadoNo es un fallo y no se descargó nada. Parta el rango por los tramos que devuelve la respuesta, o use limite.
404Trabajo inexistente o vencidoPasaron más de 2 horas. Inicie la extracción de nuevo.
502No hay comunicación con el componente de extracciónReintente en unos minutos; si persiste, avise a Ágil Factura.
504La petición excedió el tiempo permitidoOcurre al usar /stream. Cambie al flujo asíncrono.

Errores por documento

Una extracción puede terminar correctamente y aun así tener documentos individuales con error. Revise siempre los contadores del resultado:

  • Procesados — cuántos se intentaron.
  • OK — cuántos se obtuvieron completos.
  • No coincide — el adquiriente del documento no corresponde a su NIT.
  • Error — la DIAN no devolvió el documento. Reintente esos CUFE por separado.
12

Consumo

El servicio se factura por lo que se usa, y usted puede consultarlo cuando quiera con su misma API Key. La idea es que vea exactamente la misma cifra que vemos nosotros, sin tener que pedirla.

Qué se mide

Consultas — cada llamada a /api/cufes, sin importar cuántos CUFE devuelva el listado. Documentos — cada XML solicitado a /api/ubl2.1/xml/by-cufes. Consultar su consumo no se cobra.

GET /api/mi-consumo X-Api-Key

Sin parámetros devuelve el mes en curso. Acepta desde y hasta en formato AAAA-MM-DD para consultar otro período.

{
  "success": true,
  "cliente": "Su Empresa S.A.S.",
  "periodo": { "desde": "2026-08-01", "hasta": "2026-08-31" },
  "consumo": { "consultas": 50, "documentos": 400 },
  "importe": {
    "cuota_mensual":       300000,   // fija, se suma al consumo
    "precio_consulta":     280,
    "subtotal_consultas":  14000,
    "tramo_documento":     "1 – 500",
    "precio_documento":    150,
    "subtotal_documentos": 60000,
    "consumido":           74000,
    "total":               374000,
    "moneda":              "COP"
  },
  "detalle": [
    { "endpoint": "cufes", "unidad": "consulta",
      "total": 50, "llamadas": 50 },
    { "endpoint": "xml/by-cufes", "unidad": "documento",
      "total": 400, "llamadas": 4 }
  ]
}

El total es la cuota fija mensual más el consumo. Las tarifas del ejemplo son las de referencia; las suyas son las de su contrato y el endpoint siempre devuelve las que se le aplican a usted.

El precio del documento depende del volumen del mes

El tramo_documento lo define el total del mes y se aplica a todos los documentos, incluidos los primeros — no es un cálculo por escalones. Por eso, mientras el mes no cierre, el importe es una estimación: si el consumo sigue creciendo y cambia de tramo, el precio unitario baja y el total puede resultar menor que el que vio a mitad de mes.

13

Checklist de integración

Antes de dar por terminada la integración, verifique que su sistema:

  • Solicita un enlace AuthToken nuevo por empresa en cada extracción, sin reutilizar el anterior.
  • Usa /api/cufes y /api/ubl2.1/xml/by-cufes como flujo principal.
  • Divide los CUFEs en lotes de 100 a 150, ajustados a la medición de su servidor.
  • Procesa los lotes en serie, no en paralelo.
  • Almacena los XML por su cuenta, indexados por CUFE, y no vuelve a pedir lo ya descargado.
  • Compara ok contra total y reintenta solo los CUFEs fallidos.
  • Extrae compras y ventas por separado (Received y Sent).
  • Corre en un proceso de fondo o tarea programada, no dentro de una petición de usuario.
  • Guarda las credenciales y los certificados fuera del código, y los consume solo desde el servidor.
  • Reintenta con espera progresiva cuando la DIAN no responde.
  • Confirmó con su proveedor que los tiempos de espera del servidor están en 600 segundos.
  • Revisa /api/mi-consumo periódicamente y vigila el conteo de consultas y documentos.