Saltar al contenido principal
Escríbenos por WhatsApp
Documentación de las APIs

Kiai Facturación V1

Documentación de la API V1 de Facturación Electrónica

Conecta tu software y emite ante la DIAN facturas, notas crédito y débito y facturas del sector salud, con Kiai como proveedor tecnológico: tú envías el documento y los totales, y la API los verifica, los firma y los transmite.

  • OAuth 2.0client_credentials
  • DIANHabilitación y producción
  • DocumentosFactura, notas y salud
  • WebhooksCon firma verificable

1. Qué es, qué no es y dónde está

1.1 En una frase

  • Qué es: una API REST (JSON sobre HTTPS) con la que tu software emite y consulta facturas de venta, notas crédito/débito y facturas del sector salud ante la DIAN, usando a KIAI S.A.S (NIT 901337751) como proveedor tecnológico, que genera el XML UBL, calcula el CUFE/CUDE, firma, transmite a la DIAN, produce el PDF y el AttachedDocument y los entrega al comprador.
  • Qué no es: no calcula tus impuestos ni tus totales (tú los envías y la API los verifica al centavo), no emite tokens ni gestiona usuarios (eso lo hace el servicio de identidad de Kiai), no hace por ti los trámites ante la DIAN (habilitación, resoluciones de numeración) y no cubre nómina electrónica, RIPS ni el documento equivalente POS.

1.2 Ambientes y URLs

QuéURLAutenticación
API v1https://documentos.kiai.co/api/v1Token Bearer
Endpoint de tokenhttps://usuarios.kiai.co/api/oauth/tokenclient_id + client_secret
Descubrimiento del servicio de identidadhttps://usuarios.kiai.co/.well-known/openid-configurationPública
Versión desplegadahttps://documentos.kiai.co/api/buildinfoPública
Salud del proceso / de la basehttps://documentos.kiai.co/health/live · /health/readyPública
Estado de Kiai y del canal DIANhttps://documentos.kiai.co/api/v1/estado-serviciosPública
  • La API tiene un solo ambiente público, el de producción. El ambiente DIAN (habilitación o producción) no depende de la URL: es una propiedad de tu emisor (sección 3). Cada documento dice en qué ambiente se emitió (campo ambiente).
  • No hay Swagger en producción. El contrato es el archivo openapi.yaml (OpenAPI 3.0.3), que describe cada campo; su lista servers ya incluye https://documentos.kiai.co/api/v1.
curl -s https://documentos.kiai.co/api/buildinfo
# {"version":"1.0.0+4478.644b40e","commitSha":"644b40ed0d2df63142e72ef7713bc22a6c07db50","buildId":"4478","buildTime":"2026-09-24T20:12:03Z","environment":"prod"}

1.3 Convenciones

Tipo, longitud y obligatoriedad de cada campo que envías y recibes: Anexo C — Diccionario de campos.

TemaRegla
FormatoJSON UTF-8, nombres en camelCase, enumeraciones como texto ("emitida", "credito"). En las respuestas los campos opcionales vienen presentes con null: la forma del JSON es estable.
Contrato estrictoUna propiedad que no existe en el esquema de entrada responde 400 con la ruta del campo como clave (p. ej. lineas[0].tarifaIVA). Nada se descarta en silencio.
CompatibilidadKiai puede agregar campos nuevos a las respuestas, y códigos o estados nuevos en los campos que los llevan, sin cambiar de versión: tu integración debe ignorar los campos que no conozca y tratar un código o un estado desconocido como tal, sin fallar. Quitar o renombrar un campo, o cambiarle el tipo, exige una versión nueva (v2).
Tu empresaNunca viaja en el body, en la ruta ni en un encabezado: sale del token. Enviar emisorId responde 400 explicándolo.
IdentificadoresUUID.
FechasFechas AAAA-MM-DD; fechas-hora ISO 8601 con zona (p. ej. 2026-09-01T07:30:00-05:00). Las respuestas devuelven fechas-hora en UTC (Z).
MontosMáximo 2 decimales en montos, tarifas y porcentajes; máximo 6 en cantidades.
ListadosSubconjunto OData: $top (1 a 100, por defecto 20), $skip, $count=true, $filter, $orderby. Respuesta { "value": [...], "count": n } (count solo con $count=true). Un filtro no soportado responde 400.
CorrelaciónEnvía X-Correlation-Id (si no lo envías, se genera). Se devuelve en la respuesta y aparece como correlationId en los errores: dáselo a soporte.
IdempotenciaTodo POST acepta Idempotency-Key (texto opaco, p. ej. un UUID; el contrato admite hasta 200 caracteres). Ver abajo.
LímitesPor emisor. Superarlo responde 429 con Retry-After.
ErroresProblem Details (RFC 9457), application/problem+json, con el campo fuente (cliente, dian o kiai). Ver sección 9.

Idempotencia (Idempotency-Key). Úsala en todo POST que crea o emite:

  • Misma llave y mismo cuerpo ⇒ la misma respuesta, sin volver a procesar (no duplica documentos ni quema consecutivos).
  • Misma llave con otro cuerpo, o en otra ruta ⇒ 422. Usa una llave nueva por operación.
  • Si la primera petición todavía se está procesando ⇒ 409 con Retry-After: 2.
  • Solo se recuerda una respuesta exitosa (2xx), por emisor. Si la petición falló, la llave queda libre para reintentar con el dato corregido.
  • Trata cada llave como de un solo uso. Kiai la conserva al menos 48 horas.
  • No aplica al alta del emisor (todavía no existe el emisor al que asociarla).

Límites de uso. Por defecto: 600 solicitudes por minuto por emisor, y 60 emisiones por minuto por emisor (POST .../emision). Otro emisor no se ve afectado por tu consumo. Son los valores vigentes en producción.

1.4 El recorrido completo

  1. Creación. Tu empresa tiene cuenta en usuarios.kiai.co con el producto Facturación Electrónica activo, y un usuario administrador crea una credencial de servicio con los permisos de Facturación (sección 2.1).
  2. Autenticación. Tu software pide un token con resource=facturacion (sección 2.2) y lo usa en cada llamada (sección 2.3).
  3. Tu empresa en la API. Das de alta tu emisor en habilitación (sección 3.3): con el testSetId de la DIAN si es la primera vez que facturas con Kiai, o sin él si ya estás habilitado con Kiai (sección 3.2). Los cambios posteriores los hace Kiai (sección 3.8).
  4. Consultas tus numeraciones: la de pruebas SETT y las de notas NC/ND (sección 4).
  5. Documentos. Si te toca, emites el set de pruebas: creas cada documento en borrador, lo corriges si hace falta y lo emites (sección 5). Sigues el resultado por webhook o consultando el documento (secciones 6 y 8).
  6. Cuando la DIAN aprueba tu set (o de una vez, si ya estabas habilitado), Kiai te marca como habilitado.
  7. Solicitas en la DIAN tu numeración de producción, Kiai pasa tu emisor a producción (sección 3.6) y operas.

¿Eres aliado de Kiai? Si registras a tus clientes desde tu sistema, el recorrido empieza antes: los das de alta y los habilitas ante la DIAN por API. Ver la sección 11.


2. Autenticación

La API no tiene usuarios ni emite tokens. Acepta solo tokens JWT firmados (RS256) por el servicio de identidad de Kiai (usuarios.kiai.co), dirigidos a Facturación. La integración usa credenciales de servicio (client_credentials).

2.1 Obtén tu credencial (client_id / client_secret)

La credencial se crea por autoservicio, con la sesión de un usuario de tu empresa en usuarios.kiai.co, sección INTEGRACIONES:

  1. Elige un nombre (p. ej. “ERP producción”) y los permisos (scopes):
    • facturacion.emitir — crear, emitir, transmitir y notificar documentos.
    • facturacion.consultar — consultar documentos, PDF, XML y numeraciones. Los catálogos los lee cualquier token válido de Facturación.
  2. Recibes el client_id (empieza por kiai_live_) y el client_secret (empieza por ksc_). El client_secret se muestra una sola vez: guárdalo en tu bóveda de secretos.

Reglas:

  • La credencial pertenece a tu empresa: todo token que obtengas con ella opera a nombre de esa empresa.
  • Máximo 10 credenciales activas por empresa.
  • Rotar el secreto invalida el anterior de inmediato; revocar la credencial impide pedir tokens nuevos. En los dos casos, los tokens ya emitidos siguen valiendo hasta que expiran (máximo 60 minutos). Las dos acciones están en la misma sección INTEGRACIONES.
  • Recomendado: una credencial por sistema, con el mínimo de permisos. Un tablero que solo consulta documentos no necesita facturacion.emitir. Ojo: la descarga en lote (POST /descargas) sí lo exige, porque es un POST.
  • Si integras a nombre de varias empresas (aliado de Kiai), cada empresa cliente necesita su propia credencial: un token opera siempre a nombre de una sola empresa. Para registrar y habilitar a esas empresas por API, ver la sección 11.

En INTEGRACIONES los permisos aparecen como Facturación · emitir y Facturación · consultar. Necesitas un usuario administrador de tu empresa en usuarios.kiai.co; si aún no lo tienes, pídeselo a Kiai. Kiai nunca ve tu client_secret: lo generas y lo guardas tú.

2.2 Pide el token

POST https://usuarios.kiai.co/api/oauth/token, con los datos en el cuerpo (client_secret_post, el único método soportado; no uses autenticación HTTP Basic):

curl -s -X POST https://usuarios.kiai.co/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=<TU_CLIENT_ID>" \
  --data-urlencode "client_secret=<TU_CLIENT_SECRET>" \
  --data-urlencode "resource=facturacion"

También acepta JSON:

curl -s -X POST https://usuarios.kiai.co/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"client_credentials","client_id":"<TU_CLIENT_ID>","client_secret":"<TU_CLIENT_SECRET>","resource":"facturacion"}'

Respuesta:

{
  "access_token": "<TOKEN_JWT>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "facturacion.emitir facturacion.consultar"
}

Dos reglas que evitan la mayoría de los 401:

  • resource=facturacion es obligatorio (RFC 8707). Sin él, el token sale con una audiencia genérica y la API lo rechaza con 401 aunque la credencial sea correcta.
  • No envíes scope. Los permisos se fijan al crear la credencial; el endpoint de token ignora ese parámetro y el token trae todos los permisos de la credencial (los ves en el campo scope de la respuesta).

Si decodificas el token (su contenido no es secreto; su firma sí), debe traer aud: "facturacion", token_type: "m2m", tu companyId y uno o más scope.

2.3 Usa el token

API=https://documentos.kiai.co/api/v1
TOKEN=<TU_ACCESS_TOKEN>

# Prueba de humo: no exige emisor ni permisos especiales
curl -s -G "$API/catalogos/tipos-documento" --data-urlencode '$top=2' -H "Authorization: Bearer $TOKEN"

Antes del alta del emisor, cualquier llamada sobre tus documentos o numeraciones responde 403 «La empresa del token no tiene la facturación activa»: es lo esperado hasta la sección 3.3.

  • Vida del token: 60 minutos (expires_in: 3600). Guárdalo en memoria y reutilízalo; pide uno nuevo unos minutos antes de que expire o cuando recibas un 401 por expiración. No pidas un token por cada llamada.
  • La API tolera hasta 1 minuto de diferencia de reloj.
  • Un token de sesión del portal (el de un usuario que inició sesión) no sirve aquí: responde 401 porque fue emitido para otro destinatario.

2.4 Qué permiso exige cada operación

OperaciónPermiso requerido
Lecturas (GET)facturacion.consultar
Escrituras (POST, PUT, PATCH, DELETE)facturacion.emitir
Catálogos (/catalogos/...)Cualquier token válido de Facturación
estado-servicios, buildinfo, healthSin token

Un ERP que emite y consulta lleva los dos permisos. Si falta el que exige la operación, la API responde 403 nombrando cuál falta.

2.5 Errores de autenticación

Endpoint de token (usuarios.kiai.co), formato { "error": "...", "error_description": "..." }:

HTTPerrorCausa
400unsupported_grant_typegrant_type distinto de client_credentials.
400invalid_requestFalta client_id o client_secret.
400invalid_targetEl resource no existe o la credencial no tiene ningún permiso de ese recurso (p. ej. una credencial solo de Firma pidiendo facturacion).
401invalid_clientclient_id o client_secret inválidos, o credencial revocada.

API v1 — el detail del 401 dice el motivo real:

HTTPdetail (resumen)Qué hacer
401“El token es válido, pero fue emitido para otro destinatario…”Pide el token con resource=facturacion.
401“El token expiró…”Pide un token nuevo.
401“No se pudo verificar la firma del token…”El token no viene de usuarios.kiai.co o se alteró.
401“Falta el token Bearer o es inválido/expirado.”Revisa el encabezado Authorization: Bearer <token>.
403“La credencial de servicio no tiene el scope ’…’”Usa una credencial con ese permiso.
403“El token de Gestión de Usuarios no identifica a una empresa (claim companyId).”La credencial no está asociada a una empresa: contacta a Kiai.
403“La empresa del token no tiene la facturación activa.”Aún no das de alta tu emisor (sección 3.3), o Kiai lo desactivó.
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.2",
  "title": "No autenticado.",
  "status": 401,
  "detail": "El token es válido, pero fue emitido para otro destinatario. Esta API solo acepta tokens cuya audiencia (aud) sea la del producto Facturación. Un token de sesión del portal —que vale para todos los productos— no opera aquí: usa una credencial de servicio del producto Facturación.",
  "instance": "/api/v1/facturas",
  "traceId": "00-84b1...",
  "correlationId": "5952f26eedc741b2a2ce29648af4a222",
  "fuente": "cliente"
}

3. Alta del emisor y habilitación ante la DIAN

3.1 El emisor es tu empresa

En la API, el emisor es tu empresa como facturador: su id es el companyId de tu empresa y sale del token. No existe un encabezado ni un campo para elegir emisor; una credencial solo puede operar el emisor de su propia empresa.

Solo el id viene del servicio de identidad. Los datos fiscales (NIT, razón social, régimen, dirección…) son los que envías en el alta y viven en esta API: cambiar tu empresa en usuarios.kiai.co no cambia tus documentos. Para corregirlos, ver 3.8.

Hoy no aportas certificado de firma ni PIN: la API no los recibe y la firma de los documentos la resuelve Kiai como proveedor tecnológico.

3.2 Antes de empezar, en la DIAN

Si ya facturas electrónicamente con Kiai como proveedor tecnológico (por ejemplo, desde el portal de Kiai), ya estás habilitado ante la DIAN con el mismo software que usa esta API: no repites el set de pruebas.

  1. Da de alta tu emisor sin testSetId (3.3).
  2. Avísale a Kiai: te marca como habilitado (3.6) y acuerdan el paso a producción.
  3. Si quieres probar antes, emite documentos en habilitación: sin set de pruebas, la DIAN los valida en el momento. Si ya emitiste documentos de prueba con Kiai, fija el consecutivo siguiente por encima de los que usaste (4.2): la DIAN rechaza un número repetido («Regla: 90, Rechazo: Documento procesado anteriormente»).

Si es la primera vez que facturas con Kiai:

  1. Entra al portal de habilitación de la DIAN: empresas en https://catalogo-vpfe-hab.dian.gov.co/User/CompanyLogin, personas naturales en https://catalogo-vpfe-hab.dian.gov.co/User/PersonLogin. Abre el enlace que llega al correo de tu RUT.
  2. Ve a «Registro y habilitación» › «Documentos electrónicos» › «Factura electrónica» › «Configurar modos de operación». Elige el modo «Software de un proveedor tecnológico», selecciona KIAI S.A.S. (NIT 901337751) y pulsa «Asociar».
  3. En «Listado de modos de operación asociados», en la fila del software de Kiai, abre «Detalle de set de pruebas» y copia el TestSetId. Lo envías en el alta del emisor (3.3).

3.3 Da de alta tu emisor

POST /api/v1/emisores con el token de tu credencial. La empresa sale del token: no la envíes.

El testSetId va solo aquí. No lo pegues también en el paso de habilitación de usuarios.kiai.co: ese paso corre por su cuenta un set de pruebas automático contra el mismo identificador, y los dos se estorbarían. Si ya lo hiciste por el portal, estás en el caso de 3.2 «ya habilitado»: da de alta sin testSetId y avísale a Kiai.

curl -s -X POST "$API/emisores" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tipoDocumento": "31",
    "numeroDocumento": "<TU_NIT_SIN_DV>",
    "dv": "<TU_DV>",
    "tipoOrganizacion": "1",
    "razonSocial": "<RAZON_SOCIAL>",
    "regimen": "48",
    "responsabilidadesFiscales": "O-13",
    "direccion": "<DIRECCION>",
    "codigoMunicipio": "11001",
    "municipio": "Bogotá D.C.",
    "departamento": "Bogotá",
    "codigoPostal": "110111",
    "codigoPais": "CO",
    "correo": "<CORREO_DE_FACTURACION_DE_TU_EMPRESA>",
    "telefono": "<TELEFONO>",
    "testSetId": "<TU_TEST_SET_ID>",
    "ambiente": "habilitacion"
  }'
CampoObligatorioRegla
tipoDocumentoSíCódigo DIAN numérico (31 = NIT).
numeroDocumentoSíHasta 20 caracteres: dígitos, letras y guiones. Sin DV.
dvSi tipoDocumento = 31Un dígito; se valida contra el NIT con el algoritmo mod-11 de la DIAN.
tipoOrganizacionSí1 persona jurídica, 2 persona natural.
razonSocialSíHasta 450 caracteres. nombres y apellidos son opcionales.
regimenSíCódigo DIAN (48 responsable de IVA, 49 no responsable).
responsabilidadesFiscalesNoCódigos separados por ; (p. ej. O-13;O-15).
direccionSíHasta 300 caracteres.
codigoMunicipioSíCódigo DANE de exactamente 5 dígitos. municipio y departamento son obligatorios.
codigoPaisNoISO 3166-1 de 2 letras; por defecto CO.
correoSíCorreo de tu empresa. Kiai lo usa para avisarte cuando una numeración está por vencer o agotarse.
testSetIdPara el set de pruebasEl que te asignó la DIAN. Sin él, tus documentos no van al set de pruebas.
ambienteNoSolo habilitacion (por defecto). produccion responde 400: el paso a producción lo hace Kiai (3.6).
activoNoPor defecto true. No lo envíes: con false el emisor nace dado de baja y todo responde 403 hasta que Kiai lo reactive.

No envíes emisorId, empresaId, softwareId, softwarePin, certificadoSerial ni certificadoPin: responden 400 (los cuatro últimos son material de Kiai como proveedor tecnológico).

Respuesta 201 (con encabezado Location):

{
  "id": "<ID_EMISOR = companyId de tu empresa>",
  "tipoDocumento": "31",
  "numeroDocumento": "<TU_NIT_SIN_DV>",
  "dv": "<TU_DV>",
  "tipoOrganizacion": "1",
  "razonSocial": "<RAZON_SOCIAL>",
  "nombres": null,
  "apellidos": null,
  "regimen": "48",
  "responsabilidadesFiscales": "O-13",
  "direccion": "<DIRECCION>",
  "codigoMunicipio": "11001",
  "municipio": "Bogotá D.C.",
  "departamento": "Bogotá",
  "codigoPostal": "110111",
  "codigoPais": "CO",
  "correo": "<CORREO_DE_FACTURACION_DE_TU_EMPRESA>",
  "telefono": "<TELEFONO>",
  "testSetId": "<TU_TEST_SET_ID>",
  "ambiente": "habilitacion",
  "habilitadoDian": false,
  "activo": true,
  "fechaCreacion": "2026-09-23T15:04:05.123456Z",
  "fechaActualizacion": "2026-09-23T15:04:05.123456Z"
}

