Facturación electrónica
Emisión de facturas de venta, documento soporte, notas crédito y débito, y notas de ajuste.
Esta página se lee antes de ejecutar: explica qué vas a enviar y qué se hace con ello. Al final están los enlaces a las funciones, en el orden en que conviene probarlas.
1 · Qué es un documento electrónico
Un documento electrónico es una factura —o la nota que la ajusta— que existe como un XML firmado y que la DIAN valida y almacena. El emisor no le manda un archivo a su cliente y ya: lo reporta a la DIAN, la DIAN lo valida y devuelve un identificador único con el que ese documento queda referenciado para siempre.
El Proveedor Tecnológico es quien hace ese trabajo por cuenta del emisor. Tu integración le entrega un JSON con los datos del negocio; el Proveedor Tecnológico calcula el identificador, verifica el documento, construye el XML en el estándar UBL 2.1, lo firma y lo reporta. Lo que recibes de vuelta es el identificador y el estado con el que la DIAN lo recibió.
Ese identificador se llama distinto según el documento —CUFE en las facturas, CUDE en las notas, CUDS en el documento soporte— pero cumple siempre la misma función: es la huella del documento. Se calcula a partir de los totales, de la identidad de las partes y, en las facturas, de la clave técnica de la resolución. Cambiar un centavo cambia la huella.
2 · Qué documentos existen
Son seis, y el campo que decide cuál estás emitiendo es tddocumentoelectronico.
El mismo JSON y el mismo endpoint sirven para todos: lo que cambia es ese código y los bloques que ese
código exige.
Dos consecuencias prácticas que conviene tener claras antes de probar: en el documento soporte el emisor es quien compra y el receptor quien vende —es el error conceptual más común al integrarlo— y una nota nunca modifica la factura, la acompaña referenciándola.
3 · El ciclo de un documento
Entre tu JSON y la respuesta de la DIAN hay cinco pasos, y todos pueden fallar por razones distintas. Saber en cuál estás es la diferencia entre corregir el dato correcto y cambiar cosas al azar.
- 1 · JSON — Tu integración envía el documento con los datos del negocio.
- 2 · Verificación — El servidor aplica sus reglas y acumula todas las que se incumplan, no se detiene en la primera.
- 3 · XML UBL 2.1 — Se construye el documento en el estándar de la DIAN, con los totales redondeados a los decimales que pediste. Se genera en UTF-8 y el documento lo declara en su primera línea: leerlo con otra codificación convierte cada tilde en dos caracteres raros.
- 4 · Firma — Se firma con XAdES-BES. Aquí se sella el contenido: cualquier cambio posterior invalida la firma.
- 5 · Reporte a la DIAN — Se envía y se espera su respuesta. Es el paso más lento y el que no depende de ti.
Lo primero que se mira es la numeración del documento. Si nunca se reportó, el documento sigue el proceso completo. Si ya se reportó, se compara con el que está guardado: llegando igual responde 200 con el estado del documento, su UUID y el documento adjunto, sin volver a reportarlo ni gastar otro consecutivo; llegando con datos distintos responde 409 con la regla 90 de la DIAN y entrega el XML del documento que sí quedó reportado. Para el documento nuevo siguen las validaciones: un incumplimiento responde 400 con una entrada por regla, cada una con su código de la DIAN, su código de InSoft y su mensaje. Solo cuando las pasa se construye el documento electrónico, se firma, se reporta a la DIAN y se espera su respuesta. Si la DIAN lo rechaza, el 400 trae sus mensajes; si no se pudo establecer conexión con ella, responde también 400 pidiendo reintentar más tarde, y en ese caso el documento no quedó reportado. Las validaciones van antes de construir, así que un documento que no va a ser aceptado se rechaza sin gastar un consecutivo.
Desliza el diagrama para verlo completo.
Ver la fuente Mermaid del diagrama
flowchart TD
A(["El documento llega en JSON"]) --> B{"¿Ya se reportó esa numeración?"}
B -- Sí --> G{"¿Llega igual que antes?"}
G -- Sí --> R0(["200 · estado, UUID y documento adjunto,<br/>sin volver a reportarlo"])
G -- No --> R1(["409 · regla 90, entrega el XML del documento"])
B -- No --> C{"¿Cumple las reglas?"}
C -- No --> R2(["400 · una entrada por regla incumplida,<br/>con su código DIAN y su código InSoft"])
C -- Sí --> D["Construye el documento y lo firma,<br/>lo reporta y espera a la DIAN"]
D --> E{"¿La DIAN lo aceptó?"}
E -- No --> R3(["400 · el rechazo de la DIAN, o «intente más tarde»<br/>si no hubo conexión"])
E -- Sí --> H(["200 · estado del documento, su UUID<br/>y el documento adjunto"])4 · Los códigos td… del nivel raíz
Casi todo campo que empieza por td es un código de catálogo de la DIAN, no un texto libre. El tipo de documento, el tipo de operación,
la forma de pago, la clase de tributo, el motivo de una nota: todos se escriben con el código que la DIAN
publica en su anexo técnico, y el nombre solo existe para que un humano lo lea.
Esto importa por cómo falla. Los enumerados son tolerantes: si el valor no pertenece al catálogo el
servidor no rechaza la petición, asigna el valor vacío y la validación posterior lo reporta. Un tddocumento escrito como 3 en lugar de 31 no produce un error de formato: produce un rechazo por falta de tipo de documento, que es un mensaje que
no apunta al verdadero problema.
Por eso en la consola de ejecución todo dato con catálogo se escoge en una lista, nunca se teclea, y la tabla Datos que recibe de cada ficha lista los valores admitidos con su código por delante.
Desliza la tabla para verla completa.
Estos cuatro son los del nivel raíz: los que se resuelven antes de armar cualquier bloque, porque deciden qué documento es y qué le va a exigir el servidor. Los demás códigos viven dentro de un objeto —el del tercero, del medio de pago, del descuento, del ítem, del mandante, del transporte, del periodo del servicio y de la nota— y están en la tabla de ese objeto, en la sección siguiente.
5 · Anatomía del JSON
El cuerpo de la petición es un objeto llamado TDocumentoFE,
y se lee por bloques: cada uno responde una pregunta del negocio. El detalle campo por campo está en
las tablas que siguen.
Nivel raízQué documento es, cómo se numera y con cuántos decimales se calculan sus totales.
resolucionCon qué rango de numeración autorizado por la DIAN se emite.
emisor · receptorQuién factura y a quién. Los dos llevan la misma estructura.
itemsQué se vendió: al menos una línea, con su cantidad, su precio y sus tributos.
tributos · resumentributosLos impuestos y las retenciones, por línea y consolidados.
mediosdepagoCómo se paga, y cuándo si es a crédito.
descuentoscargos · anticiposLo que baja o sube el valor a pagar.
datosnota · docsreferenciaSolo en las notas: el motivo del ajuste y el documento que corrige.
- Los nombres no distinguen mayúsculas, y lo desconocido se ignora sin error.
numerodocumentoyNumeroDocumentoson el mismo campo, y una clave que el servidor no lee —comofhcreen los ejemplos— no estorba. - Las fechas van en ISO 8601 con zona —
"2026-08-06T10:30:00-05:00"— y el servidor opera en UTC-5. La del documento es opcional: sinfechadocumentose usa el momento en que se recibe, y por eso los ejemplos no la traen. - Todo valor se castea, nunca se rechaza por tipo.
"100000"y100000son equivalentes, y un texto no numérico se convierte en cero. De ahí que un error de formato se manifieste como una regla de negocio incumplida y no como un error de lectura del JSON.
Los títulos que siguen son los objetos que se repiten a lo largo del documento. La columna «Tipo»
de la tabla de datos de cualquier ficha enlaza aquí: es donde se dice qué va dentro de un TUbicacion o de un Array<TImpuesto>.
El asterisco marca lo que hay que enviar, igual que en la tabla de datos de cada ficha, y «no se envía» los que calcula el Proveedor Tecnológico: van en la respuesta y en el XML, pero mandarlos no cambia nada porque se recalculan siempre. Lo que depende de otro dato lo dice la explicación del atributo.
TResolucion
Dónde llega: resolucion del documento
La autorización de numeración que la DIAN le dio al emisor: qué prefijo usa, qué rango de consecutivos tiene y hasta cuándo. En el sandbox no se envía: el servicio asigna la que tiene registrada y descarta la que llegue.
Desliza la tabla para verla completa.
- En nota crédito, nota débito y nota de ajuste no se valida el número ni el rango ni la vigencia: basta el
prefijo. Los ejemplos van con valores neutros. - El mismo objeto lo usa nómina, donde solo aporta el prefijo.
TItem
Dónde llega: items, una línea del documento por elemento
Una línea: qué se vendió, cuánto, a qué precio y con qué tributos. Al menos una, con cantidad mayor que cero. El número de línea no se envía: es la posición dentro del arreglo.
Desliza la tabla para verla completa.
- El precio, la cantidad y los tributos de cada línea son la base de todos los totales del documento y del identificador: un centavo de diferencia cambia el UUID y la DIAN rechaza.
TMandante
Dónde llega: items[].mandante, en operación de mandato
El tercero por cuenta de quien se factura la línea. Todo ítem lo exige cuando la operación es un mandato —tdoperacionfe = 11—.
Desliza la tabla para verla completa.
TTransporteCarga
Dónde llega: items[].datostransporte, en transporte de carga
La remesa que respalda la línea. Todo ítem lo exige cuando la operación es transporte de carga —tdoperacionfe = 12—.
Desliza la tabla para verla completa.
TPeriodoItem
Dónde llega: items[].periodoservicio, en documento soporte
Cuándo se hizo la compra que respalda la línea. Todo ítem del documento soporte lo exige.
Desliza la tabla para verla completa.
TTercero
Dónde llega: emisor y receptor del documento; en nómina es el empleador, que es el único tercero que ese documento lleva
Las dos partes del documento, con la misma estructura: quién es, cómo se identifica ante la DIAN, dónde está y a dónde se le notifica. En el documento soporte los papeles se invierten: el emisor es quien compra.
Desliza la tabla para verla completa.
- El emisor es colombiano: el país de sus dos ubicaciones tiene que ser
COy su documento un NIT. En documento soporte y nota de ajuste la exigencia solo aplica contdoperacionfe = 10, donde el emisor puede ser un no residente. - El receptor no: admite terceros del exterior con cualquier
codigopais.
TUbicacion
Dónde llega: emisor.ubicacion, emisor.ubicacionfiscal, las dos del receptor y entrega
Una dirección con la codificación que exige el anexo técnico de la DIAN: el municipio y el departamento van por su código, no por su nombre. El listado de municipios de Colombia que adopta ese anexo es el que fija esos códigos.
Desliza la tabla para verla completa.
TImpuesto
Dónde llega: items[].tributos y resumentributos
Un tributo: su clase, su base, su valor y si se calcula por tarifa o por unidad. El servidor separa solo los impuestos de las retenciones —05 ReteIVA, 06 ReteRenta, 07 ReteICA— y los agrupa por clase.
Desliza la tabla para verla completa.
resumentributostiene que ser la suma por clase de los tributos de los ítems. De ahí salen el total con impuestos, el UUID y el QR: si no cuadra, la DIAN rechaza el documento.- Las retenciones no se emiten en nota crédito, nota débito ni nota de ajuste.
TMedioPago
Dónde llega: mediosdepago, un elemento por medio
Cómo se paga el documento. El Proveedor Tecnológico lo exige en todo documento de facturación —factura, documento soporte y sus notas—: sin él responde la regla AN01.
Desliza la tabla para verla completa.
TCuentaBeneficiario
Dónde llega: mediosdepago[].cuentabeneficiario
La cuenta a la que se abona el pago, cuando el medio lo tiene: una transferencia o una consignación. Todo el bloque es opcional, y va dentro del medio de pago, no del documento.
Desliza la tabla para verla completa.
- Cada campo se emite solo si llega, y ninguno es obligatorio: una cuenta con solo el número es válida. Lo que decide si el bloque existe en el XML es que
cuentabeneficiariovenga en el medio de pago.
TDescuentoCargo
Dónde llega: descuentoscargos del documento y items[].descuentoscargos
Un descuento o un recargo. El mismo objeto sirve en los dos niveles, y el nivel cambia el efecto: el de la línea reduce la base gravable, el del documento no.
Desliza la tabla para verla completa.
- El descuento de una línea se resta de su total bruto, y por eso baja la base gravable. El del documento no toca el bruto: se refleja en el valor a pagar.
- El total de descuentos del documento no puede superar su total bruto.
TAnticipo
Dónde llega: anticipos
Un pago recibido antes de emitir el documento. Su total se resta del valor a pagar.
Desliza la tabla para verla completa.
- No se emiten en nota crédito ni en nota de ajuste.
TNota
Dónde llega: datosnota, y solo en las notas
Qué clase de nota es y por qué se ajusta el documento. Es obligatorio en nota crédito, nota débito y nota de ajuste: es lo que convierte el documento en la corrección de algo concreto.
Desliza la tabla para verla completa.
- En la nota crédito y en la débito, envía siempre
tddocumentonota: es contra su valor que el servidor resuelve el catálogo del motivo. El orden de las claves en el objeto no importa; que esté, sí. - En la nota de ajuste, déjalo fuera. El ajuste es el caso por defecto: sin
tddocumentonotael motivo se lee con el catálogo del ajuste, que es el que le corresponde. Enviar20ahí no rompe el XML —el campo no llega a él—, pero hace que el código del motivo signifique otra cosa.
TDocumentoElectronico
Dónde llega: docsreferencia y docsadicionales de las notas, y la referencia de la nota de ajuste de nómina y del evento
Un documento ya reportado al que este se refiere. No es el documento que se emite: es la identificación del que se corrige o del que se acepta.
Desliza la tabla para verla completa.
- Cuando la nota no referencia un documento —
22y32—, este bloque va vacío y el periodo de facturación pasa a ser obligatorio.
TMoneda
Dónde llega: moneda del documento
La divisa en la que se factura. El XML siempre se expresa en pesos: con este bloque el servidor agrega además los totales convertidos, y solo en facturas.
Desliza la tabla para verla completa.
- En notas y en documento soporte el bloque se acepta pero no se emiten los totales convertidos: esa extensión es solo de las facturas.
6 · Qué valida el servidor
Antes de construir el XML, el servidor recorre el documento y aplica más de 140 reglas. No se detiene en
la primera que falla: acumula todas y responde 400 con la lista completa. Es deliberado, y es lo que hace que valga la pena leer la respuesta entera en lugar
de corregir de a un campo.
Cada regla incumplida llega con dos códigos y un mensaje:
codigodian— el código de la regla en el anexo técnico de la DIAN. Es el que se cita cuando hay que consultar la norma.codigoinsoft— el código propio de InSoft. Cuando una sola regla de la DIAN cubre varias verificaciones distintas, este las diferencia con sufijos_1y_2. Es el que te dice exactamente que verificación fallo.mensaje— que hay que corregir, en español.
Las reglas se agrupan por bloque del documento y el prefijo del código lo delata: AB son las de la resolución, AD las generales del documento, y así con el resto. Un grupo entero de rechazos con el mismo prefijo casi siempre significa que falta un bloque completo, no que haya diez errores distintos.
La función que te deja ver esa lista sin gastar nada es la generación del XML: recorre la verificación completa y devuelve las reglas incumplidas sin reportar el documento a la DIAN.
Recorre el mismo camino de la emisión pero se detiene antes de la DIAN. Con el documento en JSON calcula el UUID que tendría —CUFE, CUDE o CUDS— y lo valida contra las reglas: si alguna se incumple responde 400 con una entrada por regla, cada una con su código y su mensaje. Si las cumple, construye el documento electrónico, lo firma y devuelve el XML como application/xml, no como JSON: la respuesta es el documento mismo y no un objeto que lo envuelva. El documento no se reporta, así que no gasta consecutivos y la prueba se puede repetir sin costo. A diferencia de la emisión, aquí solo se fuerza el ambiente: no se reemplazan emisor, receptor ni resolución, y la contrapartida es que el CUFE obtenido es el real solo si la resolución y su clave técnica son las verdaderas del emisor.
Desliza el diagrama para verlo completo.
Ver la fuente Mermaid del diagrama
flowchart TD
A(["El documento llega en JSON"]) --> D["Calcula el UUID del documento<br/>CUFE · CUDE · CUDS"]
D --> F{"¿Cumple las reglas?"}
F -- No --> G(["400 · una entrada por regla incumplida,<br/>con su código DIAN y su código InSoft"])
F -- Sí --> H["Construye el documento electrónico<br/>y lo firma"]
H --> J(["200 · el XML firmado, no se reporta a la DIAN<br/>application/xml"])7 · Y que hace el sandbox con esto
Tres procesos son iguales para todas las funciones del portal, así que se explican una sola vez, aquí. El
primero contesta por qué el documento que recibes de vuelta no es idéntico al que enviaste; el segundo,
por qué un reenvío puede responder 409 con la regla
90 de la DIAN —y por qué ese 409 no es un fracaso, porque trae el documento adjunto—; el tercero, por qué
una función puede responder 401 aunque tu JSON esté
perfecto.
El ambiente es siempre el de habilitación. La identidad del emisor y del receptor pasa a ser la de la empresa de pruebas, mientras se conservan la ubicación, las responsabilidades fiscales y los datos de contacto, porque son el caso de prueba del desarrollador. La resolución se reemplaza siempre por la del sandbox —en una nota, por su prefijo de nota—, sin conservar nada de la que llegue. La numeración la asigna el sandbox solo cuando el número llega con el valor `<auto>`; cualquier otro valor viaja tal como se escribió. Todo lo demás viaja tal como se envió —ítems, tributos, descuentos, anticipos y totales— y con eso se reporta a la DIAN. La respuesta corta es una línea: se reemplaza quién emite, no qué se factura.
Ver la fuente Mermaid del diagrama
flowchart TD
J(["Documento que envía el desarrollador"]) --> A["Ambiente: siempre habilitación"]
A --> B["Identidad del emisor y del receptor:<br/>la de la empresa de pruebas"]
B --> C["Se conservan ubicación, responsabilidades<br/>fiscales y datos de contacto"]
C --> D["Resolución y numeración:<br/>las del sandbox, si están registradas"]
D --> E["Todo lo demás viaja como se envió:<br/>ítems, tributos, descuentos y totales"]
E --> F(["Se reporta a la DIAN"])La numeración es la llave: un documento se identifica por el emisor, el tipo, el ambiente y su número. Si esa numeración no se ha reportado, el envío sigue su curso normal. Si ya se reportó, se vuelve a calcular el UUID del documento —el CUFE, el CUDE o el CUDS— y se compara con el del que está guardado: si coincide, el documento es el mismo y la respuesta es 200 con el que ya se había reportado, sin duplicarlo ni gastar otro consecutivo. Si no coincide, es decir si cambió cualquier dato que entra en el UUID, la respuesta es 409 con la regla 90 de la DIAN: rechazo porque el documento ya había sido enviado previamente. El 409 no es un callejón sin salida: trae el documento adjunto en base64, que es la forma de recuperar lo que ya se emitió. La misma regla 90 la puede aplicar la DIAN por su cuenta, aunque el Proveedor Tecnológico no tenga registro del documento, y la respuesta es la misma. Dos casos que conviene anticipar: la nota de ajuste de nómina responde 409 en todo reenvío, y en el sandbox enviar el número en «auto» evita el choque, porque cada envío toma un consecutivo nuevo.
Desliza el diagrama para verlo completo.
Ver la fuente Mermaid del diagrama
flowchart TD
A(["Se envía un documento<br/>con una numeración"]) --> B{"¿Esa numeración ya se reportó?"}
B -- No --> S(["Sigue el proceso normal de emisión"])
B -- Sí --> C{"¿El documento sigue siendo el mismo?"}
C -- Sí --> D(["200 · devuelve el que ya se reportó,<br/>sin duplicarlo"])
C -- No --> E(["409 · regla 90, rechazo, y entrega<br/>el XML del documento"])Toda función del sandbox pasa por la misma verificación. Si no llega el encabezado Authorization responde 400. Si llega pero el token no es válido, responde 401. Con el token válido comprueba que el desarrollador tenga perfil registrado y activo: si no lo tiene responde 401, y el mensaje distingue las tres causas —cuenta sin correo, perfil inexistente y perfil suspendido— para que no haya que adivinar cuál es. Lo observable es que un token válido no alcanza: la identidad sale siempre del token, y nada de lo que se envíe en la ruta o en el cuerpo cambia de qué desarrollador se trata. En la etapa productiva la compuerta comprueba además dos cosas contra el documento que se envía, y las dos responden 401: que el NIT del emisor sea el autorizado en la credencial, y que el tipo de documento electrónico corresponda al servicio autorizado —facturación no emite nómina, y al revés—. En el sandbox esas dos no se alcanzan a ver, porque el emisor lo fuerza el propio sandbox.
Desliza el diagrama para verlo completo.
Ver la fuente Mermaid del diagrama
flowchart TD
R(["Petición del desarrollador<br/>Authorization: Bearer idToken"]) --> T{"¿Viene el token?"}
T -- No --> E1(["400 · No se especificó el token<br/>de autenticación"])
T -- Sí --> F{"¿El token es válido?"}
F -- No --> E2(["401 · Token de autenticación no válido"])
F -- Sí --> Q{"¿Perfil registrado y activo?"}
Q -- No --> E4(["401 · Perfil no registrado o suspendido,<br/>con el motivo en el mensaje"])
Q -- Sí --> OK(["200 · Autorizado, continúa la operación"])8 · Ahora si, a probar
El orden importa, y no por pedagogía: importa porque equivocarse cuesta distinto en cada función.
- Antes que nada: consultar las resoluciones del emisor → Es una lectura: no gasta consecutivos ni emite nada. Devuelve los rangos que la DIAN tiene
autorizados con su prefijo, su vigencia y la
clavetecnica, que es el dato con el que se calcula el CUFE. Sin él la generación del XML no se puede completar. Si ya sabes con cuál vas a numerar, la consulta puntual te refresca su vigencia y su clave. - Después: generar el XML sin reportarlo → No gasta consecutivos, no llega a la DIAN y se puede repetir cuantas veces quieras. Devuelve las reglas incumplidas una por una y el XML firmado, para compararlo nodo por nodo con el que arma tu integración. Es donde sale barato equivocarse. Ojo: aquí la resolución completa y el consecutivo son obligatorios, porque esta función no numera ni registra resolución.
- Por último: enviar una factura electrónica de venta → Aquí el documento se reporta de verdad al ambiente de habilitación y la DIAN devuelve el CUFE. El
sandbox pone la identidad de la empresa de pruebas, su resolución y el consecutivo, de modo que
basta con dejar
numerodocumentoen<auto>. Con el CUFE que devuelva ya puedes probar las notas y los eventos.