Colombia
  • Colombia
  • Costa Rica
  • Panamá
  • República Dominicana
  • Blog Alanube Perú

Cómo integrar API DIAN Colombia con Alanube: guía técnica 2026

Compartir

Índice

Integrar API DIAN Colombia con la API REST de Alanube es un proceso síncrono que se resuelve en cinco pasos: obtener el Bearer Token, construir el payload con el schema real, llamar a POST /e-provider/col/v1/invoices, leer el legalStatus de la respuesta y manejar los errores AEP. Esta guía técnica recorre cada paso con código Python real para integrar API DIAN Colombia desde cero.

La DIAN exige que cada comprobante fiscal viaje firmado digitalmente, sea validado en tiempo real y devuelva un CUFE (Código Único de Factura Electrónica) antes de ser entregado al cliente. Para el equipo de desarrollo, eso implica gestionar certificados, construir el documento técnico y manejar respuestas síncronas — todo antes de poder imprimir una representación gráfica.

Esta guía recorre el flujo completo de integración con la API de Alanube Colombia: desde la autenticación hasta la recepción del CUFE, pasando por la construcción del payload, la lectura de las propiedades status, legalStatus y governmentResponse en la respuesta, y los errores más frecuentes en producción.

Índice de contenidos

  • 1. Prerrequisitos
  • 2. Autenticación con Bearer Token
  • 3. Estructura del payload de factura
  • 4. Envío a la DIAN: integrar API DIAN Colombia y obtener el CUFE
  • 5. Manejo de intermitencias y webhooks de respaldo
  • 6. Pruebas en sandbox
  • 7. Errores reales reportados por la DIAN
  • 8. Preguntas frecuentes

Prerrequisitos

Antes de realizar el primer request, el equipo de integración debe tener disponibles:

  1. Credenciales de sandbox de Alanube Colombia (Token). Las credenciales se solicitan a través del agente comercial — no se generan en auto-servicio desde la documentación.
  2. RUT de la empresa habilitada ante la DIAN para facturación electrónica.
  3. Certificado digital gestionado por una entidad certificadora digital autorizada por la DIAN.
  4. Rango de numeración aprobado por la DIAN para la modalidad electrónica (resolución de numeración con prefijo, número inicial y número final).
  5. Ambiente de pruebas configurado: base URL https://sandbox-api.alegra.com/e-provider/col/v1.