Errores propios del alta: 409 si tu empresa ya tiene emisor o si ya existe un emisor con ese documento; 403 si el token no identifica una empresa o si la credencial no tiene facturacion.emitir; 400 si envías ambiente: "produccion" o un campo que no te corresponde.

Revisa los datos antes de enviarlos. Después del alta, editar el emisor (incluido el testSetId y el ambiente) es una operación que solo hace Kiai. Los datos del emisor van en cada XML que firmas.

Para consultar tu emisor: GET /api/v1/emisores/{id} (solo tu propio id; cualquier otro responde 404).

3.4 Corre el set de pruebas

  1. Consulta tus numeraciones (GET /api/v1/numeraciones-autorizadas, sección 4). En habilitación recibes:
    • SETT (tipo factura, rango 1 a 5.000.000, resolución 18760000001),
    • NC (nota crédito) y ND (nota débito), desde 1, sin vencimiento.
  2. Crea y emite los documentos que exige tu set de pruebas usando el id de cada numeración como resolucionId (sección 5).
  3. Con testSetId y todavía sin habilitar, cada documento se envía al set de pruebas de la DIAN, que lo procesa de forma asíncrona: el documento queda transmisionEstado: "pendiente", con el mensaje “Enviada al set de pruebas de habilitación DIAN; procesamiento asíncrono (consultar por ZipKey).” y el ZipKey en transmisionTrackId.
  4. Kiai vuelve a consultar a la DIAN periódicamente (cada ~5 minutos por defecto) hasta obtener el veredicto; el documento pasa entonces a aceptada o rechazada y, si tienes webhook, te llega el evento.

Tenlo en cuenta:

  • Una nota con referencia exige que la factura esté aceptada. En el set de pruebas eso tarda unos minutos: espera el veredicto de la factura antes de crear sus notas (si no, la API responde 409).
  • Cuántos documentos de cada tipo exige tu set lo define la DIAN: lo ves en su portal, en «Detalle de set de pruebas».
  • En habilitación también se envía automáticamente al adquiriente cada documento que la DIAN acepta: usa buzones de prueba tuyos, nunca el correo de un cliente real.
  • Cuando la DIAN aprueba tu set, deja de procesar documentos de prueba. Si sigues enviando, esos documentos quedan rechazada con el mensaje «La DIAN no procesó el documento porque el set de pruebas del emisor ya fue aprobado…». Es la señal de avisarle a Kiai (3.6); después de que te marque habilitado, retransmítelos con POST /facturas/{id}/transmision (sección 6) y la DIAN los valida en el momento.

3.5 Sigue el avance

GET /api/v1/emisores/{id}/habilitacion (permiso facturacion.consultar):

{
  "emisorId": "<ID_EMISOR>",
  "ambiente": "habilitacion",
  "habilitadoDian": false,
  "testSetId": "<TU_TEST_SET_ID>",
  "documentos": [
    { "tipo": "factura", "total": 10, "aceptadas": 8, "rechazadas": 1, "pendientes": 1 },
    { "tipo": "nota-credito", "total": 1, "aceptadas": 1, "rechazadas": 0, "pendientes": 0 },
    { "tipo": "nota-debito", "total": 1, "aceptadas": 0, "rechazadas": 0, "pendientes": 1 },
    { "tipo": "documento-soporte", "total": 0, "aceptadas": 0, "rechazadas": 0, "pendientes": 0 }
  ]
}

Los conteos cuentan documentos emitidos (con número) según su estado de transmisión.

3.6 Habilitación y paso a producción

  1. Cuando la DIAN aprueba tu set de pruebas, avísale a Kiai. Kiai marca tu emisor como habilitado: en GET /emisores/{id}/habilitacion verás "habilitadoDian": true. Desde ese momento tus transmisiones ya no van al set de pruebas: la DIAN responde en el momento (aceptada o rechazada). Kiai reporta además el resultado a tu cuenta en usuarios.kiai.co. Avísale a Kiai por correo (hola@kiai.co) o por WhatsApp.
  2. Solicita en la DIAN tu numeración de facturación de producción y asóciala al software de Kiai. La API la toma directamente de la DIAN (no hay que registrarla en Kiai) y la verás con origen: "dian". Kiai te acompaña en ese paso.
  3. Kiai cambia tu emisor a ambiente: "produccion" (editar el emisor es una operación de plataforma). Verifícalo con GET /emisores/{id}.
  4. En producción, GET /numeraciones-autorizadas devuelve tu rango de la DIAN y unas NC/ND de producción, que empiezan de nuevo en 1. Las numeraciones de habilitación quedan en ese ambiente: emitir con ellas en producción responde 409.

3.7 Quién puede llamar cada operación del emisor

OperaciónEndpointQuién
Dar de alta tu emisorPOST /api/v1/emisoresTu empresa (el emisor sale del token).
Ver tu emisorGET /api/v1/emisores/{id}Tu empresa, solo con tu propio id.
Ver el avance de habilitaciónGET /api/v1/emisores/{id}/habilitacionTu empresa.
Editar, cambiar ambiente o dar de bajaPUT /api/v1/emisores/{id}Solo Kiai.
EliminarDELETE /api/v1/emisores/{id}Solo Kiai.
Marcar como habilitadoPOST /api/v1/emisores/{id}/habilitacionSolo Kiai.

Si tu credencial intenta un PUT o un DELETE sobre el emisor, recibe 403 «El token es válido pero no tiene permisos para este recurso.».

3.8 Cambiar los datos de tu empresa

Tu software no puede modificar el emisor: no hay PUT ni PATCH para ti. Revisa bien los datos del alta, porque van dentro de cada XML que se firma.

Para cambiar algo (dirección, correo, teléfono, responsabilidades fiscales, razón social):

  1. Pídeselo a Kiai (hola@kiai.co o WhatsApp) con todos los datos de tu empresa, no solo el que cambia. El cambio reemplaza el registro completo: un dato opcional que no envíes queda vacío.
  2. Kiai lo aplica y lo verificas con GET /api/v1/emisores/{id}.
  3. Los documentos ya emitidos no cambian: cada uno conserva los datos con que se firmó. El cambio aplica a los documentos que emitas después.

La baja también la hace Kiai: desde ese momento toda llamada de tu credencial responde 403. Kiai no puede eliminar un emisor que ya tiene documentos.


4. Numeraciones autorizadas

La DIAN es la fuente de verdad. No hay alta ni importación de resoluciones: GET /api/v1/numeraciones-autorizadas pregunta a la DIAN, en el ambiente de tu emisor, qué rangos tienes autorizados con el software de Kiai y los deja listos para emitir. Tú solo puedes mover el consecutivo a seguir (y poner un logo).

  • Rango con clave técnica ⇒ factura; sin clave técnica ⇒ documento-soporte.
  • En habilitación, si la DIAN no reporta el set de pruebas, se agrega SETT (1 a 5.000.000).
  • Siempre se agregan las numeraciones de notas NC y ND, desde 1, sin resolución y sin vencimiento.
  • El id de cada numeración es estable: es el resolucionId que envías al crear factura o nota.
  • La clave técnica nunca se expone.
  • Si la DIAN deja de reportar una numeración, no se borra: queda no-autorizada y emitir con ella responde 409.
curl -s "$API/numeraciones-autorizadas" -H "Authorization: Bearer $TOKEN"

Respuesta en habilitación (los id son ilustrativos):

{
  "ambiente": "habilitacion",
  "avisoDian": "La DIAN no reportó rangos de numeración para el emisor (301: No fue encontrado ningún rango de numeración para el NIT).",
  "value": [
    {
      "id": "7c2e1a90-3b5d-8f42-9a61-4d0b2c8e7f13",
      "tipoDocumento": "factura",
      "origen": "set-pruebas",
      "ambiente": "habilitacion",
      "prefijo": "SETT",
      "numeroResolucion": "18760000001",
      "rangoDesde": 1,
      "rangoHasta": 5000000,
      "vigenciaDesde": "2019-01-19",
      "vigenciaHasta": "2030-01-19",
      "consecutivoSiguiente": 1,
      "ultimoConsecutivoEmitido": null,
      "disponibles": 5000000,
      "estado": "vigente"
    },
    {
      "id": "0e9d4c21-58a7-8b3c-b2f0-6a1e9d3c5b77",
      "tipoDocumento": "nota-credito",
      "origen": "por-defecto",
      "ambiente": "habilitacion",
      "prefijo": "NC",
      "numeroResolucion": null,
      "rangoDesde": 1,
      "rangoHasta": 999999999999999999,
      "vigenciaDesde": null,
      "vigenciaHasta": null,
      "consecutivoSiguiente": 1,
      "ultimoConsecutivoEmitido": null,
      "disponibles": 999999999999999999,
      "estado": "vigente"
    },
    {
      "id": "5b8f2d64-9c13-8e07-a4d9-3f6b1a0c2e58",
      "tipoDocumento": "nota-debito",
      "origen": "por-defecto",
      "ambiente": "habilitacion",
      "prefijo": "ND",
      "numeroResolucion": null,
      "rangoDesde": 1,
      "rangoHasta": 999999999999999999,
      "vigenciaDesde": null,
      "vigenciaHasta": null,
      "consecutivoSiguiente": 1,
      "ultimoConsecutivoEmitido": null,
      "disponibles": 999999999999999999,
      "estado": "vigente"
    }
  ]
}

avisoDian explica situaciones que no bloquean: la DIAN no reportó rangos (301/302), o no respondió y se muestran las numeraciones ya registradas. Otras respuestas:

  • 409 fuente: dian — la DIAN rechazó la consulta; reglasDian trae el código (p. ej. 303: el software no corresponde al NIT, es decir, tu numeración no está asociada al software de Kiai en la DIAN).
  • 409 fuente: cliente — problema con el certificado de firma: contacta a Kiai.
  • 503 — la DIAN no respondió y aún no tienes numeraciones registradas: reintenta según Retry-After.

4.1 Estados de una numeración

estadoSignificado (respecto a la fecha de Colombia)
vigenteSe puede emitir.
por-vencer30 días o menos para el fin de la vigencia.
por-iniciarLa vigencia aún no empieza.
vencidaVigencia terminada.
incompletaLa DIAN no informó rango o vigencia válidos: se lista al final, no se registra ni se emite.
no-autorizadaLa DIAN dejó de reportarla: se conserva por sus documentos; emitir con ella responde 409.

4.2 Fija el consecutivo a seguir

Útil si migras de otro proveedor y ya usaste números del rango. Es el único dato editable:

curl -s -X PUT "$API/numeraciones-autorizadas/<ID_NUMERACION>/consecutivo-siguiente" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "consecutivoSiguiente": 1201 }'
  • Debe estar dentro del rango (bordes incluidos); si no, 400.
  • Debe ser mayor que el último número ya emitido con esa numeración; si no, 409 (reusar un número duplicaría el documento ante la DIAN).
  • Cualquier otra propiedad en el cuerpo responde 400.

5. Emitir documentos

5.1 El ciclo: borrador y emisión

Emitir son dos llamadas:

  1. POST /facturas (o /notas) crea el documento en borrador (201). La API verifica la estructura y el cuadre de totales; mientras sea borrador puedes corregirlo (PUT) o eliminarlo (DELETE).
  2. POST /facturas/{id}/emision (o /notas/{id}/emision) emite: toma el siguiente consecutivo de la numeración, asigna número y CUFE/CUDE y deja el documento emitida con transmisionEstado: "pendiente". La transmisión a la DIAN ocurre justo después, en segundo plano (sección 6).

Después de emitir, el documento no se edita ni se elimina. El comprador (adquiriente) viaja dentro del documento: no hace falta registrarlo antes. La API lo guarda o actualiza en tu directorio de adquirientes y el documento conserva una copia fija de esos datos, que es la que usan el XML, el CUFE, el PDF y el correo.

Envía Idempotency-Key en las dos llamadas: un reintento por un corte de red no duplica el borrador ni gasta un consecutivo. Genera la llave una vez por operación y reutilízala si reintentas; los ejemplos usan uuidgen en línea solo por brevedad.

5.1.1 Modificar o eliminar un borrador

OperaciónLlamadaComportamiento
CorregirPUT /facturas/{id} (o /notas/{id})Reemplaza el borrador completo, con el mismo cuerpo que el POST; vuelve a verificar los totales. Responde 200.
EliminarDELETE /facturas/{id} (o /notas/{id})Responde 204.
Cualquiera de los dos sobre un documento emitido—409: un documento emitido no se edita ni se elimina.
  • PUT y DELETE no usan Idempotency-Key (solo aplica a los POST).
  • La fecha del documento: si no la envías, queda la del día en que creas el borrador, y la emisión no la cambia. Si creas el borrador un día y lo emites otro, envía fecha explícita.

5.2 Totales: tú los envías, la API los verifica al centavo

Envías por línea los impuestos, retenciones y cargos/descuentos, y los totales del documento. La API recalcula todo con las reglas del Anexo Técnico FE 1.9 y responde 400 si algún valor no cuadra, con la ruta del campo y el valor recibido frente al esperado. El error aparece en el campo que origina el descuadre, no en sus totales.

ConceptoRegla
RedondeoMitad al par (“bancario”) a 2 decimales. Montos, tarifas y porcentajes: máximo 2 decimales. Cantidad: máximo 6.
valorTotal de la línearound(cantidad × precioUnitario) − descuentos de línea + cargos de línea. En muestra gratis, 0.
Cargo/descuento con porcentajevalor = round(base × porcentaje / 100). En la línea, base = round(cantidad × precioUnitario). Un descuento no puede superar su base.
Impuesto porcentual (IVA 01, INC 04, ICA 03…)valor = round(base × tarifa / 100); ICA por mil (/ 1000). base = valorTotal de la línea (en muestra gratis, round(cantidad × precioReferencia)). IVA admite 0, 5, 16 y 19; INC admite 2, 4, 8 y 16.
Impuesto nominal (bolsas 22, IBUA 34…)valor = valorUnitario × base, con base = cantidad de la línea y unidadMedidaBase. IBUA / 100.
Retenciones (05 ReteIVA, 06 ReteRenta, 07 ReteICA)valor = round(base × tarifa / 100); ReteICA por mil. ReteIVA se practica sobre el IVA de la línea (15 o 100). Son informativas: no afectan totalAPagar.
valorBrutoΣ valorTotal de las líneas.
baseGravableΣ, por línea, la base de su primer impuesto porcentual.
totalImpuestosΣ impuestos de las líneas.
valorConImpuestosvalorBruto + totalImpuestos.
totalDescuentos / totalCargosΣ cargos/descuentos de cabecera (no afectan la base gravable).
totalAnticiposΣ anticipos (en salud, Σ recaudos). Informativos, salvo en salud de acreditación.
totalRetencionesΣ retenciones de líneas y documento.
totalAPagarvalorConImpuestos − totalDescuentos + totalCargos (en salud de acreditación, además − recaudos).

En una línea no se repite un tributo, y una línea sin impuestos es un ítem excluido. Los códigos válidos de cada campo son los de las tablas del Anexo Técnico de Factura Electrónica de la DIAN, versión 1.9 (Resolución 000165): por ejemplo, 13.2.1 para tipos de documento, 13.3.6 para unidades de medida, 13.3.4.2 para medios de pago y 13.4.3 para municipios. GET /api/v1/catalogos/{tipo} trae hoy los valores más usados, no la lista completa.

5.3 Factura de venta

Ejemplo del contrato: IVA 19 %, INC 8 %, impuesto a las bolsas, ReteFuente, ReteICA, descuento de línea y descuento de cabecera. Ajusta resolucionId y las fechas a tu caso (las del ejemplo son ilustrativas).

curl -s -X POST "$API/facturas" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d @factura.json

factura.json:

{
  "adquiriente": {
    "tipoDocumento": "31",
    "numeroDocumento": "800197268",
    "dv": "4",
    "tipoOrganizacion": "1",
    "razonSocial": "Cliente SAS",
    "regimen": "48",
    "responsabilidadesFiscales": "O-13",
    "direccion": "Calle 9 # 8-7",
    "codigoMunicipio": "11001",
    "municipio": "Bogota",
    "departamento": "Bogota D.C.",
    "correo": "<CORREO_DE_PRUEBAS_TUYO>"
  },
  "resolucionId": "<ID_NUMERACION_FACTURA>",
  "fecha": "2026-09-13",
  "moneda": "COP",
  "formaPago": "credito",
  "fechaVencimiento": "2026-10-13",
  "mediosPago": [ { "codigo": "47", "referencia": "TRF-001" } ],
  "periodo": { "fechaInicio": "2026-09-01T00:00:00-05:00", "fechaFin": "2026-09-30T23:59:59-05:00" },
  "ordenReferencia": { "numero": "OC-2026-15", "fecha": "2026-09-05" },
  "lineas": [
    {
      "descripcion": "Servicio de consultoría",
      "codigo": "43231500",
      "esquemaCodigo": "001",
      "unidadMedida": "94",
      "cantidad": 3,
      "precioUnitario": 10000.00,
      "cargosDescuentos": [
        { "esCargo": false, "codigoRazon": "00", "descripcion": "Pronto pago", "porcentaje": 10, "base": 30000.00, "valor": 3000.00 }
      ],
      "impuestos": [
        { "tributo": "01", "base": 27000.00, "tarifa": 19.00, "valor": 5130.00 }
      ],
      "retenciones": [
        { "tributo": "06", "base": 27000.00, "tarifa": 2.50, "valor": 675.00 },
        { "tributo": "07", "base": 27000.00, "tarifa": 9.66, "valor": 260.82 }
      ],
      "valorTotal": 27000.00
    },
    {
      "descripcion": "Almuerzo ejecutivo",
      "unidadMedida": "94",
      "cantidad": 2,
      "precioUnitario": 15000.50,
      "impuestos": [
        { "tributo": "04", "base": 30001.00, "tarifa": 8.00, "valor": 2400.08 }
      ],
      "valorTotal": 30001.00
    },
    {
      "descripcion": "Producto en bolsa",
      "unidadMedida": "94",
      "cantidad": 4,
      "precioUnitario": 100.00,
      "impuestos": [
        { "tributo": "01", "base": 400.00, "tarifa": 19.00, "valor": 76.00 },
        { "tributo": "22", "base": 4, "valorUnitario": 66.00, "unidadMedidaBase": "94", "valor": 264.00 }
      ],
      "valorTotal": 400.00
    }
  ],
  "cargosDescuentos": [
    { "esCargo": false, "codigoRazon": "01", "descripcion": "Descuento comercial", "base": 65271.08, "valor": 1000.00 }
  ],
  "totales": {
    "valorBruto": 57401.00,
    "baseGravable": 57401.00,
    "totalImpuestos": 7870.08,
    "valorConImpuestos": 65271.08,
    "totalDescuentos": 1000.00,
    "totalCargos": 0.00,
    "totalAnticipos": 0.00,
    "totalRetenciones": 935.82,
    "totalAPagar": 64271.08
  }
}

Campos de la factura que conviene conocer:

  • formaPago: contado o credito; con credito, fechaVencimiento es obligatoria y no puede ser anterior a la fecha. mediosPago lleva al menos uno (10 efectivo, 42 consignación, 47 transferencia, 48/49 tarjeta…).
  • fecha es opcional: por defecto, la fecha de hoy en Colombia.
  • adquiriente puede ser { "consumidorFinal": true }: la API completa los datos DIAN del consumidor final (13-222222222222); si envías correo, debe ser válido.
  • Otros casos que admite el mismo recurso: exportación (moneda distinta de COP exige trm), mandato (mandante), anticipos (anticipos, informativos), contingencia (contingencia, tipos 03/04) y muestras gratis (esMuestraGratis con precioReferencia y codigoPrecioReferencia). El detalle está en el esquema FacturaInput del contrato.

Respuesta 201 (abreviada): lo guardado, idéntico a lo que irá al XML.

{
  "id": "<ID_FACTURA>",
  "emisorId": "<ID_EMISOR>",
  "adquirienteId": "<ID_ADQUIRIENTE>",
  "adquiriente": { /* copia del comprador, con "consumidorFinal": false */ },
  "resolucionId": "<ID_NUMERACION_FACTURA>",
  "tipoDocumento": "factura-venta",
  "ambiente": null,
  "estado": "borrador",
  "numero": null,
  "cufe": null,
  "fecha": "2026-09-13",
  "fechaEmision": null,
  "moneda": "COP",
  /* … trm, fechaTasa, mandante, anticipos, contingencia, periodo, sectorSalud, formaPago, mediosPago,
     fechaVencimiento, ordenReferencia, observaciones, cargosDescuentos, retenciones, totales … */
  "transmisionEstado": null,
  "transmisionMensaje": null,
  "transmisionFecha": null,
  "transmisionTrackId": null,
  "lineas": [ /* con "numero" y "esquemaCodigo" resueltos */ ],
  "fechaCreacion": "2026-09-23T15:04:05.123456Z",
  "fechaActualizacion": "2026-09-23T15:04:05.123456Z"
}

El dueño del documento viaja como emisorId (el id de tu emisor), igual en la API y en el openapi.yaml.

Emite:

curl -s -X POST "$API/facturas/<ID_FACTURA>/emision" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"

Respuesta 200 (abreviada):

{
  "id": "<ID_FACTURA>",
  "tipoDocumento": "factura-venta",
  "ambiente": "habilitacion",
  "estado": "emitida",
  "numero": "SETT1",
  "cufe": "<CUFE: 96 caracteres hexadecimales (SHA-384)>",
  "fechaEmision": "2026-09-23T15:04:06.654321Z",
  /* … */
  "transmisionEstado": "pendiente",
  "transmisionMensaje": null,
  "transmisionFecha": null,
  "transmisionTrackId": null
}

La emisión responde 409 si el documento ya no está en borrador, no tiene líneas, o la numeración no es de tipo factura, es de otro ambiente DIAN que el de tu emisor, no está vigente, está agotada o ya no está autorizada. En ese caso no se consume el consecutivo. La emisión responde aunque la DIAN esté caída: el documento queda pendiente y se transmite cuando vuelva.

5.4 Notas crédito y débito

Mismo contrato de líneas, impuestos y totales que la factura. facturaId decide la forma de la nota y el tipo de operación DIAN (tipoOperacion) se deriva solo:

Con referencia (envías facturaId)Sin referencia (omites facturaId)
tipoOperacion20 crédito · 30 débito22 crédito · 32 débito
FacturaDebe estar aceptada por la DIAN, ser tuya, en la misma moneda y con fecha ≤ la de la nota.—
adquirienteNo se envía (400): la nota copia el de la factura.Obligatorio.
conceptoCorreccionObligatorio.Prohibido.
periodoOpcional.Obligatorio.
resolucionIdEl id de la numeración NC o ND.Igual.

Conceptos de corrección: crédito 1 devolución parcial, 2 anulación, 3 rebaja o descuento, 4 ajuste de precio, 5 descuento por pronto pago, 6 descuento por volumen; débito 1 intereses, 2 gastos por cobrar, 3 cambio del valor, 4 otros.

Reglas contra la factura (se revalidan al emitir):

  • La nota crédito no puede superar el saldo (GET /facturas/{id}/saldo: total de la factura − notas crédito emitidas no rechazadas). Así haces correcciones parciales o totales.
  • La anulación (concepto 2) debe ser exactamente por el saldo total.
  • La nota débito no tiene tope.
  • Si la factura es del sector salud, la nota hereda su extensión de salud y su periodo.
  • Las notas no llevan anticipos. Las retenciones se guardan y se devuelven, pero no van al XML de la nota.

Nota crédito con referencia (ejemplo del contrato; cabe dentro del saldo de la factura del ejemplo 5.3):

{
  "tipo": "credito",
  "facturaId": "<ID_FACTURA_ACEPTADA>",
  "resolucionId": "<ID_NUMERACION_NC>",
  "conceptoCorreccion": "1",
  "moneda": "COP",
  "lineas": [
    {
      "descripcion": "Servicio de consultoría",
      "codigo": "43231500",
      "esquemaCodigo": "001",
      "unidadMedida": "94",
      "cantidad": 3,
      "precioUnitario": 10000,
      "cargosDescuentos": [ { "esCargo": false, "codigoRazon": "00", "porcentaje": 10, "base": 30000, "valor": 3000 } ],
      "impuestos": [ { "tributo": "01", "base": 27000, "tarifa": 19, "valor": 5130 } ],
      "retenciones": [ { "tributo": "06", "base": 27000, "tarifa": 2.5, "valor": 675 } ],
      "valorTotal": 27000
    },
    {
      "descripcion": "Almuerzo ejecutivo",
      "unidadMedida": "94",
      "cantidad": 2,
      "precioUnitario": 15000.50,
      "impuestos": [ { "tributo": "04", "base": 30001, "tarifa": 8, "valor": 2400.08 } ],
      "valorTotal": 30001
    }
  ],
  "cargosDescuentos": [ { "esCargo": false, "codigoRazon": "01", "descripcion": "Descuento comercial", "base": 64531.08, "valor": 1000 } ],
  "totales": {
    "valorBruto": 57001,
    "baseGravable": 57001,
    "totalImpuestos": 7530.08,
    "valorConImpuestos": 64531.08,
    "totalDescuentos": 1000,
    "totalCargos": 0,
    "totalAnticipos": 0,
    "totalRetenciones": 675,
    "totalAPagar": 63531.08
  }
}

Nota débito con referencia (intereses):

{
  "tipo": "debito",
  "facturaId": "<ID_FACTURA_ACEPTADA>",
  "resolucionId": "<ID_NUMERACION_ND>",
  "conceptoCorreccion": "1",
  "lineas": [
    {
      "descripcion": "Intereses de financiacion por pago a credito",
      "codigo": "INT-FIN-01",
      "unidadMedida": "94",
      "cantidad": 1,
      "precioUnitario": 15000,
      "impuestos": [ { "tributo": "01", "base": 15000, "tarifa": 19, "valor": 2850 } ],
      "valorTotal": 15000
    }
  ],
  "totales": {
    "valorBruto": 15000,
    "baseGravable": 15000,
    "totalImpuestos": 2850,
    "valorConImpuestos": 17850,
    "totalDescuentos": 0,
    "totalCargos": 0,
    "totalAnticipos": 0,
    "totalRetenciones": 0,
    "totalAPagar": 17850
  }
}

Nota crédito sin referencia (ajuste de un periodo):

{
  "tipo": "credito",
  "adquiriente": {
    "tipoDocumento": "31",
    "numeroDocumento": "800197268",
    "dv": "4",
    "tipoOrganizacion": "1",
    "razonSocial": "Cliente SAS",
    "regimen": "48",
    "responsabilidadesFiscales": "O-13",
    "direccion": "Calle 9 # 8-7",
    "codigoMunicipio": "11001",
    "municipio": "Bogota",
    "departamento": "Bogota D.C.",
    "correo": "<CORREO_DE_PRUEBAS_TUYO>"
  },
  "resolucionId": "<ID_NUMERACION_NC>",
  "periodo": { "fechaInicio": "2026-08-01T00:00:00-05:00", "fechaFin": "2026-08-31T23:59:59-05:00" },
  "lineas": [
    {
      "descripcion": "Ajuste comercial sin referencia a factura",
      "codigo": "AJU-SREF-01",
      "unidadMedida": "94",
      "cantidad": 1,
      "precioUnitario": 20000,
      "impuestos": [ { "tributo": "01", "base": 20000, "tarifa": 19, "valor": 3800 } ],
      "valorTotal": 20000
    }
  ],
  "totales": {
    "valorBruto": 20000,
    "baseGravable": 20000,
    "totalImpuestos": 3800,
    "valorConImpuestos": 23800,
    "totalDescuentos": 0,
    "totalCargos": 0,
    "totalAnticipos": 0,
    "totalRetenciones": 0,
    "totalAPagar": 23800
  }
}

Crea con POST /api/v1/notas y emite con POST /api/v1/notas/{id}/emision, igual que la factura. La respuesta trae tipoDocumento (nota-credito / nota-debito), numero (p. ej. NC1), cude y los campos de transmisión.

5.5 Factura del sector salud (Resolución 948 de 2026)

Es una factura normal con el bloque sectorSalud, que la marca como factura de salud. Exige periodo (fecha y hora del primer y último servicio) y moneda COP, y no admite anticipos ni mandante (los anticipos se informan como recaudo con concepto 04). Los datos del paciente no se envían aquí: van en el RIPS, que no hace parte de esta API.

tipoOperacionExtensión de saludRecaudos¿Restan de totalAPagar?
SS-CUFE, SS-CUDE, SS-POS, SS-SNum (acreditación)SíObligatorios (1 a 4)Sí
SS-ReporteSíObligatorios (1 a 4)No (informativos)
SS-SinAporteSíNo lleva—
SS-RecaudoNoNo lleva—

Campos del bloque (cuando lleva extensión): codigoPrestador (código REPS de 10 dígitos), modalidadPago (01–05), coberturaPlan (02–17), y numeroContrato o facturaSinContrato (01–07); con póliza, numeroPoliza (excluyente con el contrato). Recaudos: un concepto por grupo, sin repetir (01 copago, 02 cuota moderadora, 03 pagos compartidos, 04 anticipo, 05 no aplica con valor 0, que va solo), cada uno con valor (máximo 2 decimales) y fechaRecibido. La suma de recaudos no puede superar el total del documento. En acreditación, totalAnticipos = Σ recaudos y totalAPagar = valorConImpuestos − totalDescuentos + totalCargos − Σ recaudos.

Ejemplo SS-CUFE: consulta con IVA 0 %, material con IVA 19 %, copago y cuota moderadora:

{
  "adquiriente": {
    "tipoDocumento": "31",
    "numeroDocumento": "800197268",
    "dv": "4",
    "tipoOrganizacion": "1",
    "razonSocial": "Cliente SAS",
    "regimen": "48",
    "responsabilidadesFiscales": "O-13",
    "direccion": "Calle 9 # 8-7",
    "codigoMunicipio": "11001",
    "municipio": "Bogota",
    "departamento": "Bogota D.C.",
    "correo": "<CORREO_DE_PRUEBAS_TUYO>"
  },
  "resolucionId": "<ID_NUMERACION_FACTURA>",
  "moneda": "COP",
  "formaPago": "contado",
  "mediosPago": [ { "codigo": "10" } ],
  "periodo": { "fechaInicio": "2026-09-20T07:30:00-05:00", "fechaFin": "2026-09-22T18:00:00-05:00" },
  "sectorSalud": {
    "tipoOperacion": "SS-CUFE",
    "codigoPrestador": "0500102345",
    "modalidadPago": "04",
    "coberturaPlan": "16",
    "facturaSinContrato": "01",
    "recaudos": [
      { "concepto": "01", "valor": 12000, "fechaRecibido": "2026-09-20" },
      { "concepto": "02", "valor": 4500, "fechaRecibido": "2026-09-20" }
    ]
  },
  "lineas": [
    {
      "descripcion": "Consulta de medicina especializada",
      "codigo": "CONS-ESP-01",
      "unidadMedida": "94",
      "cantidad": 1,
      "precioUnitario": 100000,
      "impuestos": [ { "tributo": "01", "base": 100000, "tarifa": 0, "valor": 0 } ],
      "valorTotal": 100000
    },
    {
      "descripcion": "Material medico quirurgico",
      "codigo": "MAT-MQ-01",
      "unidadMedida": "94",
      "cantidad": 2,
      "precioUnitario": 10000,
      "impuestos": [ { "tributo": "01", "base": 20000, "tarifa": 19, "valor": 3800 } ],
      "valorTotal": 20000
    }
  ],
  "totales": {
    "valorBruto": 120000,
    "baseGravable": 120000,
    "totalImpuestos": 3800,
    "valorConImpuestos": 123800,
    "totalDescuentos": 0,
    "totalCargos": 0,
    "totalAnticipos": 16500,
    "totalRetenciones": 0,
    "totalAPagar": 107300
  }
}

Un código fuera de catálogo responde 400: la API nunca lo reemplaza por un valor por defecto.

5.6 Documento soporte

El servicio tiene un recurso de documento soporte (compras a no obligados a facturar) en /api/v1/documentos-soporte, pero no hace parte del contrato publicado ni de esta integración. No lo uses: si lo necesitas, pídeselo a Kiai.


6. Estados, consulta y transmisión

6.1 Estados de un documento

CampoValoresQué significa
estadoborrador → emitidaBorrador editable; emitida con número y CUFE/CUDE.
transmisionEstadonull → pendiente → aceptada | rechazadanull en borrador. pendiente: en camino a la DIAN o esperando su veredicto. aceptada: validado por la DIAN. rechazada: la DIAN no lo validó.
ambientenull | habilitacion | produccionSe fija al emitir y no cambia: te dice sin ambigüedad si el documento fue de prueba o real.
transmisionMensajetextoDetalle del último resultado, incluidos los errores de la DIAN.
transmisionTrackIdtextoCUFE/CUDE que devuelve la DIAN; en el set de pruebas, el ZipKey.

El contrato lista también el estado anulada para facturas, pero ningún flujo lo asigna: no lo esperes. Anular una factura se hace con una nota crédito de concepto 2 por el saldo total: la factura sigue emitida y su saldo queda en 0.

Un documento solo está emitido ante la DIAN cuando transmisionEstado es aceptada. No lo entregues ni lo des por válido con pendiente.

6.2 La transmisión es asíncrona

  • Al emitir, el documento queda pendiente y Kiai lo transmite en segundo plano, normalmente en los segundos siguientes.
  • Si la DIAN falla de forma transitoria, Kiai reintenta solo: hasta 8 intentos con esperas crecientes (de 5 segundos hasta 5 minutos). Una caída de la DIAN no hace perder el documento.
  • Si después de esos intentos sigue pendiente y no tiene transmisionTrackId, fuerza un reintento con POST .../transmision (sección 6.4).
  • En el set de pruebas de habilitación el veredicto llega después (ver 3.4). Un documento pendiente con transmisionTrackId (el ZipKey) ya está en manos de la DIAN: no lo retransmitas, porque le mandarías una copia y no acelera nada. Espera el veredicto.
  • rechazada es terminal: corrige lo que indican las reglas y crea un documento nuevo (el rechazado ya consumió su número y no se puede editar). Retransmitir sin cambios da el mismo rechazo.
  • Excepción: durante las 72 horas siguientes a un rechazo, Kiai vuelve a consultar a la DIAN. Si la DIAN confirma que el documento sí quedó aceptado (típico de un rechazo por documento duplicado, regla 90), Kiai lo corrige a aceptada y te llega el webhook. Antes de reemitir un documento rechazado por duplicado, espera esa corrección o consulta GET .../application-response.

Para enterarte del resultado sin consultar, registra un webhook (sección 8).

6.3 Consultar

# Un documento, con sus líneas
curl -s "$API/facturas/<ID_FACTURA>" -H "Authorization: Bearer $TOKEN"
curl -s "$API/notas/<ID_NOTA>" -H "Authorization: Bearer $TOKEN"

# Listados paginados (resumen, sin líneas)
curl -s -G "$API/facturas" -H "Authorization: Bearer $TOKEN" \
  --data-urlencode "\$filter=estado eq 'emitida' and numero eq 'SETT1'" \
  --data-urlencode "\$orderby=fechaCreacion desc" \
  --data-urlencode "\$top=20" --data-urlencode "\$count=true"
Recurso$filter admitido (combinable con and)$orderby
GET /facturasestado eq '..', numero eq '..', cufe eq '..', adquirienteId eq {uuid}fecha, numero, fechaCreacion (con o sin desc)
GET /notastipo eq '..', estado eq '..', numero eq '..', cude eq '..', facturaId eq {uuid}igual

El listado es un resumen: no trae transmisionEstado ni permite filtrar por él. Para vigilar tus documentos pendientes, guarda el id de cada uno y consúltalo por id, o usa webhooks (sección 8).

Otras consultas útiles:

  • GET /facturas/{id}/saldo — saldo acreditable frente a sus notas.
  • GET /auditoria?documentoId={id} — la línea de vida del documento (creado, cambios de estado, transmisiones), con el usuario o proceso que originó cada cambio y su correlationId.
  • GET /facturas/{id}/application-response (o /notas/{id}/...) — la respuesta firmada de la DIAN que prueba la validación, consultada en vivo por CUFE/CUDE. 409 si aún no hay CUFE o la DIAN todavía no tiene el documento; 503 si la DIAN no responde.
  • Un documento de otro emisor responde 404, igual que uno inexistente.

6.4 Reintentar la transmisión

POST /facturas/{id}/transmision (o /notas/{id}/transmision), sin cuerpo:

RespuestaSignificado
200Resultado registrado en el documento: aceptada, o pendiente si fue al set de pruebas.
422 fuente: dianLa DIAN rechazó el documento; trae reglasDian. Queda rechazada.
503 fuente: dianLa DIAN no respondió (o su canal está caído). El documento queda pendiente y Kiai lo reintenta solo. Respeta Retry-After.
409No está emitido, ya fue aceptado (la DIAN lo rechazaría como duplicado), o una falla que reintentar no resuelve del lado de Kiai (p. ej. el certificado de firma): en ese caso contacta a Kiai.
404No existe para tu emisor.

7. PDF, XML, AttachedDocument y entrega al adquiriente

7.1 Representación gráfica (PDF)

curl -s "$API/facturas/<ID_FACTURA>/pdf" -H "Authorization: Bearer $TOKEN" -o factura.pdf

Disponible desde que el documento está emitido (409 en borrador). Incluye emisor y adquiriente, detalle con descuentos y cargos, impuestos por tributo y tarifa, retenciones, datos de salud cuando aplica, montos en formato colombiano, fechas en hora de Colombia, CUFE/CUDE y código QR. Si el generador está atendiendo el máximo de documentos simultáneos responde 503 con Retry-After: reintenta.

Personalización opcional: logo por numeración (PUT /numeraciones-autorizadas/{id}/logo, PNG o JPEG en base64, máximo 1 MB) y plantillas HTML propias (/plantillas-representacion-grafica, por tipo de documento o numeración; editarlas no cambia cómo se imprime un documento ya emitido). Cómo escribir una plantilla y qué variables puedes usar: Anexo B.

7.2 XML firmado

GET /facturas/{id}/xml (o /notas/{id}/xml): el XML UBL firmado, idéntico al transmitido a la DIAN, con el nombre de archivo de la convención DIAN. 409 en borrador.

7.3 AttachedDocument

GET /facturas/{id}/attached-document (o /notas/{id}/...): el contenedor electrónico que se entrega al comprador (Anexo Técnico 1.9 §6.4): el documento firmado tal como lo validó la DIAN más su ApplicationResponse, firmado.

  • ?formato=xml (por defecto): el AttachedDocument (ad…xml).
  • ?formato=zip: el paquete de entrega (z…zip) con el AttachedDocument y el PDF.
  • Solo para documentos aceptados por la DIAN (si no, 409).
  • Se arma en el momento consultando a la DIAN, así que tarda lo que tarde la DIAN; si no responde, 503 con Retry-After.