Para producción, la raíz del servicio mantiene la misma estructura (https://api.alegra.com/e-provider/col/v1). En este artículo nos quedamos en sandbox para que los ejemplos sean reproducibles sin impacto fiscal.

Autenticación con Bearer Token

La API de Alanube Colombia se autentica enviando un Bearer Token en el header Authorization: Bearer ${ALEGRA_TOKEN} de cada request. El Token se entrega junto con las credenciales de sandbox. La vigencia del Token es configurable según el contrato del cliente y debe consultarse en el portal de Alanube; el Token no se refresca con un patrón de expires_in ni requiere flujo de autorización adicional — es un valor estático que se rota coordinándolo con el equipo de soporte de Alanube cuando se necesite.

import os
import requests

ALEGRA_TOKEN = os.environ["ALEGRA_TOKEN"]
BASE_URL = "https://sandbox-api.alegra.com/e-provider/col/v1"

HEADERS = {
    "Authorization": f"Bearer {ALEGRA_TOKEN}",
    "Content-Type": "application/json"
}

# Test rápido: GET /invoices debería responder 200 con el listado paginado
response = requests.get(f"{BASE_URL}/invoices", headers=HEADERS, timeout=10)
response.raise_for_status()
print(response.json())

# Verificar contra docs oficiales antes de producción

Si el Token no es válido, el endpoint responde 401 Unauthorized. Si está vencido o fue rotado, el flujo es el mismo: rotar la variable de entorno y reintentar.

Estructura del payload de factura

El payload sigue el modelo de datos de la DIAN (Resolución 000165 y sus actualizaciones) pero se envía como JSON — Alanube transforma internamente a UBL 2.1 XML y aplica la firma digital. Los campos críticos del schema real del endpoint POST /invoices se nombran en camelCase:

  • documentType (string enum): "01" Estándar, "02" Exportación, "03" Mandato, "04" Contingencia, "05" Transporte, "06" AIU, "07" Compra divisa, "08" Venta divisa.
  • number (number, requerido): número del documento dentro del rango autorizado en resolution.
  • resolution (object, requerido): datos de la resolución DIAN de numeración (prefijo, rango, fecha).
  • company (object, requerido): emisor (datos fiscales del AccountingSupplierParty).
  • customer (object, requerido): adquirente (AccountingCustomerParty).
  • items (array, requerido): líneas de detalle con base gravable e impuestos.
  • payments (array, requerido): formas de pago (PaymentMeans).
  • totalAmounts (object, requerido): totales por concepto y total general.
  • note (array of strings): notas libres del emisor.
  • discountsAndCharges, advancePayments, creditNoteReference, debitNoteReference, foreignCurrency, additionalDocumentReference (este último obligatorio en documentType = "04" Contingencia).

El schema completo, con todos los campos opcionales y su anidamiento exacto, está en la documentación técnica https://e-provider-docs.alegra.com/. Mantén el payload alineado con la versión vigente; los esquemas se actualizan junto con las resoluciones de la DIAN.

import os
import requests

ALEGRA_TOKEN = os.environ["ALEGRA_TOKEN"]
BASE_URL = "https://sandbox-api.alegra.com/e-provider/col/v1"

HEADERS = {
    "Authorization": f"Bearer {ALEGRA_TOKEN}",
    "Content-Type": "application/json"
}

invoice_payload = {
    "documentType": "01",            # Factura estándar
    "number": 1001,
    "note": ["Factura emitida vía API"],
    "resolution": {
        "prefix": "FE",
        "resolutionNumber": "18760000001",
        "rangeStart": 1,
        "rangeEnd": 10000
    },
    "company": {
        "organizationType": "1",
        "identificationType": "31",
        "identificationNumber": "900559088",
        "dv": "2",
        "name": "Empresa de Pruebas SAS",
        "regimeCode": "R-99-PN",
        "email": "facturacion@empresa.com",
        "address": {"city": "11001", "department": "11", "country": "CO"}
    },
    "customer": {
        "organizationType": "2",
        "identificationType": "13",
        "identificationNumber": "1020304050",
        "name": "Cliente Final",
        "email": "cliente@correo.com",
        "address": {"city": "11001", "department": "11", "country": "CO"}
    },
    "items": [
        {
            "code": "SKU-001",
            "description": "Servicio profesional",
            "quantity": 1,
            "unitPrice": 1000000,
            "taxes": [{"code": "01", "percentage": 19}]
        }
    ],
    "payments": [
        {"paymentForm": "1", "paymentMethod": "10", "paymentDueDate": "2026-05-25"}
    ],
    "totalAmounts": {
        "grossTotal": 1000000,
        "taxableAmount": 1000000,
        "taxTotal": 190000,
        "payableAmount": 1190000
    }
}

response = requests.post(f"{BASE_URL}/invoices", json=invoice_payload, headers=HEADERS, timeout=30)
response.raise_for_status()
print(response.json())

# Verificar contra docs oficiales antes de producción

Envío a la DIAN: integrar API DIAN Colombia y obtener el CUFE

El endpoint principal es POST /e-provider/col/v1/invoices. Colombia opera bajo un modelo síncrono: la DIAN responde en línea con la validación, y la respuesta 201 ya trae el CUFE y tres propiedades clave: status, legalStatus y governmentResponse.

Los valores reales que devuelve la API:

  • status ∈ {REGISTERED, WAITING_RESPONSE, FAILED, SENT}.
  • legalStatus ∈ {ACCEPTED, ACCEPTED_WITH_OBSERVATIONS, REJECTED}.
  • governmentResponse.code ∈ {"00", "66", "90", "99"} con message y errorMessages[].
import os
import requests

ALEGRA_TOKEN = os.environ["ALEGRA_TOKEN"]
BASE_URL = "https://sandbox-api.alegra.com/e-provider/col/v1"

HEADERS = {
    "Authorization": f"Bearer {ALEGRA_TOKEN}",
    "Content-Type": "application/json"
}


class InvoiceRejectedByRule90(Exception): pass
class InvoiceAlreadyAccepted(Exception): pass
class InvoiceInProcess(Exception): pass


def emit_invoice(payload: dict) -> dict:
    response = requests.post(
        f"{BASE_URL}/invoices",
        json=payload,
        headers=HEADERS,
        timeout=30
    )

    if response.status_code >= 400:
        body = response.json()
        for err in body.get("errors", []):
            code = err["code"]
            if code == "AEP6011":
                raise InvoiceRejectedByRule90(payload["number"])
            if code == "AEP6009":
                raise InvoiceAlreadyAccepted(payload["number"])
            if code == "AEP6010":
                raise InvoiceInProcess(payload["number"])
        response.raise_for_status()

    body = response.json()
    # body trae: id, cufe, status, legalStatus, governmentResponse, ...
    return body


def get_invoice(invoice_id: str) -> tuple[str, str]:
    response = requests.get(f"{BASE_URL}/invoices/{invoice_id}", headers=HEADERS, timeout=30)
    response.raise_for_status()
    body = response.json()
    return body["status"], body["legalStatus"]  # ej: ("SENT", "ACCEPTED")

# Verificar contra docs oficiales antes de producción

La respuesta exitosa ya trae status, legalStatus y el CUFE. No hay que esperar a un evento posterior cuando el flujo es nominal — la DIAN ya respondió y el documento quedó procesado. Solo cuando la DIAN no respondió en línea (intermitencia) el documento queda en WAITING_RESPONSE, y la resolución llega después por consulta o por webhook.

Manejo de intermitencias y webhooks de respaldo

En Colombia el modelo es síncrono, por lo que los webhooks no son el flujo principal. Cobran relevancia en escenarios de intermitencia: cuando la DIAN no logra responder en línea, la respuesta del POST devuelve status = WAITING_RESPONSE hasta que la entidad confirme. La resolución llega después por consulta directa al recurso o por webhook.

Los webhooks reales de la API de Alanube Colombia (nombres oficiales):

  • invoices.emissionFinished — emisión de factura finalizada (sea ACCEPTED, ACCEPTED_WITH_OBSERVATIONS o REJECTED).
  • creditNotes.emissionFinished — emisión de nota crédito finalizada.
  • general.governmentStatusChanged — cambio en el estado legal (legalStatus) de cualquier documento.

No existen los eventos invoice.accepted ni invoice.rejected como webhooks separados: la información del veredicto viaja en el payload del evento, en el campo legalStatus del documento.

import os
import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["ALEGRA_WEBHOOK_SECRET"]


@app.route("/webhooks/alanube", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Alegra-Signature", "")
    body = request.get_data()
    expected = hmac.new(WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        return jsonify({"error": "firma invalida"}), 401

    event = request.json or {}
    event_type = event.get("event")  # ej. invoices.emissionFinished
    data = event.get("data", {})

    if event_type == "invoices.emissionFinished":
        legal_status = data.get("legalStatus")
        if legal_status == "ACCEPTED":
            # Persistir CUFE y marcar la factura como aceptada
            pass
        elif legal_status == "ACCEPTED_WITH_OBSERVATIONS":
            # Persistir y registrar observaciones devueltas por la DIAN
            pass
        elif legal_status == "REJECTED":
            # Notificar al equipo de operaciones
            pass

    elif event_type == "general.governmentStatusChanged":
        # Cambio en legalStatus de un documento previamente emitido
        pass

    return jsonify({"received": True}), 200

# Verificar contra docs oficiales antes de producción

La configuración del endpoint receptor se realiza a través de la API de Alanube. Diseña el handler de forma idempotente — un mismo evento podría reentregarse durante una intermitencia prolongada.

Pruebas en sandbox

El ambiente de pruebas de Alanube Colombia no es una simulación interna: está conectado al ambiente de pruebas de la DIAN, de modo que los documentos enviados son validados realmente por la entidad y la respuesta refleja el comportamiento productivo, sin impacto fiscal.

  • Base URL sandbox: https://sandbox-api.alegra.com/e-provider/col/v1.
  • NIT de pruebas en sandbox: 900559088, DV 2. Usa un prefijo único por equipo para evitar choques de Regla 90.
  • Credenciales: se solicitan a través del agente comercial.
  • Los CUFEs generados en sandbox no tienen validez fiscal.
  • La respuesta es síncrona, igual que en producción. Los escenarios de intermitencia se reproducen solo cuando la DIAN de pruebas presenta fallos.

Errores reales reportados por la DIAN

El catálogo de validaciones lo administra la DIAN, no la API; los códigos viajan en la respuesta dentro del array errors con la forma {"code": "...", "message": "..."}. Los códigos relevantes para el flujo de facturas son la familia AEP6xxx (facturas) y EPRxxx (transporte). El error más recurrente en integraciones nuevas es el de la Regla 90 — Documento procesado anteriormente:

{
  "errors": [
    {
      "code": "AEP6011",
      "message": "The invoice with this number is rejected by rule 90, please check the number and try again later"
    }
  ]
}

Se dispara cuando la combinación prefijo + consecutivo ya fue registrada por la DIAN. Otros códigos relacionados que conviene capturar:

  • AEP6009 — el documento con ese número ya fue registrado y aceptado por la DIAN.
  • AEP6010 — el documento ya fue enviado y está en proceso. Consultar status/legalStatus antes de reintentar.
  • AEP1001 / AEP1005 — certificado inválido / certificado vencido.
  • AEP6008 — compañía no habilitada en DIAN para facturación electrónica.
  • AEP6012 a AEP6017 — atributos obligatorios faltantes en company (organizationType, identificationType, name, regimeCode, email, address).
  • AEP6022 — instance.supplier.address.city no existe en la tabla de municipalidades DIAN.
  • AEP6023 / AEP6024 — payments.paymentDueDate requerido si paymentForm = "2" (crédito) y debe ser mayor a la fecha de emisión.
  • EPR500 / EPR503 / EPR504 — errores de comunicación, timeout o resend con la DIAN.

La solución general es consultar el estado del documento antes de reintentar emisiones tras un timeout y nunca reusar un consecutivo que ya alcanzó legalStatus = ACCEPTED. Para el detalle completo de códigos y reglas de validación, consulta la documentación oficial de Alegra e-provider.

FAQs

¿Cuál es la diferencia entre CUFE, CUDE y CUNE en Colombia?

El CUFE (Código Único de Factura Electrónica) identifica facturas electrónicas de venta. El CUDE (Código Único de Documento Electrónico) identifica el resto de documentos electrónicos — notas crédito, notas débito y documentos soporte. El CUNE corresponde exclusivamente a Nómina Electrónica. La API devuelve el código correcto según el tipo de documento enviado; el integrador no necesita calcularlos manualmente.

¿Cuánto tarda tener la primera prueba corriendo en sandbox?

Considerando que las credenciales se solicitan al agente comercial y deben provisionarse, lo realista es 2 a 3 días hábiles desde el primer contacto hasta el primer request exitoso. Una vez tienes el Token, ejecutar el primer POST /invoices toma minutos: la documentación interactiva en https://e-provider-docs.alegra.com/ permite probar los endpoints directamente desde el navegador con plantillas en Shell (curl), Python, Node.js, Ruby y PHP.

¿Qué pasa si la DIAN no responde y el documento queda en WAITING_RESPONSE?

Significa que se entró al escenario de intermitencia: la DIAN no respondió en línea y el documento espera resolución. No se debe reenviar el mismo prefijo + consecutivo mientras el documento siga en WAITING_RESPONSE; reenviarlo después de que la primera emisión haya quedado ACCEPTED activa la Regla 90 (AEP6011). La práctica correcta es consultar el documento por su id con GET /invoices/{id} y revisar las propiedades status y legalStatus; cuando la DIAN resuelve, el documento se mueve a status = SENT con su legalStatus final (ACCEPTED, ACCEPTED_WITH_OBSERVATIONS o REJECTED), y el webhook general.governmentStatusChanged o invoices.emissionFinished te notifica el cambio si lo tienes configurado.

¿La API de Alanube Colombia cubre todo el ecosistema DIAN?

Sí. La API de Alanube Colombia cubre facturas electrónicas de venta (/invoices), notas de crédito (/credit-notes), notas de débito (/debit-notes), documentos equivalentes POS (/equivalent-documents-pos), documentos soporte de adquisiciones a no obligados (/support-documents) y comprobantes de nómina electrónica. Cada tipo tiene su propio endpoint y esquema en https://e-provider-docs.alegra.com/. Si tu proyecto requiere habilitación ante la DIAN, certificado digital y emisión en producción bajo un único proveedor tecnológico, Alanube Colombia es la opción que el equipo técnico de tu integración puede levantar en pocos días hábiles.

Gracias por llegar hasta aquí,
Equipo Alanube 👋

Compartir

¿Te gustó este artículo?

Suscríbete y recibe las novedades en tu correo.

Comentarios

guest
0 Comentarios
Oldest
Newest Most Voted

Notas relacionadas

0
Would love your thoughts, please comment.x
()
x