curl -s "$API/facturas/<ID_FACTURA>/attached-document?formato=zip" \
  -H "Authorization: Bearer $TOKEN" -o entrega.zip

7.4 Envío automático al adquiriente

Cuando la DIAN acepta una factura o nota, Kiai programa un único envío automático al correo del adquiriente, con el z…zip (AttachedDocument + PDF):

  • Reintenta ante fallas transitorias (la DIAN no responde al armar el AttachedDocument, el proveedor de correo no confirma) y lo da por fallido ante fallas permanentes (adquiriente sin correo o con correo inválido).
  • Si el documento ya tenía una notificación exitosa, queda omitido.
  • Consulta su estado en GET /facturas/{id}/notificaciones/envio-automatico (o /notas/{id}/...): pendiente, enviado, fallido u omitido, con intentos, motivo y proximoIntento. Responde 404 si no hay envío automático (aún no aceptado, deshabilitado, o registro purgado tras 30 días).
  • También está activo para emisores en habilitación (ver 3.4).
  • Los correos salen de noreply@kiai.co, con el nombre «Kiai Facturación Electrónica».

7.5 Envío manual y trazabilidad de entrega

POST /facturas/{id}/notificacion (o /notas/{id}/notificacion) envía el mismo z…zip. Úsalo para reenviar o copiar a otras personas; funciona también en habilitación.

curl -s -X POST "$API/facturas/<ID_FACTURA>/notificacion" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "copiaA": ["contabilidad@tuempresa.co"] }'
  • Sin cuerpo: al correo del adquiriente.
  • destinatarios: reemplaza al adquiriente (p. ej. para una dirección corregida).
  • copiaA: suma personas sin quitarle la entrega al adquiriente.
  • anexos: hasta 5 archivos (5 MB en total; pdf, png, jpg, jpeg, gif, bmp, zip, xls, xlsx), en base64; viajan dentro del zip, en anexos.zip.
  • 409 si la DIAN no ha aceptado el documento o no hay destinatarios; 503 (sin enviar nada) si la DIAN no responde al armar el AttachedDocument.

GET /facturas/{id}/notificaciones lista cada envío (más recientes primero). enviado: true significa que el proveedor de correo aceptó el mensaje; lo que pasó después está en estadoEntrega: enviado, demorado, entregado, queja, rebotado o rechazado (con varios destinatarios manda el peor resultado), con motivoEntrega, entregaReintentable y el detalle por destinatario en eventosEntrega.


8. Webhooks

Kiai te avisa cuando un documento llega a un estado final de transmisión, sin que tengas que consultar.

8.1 Registra tu endpoint

curl -s -X POST "$API/webhooks" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{
    "url": "https://tu-sistema.example.com/kiai/webhooks",
    "secret": "<SECRETO_COMPARTIDO_DE_16_A_200_CARACTERES>",
    "eventos": "documento.transmision.aceptada;documento.transmision.rechazada"
  }'
  • url: HTTPS absoluta (máximo 500 caracteres), pública (no se entrega a direcciones internas o privadas).
  • secret: entre 16 y 200 caracteres. Se usa para firmar y nunca se devuelve.
  • eventos: tipos separados por ;, o * (por defecto) para todos.
  • 201 con la suscripción (sin el secreto). 409 si ya tienes una suscripción a esa URL.
  • Registra el webhook antes de emitir: un evento se reparte entre las suscripciones activas en ese momento y no se reenvía a las que se creen después.
  • Otras operaciones: GET /webhooks, GET /webhooks/{id}, DELETE /webhooks/{id} (cancela sus entregas pendientes). No hay edición: para cambiar el secreto, elimina y vuelve a crear la suscripción.

8.2 La entrega

POST a tu URL con Content-Type: application/json y estos encabezados:

EncabezadoContenido
X-Kiai-Signaturesha256=<HMAC-SHA256(secret, cuerpo) en hexadecimal minúsculas>
X-Kiai-Event-IdId del evento (el mismo en todos los reintentos): úsalo para no procesarlo dos veces.
X-Kiai-Event-Typedocumento.transmision.aceptada o documento.transmision.rechazada.

Cuerpo:

{
  "eventId": "<ID_EVENTO>",
  "type": "documento.transmision.aceptada",
  "documentoTipo": "factura",
  "documentoId": "<ID_DOCUMENTO>",
  "emisorId": "<ID_EMISOR>",
  "estadoTransmision": "aceptada",
  "ocurridoEn": "2026-09-23T15:04:21.1234567Z"
}

documentoTipo puede ser factura, nota, documento-soporte o evento-radian. El evento no trae el documento: consúltalo con GET para ver su detalle (transmisionMensaje, reglas, número).

8.3 Verifica la firma

Calcula el HMAC sobre el cuerpo crudo (los bytes tal como llegaron, antes de parsear el JSON) y compáralo en tiempo constante.

Node.js:

const crypto = require('node:crypto');

function firmaValida(cuerpoCrudo /* Buffer */, cabecera /* X-Kiai-Signature */, secreto) {
  const esperada = 'sha256=' + crypto.createHmac('sha256', secreto).update(cuerpoCrudo).digest('hex');
  const a = Buffer.from(esperada, 'utf8');
  const b = Buffer.from(cabecera || '', 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

C#:

using System.Security.Cryptography;
using System.Text;

static bool FirmaValida(string cuerpoCrudo, string cabecera, string secreto)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secreto));
    var esperada = "sha256=" + Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(cuerpoCrudo))).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(esperada), Encoding.UTF8.GetBytes(cabecera ?? ""));
}

8.4 Reintentos

  • Una entrega cuenta como exitosa solo con una respuesta 2xx en menos de 15 segundos. No se siguen redirecciones: un 3xx cuenta como fallo.
  • Ante un fallo, Kiai reintenta con esperas crecientes (10 s, 20 s, 40 s, 80 s, 160 s): 6 intentos en total, unos 5 minutos. Si todos fallan, la entrega queda fallida.
  • Consulta la cola y las fallidas en GET /webhooks/entregas?estado=pendiente|entregada|fallida (con intentos, httpStatus y ultimoError). No hay reenvío manual de una entrega fallida: recupera el estado consultando los documentos.

Buenas prácticas: responde 2xx rápido y procesa después; ignora los X-Kiai-Event-Id repetidos; no asumas orden entre eventos (pueden reintentarse por separado); y ten una consulta periódica de respaldo de tus documentos pendiente.


9. Errores

9.1 Formato

Todo error es un Problem Details (RFC 9457) con Content-Type: application/problem+json:

CampoContenido
type, title, status, detail, instanceEstándar RFC 9457. El detail está en español.
traceIdId de la traza en Kiai.
correlationIdTu X-Correlation-Id (o el generado).
fuenteDe quién es la falla: cliente (la solicitud erró), dian (la DIAN no respondió o rechazó), kiai (error interno de Kiai).
errorsSolo en 400: diccionario campo → mensajes.
servicioDian, reglasDian, numeroDocumentoSolo en errores atribuidos a la DIAN. Cada regla: codigo, mensaje, tipo (rechazo impide la validación; notificacion solo advierte).

9.2 Códigos

HTTPfuenteSignificadoQué hacer
400clienteSolicitud inválida: campo obligatorio, formato, código fuera de catálogo, totales que no cuadran, propiedad que no existe en el contrato, filtro OData no soportado.Corrige lo que indica errors y reenvía. No reintentes igual.
401clienteSin token, token vencido, firma inválida o audiencia distinta de facturacion.Ver 2.5.
403clienteFalta el permiso de la operación, el token no identifica una empresa, tu empresa no tiene emisor activo, u operación reservada a Kiai.Ver 2.5.
404clienteEl recurso no existe o es de otro emisor.Revisa el id.
409clienteConflicto con el estado: documento que ya no es borrador, numeración no vigente, agotada, de otro tipo o ambiente, reglas de nota (factura no aceptada, saldo), consecutivo no válido, Idempotency-Key en curso, edición concurrente, certificado de firma no utilizable.Lee el detail. Si es la llave en curso, espera Retry-After. Si es el certificado, contacta a Kiai.
409dianLa DIAN rechazó una consulta (numeraciones, ApplicationResponse, eventos): trae reglasDian.Corrige lo que indica la regla (p. ej. la asociación de tu numeración al software de Kiai).
412clienteIf-Match no coincide con la versión actual (adquirientes).Relee el recurso y reintenta.
422dianLa DIAN rechazó el documento transmitido: trae reglasDian y numeroDocumento. El documento queda rechazada.Corrige y emite un documento nuevo (ver 6.2).
422clienteIdempotency-Key reutilizada con otro cuerpo.Usa una llave nueva.
429kiaiSuperaste el límite de tu emisor.Espera Retry-After.
500kiaiError interno de Kiai.Reintenta más tarde; si persiste, reporta traceId y correlationId.
503dianLa DIAN no respondió o su canal está caído. Si era una transmisión, el documento queda pendiente y Kiai lo reintenta.Espera Retry-After. Confirma en GET /estado-servicios.
503—El generador de PDF está ocupado (title: “El generador de PDF está ocupado.”).Reintenta tras Retry-After.

Ese 503 llega hoy con fuente: dian aunque la causa no es la DIAN: distínguelo por el title.

9.3 Ejemplo: la DIAN rechaza el documento (422)

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.21",
  "title": "La DIAN rechazó el documento",
  "status": 422,
  "detail": "La DIAN rechazó la factura SETT15: Regla: FAJ43a, Rechazo: El DV del NIT del emisor no corresponde. El documento quedó registrado como rechazado; corrige lo que indican las reglas y vuelve a emitirlo.",
  "instance": "/api/v1/facturas/7d9f.../transmision",
  "traceId": "00-84b1...",
  "correlationId": "5952f26eedc741b2a2ce29648af4a222",
  "fuente": "dian",
  "servicioDian": "SendBillSync",
  "numeroDocumento": "SETT15",
  "reglasDian": [
    { "codigo": "FAJ43a", "mensaje": "El DV del NIT del emisor no corresponde.", "tipo": "rechazo" }
  ]
}

9.4 Ejemplo: totales que no cuadran (400)

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "La solicitud no es válida.",
  "status": 400,
  "errors": {
    "totales.totalAPagar": [ "El valor no cuadra: se recibió 64271.00 y el valor esperado es 64271.08." ]
  },
  "traceId": "00-84b1...",
  "correlationId": "5952f26eedc741b2a2ce29648af4a222",
  "fuente": "cliente"
}

Las claves de errors son la ruta del campo en tu cuerpo, p. ej. lineas[0].impuestos[1].valor. Una propiedad que no existe en el contrato aparece con su nombre tal como la escribiste y el mensaje “La propiedad ’…’ no existe en el contrato de la API v1…”.

9.5 Para diagnosticar

  • Envía siempre X-Correlation-Id y guárdalo junto a cada documento.
  • Ante una intermitencia, GET /api/v1/estado-servicios (sin token) dice si Kiai y el canal DIAN están operativo, intermitente o caido.
  • Al reportar un caso a Kiai, incluye correlationId, traceId, el id del documento y la hora.

10. Checklist de salida a producción

Credenciales y tokens

  • Credencial de Facturación con los permisos mínimos por sistema; client_secret en una bóveda, nunca en el código ni en logs.
  • Token pedido con resource=facturacion, reutilizado durante su vida (60 min) y renovado antes de expirar.
  • Procedimiento de rotación y revocación de la credencial definido.

Emisor y DIAN

  • Set de pruebas aprobado por la DIAN (o ya estabas habilitado con Kiai, 3.2) y habilitadoDian: true en GET /emisores/{id}/habilitacion.
  • Emisor en ambiente: "produccion" (lo hace Kiai) confirmado con GET /emisores/{id}.
  • Datos del emisor correctos (NIT y DV, razón social, régimen, responsabilidades, dirección, municipio): van en cada XML.
  • Numeración de producción visible en GET /numeraciones-autorizadas con origen: "dian" y estado: "vigente".
  • Si migras de otro proveedor: consecutivo siguiente fijado en la factura y en NC/ND.
  • correo del emisor vigente (recibe los avisos de numeración por vencer o agotarse).

Emisión

  • Idempotency-Key en todos los POST, con reintentos seguros ante cortes de red.
  • Totales calculados con redondeo mitad al par a 2 decimales; cantidades con máximo 6 decimales.
  • Manejo de 400 (errores por campo), 409, 422 (no reintentar sin corregir) y 429/503 (respetar Retry-After).
  • Un documento solo se da por válido con transmisionEstado: "aceptada"; los pendiente se vigilan.
  • Se guardan id, numero, cufe/cude, ambiente y correlationId de cada documento.
  • Correos reales y válidos de los adquirientes (reciben el envío automático).

Operación

  • Webhook registrado antes de emitir, con firma verificada e idempotencia por X-Kiai-Event-Id.
  • Consulta de respaldo para documentos pendiente y para entregas de webhook fallida.
  • Monitoreo de GET /estado-servicios y registro de correlationId en tus logs.
  • Política propia de conservación de tus documentos (XML firmado, AttachedDocument, PDF).

11. Aliados: registra y habilita a tus clientes por API

Un aliado es una empresa que registra a sus clientes en Kiai y los lleva hasta quedar habilitados ante la DIAN, todo desde su propio sistema. Si solo integras tu propia empresa, esta sección no es para ti: sigue de la 2 a la 10.

11.1 Cómo funciona

Estas operaciones son del servicio de identidad de Kiai (usuarios.kiai.co), no de la API de documentos:

QuéURLAutenticación
Endpoint de tokenhttps://usuarios.kiai.co/api/oauth/tokenclient_id + client_secret de aliado
Clienteshttps://usuarios.kiai.co/api/v1/clientesToken Bearer de aliado

El recorrido:

  1. Kiai le da a tu empresa el rol de aliado y recibes tu credencial (11.2).
  2. Pides un token (11.3).
  3. Registras a tu cliente con los productos que va a usar (11.4) y consultas hasta que Facturación quede LISTO (11.5).
  4. Tu cliente asocia el software de Kiai en la DIAN y te entrega su TestSetId (los pasos están en 3.2).
  5. Pides su habilitación (11.6) y consultas hasta que quede HABILITADO.
  6. Creas la credencial de Facturación de tu cliente (11.7) y con ella emites a su nombre.

Reglas que conviene saber antes de empezar:

  • Tus clientes son tuyos. No dices en el cuerpo quién es el aliado: sale de tu credencial. Un campo de aliado en el cuerpo se rechaza (400 ALIADO_NO_SE_ENVIA), y solo ves y operas los clientes que registraste tú.
  • El registro responde de inmediato. Tu cliente queda creado al instante; cada producto se activa después, por su cuenta, y tú consultas su estado.
  • Registrar de nuevo es seguro. Si envías otra vez el mismo cliente, recibes 200 con el mismo registro: no se crea un duplicado.
  • No hay ambiente de pruebas para el registro. Cada cliente que registras queda creado de verdad: usa los datos reales de tu cliente.

11.2 Tu credencial de aliado

El rol de aliado lo otorga Kiai. Escríbenos (ver Soporte) con el NIT de tu empresa; con el rol recibes tu credencial:

  • client_id: empieza por kiai_ali_.
  • client_secret: se muestra una sola vez. Guárdalo en tu bóveda de secretos. Kiai solo conserva una huella y no puede recuperarlo.

Reglas:

  • Hay una credencial activa por aliado.
  • Si pierdes el secreto o sospechas que se filtró, avísale a Kiai de inmediato: se rota (mismo client_id, secreto nuevo) o se revoca.
  • Esta credencial registra, consulta y habilita clientes, y crea la credencial con que emites por cada uno (11.7). Ella misma no emite documentos.

11.3 Pide el token

POST https://usuarios.kiai.co/api/oauth/token, igual que en 2.2 pero sin resource:

curl -s -X POST https://usuarios.kiai.co/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=<TU_CLIENT_ID_DE_ALIADO>" \
  --data-urlencode "client_secret=<TU_CLIENT_SECRET>"

También acepta JSON con los mismos campos. Respuesta:

{
  "access_token": "<TOKEN_JWT>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "clientes.registrar clientes.leer"
}
  • Vida del token: 60 minutos. Reutilízalo y pide uno nuevo antes de que expire; no hay token de refresco.
  • Úsalo como Authorization: Bearer <access_token> en las llamadas de esta sección.

Errores de este endpoint (formato { "error": "...", "error_description": "..." }):

HTTPerrorCausa
400invalid_request / unsupported_grant_typeFalta un campo, o grant_type no es client_credentials.
401invalid_clientclient_id o client_secret incorrectos, o la credencial fue revocada.
403access_denied con ALIADO_BLOQUEADOKiai bloqueó tu rol de aliado. Contacta a Kiai.
403access_denied con NO_ES_ALIADOTu empresa ya no tiene el rol de aliado.

11.4 Registra a tu cliente

POST https://usuarios.kiai.co/api/v1/clientes con tu token de aliado:

curl -s -X POST https://usuarios.kiai.co/api/v1/clientes \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "empresa": {
      "tipoPersona": "juridica",
      "tipoDocumento": "31",
      "documento": "900123456",
      "digitoVerificacion": "8",
      "razonSocial": "Mi Cliente S.A.S",
      "regimen": "48",
      "responsabilidades": ["O-13"],
      "ubicacion": {
        "pais": "CO",
        "departamento": "05",
        "ciudad": "05001",
        "direccion": "Calle 10 # 20-30"
      }
    },
    "responsable": {
      "nombres": "Ana María",
      "primerApellido": "Gómez",
      "correo": "ana@micliente.co",
      "telefono": "3001234567"
    },
    "productos": ["EINV"]
  }'
CampoObligatorioRegla
empresa.tipoPersonaSí"juridica" o "natural".
empresa.tipoDocumentoSíCódigo DIAN: "31" NIT, "13" cédula de ciudadanía.
empresa.documentoSíSolo dígitos, como texto (entre comillas); máximo 18. Sin puntos, guiones ni espacios.
empresa.digitoVerificacionNoUn dígito. Con NIT, si lo envías debe ser el correcto.
empresa.razonSocialSíMáximo 200 caracteres.
empresa.regimenRecomendadoCódigo DIAN: "48" responsable de IVA, "49" no responsable.
empresa.responsabilidadesRecomendadoCódigos del RUT ("O-13", "R-99-PN"…); máximo 30.
empresa.ubicacionRecomendadopais ("CO"), departamento y ciudad con su código DANE ("05", "05001"), codigoPostal y direccion (máximo 100 caracteres).
responsable.nombresSíMáximo 50 caracteres.
responsable.primerApellidoSíCon segundoApellido (opcional), máximo 100 caracteres entre los dos.
responsable.correoSíMáximo 50 caracteres, sin tildes. No puede tener ya una cuenta en Kiai.
responsable.telefonoSíEntre 7 y 20 caracteres: dígitos y espacios; puede empezar con +.
productosSíAl menos uno. Facturación electrónica es "EINV".

Tenlo en cuenta:

  • Envía los datos fiscales como están en el RUT de tu cliente: son los que usa Facturación.
  • El responsable recibe un correo para activar su cuenta y queda como administrador de la empresa. La activación no detiene el recorrido: puedes habilitar a tu cliente y crear su credencial aunque todavía no haya activado su cuenta. Mientras no la active, esa persona no puede entrar a usuarios.kiai.co.
  • Persona natural: "tipoPersona": "natural", con su cédula ("13") o su NIT ("31").
  • Campos que no existen en el contrato se rechazan (400 CAMPOS_DESCONOCIDOS): un error de escritura no pasa en silencio.

Respuesta 201 con la ficha del cliente (la misma de 11.5) y la cabecera Location. Guarda el id: es el que usas para consultarlo y habilitarlo.

11.5 Consulta a tus clientes

LlamadaQué devuelve
GET /api/v1/clientes/{id}Un cliente tuyo. Uno que no es tuyo responde igual que uno que no existe: 404.
GET /api/v1/clientes?$top=20&$skip=0&$count=trueTus clientes, del más reciente al más antiguo, con el estado de cada producto. $top máximo 100.

Solo se admiten $top, $skip y $count; cualquier otro operador responde 400 OPERADOR_NO_SOPORTADO.

{
  "id": "<ID_CLIENTE>",
  "tipoPersona": "juridica",
  "tipoDocumento": "31",
  "documento": "900123456",
  "digitoVerificacion": "8",
  "razonSocial": "Mi Cliente S.A.S",
  "correo": "ana@micliente.co",
  "creadoEn": "2026-10-08T14:02:11Z",
  "productos": [
    {
      "codigo": "EINV",
      "nombre": "Kiai Facturación",
      "estado": "HABILITADO",
      "aprovisionamiento": "LISTO",
      "motivoAprovisionamiento": null,
      "referenciaExterna": "<ID_EN_FACTURACION>",
      "habilitaciones": [
        {
          "tipoDocumento": "FACTURA_ELECTRONICA",
          "estado": "HABILITADO",
          "motivoRechazo": null,
          "actualizadoEn": "2026-10-08T14:14:09Z",
          "testSetId": "<TEST_SET_ID>",
          "documentosPedidos": 1
        }
      ],
      "motivoRechazo": null
    }
  ]
}

Cada producto trae dos estados. El primero, aprovisionamiento, dice si el producto ya está activo para tu cliente:

aprovisionamientoQué significaQué hacer
PENDIENTESe está activando.Consulta de nuevo en unos segundos.
LISTOActivo en el producto.Ya puedes pedir la habilitación (11.6).
ESPERANDO_ALIADOKiai está terminando de activar tu empresa como aliado. No es un error.Nada: se activa solo. No registres de nuevo.
ERRORNo se pudo activar; motivoAprovisionamiento dice por qué.Corrige el dato y registra de nuevo.

El segundo, estado, es la habilitación ante la DIAN: SIN_INICIAR, EN_PROCESO, HABILITADO o RECHAZADO (11.6).

11.6 Habilita a tu cliente ante la DIAN

Antes, tu cliente asocia el software de KIAI S.A.S. en el portal de habilitación de la DIAN y copia su TestSetId: son los pasos de «Si es la primera vez que facturas con Kiai», en 3.2. La DIAN entrega un TestSetId por tipo de documento.

Con el producto EINV en LISTO, pides la habilitación un tipo de documento a la vez y en este orden:

  1. Factura electrónica (FACTURA_ELECTRONICA).
  2. Cuando la factura quedó HABILITADO, el documento equivalente POS (DOCUMENTO_EQUIVALENTE_POS), si tu cliente lo usa.
curl -s -X POST https://usuarios.kiai.co/api/v1/clientes/<ID_CLIENTE>/productos/EINV/habilitaciones \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "tipoDocumento": "FACTURA_ELECTRONICA", "testSetId": "<TEST_SET_ID_DE_TU_CLIENTE>" }'
  • Respuesta 202 con la cabecera Location: Kiai envía el set de pruebas a la DIAN por su cuenta. Tú no emites documentos de prueba.
  • Repetir el mismo pedido no envía nada otra vez: responde 200 con el estado actual.
  • Sigue el resultado con GET /api/v1/clientes/{id}, dentro del producto EINV: cada tipo trae su estado, su testSetId y cuántos documentos de prueba se enviaron (documentosPedidos). Suele resolverse en segundos; si la DIAN tarda, consulta cada 10 a 30 segundos.
  • Si queda RECHAZADO, motivoRechazo dice por qué y qué hacer. Corregido el problema, vuelve a pedirla.
  • El estado del producto sigue a la factura electrónica: con la factura en HABILITADO, tu cliente ya está habilitado ante la DIAN con Kiai como proveedor tecnológico.
HTTPcodigoCuándo
409PRODUCTO_NO_LISTOEl producto EINV de tu cliente todavía no está LISTO.
409HABILITACION_EN_CURSOTu cliente ya tiene una habilitación en curso: espera el resultado.
409YA_HABILITADOEse tipo de documento ya quedó habilitado con otro set.
409FACTURA_ELECTRONICA_PRIMEROPediste el POS sin la factura electrónica habilitada.
400PRODUCTO_SIN_HABILITACIONEse producto no se habilita ante la DIAN (solo EINV).
404PRODUCTO_NO_ENCONTRADOTu cliente no tiene ese producto.

11.7 Emite a nombre de tu cliente

Tu credencial de aliado no emite documentos. Para emitir por tu cliente creas, con tu mismo token de aliado, la credencial de Facturación de su empresa:

curl -s -X POST https://usuarios.kiai.co/api/v1/clientes/<ID_CLIENTE>/productos/EINV/credenciales \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "nombre": "ERP de mi empresa (producción)" }'

Respuesta 201:

{
  "clientId": "<CLIENT_ID_DE_TU_CLIENTE>",
  "clientSecret": "<CLIENT_SECRET_DE_TU_CLIENTE>",
  "nombre": "ERP de mi empresa (producción)",
  "scopes": "facturacion.emitir facturacion.consultar",
  "aviso": "Guarda el clientSecret ahora: no se volverá a mostrar. ..."
}
  • El cuerpo es un objeto JSON y puede ir vacío ({}). El nombre es opcional (máximo 100 caracteres); sin él, la credencial se llama «Integración de» y la razón social de tu empresa. No admite otros campos: los permisos son siempre facturacion.emitir y facturacion.consultar.
  • El clientId empieza por kiai_live_. El clientSecret se muestra una sola vez: guárdalo en tu bóveda de secretos, asociado a ese cliente.
  • La credencial es de la empresa de tu cliente. Con ella pides el token con resource=facturacion (2.2), consultas sus numeraciones (sección 4) y emites (sección 5): ese token opera a nombre de tu cliente, no de tu empresa.
  • Un token opera a nombre de una sola empresa. Guarda una credencial por cliente y pide el token con la del cliente por el que vas a emitir. No hay un token que sirva para varios.
  • Hay una credencial activa creada por el aliado por cliente. Por esta vía solo se crea: no se rota ni se revoca. Si pierdes el secreto, tu cliente la revoca en usuarios.kiai.co, sección INTEGRACIONES, y creas otra.
  • Tu cliente la ve en INTEGRACIONES, junto a las que él mismo cree (2.1), y puede revocarla cuando quiera.
  • Tu cliente ya está habilitado con Kiai: no repite el set de pruebas ni envía ese TestSetId por otro camino (3.3). El paso a producción se coordina con Kiai (3.6).
HTTPcodigoCuándo
409PRODUCTO_NO_LISTOEl producto EINV de tu cliente todavía no está LISTO.
409CREDENCIAL_ACTIVATu cliente ya tiene una credencial activa creada por su aliado.
400PRODUCTO_SIN_CREDENCIALEse producto no tiene credencial por esta vía (solo EINV).
404PRODUCTO_NO_ENCONTRADOTu cliente no tiene ese producto.
503PERMISOS_NO_DISPONIBLESLos permisos de Facturación no se pueden otorgar en este momento. Contacta a Kiai.

11.8 Límites

  • Tope diario de clientes nuevos. Tu rol de aliado puede tener un tope de clientes nuevos por día (día de Colombia). Volver a registrar uno tuyo no cuenta. Al llegar al tope: 429 TOPE_DIARIO_ALCANZADO con Retry-After (segundos hasta la medianoche de Colombia). Si necesitas más, pídeselo a Kiai. No reintentes automáticamente un 429 de este endpoint: la espera puede ser de horas.
  • Altas simultáneas. Si envías muchas altas a la vez, Kiai las atiende una tras otra. Si una espera demasiado: 503 ALTAS_SIMULTANEAS con Retry-After de unos segundos; reintenta.

11.9 Errores

Todo error de estas operaciones responde application/problem+json (RFC 9457) con un campo codigo estable. Tu sistema debe decidir por codigo, no por el texto de detail, que puede cambiar.

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
  "title": "Cliente no encontrado",
  "status": 404,
  "detail": "No tienes un cliente con ese id.",
  "traceId": "00-84b1...",
  "codigo": "CLIENTE_NO_ENCONTRADO"
}
HTTPcodigoCuándo
400DATOS_INVALIDOSUn dato no cumple el formato; errors dice cuál campo y por qué.
400CAMPOS_DESCONOCIDOSEl cuerpo trae campos que no existen en el contrato.
400ALIADO_NO_SE_ENVIAEl cuerpo intenta decir quién es el aliado.
400PRODUCTO_NO_SOPORTADOUn producto que no se registra por esta vía.
400OPERADOR_NO_SOPORTADO / PARAMETRO_INVALIDOPaginación con operadores o valores no admitidos.
403ALIADO_BLOQUEADOKiai bloqueó tu rol de aliado; aplica también a tokens ya emitidos.
403NO_ES_ALIADOTu empresa no tiene el rol de aliado.
403TOKEN_SIN_EMPRESAEl token no identifica a tu empresa; pide uno nuevo.
404CLIENTE_NO_ENCONTRADOEl cliente no existe o no es tuyo.
409DOCUMENTO_REGISTRADOEse documento ya es cliente de Kiai por otra vía. Contacta a Kiai.
409CORREO_NO_DISPONIBLEEl correo del responsable ya tiene una cuenta en Kiai.
409CORREO_DISTINTOReenviaste un cliente tuyo con otro responsable.
409PRODUCTOS_DISTINTOSReenviaste un cliente tuyo pidiendo productos que no tenía.
429TOPE_DIARIO_ALCANZADOLlegaste al tope diario de clientes nuevos (ver Retry-After).
503ALTAS_SIMULTANEASOtra alta tuya está en curso (ver Retry-After).

Ante un incidente, envíanos el traceId de la respuesta y la fecha y hora. Nunca nos envíes tu client_secret.


Anexo A — Otros recursos del contrato

RecursoPara quéNotas
/adquirientesDirectorio de compradores (crear, listar, editar, eliminar).No es obligatorio: el comprador viaja dentro del documento. GET por id devuelve ETag; envíalo en If-Match al actualizar (412 si cambió).
/adquirientes/dian/{tipoDocumento}/{numeroDocumento}Consulta en la DIAN el nombre y correo registrados de un documento.Solo lectura; útil para autocompletar.
/catalogos/{tipo} y /catalogos/{tipo}/{codigo}Catálogos DIAN/DANE con los valores más usados (la lista completa es la del Anexo Técnico 1.9): tipos-documento, tipos-organizacion, regimenes, responsabilidades-fiscales, unidades-medida, formas-pago, medios-pago, departamentos, municipios.$top por defecto 100; padreCodigo eq '..' filtra municipios por departamento.
/auditoria?documentoId=Línea de vida de un documento.Solo de tu emisor.
POST /descargasZIP con PDF + XML firmado de hasta 50 documentos y un manifiesto.json con el estado de cada uno.Los no encontrados o en borrador se reportan en el manifiesto.
/facturas/{facturaId}/eventosEventos RADIAN (030 acuse, 031 reclamo, 032 recibo del bien, 033 aceptación expresa, 034 tácita) que registras sobre una factura emitida.GET .../eventos/dian consulta en vivo en la DIAN todos los eventos de la factura, incluidos los de terceros.
/plantillas-representacion-graficaPlantillas HTML propias para el PDF.El HTML se valida al guardarlo: sin scripts y con las imágenes embebidas como data:. Ver el Anexo B.
/estado-serviciosEstado de Kiai y del canal DIAN.Sin token.

Anexo B — Plantillas del PDF

Si no creas ninguna plantilla, el PDF de tus documentos sale con la plantilla base de Kiai. Si quieres tu propio diseño, crea una plantilla HTML con /plantillas-representacion-grafica. Este anexo explica cómo se escribe, qué se valida al guardarla y qué variables puedes usar.

B.1 Cómo funciona

B.1.1 El motor: marcadores de doble corchete

La plantilla es HTML con marcadores de un motor propio de Kiai. No es Scriban, Liquid, Handlebars ni Razor: no hay expresiones, ni comparaciones, ni else, ni filtros. Todos los valores llegan ya formateados como texto (montos, fechas y nombres de catálogo); la plantilla solo decide dónde van y si se muestran.

MarcadorQué hace
[[variable]]Imprime el valor, escapado como HTML. Los saltos de línea se imprimen como <br>.
[[FOR:lista]]…[[END:lista]]Repite el contenido por cada elemento de la lista. Se puede anidar.
[[indice]] (o [[Index]])Dentro de un FOR, la posición del elemento empezando en 1. En FOR anidados es la del FOR más interno.
[[IF:nombre]]…[[END:IF:nombre]]Imprime el contenido si nombre es una condición activa, una lista con elementos o una variable con valor no vacío.
[[IFNOT:nombre]]…[[END:IFNOT:nombre]]Lo contrario de IF (hace las veces de else).
[[FIRST:lista]]…[[END:FIRST:lista]] · [[LAST:lista]]…[[END:LAST:lista]]Dentro del FOR de esa lista, solo en el primer / último elemento. Existen por compatibilidad con plantillas antiguas.
<template id="pie-pagina">…</template>Pie de cada página del PDF. Admite <span class="pageNumber"></span> y <span class="totalPages"></span>.

Ejemplo mínimo (pasa la validación al guardarlo):

<html><body>
  <h1>[[documento.titulo]] No. [[documento.numero]]</h1>
  <p>[[emisor.razonSocial]] · [[emisor.documento]]</p>
  [[FOR:lineas]]<p>[[indice]]. [[descripcion]] — [[total]]</p>[[END:lineas]]
  <p>Total a pagar: [[documento.moneda]] [[totales.totalAPagar]]</p>
</body></html>

Reglas del motor que conviene conocer:

  • Los nombres distinguen mayúsculas y minúsculas. [[Documento.Numero]] no existe; es [[documento.numero]].
  • Sin espacios dentro del marcador. Un marcador admite de 1 a 120 caracteres sin espacios, corchetes, <, > ni comillas. [[ documento.numero ]] (con espacios) no se reconoce: se imprime literalmente en el PDF y la validación no lo reporta, porque para el motor es texto.
  • Una sola pasada. Un dato que contenga [[…]] (por ejemplo, en la descripción de una línea) se imprime como texto; nunca se interpreta.
  • Ámbito dentro de un FOR. Una variable se busca primero en el elemento actual y luego hacia afuera, hasta el documento. Dentro de [[FOR:lineas]], [[valor]] es el valor de la línea; dentro de un [[FOR:impuestos]] anidado en la línea, [[valor]] es el del impuesto. Las variables del documento ([[documento.numero]]…) se pueden usar en cualquier nivel.
  • Las listas existen siempre, aunque estén vacías. Un [[FOR:impuestos]] dentro de una línea sin impuestos no imprime nada: no toma los impuestos del documento.
  • Un monto en cero se imprime (0,00) y cuenta como valor no vacío para IF. Para ocultar filas opcionales usa las condiciones (totales.hayDescuentos, totales.hayCargos…), no la variable del monto.
  • Al imprimir, el motor es tolerante: una variable desconocida se imprime vacía y un bloque mal cerrado se cierra solo, para no tumbar un PDF. Al guardar la plantilla, en cambio, esos errores se rechazan (ver B.2).

B.1.2 A qué documentos aplica

DocumentoCómo se descarga el PDFCondición en la plantilla
Facturas de venta (tipos 01 a 04)GET /api/v1/facturas/{id}/pdffactura
Nota crédito (91) y nota débito (92)GET /api/v1/notas/{id}/pdfnota, notaCredito, notaDebito
  • Solo hay PDF de documentos emitidos: un borrador responde 409.
  • El PDF es tamaño carta, con márgenes de 10 mm (20 mm abajo cuando la plantilla trae pie de página). El archivo se llama como el número del documento (SETT123.pdf).
  • Si el generador está saturado responde 503 con Retry-After: 5.
  • No hay vista previa. Ningún endpoint renderiza una plantilla sin emitir un documento. Para ver el resultado, emite un documento después de crear o editar la plantilla y descarga su PDF (ver B.1.4). Hazlo mientras tu emisor está en habilitación: en producción, cada documento que emites es real ante la DIAN.

B.1.3 Cómo se asigna una plantilla

Recurso: /api/v1/plantillas-representacion-grafica (listar, obtener, crear, actualizar, eliminar). Leer exige el scope facturacion.consultar; crear, actualizar y eliminar exigen facturacion.emitir. La empresa dueña sale del token: no la envíes.

Cuerpo de POST y PUT:

{
  "nombre": "Sede Medellín",
  "html": "<html>…</html>",
  "tipoDocumento": "factura",
  "numeracionId": "<ID_NUMERACION>"
}
  • tipoDocumento: factura, nota-credito o nota-debito; omitido o null = todos. Se compara contra el tipo de la numeración con que se emitió el documento; todas las facturas se emiten con numeraciones de tipo factura.
  • numeracionId: una numeración autorizada de tu empresa (por ejemplo, la de una sede o punto de venta); omitido o null = todas.

Como el HTML viaja dentro del JSON, lo más cómodo es armar el cuerpo desde el archivo:

jq -n --rawfile html plantilla.html \
  '{nombre: "Sede Medellín", html: $html, tipoDocumento: "factura"}' |
curl -s -X POST "$API/plantillas-representacion-grafica" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-binary @-

Cuando se imprime un documento, entre las plantillas vigentes en la fecha de emisión de ese documento gana la más específica:

#PlantillaGana sobre
1Con numeración y tipo de documentotodas
2Solo con numeración3, 4, 5
3Solo con tipo de documento4, 5
4Sin asignación (general de tu empresa)5
5Plantilla base de Kiai (si no tienes ninguna aplicable)—

A igual especificidad gana la que empezó a regir más tarde. La plantilla base de Kiai no se puede descargar por la API.

B.1.4 Versionado: lo ya emitido no cambia

  • El id de una plantilla no cambia al editarla; version sube en cada PUT.
  • Cada edición crea una versión nueva, que rige desde ese instante (vigenteDesde). Un documento se imprime siempre con la versión vigente cuando se emitió: editar o eliminar una plantilla no cambia el PDF de lo ya emitido.
  • Por lo mismo, una plantilla nueva no se aplica a documentos emitidos antes de crearla.
  • DELETE hace que deje de listarse y de aplicarse a documentos nuevos, que pasan a la siguiente plantilla aplicable de la tabla anterior (o a la base de Kiai si no queda ninguna).

B.2 Validación al guardar

POST y PUT validan el HTML al guardarlo, no al emitir.

B.2.1 Respuestas

CódigoCuándo
400La plantilla no pasa la validación. El detalle va en errors.html (un mensaje por hallazgo, en español), o en errors.nombre, errors.tipoDocumento o errors.numeracionId. Se reportan hasta 20 hallazgos; si hay más, un último mensaje pide corregirlos y reenviar.
409Ya hay una plantilla vigente con ese nombre en tu empresa; la numeración no existe o no es de tu empresa; u otra edición simultánea ganó la carrera (recarga y reintenta).
404GET, PUT o DELETE sobre una plantilla que no existe o ya se eliminó.

B.2.2 Qué se rechaza

Campos:

  • nombre vacío o de más de 120 caracteres.
  • html vacío o de más de 128 KiB en UTF-8 (131.072 bytes). La plantilla base de Kiai pesa unos 20 KB.
  • tipoDocumento fuera de factura, nota-credito y nota-debito.
  • numeracionId con el GUID vacío (00000000-…).

Estructura y diccionario (el HTML se analiza con el mismo analizador que usa el motor al imprimir):

  • Un bloque sin cerrar o un cierre sin apertura.
  • Una variable, lista o condición que no existe en el ámbito donde se usa. [[descripcion]] solo existe dentro de [[FOR:lineas]]; [[IF:obsequio]], también.
  • [[FOR:x]], [[FIRST:x]] o [[LAST:x]] sobre algo que no es una lista (el mensaje trae las listas disponibles).
  • [[IF:x]] sobre un nombre que no es condición, lista ni variable.
  • Una condición usada como variable: [[factura]] no es válido; es [[IF:factura]].

Seguridad. El HTML lo abre el navegador de Kiai para imprimir el PDF, así que se rechaza todo lo que ejecute código, incruste otro documento o cargue algo de afuera:

  • Etiquetas <script>, <iframe>, <frame>, <frameset>, <object>, <embed>, <applet>, <portal>, <base> y <link>. La revisión es sobre el texto completo: también se rechazan dentro de un comentario HTML.

  • Atributos de evento on…= (onclick, onload, onerror…).

  • Cualquier URL en los atributos src, srcset, href, xlink:href, poster, action, formaction, background, cite, ping y manifest, en url(…) de CSS o en @import, salvo:

    • un ancla #…;
    • un marcador [[variable]] completo (por ejemplo src="[[documento.codigoQr]]");
    • un data: literal;
    • el origen https://assets.kiai.co, que el contrato menciona (ver la advertencia de B.2.3).

    Eso incluye rutas relativas (/logo.png), http://, file:, javascript: y enlaces absolutos a otros sitios. Por ejemplo, un <a href="https://catalogo-vpfe.dian.gov.co/…"> escrito a mano se rechaza: usa href="[[documento.urlConsultaDian]]".

B.2.3 Imágenes, fuentes y CSS

  • CSS: en <style> dentro de la plantilla o en atributos style. <link rel="stylesheet"> se rechaza.
  • Imágenes: embebidas como data: (base64). El logo y el QR ya llegan así ([[documento.logo]], [[documento.codigoQr]]). El logo es el de la numeración (PUT /api/v1/numeraciones-autorizadas/{id}/logo, PNG o JPEG de hasta 1 MB).
  • Fuentes: el generador tiene instaladas Liberation (Sans, Serif, Mono) y DejaVu. Una fuente propia tiene que ir embebida con @font-face { src: url(data:…) }, dentro del tope de 128 KiB.
  • Sin JavaScript: el PDF se genera con JavaScript deshabilitado.
  • ⚠️ https://assets.kiai.co se acepta al guardar, pero hoy no se descarga al imprimir. El generador solo carga recursos data:: una imagen apuntada a otro origen sale vacía en el PDF. Embebe todo como data:.
  • ⚠️ Usa las variables de imagen solo en atributos (src), no dentro de <style>. Los valores se escapan como HTML y el + del base64 sale como &#x2B;. En un atributo el navegador lo decodifica; en el CSS de un <style> no, y la imagen queda rota. background: url('[[documento.logo]]') pasa la validación pero no se ve.
  • Pie de página: el contenido del último <template id="pie-pagina"> del documento se imprime en el margen inferior de cada hoja. Se dibuja fuera del documento, así que no hereda el CSS de <style>: sus estilos van en línea. Admite las variables del diccionario.
  • El tamaño carta y los márgenes los fija el servicio al generar el PDF: no dependas de @page para cambiarlos.

B.3 Diccionario de variables

Todas las variables son texto ya formateado. No hay helpers ni filtros: el formato lo pone el servicio, siempre con convención colombiana.

FormatoCómo saleEjemplo
MontoMiles con punto, dos decimales con coma10.000,00 · -3.000,00
Cantidad, tarifa, porcentajeMínimo dos decimales y hasta seis3,00 · 1,50 · 0,125
Fechadd/MM/yyyy13/09/2026
Fecha y horadd/MM/yyyy HH:mm:ss, hora de Colombia (UTC-5)13/09/2026 15:49:26
Valor ausenteCadena vacía—

Tipos en las tablas: texto, monto, decimal, fecha, fecha y hora, data URI, URL.

B.3.1 Documento

VariableTipoDescripciónEjemplo
documento.titulotextoNombre del tipo de documentoFactura electrónica de venta · Nota crédito electrónica
documento.codigoTipotextoCódigo DIAN del tipo de documento01 · 91
documento.numerotextoNúmero completo (prefijo + consecutivo)SETT123
documento.prefijotextoPrefijo de la numeraciónSETT
documento.fechaEmisionfecha y horaFecha de emisión13/09/2026 15:49:26
documento.fechaValidacionfecha y horaFecha de validación DIAN, o Pendiente de validación si aún no se transmitió13/09/2026 16:00:53
documento.tipoOperaciontextoTipo de operación con su nombre10 - Estándar · 20 - Nota crédito que referencia una factura electrónica · SS-CUFE - Sector salud
documento.codigoTipoOperaciontextoCódigo del tipo de operación10
documento.monedatextoMoneda (código ISO)COP
documento.tasaCambiomontoTRM; vacía si el documento no trae tasa de cambio3.950,50
documento.fechaTasaCambiofechaFecha de la TRM; vacía sin tasa de cambio13/09/2026
documento.cantidadLineastextoNúmero de líneas3
documento.ambientetextoProducción o HabilitaciónProducción
documento.etiquetaIdentificadortextoCUFE (facturas) o CUDE (notas)CUFE
documento.identificadortextoCUFE o CUDE del documentoa1b2c3… (96 caracteres)
documento.cufetextoIgual a documento.identificador (también en notas)a1b2c3…
documento.urlConsultaDianURLConsulta del documento en el catálogo DIAN de su ambiente (es el contenido del QR)https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=a1b2…
documento.codigoQrdata URIImagen PNG del QR, generada por Kiai. Úsala en <img src>data:image/png;base64,iVBOR…
documento.logodata URILogo de la numeración (PNG o JPEG); vacío si no tienedata:image/png;base64,iVBOR…
documento.observacionestextoObservaciones del documento, una por línea; si el emisor es un consorcio, además sus participantes con su porcentajeEntrega en bodega 3
documento.leyendaLegaltextoLeyenda de la factura como título valor, solo en facturas (01–04); vacía en notas. La redacta Kiai e incluye la razón social del emisorEsta factura electrónica de venta se asimila…

B.3.2 Forma y medios de pago

En las notas estas variables salen vacías (y la lista mediosPago, vacía).

VariableTipoDescripciónEjemplo
documento.formaPagotextoCrédito si el código es 2; Contado en cualquier otro casoCrédito
documento.codigoFormaPagotextoCódigo de forma de pago2
documento.fechaVencimientofechaVencimiento; solo con forma de pago crédito13/10/2026
documento.mediosPagotextoMedios de pago con su nombre, separados por ; 47 - Transferencia bancaria
documento.referenciaPagotextoReferencias de pago informadas, separadas por ; TRF-001

B.3.3 Referencias, periodo, orden de compra y contingencia

VariableTipoDescripciónEjemplo
referencia.facturatextoFactura que corrige la nota (notas con referencia)SETT1
referencia.fechafechaFecha de esa factura13/09/2026
referencia.cufetextoCUFE de esa facturaf9e8…
referencia.conceptotextoConcepto de corrección con su descripción oficial1 - Devolución parcial de los bienes y/o no aceptación parcial del servicio
referencia.codigoConceptotextoCódigo del concepto1
referencia.descripcionConceptotextoDescripción del conceptoDevolución parcial de los bienes y/o no aceptación parcial del servicio
documento.periodotextoPeriodo facturado01/08/2026 00:00:00 a 31/08/2026 23:59:59
documento.periodoIniciofecha y horaInicio del periodo01/08/2026 00:00:00
documento.periodoFinfecha y horaFin del periodo31/08/2026 23:59:59
documento.ordenCompratextoNúmero de la orden de compraOC-4410
documento.fechaOrdenComprafechaFecha de la orden de compra01/09/2026
documento.facturaContingenciatextoConsecutivo de la factura de contingenciaCT-0012
documento.fechaFacturaContingenciafechaFecha de la factura de contingencia10/09/2026

B.3.4 Numeración y proveedor tecnológico

VariableTipoDescripciónEjemplo
resolucion.numerotextoNúmero de la resolución de la DIAN18760000001
resolucion.prefijotextoPrefijoSETT
resolucion.vigenciaDesdefechaInicio de la vigencia19/01/2019
resolucion.vigenciaHastafechaFin de la vigencia19/01/2030
resolucion.consecutivoDesdetextoInicio del rango, con prefijoSETT1
resolucion.consecutivoHastatextoFin del rango, con prefijoSETT5000000
resolucion.textotextoTexto de la autorización. En notas: Numeración del … al …Autorización de numeración DIAN No. 18760000001 vigente del 19/01/2019 al 19/01/2030. Rango autorizado del SETT1 al SETT5000000
proveedorTecnologico.razonSocialtextoRazón social del proveedor tecnológico (Kiai)KIAI S.A.S
proveedorTecnologico.nittextoNIT con DV901337751-9
proveedorTecnologico.nombreSoftwaretextoNombre del softwareKiai Facturación
proveedorTecnologico.textotextoLeyenda lista para el pieSoftware: Kiai Facturación. Proveedor tecnológico: KIAI S.A.S NIT 901337751-9

B.3.5 Emisor y adquiriente

emisor.* es siempre tu empresa, que expide el documento, y adquiriente.* es el comprador. Las mismas 22 variables existen con los dos prefijos: emisor.<campo> y adquiriente.<campo>.

CampoTipoDescripciónEjemplo
roltextoEmisor o AdquirienteAdquiriente
razonSocialtextoRazón social; en persona natural sin razón social, nombres y apellidosIPS Cliente Demo SAS
nombrestextoNombres (persona natural)María Fernanda
apellidostextoApellidos (persona natural)Gómez Ruiz
tipoDocumentotextoNombre del tipo de documento de identidadNIT · Cédula de ciudadanía
codigoTipoDocumentotextoCódigo DIAN del tipo de documento31 · 13
numeroDocumentotextoNúmero sin dígito de verificación900123456
dvtextoDígito de verificación8
nittextoNúmero con DV, si lo tiene900123456-8
documentotextoTipo y número juntosNIT 900123456-8 · Cédula de ciudadanía 1020304050
direcciontextoDirecciónCalle 10 # 20-30 Of. 301
municipiotextoMunicipioMedellín
departamentotextoDepartamentoAntioquia
ciudadtextoMunicipio y departamentoMedellín - Antioquia
codigoPostaltextoCódigo postal050021
paistextoCódigo del paísCO
telefonotextoTeléfono6044441234
correotextoCorreos sin repetidos, separados por ; cuentas@tuempresa.co; tesoreria@tuempresa.co
regimentextoRégimen con su nombre48 - Responsable de IVA
codigoRegimentextoCódigo del régimen48
responsabilidadestextoResponsabilidades fiscales con su nombreO-13 Gran contribuyente; O-15 Autorretenedor
codigosResponsabilidadestextoSolo los códigos (R-99-PN si no tiene ninguna)O-13;O-15

B.3.6 Totales

VariableTipoDescripciónEjemplo
totales.valorBrutomontoSuma del valor de las líneas, neto de sus descuentos y cargos57.401,00
totales.baseGravablemontoBase gravable57.401,00
totales.totalImpuestosmontoSuma de los impuestos7.870,08
totales.valorConImpuestosmontoValor bruto más impuestos65.271,08
totales.totalIvamontoTotal de IVA (tributo 01)5.206,00
totales.totalIncmontoTotal de INC (tributo 04; no incluye el INC de bolsas, 22)2.400,08
totales.totalIcamontoTotal de ICA (tributo 03)0,00
totales.totalDescuentosmontoDescuentos globales (de encabezado)1.000,00
totales.totalCargosmontoCargos globales (de encabezado)0,00
totales.totalAnticiposmontoAnticipos o recaudos del sector salud16.500,00
totales.totalRetencionesmontoRetenciones. Son informativas: no se restan del total a pagar935,82
totales.ajusteAlPesomontoSiempre 0,00 en la API v10,00
totales.totalAPagarmontoTotal a pagar (en notas, el total de la nota)64.271,08
totales.valorEnLetrastextoTotal a pagar en letras. En pesos termina en M/CTE; en otra moneda usa su código ISOSESENTA Y CUATRO MIL DOSCIENTOS SETENTA Y UN PESOS CON 08/100 M/CTE

B.3.7 Sector salud (Resolución 948 de 2026)

Salen vacías si el documento no es del sector salud. Una nota que corrige una factura de salud hereda estas variables (sin recaudos). Los datos del paciente no se exponen: van en el RIPS.

VariableTipoDescripciónEjemplo
salud.tipoOperaciontextoTipo de operación de salud con su nombreSS-CUFE - Acreditación de recaudos con referencia a factura electrónica
salud.codigoTipoOperaciontextoCódigoSS-CUFE
salud.codigoPrestadortextoCódigo de habilitación del prestador (REPS)0500102345
salud.modalidadPagotextoModalidad de pago con su descripción04 - Pago por evento.
salud.coberturaPlantextoCobertura con su descripción16 - Plan de beneficios en salud financiado con UPC Regimen Contributivo
salud.numeroContratotextoNúmero de contratoCT-2026-118
salud.numeroPolizatextoNúmero de pólizaPOL-88231
salud.facturaSinContratotextoMotivo de factura sin contrato, con su descripción01 - ATENCION DE URGENCIAS
salud.periodoAtenciontextoPeriodo de atención01/09/2026 07:30:00 a 10/09/2026 18:00:00

B.3.8 Listas (se recorren con [[FOR:…]])

ListaDónde se usaQué contiene
lineasDocumentoUna por línea del detalle
impuestosDocumento y dentro de cada líneaEn el documento: el resumen por tributo y tarifa. Dentro de lineas: los de esa línea
retencionesDocumentoRetenciones por tributo y tarifa (informativas)
cargosDescuentosDocumento y dentro de cada líneaEn el documento: los globales. Dentro de lineas: los de esa línea
recaudosDocumentoRecaudos del sector salud, uno por concepto (copago, cuota moderadora…)
anticiposDocumentoAnticipos informados
mediosPagoDocumentoMedios de pago (vacía en notas)
notasTotalesDocumentoAclaraciones de lo que se muestra pero no resta del total

En cualquier FOR también existe [[indice]] (o [[Index]]): la posición desde 1.

Elemento de lineas

VariableTipoDescripciónEjemplo
numerotextoNúmero de la línea1
codigotextoCódigo del producto o servicio43231500
tipoCodificaciontextoCódigo del estándar de codificación001 · 999
descripciontextoDescripciónConsulta médica general
observaciontextoObservación de la líneaIncluye materiales
marcatextoMarcaAcme
modelotextoModeloX-200
cantidaddecimalCantidad3,00
unidadtextoCódigo de la unidad de medida94
precioUnitariomontoPrecio unitario10.000,00
precioReferenciamontoPrecio de referencia; solo si la línea es obsequio12.000,00
esObsequiotextoSí o NoNo
valorBrutomontoCantidad × precio unitario, antes de descuentos y cargos30.000,00
totalDescuentosmontoDescuentos de la línea3.000,00
totalCargosmontoCargos de la línea0,00
cargosDescuentosTextotextoDescuentos y cargos de la línea, uno por renglón-3.000,00 (10,00 %)
valormontoValor de la línea, neto de descuentos y cargos27.000,00
totalImpuestosmontoImpuestos de la línea5.130,00
impuestosTextotextoImpuestos de la línea, uno por renglónIVA 19,00 %: 5.130,00
totalmontoValor más impuestos de la línea32.130,00

Condición del elemento: [[IF:obsequio]] (la línea es obsequio). Listas anidadas: impuestos y cargosDescuentos.

Elemento de impuestos y de retenciones

VariableTipoDescripciónEjemplo
codigotextoCódigo DIAN del tributo01
nombretextoNombre del tributoIVA · ReteICA
tarifatextoTarifa con su unidad: porcentaje, por mil o valor por unidad19,00 % · 9,66 ‰ · 66,00 c/u
basemontoBase27.000,00
valormontoValor5.130,00
etiquetatextoNombre y tarifaIVA 19,00 % · INC Bolsas 66,00 c/u

Elemento de cargosDescuentos

VariableTipoDescripciónEjemplo
tipotextoDescuento o CargoDescuento
codigoRazontextoCódigo de la razón00
descripciontextoDescripciónPronto pago
porcentajedecimalPorcentaje10,00
porcentajeTextotextoPorcentaje con %; vacío si es cero10,00 %
basemontoBase30.000,00
valormontoValor, siempre positivo3.000,00
valorConSignomontoNegativo en descuentos-3.000,00
textotextoValor con signo y porcentaje-3.000,00 (10,00 %)

Condición del elemento: [[IF:esDescuento]].

Elemento de recaudos (sector salud)

VariableTipoDescripciónEjemplo
conceptotextoCódigo del concepto01
nombretextoCopago, Cuota moderadora, Pagos compartidos, AnticipoCopago
etiquetatextoNombre; con (informativo) cuando no resta del totalCopago (informativo)
fechaRecibidofechaFecha del primer recaudo del concepto05/09/2026
valormontoSuma del concepto12.000,00
valorEnTotalesmontoCon signo menos cuando resta del total-12.000,00

Elemento de anticipos

VariableTipoDescripciónEjemplo
identificaciontextoIdentificación del anticipoANT-01
fechaRecibidofechaFecha de recibido01/09/2026
valormontoValor50.000,00

Elemento de mediosPago

VariableTipoDescripciónEjemplo
codigotextoCódigo DIAN del medio47
nombretextoNombre del medio (Otro si el código no está en el catálogo de Kiai)Transferencia bancaria
referenciatextoReferencia, solo si la informasteTRF-001
valormontoTotal a pagar cuando hay un único medio; vacío con varios64.271,08

Elemento de notasTotales

VariableTipoDescripciónEjemplo
textotextoAclaraciónLas retenciones son informativas: no se descuentan del total a pagar.

B.3.9 Condiciones (para [[IF:…]] y [[IFNOT:…]])

No se imprimen: solo sirven para mostrar u ocultar bloques.

CondiciónActiva cuando
facturaEs factura de venta (01 a 04)
notaEs nota crédito o débito
notaCredito · notaDebitoNota crédito (91) · nota débito (92)
autorizacionDianNo es nota (lleva autorización de numeración DIAN)
pendienteValidacionEl documento aún no tiene fecha de validación DIAN
referenciaEs una nota que referencia una factura
notaSinReferenciaEs una nota sin referencia a factura
documento.referenciasFacturaHay periodo facturado (fuera de salud), orden de compra o factura de contingencia
creditoForma de pago crédito (nunca en notas)
mediosPago.conValorHay un único medio de pago (su valor es el total)
saludEs un documento del sector salud
salud.recaudosInformativosSalud con recaudos que se informan pero no restan del total
totales.hayDescuentos · totales.hayCargosHay descuentos · cargos globales distintos de cero
totales.hayAnticiposInformativosHay anticipos informativos (fuera de salud)
obsequio(solo dentro de [[FOR:lineas]]) La línea es obsequio
esDescuento(solo dentro de [[FOR:cargosDescuentos]]) Es descuento, no cargo

IF también acepta una lista (verdadero si tiene elementos, p. ej. [[IF:retenciones]]) o una variable (verdadero si no está vacía, p. ej. [[IF:documento.logo]]).

B.4 Ejemplo completo

Encabezado con logo, comprador, tabla de líneas con sus impuestos, totales, QR, CUFE/CUDE y pie de página. Pasa la validación al guardarla: úsala como punto de partida.

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>[[documento.titulo]] [[documento.numero]]</title>
<style>
  body { font-family: "Liberation Sans", Arial, sans-serif; font-size: 9pt; color: #222; margin: 0; }
  table { width: 100%; border-collapse: collapse; }
  th, td { padding: 3px 4px; vertical-align: top; }
  .detalle th { background: #f0f0f0; text-align: left; }
  .detalle td { border-bottom: 1px solid #ddd; }
  .num { text-align: right; }
  .qr img { width: 30mm; height: 30mm; }
  .cufe { word-break: break-all; font-size: 7pt; }
</style>
</head>
<body>

<!-- Encabezado: emisor y datos del documento -->
<table>
  <tr>
    [[IF:documento.logo]]<td style="width: 35mm;"><img src="[[documento.logo]]" alt="Logo" style="max-width: 35mm; max-height: 20mm;"></td>[[END:IF:documento.logo]]
    <td>
      <strong>[[emisor.razonSocial]]</strong><br>
      [[emisor.documento]]<br>
      [[IF:emisor.direccion]][[emisor.direccion]] · [[emisor.ciudad]]<br>[[END:IF:emisor.direccion]]
      [[IF:emisor.regimen]]Régimen: [[emisor.regimen]][[END:IF:emisor.regimen]]
    </td>
    <td class="num">
      <strong>[[documento.titulo]]</strong><br>
      No. [[documento.numero]]<br>
      Emisión: [[documento.fechaEmision]]<br>
      [[IF:documento.formaPago]]Forma de pago: [[documento.formaPago]]<br>[[END:IF:documento.formaPago]]
      [[IF:credito]]Vence: [[documento.fechaVencimiento]][[END:IF:credito]]
    </td>
  </tr>
</table>

<!-- Comprador -->
<p><strong>[[adquiriente.rol]]:</strong> [[adquiriente.razonSocial]] · [[adquiriente.documento]] · [[adquiriente.ciudad]]</p>

[[IF:referencia]]
<p>Corrige la factura [[referencia.factura]] del [[referencia.fecha]]. Concepto: [[referencia.concepto]]</p>
[[END:IF:referencia]]

<!-- Detalle: un renglón por línea, con sus impuestos anidados -->
<table class="detalle">
  <tr>
    <th>#</th><th>Descripción</th><th class="num">Cant.</th><th class="num">Precio unit.</th>
    <th class="num">Valor</th><th>Impuestos</th><th class="num">Total</th>
  </tr>
  [[FOR:lineas]]
  <tr>
    <td>[[indice]]</td>
    <td>[[descripcion]][[IF:obsequio]] (obsequio)[[END:IF:obsequio]]</td>
    <td class="num">[[cantidad]] [[unidad]]</td>
    <td class="num">[[precioUnitario]]</td>
    <td class="num">[[valor]]</td>
    <td>[[FOR:impuestos]][[etiqueta]]: [[valor]]<br>[[END:impuestos]]</td>
    <td class="num">[[total]]</td>
  </tr>
  [[END:lineas]]
</table>

<!-- Totales -->
<table style="width: 60%; margin-left: auto;">
  <tr><td>Valor bruto</td><td class="num">[[totales.valorBruto]]</td></tr>
  [[FOR:impuestos]]<tr><td>[[etiqueta]]</td><td class="num">[[valor]]</td></tr>[[END:impuestos]]
  [[IF:totales.hayDescuentos]]<tr><td>Descuentos</td><td class="num">-[[totales.totalDescuentos]]</td></tr>[[END:IF:totales.hayDescuentos]]
  [[IF:totales.hayCargos]]<tr><td>Cargos</td><td class="num">[[totales.totalCargos]]</td></tr>[[END:IF:totales.hayCargos]]
  <tr><td><strong>Total a pagar</strong></td><td class="num"><strong>[[documento.moneda]] [[totales.totalAPagar]]</strong></td></tr>
</table>
<p>Son: [[totales.valorEnLetras]]</p>
[[FOR:notasTotales]]<p><em>[[texto]]</em></p>[[END:notasTotales]]

<!-- QR y CUFE/CUDE -->
<table>
  <tr>
    <td class="qr" style="width: 32mm;"><img src="[[documento.codigoQr]]" alt="QR de consulta DIAN"></td>
    <td>
      <strong>[[documento.etiquetaIdentificador]]:</strong>
      <div class="cufe">[[documento.identificador]]</div>
      Validación DIAN: [[documento.fechaValidacion]]
    </td>
  </tr>
</table>

[[IF:documento.leyendaLegal]]<p style="font-size: 7pt;">[[documento.leyendaLegal]]</p>[[END:IF:documento.leyendaLegal]]

<template id="pie-pagina">
  <div style="width: 100%; padding: 0 10mm; font-family: Arial, sans-serif; font-size: 7pt; color: #555;">
    [[resolucion.texto]]<br>
    [[proveedorTecnologico.texto]] · Página <span class="pageNumber"></span> de <span class="totalPages"></span>
  </div>
</template>
</body>
</html>

Notas del ejemplo:

  • En la tabla de totales, [[FOR:impuestos]] está fuera de lineas: recorre el resumen del documento. En el detalle, el mismo [[FOR:impuestos]] recorre los de cada línea.
  • [[totales.totalDescuentos]] llega positivo; el signo - lo pone la plantilla.
  • El pie usa estilos en línea porque no hereda el <style> del documento.

B.5 Preguntas frecuentes

  • ¿Puedo partir de la plantilla de Kiai? La API no la expone: arma la tuya a partir del ejemplo de B.4.
  • Creé la plantilla y el PDF de una factura vieja no cambió. Es lo esperado: cada documento se imprime con la plantilla vigente cuando se emitió. Emite un documento nuevo para verla.
  • Escribí una variable mal y la plantilla se guardó. Revisa que el marcador no tenga espacios ([[ documento.numero ]] no es un marcador y se imprime tal cual).
  • Mi imagen no aparece en el PDF. Al imprimir no se descarga nada de internet: embébela como data: en un atributo src, no dentro de <style> (ver B.2.3).

Anexo C — Diccionario de campos

Este anexo lista, esquema por esquema, cada campo que envías o recibes en la API v1 de Facturación: su tipo, su longitud o formato y si es obligatorio. Cada límite sale del contrato OpenAPI, de las validaciones de la API o de lo que admite el almacenamiento; cuando esas fuentes difieren, aquí ves el más estricto, así que si lo respetas no tendrás sorpresas. Primero va lo que envías (emisor, comprador, factura, nota y sus piezas) y después lo que recibes.

Convenciones

TipoQué significa
textoCadena JSON. La longitud se cuenta en caracteres.
enteroNúmero sin decimales. Los consecutivos y rangos de numeración son de 64 bits.
decimal (2)Número JSON con máximo 2 decimales. En los montos, hasta 17 dígitos enteros.
decimal (6)Número JSON con máximo 6 decimales (cantidades, unidades de un impuesto nominal, TRM).
fechaTexto AAAA-MM-DD, p. ej. 2026-09-25.
fecha y horaTexto ISO 8601 con zona, p. ej. 2026-09-01T07:30:00-05:00 o 2026-09-01T12:30:00Z.
uuidIdentificador de 36 caracteres, p. ej. 3fa85f64-5717-4562-b3fc-2c963f66afa6.
booleanotrue o false.
listaArreglo JSON; la columna Tipo dice de qué son sus elementos.
objetoObjeto JSON; su forma está en la sección enlazada.

En la columna Obligatorio de lo que envías, Sí es siempre, No es que puedes omitirlo y Condicional es que depende de lo que diga la columna Notas. En lo que recibes, Sí significa que el campo siempre trae valor y No que puede venir en null.

Dos reglas generales: la API responde 400 si el cuerpo trae una propiedad que no está en el esquema, y las listas cargosDescuentos, impuestos y retenciones no admiten null (omítelas o envía []).

C.1 Emisor (EmisorInput)

Cuerpo del alta del emisor. El id del emisor no viaja: es el companyId de tu token.

CampoTipoLongitud / formatoObligatorioNotas
tipoDocumentotexto1 o 2 dígitosSíCódigo DIAN del tipo de documento, p. ej. 31 (NIT).
numeroDocumentotextomáx. 20; solo dígitos, letras y guionSíSin el dígito de verificación.
dvtextoexactamente 1 dígitoCondicionalObligatorio con NIT (tipoDocumento = 31); se valida con el algoritmo mod-11 de la DIAN.
tipoOrganizaciontexto1 o 2Sí1 persona jurídica, 2 persona natural.
razonSocialtextomáx. 450Sí
nombrestextomáx. 200No
apellidostextomáx. 200No
regimentexto1 a 3 dígitosSíCódigo DIAN del régimen, p. ej. 48 responsable de IVA, 49 no responsable.
responsabilidadesFiscalestextomáx. 120NoCódigos DIAN separados por ;, p. ej. O-13;O-15.
direcciontextomáx. 300Sí
codigoMunicipiotextoexactamente 5 dígitos (código DANE)SíP. ej. 11001 (Bogotá).
municipiotextomáx. 120Sí
departamentotextomáx. 120Sí
codigoPostaltextomáx. 10No
codigoPaistextoexactamente 2 (ISO 3166-1 alfa-2)NoPor defecto CO.
correotextocorreo válido; máx. 254Sí
telefonotextomáx. 30No
testSetIdtextomáx. 100NoId del set de pruebas que te asignó la DIAN para la habilitación.
ambientetextosolo habilitacionNoPor defecto habilitacion. produccion responde 400: el paso a producción lo hace Kiai cuando la DIAN te habilita.
activobooleanotrue o falseNoPor defecto true. Omítelo: en false el emisor nacería dado de baja.

C.2 Adquiriente del directorio (AdquirienteInput)

Cuerpo para registrar o editar un comprador en tu directorio. No envíes emisorId: el dueño es la empresa de tu token.

CampoTipoLongitud / formatoObligatorioNotas
tipoDocumentotexto1 o 2 dígitosSíCódigo DIAN, p. ej. 31 NIT, 13 cédula de ciudadanía.
numeroDocumentotextomáx. 20; solo dígitos, letras y guionSíSin el dígito de verificación.
dvtextoexactamente 1 dígitoCondicionalObligatorio con NIT (31); se valida con mod-11.
tipoOrganizaciontexto1 o 2Sí1 persona jurídica, 2 persona natural.
razonSocialtextomáx. 450Sí
nombrestextomáx. 200No
apellidostextomáx. 200No
regimentexto1 a 3 dígitosSíP. ej. 48 responsable de IVA, 49 no responsable.
responsabilidadesFiscalestextomáx. 120NoCódigos DIAN separados por ;, p. ej. R-99-PN.
direcciontextomáx. 300Sí
codigoMunicipiotextoexactamente 5 dígitos (código DANE)Sí
municipiotextomáx. 120Sí
departamentotextomáx. 120Sí
codigoPostaltextomáx. 10No
codigoPaistextoexactamente 2 (ISO 3166-1 alfa-2)NoPor defecto CO.
correotextocorreo válido; máx. 254Sí
telefonotextomáx. 30No
esNacionalbooleanotrue o falseNoPor defecto true.
esResponsableIvabooleanotrue o falseNoPor defecto false.

C.3 Comprador en el documento (AdquirienteDocumento)

Es el adquiriente que viaja dentro de la factura y de la nota sin referencia. No tienes que registrarlo antes: la API lo crea o actualiza en tu directorio y el documento guarda una copia que no cambia aunque edites el directorio después. Tiene dos formas:

  • Comprador identificado (consumidorFinal en false u omitido): lleva los mismos campos y límites de C.2, y los obligatorios de allí también lo son aquí (por eso figuran como Condicional).
  • Consumidor final: { "consumidorFinal": true } basta. La API completa tipo de documento 13, número 222222222222, nombre “Consumidor final” y responsabilidad R-99-PN. Puedes añadir correo, telefono y la dirección; si informas direccion, codigoMunicipio o codigoPostal, la dirección debe venir completa (direccion, codigoMunicipio, municipio, departamento y codigoPais).

En la respuesta de la factura o la nota, este objeto trae todos sus campos.

CampoTipoLongitud / formatoObligatorioNotas
consumidorFinalbooleanotrue o falseNoPor defecto false.
tipoDocumentotexto1 o 2 dígitosCondicionalObligatorio para un comprador identificado. Con consumidor final, si lo envías, debe ser 13.
numeroDocumentotextomáx. 20; solo dígitos, letras y guionCondicionalObligatorio para un comprador identificado. Con consumidor final, si lo envías, debe ser 222222222222.
dvtextoexactamente 1 dígitoCondicionalObligatorio con NIT (31); se valida con mod-11.
tipoOrganizaciontexto1 o 2CondicionalObligatorio para un comprador identificado.
razonSocialtextomáx. 450CondicionalObligatoria para un comprador identificado; con consumidor final la fija la API.
nombrestextomáx. 200No
apellidostextomáx. 200No
regimentexto1 a 3 dígitosCondicionalObligatorio para un comprador identificado.
responsabilidadesFiscalestextomáx. 120NoCódigos DIAN separados por ;.
direcciontextomáx. 300CondicionalObligatoria para un comprador identificado; con consumidor final, solo si informas la dirección.
codigoMunicipiotextoexactamente 5 dígitos (código DANE)CondicionalIgual que direccion.
municipiotextomáx. 120CondicionalIgual que direccion.
departamentotextomáx. 120CondicionalIgual que direccion.
codigoPostaltextomáx. 10NoCon consumidor final, enviarlo exige la dirección completa.
codigoPaistextoexactamente 2 (ISO 3166-1 alfa-2)NoPor defecto CO.
correotextocorreo válido; máx. 254CondicionalObligatorio para un comprador identificado; opcional con consumidor final.
telefonotextomáx. 30No
esNacionalbooleanotrue o falseNoPor defecto true.
esResponsableIvabooleanotrue o falseNoPor defecto false.

C.4 Factura (FacturaInput)

Cuerpo con el que creas o editas una factura en borrador. No envíes emisorId: el emisor es la empresa de tu token.

CampoTipoLongitud / formatoObligatorioNotas
adquirienteobjeto (C.3)—SíComprador con sus datos o { "consumidorFinal": true }.
resolucionIduuid36 caracteresSíId de una numeración autorizada de tipo factura (C.26).
fechafechaAAAA-MM-DDNoPor defecto, hoy.
monedatextoexactamente 3 (código ISO 4217)NoPor defecto COP. La factura del sector salud va en COP.
trmdecimal (6)mayor que 0; hasta 12 dígitos enterosCondicionalObligatoria si moneda no es COP (exportación). Pesos por unidad de la moneda.
fechaTasafechaAAAA-MM-DDNoFecha de la TRM; por defecto, la del documento.
mandanteobjeto (C.13)—NoSolo en la factura de mandato. No se admite con sectorSalud.
anticiposlista de C.14sin límite de elementosNoInformativos: no restan del total. No se admiten con sectorSalud (allí van en recaudos).
contingenciaobjeto (C.15)—NoSolo en la factura de contingencia.
periodoobjeto (C.16)—CondicionalObligatorio con sectorSalud.
sectorSaludobjeto (C.17)—NoSu presencia marca la factura como del sector salud.
formaPagotextocontado o creditoNoPor defecto contado.
mediosPagolista de C.11mínimo 1 elementoSí
fechaVencimientofechaAAAA-MM-DD; no anterior a fechaCondicionalObligatoria si formaPago = credito.
ordenReferenciaobjeto (C.12)—NoOrden de compra o pedido.
observacionestextomáx. 1000No
lineaslista de C.6mínimo 1 elementoSí
cargosDescuentoslista de C.9sin límite de elementosNoDe cabecera: no afectan la base gravable.
retencioneslista de C.8sin límite de elementosNoDe documento; no pueden repetir un tributo que ya informaste en las líneas.
totalesobjeto (C.10)—SíLa API los verifica al centavo contra las líneas.

C.5 Nota crédito o débito (NotaInput)

La nota usa el mismo detalle que la factura: líneas, cargos y descuentos, retenciones y totales. Con facturaId corriges una factura (operación 20 crédito o 30 débito); sin él, emites una nota sin referencia (22 o 32). Las notas no llevan anticipos.

CampoTipoLongitud / formatoObligatorioNotas
tipotextocredito o debitoSí
tipoOperaciontexto20, 22, 30 o 32NoSe deduce de tipo y facturaId. Si lo envías, no puede contradecirlos.
facturaIduuid36 caracteresCondicionalFactura aceptada por la DIAN que corriges. Obligatoria con referencia (20/30); no la envíes en 22/32.
adquirienteobjeto (C.3)—CondicionalObligatorio sin referencia (22/32). Con referencia no se envía: la nota toma el comprador de la factura.
resolucionIduuid36 caracteresSíNumeración de tipo nota-credito o nota-debito (C.26).
conceptoCorrecciontextocrédito: 1 a 6; débito: 1 a 4CondicionalObligatorio con referencia; no se envía sin ella. En crédito, 2 es anulación de la factura.
periodoobjeto (C.16)—CondicionalObligatorio sin referencia (22/32); opcional con referencia.
fechafechaAAAA-MM-DDNo
monedatextoexactamente 3 (código ISO 4217)NoPor defecto COP.
observacionestextomáx. 1000No
lineaslista de C.6mínimo 1 elementoSí
cargosDescuentoslista de C.9sin límite de elementosNoDe cabecera.
retencioneslista de C.8sin límite de elementosNoSe guardan y se devuelven, pero no van en el XML de la nota.
totalesobjeto (C.10)—SíEn crédito, totalAPagar no puede superar el saldo de la factura; con concepto 2 debe ser igual.

C.6 Línea del documento (LineaDocumentoInput)

CampoTipoLongitud / formatoObligatorioNotas
numeroenteromayor o igual a 1NoEn todas las líneas o en ninguna, sin repetir. Por defecto, la posición.
descripciontextomáx. 500Sí
codigotextomáx. 60CondicionalObligatorio si esquemaCodigo es distinto de 999.
esquemaCodigotexto001 (UNSPSC), 010 (GTIN), 020 (partida arancelaria) o 999NoPor defecto 999 (estándar del contribuyente).
unidadMedidatextocódigo de la tabla 13.3.6 del Anexo TécnicoSíP. ej. 94 = unidad.
cantidaddecimal (6)mayor que 0; hasta 13 dígitos enterosSí
precioUnitariodecimal (2)mayor o igual a 0Sí0 en una muestra gratis.
esMuestraGratisbooleanotrue o falseNoPor defecto false. Una muestra gratis no admite cargos ni descuentos.
precioReferenciadecimal (2)mayor que 0CondicionalObligatorio en una muestra gratis; no se envía en las demás líneas.
codigoPrecioReferenciatexto01, 02 o 03 (tabla 13.3.10)CondicionalIgual que precioReferencia.
cargosDescuentoslista de C.9sin límite de elementosNoEn la línea sí afectan la base gravable.
impuestoslista de C.7sin tributos repetidosNoVacía = ítem excluido.
retencioneslista de C.8sin tributos repetidosNoLa ReteIVA (05) exige que la línea tenga IVA (01).
valorTotaldecimal (2)mayor o igual a 0Síround(cantidad × precioUnitario) − descuentos + cargos; 0 en una muestra gratis.

C.7 Impuesto de línea (Impuesto)

El tributo (tabla 13.2.2) decide la forma. Porcentual — 01 IVA, 03 ICA (por mil), 04 INC, 08, 20, 21, 30, 35, 36: lleva tarifa sobre base. Nominal — 02, 22 (bolsas), 23, 24, 25, 26, 32, 33, 34 (IBUA): lleva valorUnitario por unidad y unidadMedidaBase.

CampoTipoLongitud / formatoObligatorioNotas
tributotextouno de los códigos de arribaSíLas retenciones no van aquí: van en retenciones.
basedecimalporcentual: 2 decimales, mayor o igual a 0; nominal: hasta 6 decimales, mayor que 0SíPorcentual: la base gravable de la línea. Nominal: las unidades (la cantidad de la línea).
tarifadecimal (2)IVA: 0, 5, 16 o 19; INC: 2, 4, 8 o 16; los demás: mayor o igual a 0, máx. 99999.99CondicionalObligatoria en un impuesto porcentual; no se envía en uno nominal.
valorUnitariodecimal (2)mayor o igual a 0CondicionalObligatorio en un impuesto nominal; no se envía en uno porcentual.
unidadMedidaBasetextocódigo de la tabla 13.3.6CondicionalIgual que valorUnitario.
valordecimal (2)mayor o igual a 0SíPorcentual: round(base × tarifa / 100) (ICA: / 1000). Nominal: valorUnitario × base (IBUA: / 100).

C.8 Retención (Retencion)

Va en la línea o en el documento. Es informativa: no cambia totalAPagar.

CampoTipoLongitud / formatoObligatorioNotas
tributotexto05 ReteIVA, 06 ReteRenta o 07 ReteICASíSin repetir en la misma línea ni en el documento.
basedecimal (2)mayor o igual a 0SíEn la línea, la base gravable; la ReteIVA se practica sobre el IVA de la línea.
tarifadecimal (2)ReteIVA: 15 o 100; ReteRenta: 0.1, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 6, 7, 10, 11 o 20; ReteICA: mayor o igual a 0, máx. 99999.99SíLa ReteICA va por mil.
valordecimal (2)mayor o igual a 0Síround(base × tarifa / 100); en ReteICA, por mil.

C.9 Cargo o descuento (CargoDescuento)

En una línea afecta la base gravable y su base es cantidad × precioUnitario; en la cabecera no afecta la base gravable.

CampoTipoLongitud / formatoObligatorioNotas
esCargobooleanotrue o falseSítrue recargo, false descuento.
codigoRazontextodescuento: 00 o 01; recargo: 02 o 03 (tabla 13.3.8)CondicionalObligatorio en un descuento; opcional en un recargo.
descripciontextomáx. 500No
porcentajedecimal (2)de 0 a 100NoSi lo envías, valor = round(base × porcentaje / 100).
basedecimal (2)mayor o igual a 0Sí
valordecimal (2)mayor o igual a 0SíUn descuento no puede superar su base.

C.10 Totales del documento (TotalesDocumento)

Los calculas tú y la API los verifica al centavo; si no cuadran, responde 400 con el valor esperado y el recibido en cada campo.

CampoTipoLongitud / formatoObligatorioNotas
valorBrutodecimal (2)mayor o igual a 0SíSuma de valorTotal de las líneas.
baseGravabledecimal (2)mayor o igual a 0SíSuma, por línea, de la base de su primer impuesto porcentual.
totalImpuestosdecimal (2)mayor o igual a 0SíSuma de los impuestos de las líneas.
valorConImpuestosdecimal (2)mayor o igual a 0SívalorBruto + totalImpuestos.
totalDescuentosdecimal (2)mayor o igual a 0NoPor defecto 0. Suma de los descuentos de cabecera.
totalCargosdecimal (2)mayor o igual a 0NoPor defecto 0. Suma de los cargos de cabecera.
totalAnticiposdecimal (2)mayor o igual a 0NoPor defecto 0. Suma de los anticipos; en la factura de salud con recaudos, suma de los recaudos. En las notas, 0.
totalRetencionesdecimal (2)mayor o igual a 0NoPor defecto 0. Suma de las retenciones de líneas y documento (informativo).
totalAPagardecimal (2)mayor o igual a 0SívalorConImpuestos − totalDescuentos + totalCargos; en la factura de salud de acreditación (SS-CUFE, SS-CUDE, SS-POS, SS-SNum), además resta los recaudos.

C.11 Medio de pago (MedioPago)

CampoTipoLongitud / formatoObligatorioNotas
codigotextocódigo de la tabla 13.3.4.2SíP. ej. 10 efectivo, 42 consignación, 47 transferencia, 48/49 tarjeta, ZZZ acuerdo mutuo.
referenciatextomáx. 150NoPor defecto, el número del documento.

C.12 Orden de referencia (OrdenReferencia)

CampoTipoLongitud / formatoObligatorioNotas
numerotextomáx. 60SíNúmero de la orden de compra o pedido.
fechafechaAAAA-MM-DDNoPor defecto, la del documento.

C.13 Mandante (MandanteInput)

Mandante de una factura de mandato: el emisor factura por cuenta de él.

CampoTipoLongitud / formatoObligatorioNotas
tipoDocumentotextomáx. 2NoCódigo DIAN; por defecto 31 (NIT).
numeroDocumentotextomáx. 20Sí
dvtextomáx. 1NoSi aplica.
razonSocialtextomáx. 450No
codigoMandatotextomáx. 20NoCódigo de mandato.

C.14 Anticipo (AnticipoInput)

Pago recibido antes de la factura. Es informativo: no reduce el total ni cambia el CUFE.

CampoTipoLongitud / formatoObligatorioNotas
identificaciontextomáx. 60NoIdentificación del pago.
valordecimal (2)mayor que 0Sí
fechaRecibidofechaAAAA-MM-DDNoPor defecto, la del documento.
fechaRealizadofechaAAAA-MM-DDNoPor defecto, la del documento.

C.15 Contingencia (ContingenciaInput)

Factura emitida por contingencia (fuera de línea).

CampoTipoLongitud / formatoObligatorioNotas
tipotexto03 o 04NoPor defecto 03. 03 contingencia del facturador, 04 contingencia DIAN.
consecutivoReferenciatextomáx. 60CondicionalConsecutivo del documento referenciado; obligatorio si tipo = 03.
cufeReferenciatextomáx. 200CondicionalCUFE o CUDE del documento referenciado; obligatorio si tipo = 03.
fechaReferenciafechaAAAA-MM-DDNo
codigoTipoDocumentoReferenciatextomáx. 2CondicionalCódigo DIAN del tipo del documento referenciado; obligatorio si tipo = 03.

C.16 Periodo del documento (PeriodoDocumentoInput)

Fecha y hora del primer y del último servicio.

CampoTipoLongitud / formatoObligatorioNotas
fechaIniciofecha y horaISO 8601 con zonaSíP. ej. 2026-09-01T07:30:00-05:00.
fechaFinfecha y horaISO 8601 con zona; no anterior a fechaInicioSí

C.17 Sector salud (SectorSaludInput)

Factura del sector salud según la Resolución 948 de 2026 del Ministerio de Salud. Los datos del paciente no van aquí: van en el RIPS. Con este bloque la factura exige periodo y moneda COP, y no admite anticipos ni mandante.

CampoTipoLongitud / formatoObligatorioNotas
tipoOperaciontextoSS-CUFE, SS-CUDE, SS-POS, SS-SNum, SS-Recaudo, SS-Reporte o SS-SinAporteSí
codigoPrestadortextoexactamente 10 dígitos (código REPS)CondicionalObligatorio salvo en SS-Recaudo, donde no se envía.
modalidadPagotexto01 a 05CondicionalIgual que codigoPrestador.
coberturaPlantexto02 a 17CondicionalIgual que codigoPrestador. El 01 está inactivo.
numeroContratotextomáx. 64CondicionalObligatorio si hay contrato. Excluyente con numeroPoliza. No se envía en SS-Recaudo.
numeroPolizatextomáx. 50NoSOAT o plan voluntario. Excluyente con numeroContrato. No se envía en SS-Recaudo.
facturaSinContratotexto01 a 07CondicionalObligatorio sin numeroContrato y prohibido con él. No se envía en SS-Recaudo.
recaudoslista de C.18de 1 a 4 elementos, un concepto por elementoCondicionalObligatorio en SS-CUFE, SS-CUDE, SS-POS, SS-SNum y SS-Reporte; no se envía en SS-Recaudo ni en SS-SinAporte. La suma no puede superar el total de la factura.

C.18 Recaudo del sector salud (RecaudoSaludInput)

CampoTipoLongitud / formatoObligatorioNotas
conceptotexto01 a 05Sí01 copago, 02 cuota moderadora, 03 pagos compartidos en planes voluntarios, 04 anticipo, 05 no aplica (va solo).
valordecimal (2)mayor que 0; exactamente 0 con el concepto 05SíSumatoria del concepto.
fechaRecibidofechaAAAA-MM-DDSíFecha en que recibiste el recaudo.

C.19 Consecutivo siguiente (ConsecutivoSiguienteInput)

Cuerpo de PUT /numeraciones-autorizadas/{id}/consecutivo-siguiente. Sirve, por ejemplo, si vienes de otro proveedor y ya usaste números del rango.

CampoTipoLongitud / formatoObligatorioNotas
consecutivoSiguienteenteromayor o igual a 1 y dentro del rango de la numeraciónSíDebe ser mayor que el último número emitido con la numeración; si no, 409.

C.20 Suscripción a webhooks (WebhookSuscripcionInput)

CampoTipoLongitud / formatoObligatorioNotas
urltextoURL absoluta https://; máx. 500SíTu receptor de eventos.
secrettextode 16 a 200SíSecreto para verificar la firma HMAC. La API nunca lo devuelve.
eventostextomáx. 500NoPor defecto * (todos). Tipos separados por ;, p. ej. documento.transmision.aceptada;documento.transmision.rechazada.

C.21 Notificación por correo (NotificacionInput)

Cuerpo opcional al notificar una factura o una nota por correo. Sin cuerpo, el correo va al comprador del documento.

CampoTipoLongitud / formatoObligatorioNotas
destinatarioslista de texto (correos)sin límite de elementos; todos los correos del envío (con copiaA), unidos con ;, máx. 2000 caracteresNoReemplazan al correo del comprador.
copiaAlista de texto (correos)sin límite de elementos; cuenta en el máx. de 2000 caracteres del envíoNoSe suman a los destinatarios; los repetidos se descartan sin distinguir mayúsculas.
anexoslista de C.22máx. 5 elementos y 5 MB en totalNoViajan dentro del zip de entrega, en anexos.zip.

C.22 Anexo de la notificación (AnexoInput)

CampoTipoLongitud / formatoObligatorioNotas
nombreArchivotextosin límite; extensión pdf, png, jpg, jpeg, gif, bmp, zip, xls o xlsxSíLa extensión define el tipo del archivo.
contenidoBase64textobase64 válido y no vacío; 5 MB decodificados entre todos los anexosSí

C.23 Factura (Factura)

Así viene una factura en las respuestas de la API.

CampoTipoLongitud / formatoObligatorioNotas
tipoDocumentotextofactura-venta (el contrato también define documento-equivalente-pos, que esta guía no cubre)SíClase de documento, explícita.
ambientetextohabilitacion o produccionNonull mientras sea borrador. Es el ambiente con que se emitió y no cambia después.
iduuid36 caracteresSí
emisorIduuid36 caracteresSíLa empresa de tu token.
adquirienteobjeto (C.3)—SíCopia del comprador que usan el XML, el CUFE, el PDF y el correo.
adquirienteIduuid36 caracteresSíRegistro del comprador en tu directorio.
resolucionIduuid36 caracteresSí
estadotextoborrador, emitida o anuladaSíanulada está reservado: hoy ninguna operación lleva ahí. Para anular, emite una nota crédito por el saldo (concepto 2).
numerotextomáx. 40NoPrefijo + consecutivo; se asigna al emitir.
cufetextomáx. 200NoSHA-384; se calcula al emitir.
fechafechaAAAA-MM-DDSí
fechaEmisionfecha y horaISO 8601 con zonaNo
monedatextoexactamente 3Sí
trmdecimal (6)—NoSolo en exportación.
fechaTasafechaAAAA-MM-DDNo
mandanteobjeto (C.13)—NoSolo en la factura de mandato.
anticiposlista de C.14—SíPuede venir vacía.
contingenciaobjeto (C.15)—NoSolo en la factura de contingencia.
periodoobjeto (C.16)—NoEn hora de Colombia.
sectorSaludobjeto (C.17)—NoSolo en la factura del sector salud.
formaPagotextocontado o creditoSí
mediosPagolista de C.11—Sí
fechaVencimientofechaAAAA-MM-DDNo
ordenReferenciaobjeto (C.12)—No
observacionestextomáx. 1000No
cargosDescuentoslista de C.9—SíPuede venir vacía.
retencioneslista de C.8—SíPuede venir vacía.
totalesobjeto (C.10)—Sí
transmisionEstadotextopendiente, aceptada o rechazadaNoEstado del envío a la DIAN.
transmisionMensajetextomáx. 2000NoResultado de la transmisión, con los errores de la DIAN si los hubo.
transmisionFechafecha y horaISO 8601 con zonaNo
transmisionTrackIdtextomáx. 200NoCUFE que devolvió la DIAN.
lineaslista de C.6—SíLo guardado, idéntico a lo que va al XML; numero y esquemaCodigo siempre vienen resueltos.
fechaCreacionfecha y horaISO 8601 con zonaSí
fechaActualizacionfecha y horaISO 8601 con zonaSí

C.24 Nota (Nota)

Así viene una nota crédito o débito en las respuestas de la API.

CampoTipoLongitud / formatoObligatorioNotas
tipoDocumentotextonota-credito o nota-debitoSíClase de documento, explícita.
ambientetextohabilitacion o produccionNonull mientras sea borrador.
iduuid36 caracteresSí
tipotextocredito o debitoSí
tipoOperaciontexto20, 22, 30 o 32SíSiempre resuelto, aunque no lo hayas enviado.
emisorIduuid36 caracteresSíLa empresa de tu token.
adquirienteobjeto (C.3)—SíEl del cuerpo (22/32) o la copia de la factura corregida (20/30).
adquirienteIduuid36 caracteresSíRegistro del comprador en tu directorio.
resolucionIduuid36 caracteresSí
facturaIduuid36 caracteresNonull en notas sin referencia (22/32).
facturaNumerotextomáx. 40NoNúmero de la factura corregida.
facturaCufetextomáx. 200NoCUFE de la factura corregida.
conceptoCorrecciontextocrédito: 1 a 6; débito: 1 a 4Nonull en notas sin referencia.
conceptoCorreccionDescripciontextosin límiteNoDescripción oficial DIAN del concepto.
periodoobjeto (C.16)—No
estadotextoborrador o emitidaSí
numerotextomáx. 40NoPrefijo + consecutivo; se asigna al emitir.
cudetextomáx. 200NoSHA-384; se calcula al emitir.
fechafechaAAAA-MM-DDSí
fechaEmisionfecha y horaISO 8601 con zonaNo
monedatextoexactamente 3Sí
observacionestextomáx. 1000No
cargosDescuentoslista de C.9—SíPuede venir vacía.
retencioneslista de C.8—SíPuede venir vacía.
totalesobjeto (C.10)—Sí
transmisionEstadotextopendiente, aceptada o rechazadaNoEstado del envío a la DIAN.
transmisionMensajetextomáx. 2000NoResultado de la transmisión, con los errores de la DIAN si los hubo.
transmisionFechafecha y horaISO 8601 con zonaNo
transmisionTrackIdtextomáx. 200NoCUDE que devolvió la DIAN.
lineaslista de C.6—Sínumero y esquemaCodigo siempre vienen resueltos.
fechaCreacionfecha y horaISO 8601 con zonaSí
fechaActualizacionfecha y horaISO 8601 con zonaSí

C.25 Saldo de la factura (SaldoFactura)

CampoTipoLongitud / formatoObligatorioNotas
facturaIduuid36 caracteresSí
totalFacturadecimal (2)—Sí
totalNotasCreditodecimal (2)—SíNotas crédito emitidas que la DIAN no rechazó.
totalNotasDebitodecimal (2)—SíInformativo: no cambia el saldo.
saldodecimal (2)—SítotalFactura − totalNotasCredito: lo que aún puedes acreditar.

C.26 Numeración autorizada (NumeracionAutorizada)

Prefijo, resolución, rango y vigencias los dicta la DIAN y no se editan; solo puedes mover el consecutivo siguiente (C.19).

CampoTipoLongitud / formatoObligatorioNotas
iduuid36 caracteresSíEs el resolucionId que usas en facturas y notas.
tipoDocumentotextofactura, nota-credito, nota-debito o documento-soporteSídocumento-soporte no sirve para facturas ni notas.
origentextodian, set-pruebas o por-defectoSípor-defecto: las numeraciones NC y ND de las notas.
ambientetextohabilitacion o produccionSí
prefijotextomáx. 10NoP. ej. SETT.
numeroResoluciontextomáx. 50Nonull en las numeraciones por defecto de las notas.
rangoDesdeentero—Nonull solo si está incompleta.
rangoHastaentero—NoEn las notas por defecto, el mayor consecutivo que cabe en un número de 20 caracteres con prefijo.
vigenciaDesdefechaAAAA-MM-DDNonull en las notas por defecto (no vencen).
vigenciaHastafechaAAAA-MM-DDNo
consecutivoSiguienteentero—NoPróximo consecutivo que tomará la emisión; null si está incompleta.
ultimoConsecutivoEmitidoentero—Nonull si aún no has emitido con ella.
disponiblesentero—NoConsecutivos que quedan desde el siguiente.
estadotextovigente, por-vencer, vencida, por-iniciar, incompleta o no-autorizadaSípor-vencer: 30 días o menos para el fin. Emitir con una no-autorizada responde 409.

C.27 Error (ProblemDetails)

Todo error responde application/problem+json (RFC 9457). El contrato no marca campos obligatorios; las cuatro últimas filas son extensiones que solo aparecen en los casos indicados.

CampoTipoLongitud / formatoObligatorioNotas
typetextosin límiteNoURI del tipo de error.
titletextosin límiteNoResumen del error.
statusenterocódigo HTTPNo
detailtextosin límiteNoExplicación del caso concreto.
instancetextosin límiteNoRuta de la solicitud.
traceIdtextosin límiteNoCompártelo con soporte de Kiai.
correlationIdtextosin límiteNoId de correlación de la solicitud (cabecera X-Correlation-Id).
fuentetextokiai, dian o clienteNoDe quién es la falla: cliente (4xx), dian (la DIAN no respondió o rechazó) o kiai (500 o límite de solicitudes).
errorsobjetocada campo con su lista de mensajesCondicionalSolo en los 400 de validación.
servicioDiantextosin límiteCondicionalOperación de la DIAN involucrada, en el 503 y en los rechazos.
reglasDianlista de C.28—CondicionalEn el 422 de un documento rechazado y en el 409 de una consulta rechazada; puede venir vacía.
numeroDocumentotextosin límiteCondicionalNúmero del documento rechazado; solo en el 422 de una transmisión.

C.28 Regla DIAN (ReglaDian)

CampoTipoLongitud / formatoObligatorioNotas
codigotextosin límiteNoP. ej. FAJ43a; null si la DIAN no lo entregó en ese formato.
mensajetextosin límiteSíDescripción de la regla.
tipotextorechazo o notificacionSírechazo impide la validación; notificacion solo advierte.

Soporte

Escríbenos a hola@kiai.co o por WhatsApp: por esos dos canales nos avisas que la DIAN aprobó tu set de pruebas y nos pides cambios en los datos de tu empresa.

Ante un incidente, envíanos el correlationId de la respuesta, el id y el número del documento, y la fecha y hora. Nunca nos envíes tu client_secret ni un token.

Hablar con el equipo técnico de Kiai