FacEPro Cloud API v1

Documentación API REST

FacEPro Cloud · v1 · Facturación electrónica DIAN Colombia

El API de FacEPro Cloud te permite emitir facturas electrónicas, notas crédito y notas débito ante la DIAN desde cualquier aplicación (PHP, VB.NET, C#, Java, JavaScript, Python). Tú envías un JSON simple con los datos de la operación; nosotros nos encargamos de generar el XML UBL 2.1, firmarlo digitalmente (XAdES BES + WS-Security), calcular el CUFE y enviarlo a la DIAN.

Antes de empezar

Necesitas: (1) tu empresa registrada en el portal, (2) el certificado digital cargado, (3) la configuración DIAN completada (SoftwareID, PIN, Clave Técnica, Resolución), y (4) un Token API generado desde el portal.

Quick start — tu primera factura en 3 pasos

  1. 1

    Genera un Token API

    Entra al portal, ve a Tokens API, haz clic en Generar Token y copia el valor (solo se muestra una vez).

  2. 2

    Verifica el token con GET /me

    Confirma que tu token responde y que la empresa asociada es la correcta antes de enviar facturas reales.

  3. 3

    Envía tu primera factura con POST /documentos

    Recibirás un tracking_id, el cufe y el estado DIAN en una sola respuesta.

Ejemplos JSON descargables

Descarga estos archivos como punto de partida para tu integración. Cada ejemplo incluye todos los campos disponibles con valores reales que puedes adaptar.

Factura electrónica — contado
POST /api/v1/documentos · Pago al contado, IVA 19% + exento
factura-contado.json ↓
Factura electrónica — crédito
POST /api/v1/documentos · Crédito con fecha vencimiento + descuento
factura-credito.json ↓
Factura con retenciones · B2B
POST /api/v1/documentos · ReteIVA + ReteRenta + ReteICA (Gran Contribuyente)
factura-con-retenciones.json ↓
Factura con anticipo + descuento
POST /api/v1/documentos · Descuento global + anticipo previo
factura-con-anticipo-descuento.json ↓
Factura con ICA + INC
POST /api/v1/documentos · Impuestos al consumo (bar) + ICA (Bogotá)
factura-con-ica-inc.json ↓
Factura con propina
POST /api/v1/documentos · Bar/restaurante · Propina 10% (DIAN code 03, no gravable)
factura-con-propina.json ↓
Factura con producto regalo
POST /api/v1/documentos · FreeOfChargeIndicator · 2x1 / muestra (precio_referencia)
factura-con-regalo.json ↓
Factura con bolsas plásticas
POST /api/v1/documentos · Impuesto bolsas Ley 1819 (TaxScheme 22, per-unidad)
factura-con-bolsas.json ↓
Factura con IBUA
POST /api/v1/documentos · Bebidas azucaradas (TaxScheme 34, por volumen ml)
factura-con-ibua.json ↓
Factura con ICUI
POST /api/v1/documentos · Comestibles ultraprocesados (TaxScheme 35, porcentual)
factura-con-icui.json ↓
Factura con Imp. Carbono
POST /api/v1/documentos · Combustibles (TaxScheme 23, por galón)
factura-con-carbono.json ↓
Factura con licores (ICL)
POST /api/v1/documentos · ICL específico (32) + AD VALOREM (ZZ)
factura-con-licor.json ↓
Nota crédito
POST /api/v1/notas-credito · Referencia a factura previa
nota-credito.json ↓
Nota débito
POST /api/v1/notas-debito · Interés por mora
nota-debito.json ↓
Evento 030 — Acuse de recibo
POST /api/v1/eventos · Confirmar recepción de factura
evento-030-acuse-recibo.json ↓
Evento 032 — Recibo del bien
POST /api/v1/eventos · Confirmar bien entregado
evento-032-recibo-bien.json ↓
Evento 033 — Aceptación expresa
POST /api/v1/eventos · Aceptar como título valor
evento-033-aceptacion-expresa.json ↓
Evento 031 — Reclamo
POST /api/v1/eventos · Rechazar factura recibida
evento-031-reclamo.json ↓
Documento Soporte Electrónico
POST /api/v1/documentos-soporte · Compras a no obligados a facturar (vendedor + retenciones)
documento-soporte.json ↓
Tip: Estos ejemplos son ilustrativos. Cuando integres, reemplaza los CUFE de los eventos por el CUFE real de tu factura recibida, y los datos del adquirente/emisor con los de tu operación.

Autenticación

Todas las peticiones (excepto GET /health) requieren un Bearer Token en el header Authorization:

Authorization: Bearer 7|aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789...
  • El token identifica a tu empresa (tenant).
  • Está hasheado en base de datos: si lo pierdes, debes generar uno nuevo.
  • Lo puedes revocar en cualquier momento desde el portal.
  • Cada petición se valida y se registra en los logs de auditoría del API.

No expongas tu token

Nunca lo incluyas en código del lado del navegador, en repositorios públicos, ni en capturas de pantalla. Si sospechas que se filtró, revócalo de inmediato.

Convenciones

Base URL https://facepro.cdpconsultores.com/api/v1
Formato JSON (UTF-8) en request y response
Content-Type application/json
Accept application/json
Zona horaria America/Bogota (UTC-5)
Decimales punto como separador (19000.50, no 19000,50)
Fechas YYYY-MM-DD

Forma de las respuestas

Toda respuesta JSON incluye un campo ok booleano que indica si la operación fue exitosa.

// Éxito
{
  "ok": true,
  "data": { ... }
}

// Error
{
  "ok": false,
  "error": "Mensaje legible",
  "codigo": "identificador_estable"
}

CUFE y CUDE

El CUFE (Código Único de Factura Electrónica) y el CUDE (Código Único de Documento Electrónico) son hashes SHA-384 que identifican inequívocamente cada documento ante la DIAN.

CampoAplica aLargoAlgoritmo
CUFEFactura electrónica96 hexSHA-384
CUDENC, ND, eventos (ApplicationResponse)96 hexSHA-384

Cálculo del CUFE (factura)

CUFE = SHA-384(
  NumFac + FecFac + HorFac + ValFac
  + "01" + ValImpIVA
  + "04" + ValImpICA
  + "03" + ValImpINC
  + ValTot + NitOFE + NumAdq + ClTec + TipoAmb
)

Cálculo del CUDE (evento / ApplicationResponse)

CUDE = SHA-384(
  Num_DE + Fec_Emi + Hor_Emi + NitFE + DocAdq
  + ResponseCode + ID + DocumentTypeCode + Software-PIN
)
FacEPro lo calcula automáticamente. Solo recibes el CUFE/CUDE en la respuesta del API — no necesitas calcularlo en tu app.

Validar un documento en el catálogo DIAN

Cualquier persona puede verificar un documento por su CUFE en:

https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={CUFE}

Ambiente habilitación: catalogo-vpfe-hab.dian.gov.co

Impuestos avanzados, retenciones, descuentos y anticipos

Para coberturas B2B reales en Colombia, FacEPro soporta todos los conceptos exigidos por DIAN. Todos son opcionales: si no los envías, la factura es simple como antes.

Retenciones (ReteIVA / ReteRenta / ReteICA)

Aplican cuando el adquirente es Gran Contribuyente o Autoretenedor. El comprador retiene impuestos al pagar la factura.

CódigoNombreBase por defectoTarifa típica
05ReteIVAMonto del IVA15% del IVA
06ReteRenta (Retefuente)Subtotal2.5% servicios · 3.5% compras
07ReteICASubtotal0.7% a 11.04‰ según municipio
"retenciones": [
  { "codigo": "05", "porcentaje": 15.00 },
  { "codigo": "06", "porcentaje": 2.50 },
  { "codigo": "07", "porcentaje": 0.70 }
]

La respuesta del API incluye total_retencion_iva, total_retencion_renta, total_retencion_ica, total_retenciones y total_neto (lo que el cliente realmente paga).

ICA e INC por línea

Impuestos adicionales que aplican según la línea:

  • ICA (Impuesto de Industria y Comercio): por municipio, ~0.7% a 1% según actividad
  • INC (Impuesto al Consumo): 8% en bares/restaurantes, 4% en telefonía móvil
"lineas": [
  {
    "codigo": "M-001",
    "descripcion": "Cena gourmet",
    "cantidad": 1,
    "precio": 250000,
    "iva_pct": 0,
    "inc_pct": 8      // INC 8% para bares/restaurantes
  }
]

Propina (bares y restaurantes)

La propina es un incremento al total pero NO entra a la base imponible — no se le calcula IVA. DIAN la identifica con AllowanceChargeReasonCode = 03.

"factura": {
  ...
  "propina_pct": 10.00      // 10% del subtotal · o usar:
  "propina_valor": 25000      // monto fijo (mutuamente excluyente con pct)
}

Cálculo: Subtotal $250.000 · IVA $0 · INC 8% = $20.000 · Propina 10% = $25.000 → Total a pagar = $295.000. La propina aparece en ChargeTotalAmount del XML, fuera del TaxInclusiveAmount.

Descuentos y recargos globales

A diferencia de lineas[].descuento_pct (por línea), el global aplica al documento entero.

"factura": {
  ...
  "descuento_global_valor": 50000,
  "descuento_global_razon": "Cliente frecuente",

  "recargo_global_valor": 10000,
  "recargo_global_razon": "Costo de envío"
}

Anticipos (pagos previos)

Si el cliente ya pagó algo antes de la factura, declara los anticipos. Se restan del total a pagar.

"anticipos": [
  {
    "id": "ANT-001",
    "valor": 200000,
    "fecha_recibido": "2026-05-01"
  }
]

Productos de regalo (FreeOfChargeIndicator)

Para promociones 2x1, muestras, regalos comerciales o degustaciones. La línea lleva "es_regalo": true y precio_referencia con el valor de mercado del producto regalado. El cliente paga $0 por esa línea (LineExtensionAmount = 0) pero queda trazable para DIAN con el indicador <cbc:FreeOfChargeIndicator>true</cbc:FreeOfChargeIndicator> y el bloque PricingReference en el XML.

"lineas": [
  {
    "codigo": "SHAMPOO-500",
    "descripcion": "Shampoo Hidratante 500ml",
    "cantidad": 1,
    "precio": 35000,
    "iva_pct": 19
  },
  {
    "codigo": "ACOND-300",
    "descripcion": "Acondicionador 300ml (REGALO)",
    "cantidad": 1,
    "precio": 0,
    "iva_pct": 0,
    "es_regalo": true,
    "precio_referencia": 28000     // valor de mercado del producto
  }
]

Bolsas plásticas (Ley 1819 / TaxScheme 22)

Impuesto al uso de bolsas plásticas (Reforma Tributaria 2016, Art. 207). A diferencia de IVA/ICA/INC, es per-unidad (no porcentual): se cobra un valor fijo por cada bolsa entregada. Para 2026 el valor por bolsa es de $80 COP (actualizar anualmente según UVT).

"bolsas": {
  "cantidad": 3,
  "valor_unitario": 80
}

Se emite un <cac:TaxTotal> dedicado con TaxScheme/ID = 22, PerUnitAmount y BaseUnitMeasure. El total (cantidad × valor_unitario) suma al total a pagar y queda guardado en el campo total_bolsas del documento.

Impuestos sectoriales (Nivel 2)

Para sectores específicos (bebidas, comestibles ultraprocesados, combustibles, licores). Todos son opcionales y por línea: solo la empresa que vende ese producto los envía. Las tarifas las calcula y envía tu sistema (cambian cada año por UVT); FacEPro solo arma el XML. Códigos confirmados contra los XML de ejemplo del toolkit DIAN.

ImpuestoTaxSchemeTipoCampo en la línea
IBUA (bebidas azucaradas)34por volumen (ml)ibua: {volumen_ml, tarifa_por_100ml}
ICUI (comestibles ultraprocesados)35porcentualicui_pct
INCarbono (combustibles)23por unidadcarbono: {cantidad, unidad, tarifa}
ICL (licor, específico)32por grados alcohollicor: {grados, tarifa_especifico, ad_valorem_pct}
AD VALOREM (licor, 2º comp.)ZZporcentual
// Bebida azucarada (IBUA): tax = (volumen_ml / 100) × tarifa_por_100ml
{ "codigo": "COCA1L", "cantidad": 10, "precio": 1000, "iva_pct": 19,
  "ibua": { "volumen_ml": 10000, "tarifa_por_100ml": 18 } }

// Ultraprocesado (ICUI): porcentual, como el IVA
{ "codigo": "SNACK", "cantidad": 5, "precio": 20000, "iva_pct": 19, "icui_pct": 20 }

// Combustible (Carbono): tax = cantidad × tarifa
{ "codigo": "ACPM", "cantidad": 100, "precio": 9500, "iva_pct": 0,
  "carbono": { "cantidad": 100, "unidad": "GLL", "tarifa": 238 } }

// Licor: ICL específico (grados × tarifa) + AD VALOREM (% sobre base) + IVA
{ "codigo": "AGUARD", "cantidad": 1, "precio": 53000, "iva_pct": 19,
  "licor": { "grados": 37, "tarifa_especifico": 295, "ad_valorem_pct": 25 } }

Cada impuesto se emite como un <cac:TaxTotal> adicional, tanto a nivel de línea como agregado en la cabecera. Los montos suman al total a pagar y quedan en total_ibua, total_icui, total_carbono, total_icl y total_advalorem del documento.

Tipo de operación DIAN

CódigoTipoAplica a
10Estándar (default)Mayoría de facturas
09AIUConstrucción, vigilancia, aseo
11MandatosFacturación por cuenta de terceros
12ExportaciónVentas internacionales
13Cambio de divisasCasas de cambio
"factura": { ..., "tipo_operacion": "10" }
Todo es opcional. Tu integración actual NO se rompe: si no envías estos campos, la factura sale exactamente como antes.

Entrega automática al adquirente

Según el Anexo Técnico DIAN sección 9.1, el facturador electrónico debe enviar al adquirente, vía correo electrónico, un AttachedDocument (ZIP) con la factura XML firmada + el ApplicationResponse de DIAN. FacEPro lo hace automáticamente cuando incluyes adquiriente.email en el payload.

Flujo automático

  1. 1. Tu app envía POST /api/v1/documentos con adquiriente.email
  2. 2. FacEPro firma, envía a DIAN, espera veredicto
  3. 3. Si DIAN acepta → FacEPro construye el AttachedDocument, lo firma, empaqueta en ZIP
  4. 4. Envía el correo al adquiriente.email con asunto formato DIAN

Asunto del correo (formato DIAN)

NIT_FACTURADOR;RAZON_SOCIAL;NUMERO_DOC;CODIGO_TIPO_DOC;NOMBRE_COMERCIAL;LINEA_NEGOCIO

# Ejemplo:
901259449;UP MOTOS S.A.S;SETP990000023;01;UP MOTOS;Comercializadora

Adjuntos

  • entrega_{numero}.zip — AttachedDocument firmado + factura.xml + ApplicationResponse.xml
  • {numero}.pdf — Representación gráfica con QR DIAN
Reintentos automáticos: Si el envío del correo falla (SMTP caído, email inválido, etc.), FacEPro reintenta cada 5 minutos hasta 5 veces. El estado de la entrega es visible en /portal/documentos/{uuid}.

Catálogos DIAN

Estos códigos vienen de las tablas oficiales del Anexo Técnico 1.9 DIAN. Tu aplicación debe enviar exactamente estos valores.

Tipo de persona tipo_persona

CódigoDescripción
1Persona Jurídica
2Persona Natural

Tipo de documento tipo_documento

CódigoDescripción
11Registro civil
12Tarjeta de identidad
13Cédula de ciudadanía
21Tarjeta de extranjería
22Cédula de extranjería
31NIT
41Pasaporte
42Documento extranjero
50NIT otro país

Responsabilidad fiscal responsabilidad_fiscal

CódigoDescripción
O-13Gran contribuyente
O-15Autorretenedor
O-23Agente de retención IVA
O-47Régimen simple de tributación
R-99-PNNo responsable (lo más común para personas naturales y régimen simplificado)

Motivo nota crédito motivo.codigo (NC)

CódigoDescripción
1Devolución parcial de bienes y/o no aceptación parcial del servicio
2Anulación de factura electrónica
3Rebaja o descuento parcial o total
4Ajuste de precios
5Otros
6Devolución total de bienes y/o no aceptación total del servicio

Motivo nota débito motivo.codigo (ND)

CódigoDescripción
1Intereses
2Gastos por cobrar
3Cambio del valor
4Otros

Unidad de medida unidad (línea)

CódigoDescripción
94Unidad (default)
EAEach (cada uno)
KGMKilogramo
LTRLitro
MTRMetro
HURHora

Catálogo completo en el Anexo Técnico 1.9 DIAN.

GET /health público · sin autenticación

Estado del servicio

Devuelve un OK simple. Útil para monitorear que el servicio responde.

Respuesta 200

{
  "status": "ok",
  "service": "FacEPro Cloud",
  "version": "v1",
  "time": "2026-05-10T15:30:00-05:00"
}
GET /me requiere Bearer Token

Información de la empresa autenticada

Verifica que tu token funciona y a qué empresa apunta. Llámalo siempre antes de enviar el primer documento para asegurarte de que estás operando contra la empresa correcta.

Respuesta 200

{
  "user": {
    "id": 3,
    "name": "Luis Alberto",
    "email": "luis@empresa.com"
  },
  "tenant": {
    "id": 2,
    "uuid": "6a2f1c4e-...",
    "nit": "901234567",
    "dv": "1",
    "razon_social": "MI EMPRESA SAS",
    "estado": "activo"
  }
}
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/me
<?php
$ch = curl_init('https://facepro.cdpconsultores.com/api/v1/me');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_TOKEN',
    'Accept: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
print_r($data['tenant']);
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports Newtonsoft.Json.Linq

Dim cliente As New HttpClient()
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")

Dim respuesta = Await cliente.GetAsync("https://facepro.cdpconsultores.com/api/v1/me")
Dim cuerpo = Await respuesta.Content.ReadAsStringAsync()
Dim json = JObject.Parse(cuerpo)

Console.WriteLine("Empresa: " & json("tenant")("razon_social").ToString())
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");

var response = await client.GetAsync("https://facepro.cdpconsultores.com/api/v1/me");
var body = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<JsonElement>(body);

Console.WriteLine(data.GetProperty("tenant").GetProperty("razon_social"));
const response = await fetch('https://facepro.cdpconsultores.com/api/v1/me', {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const data = await response.json();
console.log(data.tenant.razon_social);
POST /documentos crea factura · permite_facturas

Emitir factura electrónica

Genera el XML UBL 2.1, lo firma con XAdES BES, calcula el CUFE, lo empaqueta en ZIP, firma el SOAP envelope con WS-Security y lo envía a la DIAN. Devuelve el documento completo con el estado DIAN incluido.

Cuerpo del request

CampoTipoRequeridoDescripción
factura.fechadatenoFecha emisión YYYY-MM-DD (default: hoy)
factura.horastringnoHora HH:mm:ss-05:00 (default: ahora)
factura.prefijostringno*Multi-serie: prefijo de la resolución DIAN a usar (ej. FE, FELV). Solo si tu empresa tiene varias series (una por sucursal/POS). Si no lo envías, se usa la marcada como principal en el panel DIAN.
factura.forma_pagostringnoNUEVO · 1=Contado (default), 2=Crédito
factura.metodo_pagostringnoNUEVO · 10=Efectivo, 41=Transferencia, 47=Transf. bancaria, 48=TC, 49=TD
factura.fecha_vencimientodatesi créditoNUEVO · Fecha vence (obligatoria si forma_pago=2)
factura.tipo_operacionstringnoNUEVO · Default 10=Estándar · 09=AIU · 11=Mandatos · 12=Exportación · etc.
factura.descuento_global_valornumbernoNUEVO · Descuento a nivel documento (en $)
factura.descuento_global_razonstringnoNUEVO · Texto del descuento (ej. "Cliente frecuente")
factura.recargo_global_valornumbernoNUEVO · Recargo a nivel documento (ej. flete, propina)
factura.recargo_global_razonstringnoNUEVO · Texto del recargo
adquiriente.tipo_personastring1=Jurídica, 2=Natural
adquiriente.tipo_documentostringVer tabla de tipos de documento
adquiriente.numero_documentostringNIT sin DV, o número de cédula
adquiriente.dvstringnoDígito verificación NIT (solo si tipo_documento = 31)
adquiriente.razon_socialstringNombre de la empresa o persona
adquiriente.nombre_comercialstringnoNUEVO · Nombre comercial (DIAN seccion 9.1)
adquiriente.responsabilidad_fiscalstringEj: R-99-PN
adquiriente.emailstringrecomendadoSi está presente, FacEPro envía automáticamente el AttachedDocument por correo al adquirente (obligación DIAN 9.1)
adquiriente.direccionstringnoDirección física
adquiriente.codigo_municipiostringnoCódigo DANE municipio (ej: 05001)
adquiriente.codigo_departamentostringnoCódigo DANE depto (ej: 05)
lineas[]arrayMínimo 1, máximo 200 ítems
lineas[].codigostringCódigo interno del producto
lineas[].descripcionstringDescripción del bien o servicio
lineas[].cantidadnumberCantidad (admite decimales)
lineas[].unidadstringnoUnidad de medida (default: 94)
lineas[].precionumberPrecio unitario sin IVA
lineas[].iva_pctnumberPorcentaje IVA (0, 5, 19)
lineas[].descuento_pctnumbernoDescuento % sobre la línea
lineas[].descuento_valornumbernoAlternativa: descuento en $ fijo
lineas[].ica_pctnumbernoNUEVO · % ICA línea (ej. 0.966 para Bogotá 9.66‰)
lineas[].inc_pctnumbernoNUEVO · % Impuesto al Consumo (ej. 8 bares, 4 telefonía)
lineas[].notastringnoNUEVO · Nota libre por línea
Retenciones — opcionales, para B2B con Gran Contribuyente / Autoretenedor
retenciones[].codigostringsí (si retenciones)05=ReteIVA · 06=ReteRenta · 07=ReteICA
retenciones[].porcentajenumbersí (si retenciones)% de retención (ej. 15 para 15% ReteIVA)
retenciones[].base_calculostringnoiva (default ReteIVA), subtotal (default otras), manual
retenciones[].base_manualnumbersi manualBase custom si base_calculo=manual
Anticipos — opcionales, pagos recibidos previos
anticipos[].valornumbersí (si anticipos)Monto recibido previamente
anticipos[].idstringnoIdentificador del anticipo (ej. recibo)
anticipos[].fecha_recibidodatenoFecha del pago anticipado
Propina — bares/restaurantes, no genera IVA (ver sección Impuestos avanzados)
factura.propina_pct / propina_valornumberno% del subtotal, o monto fijo (excluyentes)
Producto regalo (FreeOfChargeIndicator) — 2x1 / muestras
lineas[].es_regaloboolnotrue = producto gratis (precio 0)
lineas[].precio_referencianumbersi regaloValor de mercado del producto regalado
Bolsas plásticas (TaxScheme 22) — a nivel documento
bolsas.cantidad / valor_unitarioint / numbernoNº de bolsas × valor por bolsa del año
Impuestos sectoriales por línea (ver sección Impuestos sectoriales) — opcionales
lineas[].icui_pctnumbernoICUI ultraprocesados (35) · porcentual
lineas[].ibuaobjectnoIBUA bebidas (34) · {volumen_ml, tarifa_por_100ml}
lineas[].carbonoobjectnoImp. Carbono (23) · {cantidad, unidad, tarifa}
lineas[].licorobjectnoLicores ICL (32) + AdV (ZZ) · {grados, tarifa_especifico, ad_valorem_pct}

Ejemplo de request

{
  "adquiriente": {
    "tipo_persona": "2",
    "tipo_documento": "13",
    "numero_documento": "71234567",
    "razon_social": "JUAN PEREZ",
    "responsabilidad_fiscal": "R-99-PN",
    "email": "juan@correo.com",
    "direccion": "Calle 50 # 30-20",
    "codigo_municipio": "05001",
    "codigo_departamento": "05"
  },
  "lineas": [
    {
      "codigo": "PROD-001",
      "descripcion": "Repuesto motocicleta XYZ",
      "cantidad": 2,
      "precio": 50000,
      "iva_pct": 19
    },
    {
      "codigo": "SERV-010",
      "descripcion": "Mano de obra instalación",
      "cantidad": 1,
      "precio": 25000,
      "iva_pct": 19,
      "descuento_pct": 10
    }
  ]
}

Respuesta 201 Created

{
  "ok": true,
  "data": {
    "tracking_id": "550e8400-e29b-41d4-a716-446655440000",
    "numero_completo": "SETP990000011",
    "tipo_documento": "factura",
    "prefijo": "SETP",
    "consecutivo": 990000011,
    "fecha_emision": "2026-05-10T15:30:00-05:00",
    "adquirente": {
      "documento": "71234567",
      "razon_social": "JUAN PEREZ"
    },
    "totales": {
      "subtotal": 122500,
      "iva": 23275,
      "total": 145775
    },
    "cufe": "a1b2c3d4...96 chars hex...",
    "zip_key": "abcdef1234...",
    "estado": "aceptado",
    "estado_dian_codigo": "00",
    "estado_dian_mensaje": "Procesado correctamente",
    "links": {
      "self":      "https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-...",
      "consultar": "https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../consultar",
      "xml":       "https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../xml"
    }
  }
}

Ejemplos de código

curl -X POST https://facepro.cdpconsultores.com/api/v1/documentos \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "adquiriente": {
      "tipo_persona": "2",
      "tipo_documento": "13",
      "numero_documento": "71234567",
      "razon_social": "JUAN PEREZ",
      "responsabilidad_fiscal": "R-99-PN"
    },
    "lineas": [
      { "codigo": "P1", "descripcion": "Producto 1",
        "cantidad": 2, "precio": 50000, "iva_pct": 19 }
    ]
  }'
<?php
$datos = [
    'adquiriente' => [
        'tipo_persona'           => '2',
        'tipo_documento'         => '13',
        'numero_documento'       => '71234567',
        'razon_social'           => 'JUAN PEREZ',
        'responsabilidad_fiscal' => 'R-99-PN',
    ],
    'lineas' => [
        [
            'codigo'      => 'P1',
            'descripcion' => 'Producto 1',
            'cantidad'    => 2,
            'precio'      => 50000,
            'iva_pct'     => 19,
        ],
    ],
];

$ch = curl_init('https://facepro.cdpconsultores.com/api/v1/documentos');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($datos));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_TOKEN',
    'Content-Type: application/json',
    'Accept: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$result = json_decode($response, true);
if ($status === 201) {
    echo "CUFE: " . $result['data']['cufe'] . "\n";
    echo "Estado: " . $result['data']['estado'] . "\n";
} else {
    echo "Error: " . $result['error'] . "\n";
}
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Text
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Dim datos = New With {
    .adquiriente = New With {
        .tipo_persona = "2",
        .tipo_documento = "13",
        .numero_documento = "71234567",
        .razon_social = "JUAN PEREZ",
        .responsabilidad_fiscal = "R-99-PN"
    },
    .lineas = {
        New With {
            .codigo = "P1",
            .descripcion = "Producto 1",
            .cantidad = 2,
            .precio = 50000,
            .iva_pct = 19
        }
    }
}

Dim json = JsonConvert.SerializeObject(datos)
Dim contenido As New StringContent(json, Encoding.UTF8, "application/json")

Dim cliente As New HttpClient()
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")

Dim respuesta = Await cliente.PostAsync("https://facepro.cdpconsultores.com/api/v1/documentos", contenido)
Dim cuerpo = Await respuesta.Content.ReadAsStringAsync()
Dim resultado = JObject.Parse(cuerpo)

If respuesta.IsSuccessStatusCode Then
    Console.WriteLine("CUFE: " & resultado("data")("cufe").ToString())
    Console.WriteLine("Estado: " & resultado("data")("estado").ToString())
Else
    Console.WriteLine("Error: " & resultado("error").ToString())
End If
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

var datos = new {
    adquiriente = new {
        tipo_persona = "2",
        tipo_documento = "13",
        numero_documento = "71234567",
        razon_social = "JUAN PEREZ",
        responsabilidad_fiscal = "R-99-PN"
    },
    lineas = new[] {
        new {
            codigo = "P1",
            descripcion = "Producto 1",
            cantidad = 2,
            precio = 50000,
            iva_pct = 19
        }
    }
};

var json = JsonSerializer.Serialize(datos);
var content = new StringContent(json, Encoding.UTF8, "application/json");

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");

var response = await client.PostAsync("https://facepro.cdpconsultores.com/api/v1/documentos", content);
var body = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(body).RootElement;

if (response.IsSuccessStatusCode) {
    Console.WriteLine("CUFE: " + result.GetProperty("data").GetProperty("cufe"));
    Console.WriteLine("Estado: " + result.GetProperty("data").GetProperty("estado"));
} else {
    Console.WriteLine("Error: " + result.GetProperty("error"));
}
const datos = {
  adquiriente: {
    tipo_persona: '2',
    tipo_documento: '13',
    numero_documento: '71234567',
    razon_social: 'JUAN PEREZ',
    responsabilidad_fiscal: 'R-99-PN'
  },
  lineas: [
    { codigo: 'P1', descripcion: 'Producto 1',
      cantidad: 2, precio: 50000, iva_pct: 19 }
  ]
};

const response = await fetch('https://facepro.cdpconsultores.com/api/v1/documentos', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  },
  body: JSON.stringify(datos)
});

const result = await response.json();
if (response.ok) {
  console.log('CUFE:', result.data.cufe);
  console.log('Estado:', result.data.estado);
} else {
  console.error('Error:', result.error);
}
GET /documentos requiere Bearer Token

Listar documentos

Lista paginada de documentos del tenant.

Query params

ParámetroTipoDescripción
estadostringFiltrar por estado: aceptado, rechazado, pendiente, fallido
desdedateFecha emisión >= (YYYY-MM-DD)
hastadateFecha emisión <= (YYYY-MM-DD)
per_pageintItems por página (default 20, max 100)
pageintNúmero de página
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://facepro.cdpconsultores.com/api/v1/documentos?estado=aceptado&desde=2026-05-01&per_page=50"
<?php
$query = http_build_query([
    'estado'   => 'aceptado',
    'desde'    => '2026-05-01',
    'per_page' => 50,
]);

$ch = curl_init('https://facepro.cdpconsultores.com/api/v1/documentos?' . $query);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_TOKEN',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
foreach ($data['data'] as $doc) {
    echo $doc['numero_completo'] . ' - ' . $doc['estado'] . PHP_EOL;
}
Dim cliente As New HttpClient()
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")

Dim url = "https://facepro.cdpconsultores.com/api/v1/documentos?estado=aceptado&desde=2026-05-01&per_page=50"
Dim respuesta = Await cliente.GetAsync(url)
Dim cuerpo = Await respuesta.Content.ReadAsStringAsync()
Dim json = JObject.Parse(cuerpo)

For Each doc In json("data")
    Console.WriteLine(doc("numero_completo") & " - " & doc("estado"))
Next
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");

var url = "https://facepro.cdpconsultores.com/api/v1/documentos?estado=aceptado&desde=2026-05-01&per_page=50";
var response = await client.GetAsync(url);
var body = await response.Content.ReadAsStringAsync();
var data = JsonDocument.Parse(body).RootElement;

foreach (var doc in data.GetProperty("data").EnumerateArray())
{
    Console.WriteLine(doc.GetProperty("numero_completo") + " - " + doc.GetProperty("estado"));
}
const params = new URLSearchParams({
  estado: 'aceptado',
  desde: '2026-05-01',
  per_page: 50
});

const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos?${params}`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const data = await response.json();

data.data.forEach(doc => {
  console.log(`${doc.numero_completo} - ${doc.estado}`);
});
GET /documentos/{uuid} requiere Bearer Token

Detalle de documento

Devuelve un documento por su tracking_id (UUID). El formato de respuesta es idéntico al campo data de POST /documentos, más los campos de trazabilidad reemplaza y reemplazado_por (ver más abajo).

Campos de trazabilidad de re-emisiones:
  • reemplaza: objeto con tracking_id, numero_completo, estado del documento que ESTE re-emite. Solo aparece si el documento es un re-envío que reemplazó a uno rechazado.
  • reemplazado_por: mismo objeto, con el documento NUEVO que reemplazó a este. Solo aparece si este documento fue rechazado y ya se generó uno de reemplazo.

Regla para integradores: si un documento tiene estado="rechazado" y reemplazado_por.estado="aceptado", NO debe seguir marcado como "Rechazado" en tu sistema — ya fue resuelto. Márcalo como "Reemplazado" o similar.

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-e29b-41d4-a716-446655440000
<?php
$trackingId = '550e8400-e29b-41d4-a716-446655440000';

$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$doc = json_decode($response, true);
echo "Estado: " . $doc['estado'];
Dim trackingId = "550e8400-e29b-41d4-a716-446655440000"
Dim cliente As New HttpClient()
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")

Dim respuesta = Await cliente.GetAsync("https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId)
Dim cuerpo = Await respuesta.Content.ReadAsStringAsync()
Dim doc = JObject.Parse(cuerpo)

Console.WriteLine("Estado: " & doc("estado"))
var trackingId = "550e8400-e29b-41d4-a716-446655440000";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");

var response = await client.GetAsync($"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}");
var body = await response.Content.ReadAsStringAsync();
var doc = JsonDocument.Parse(body).RootElement;

Console.WriteLine("Estado: " + doc.GetProperty("estado"));
const trackingId = '550e8400-e29b-41d4-a716-446655440000';

const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const doc = await response.json();
console.log('Estado:', doc.estado);
GET /documentos/{uuid}/consultar requiere Bearer Token

Consultar estado en DIAN (GetStatusZip)

Llama el método GetStatusZip del WS DIAN con el zip_key guardado y devuelve el estado actual del documento según la DIAN. Útil cuando el envío inicial quedó pendiente o hubo un timeout.

Respuesta 200

{
  "ok": true,
  "documento": { /* DocumentoResource actualizado */ },
  "dian": {
    "is_valid": true,
    "status_code": "00",
    "status_description": "Procesado correctamente",
    "errors": []
  }
}
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../consultar
<?php
$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/consultar");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$res = json_decode($response, true);
echo $res['dian']['status_description'];
Dim respuesta = Await cliente.GetAsync("https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/consultar")
Dim cuerpo = Await respuesta.Content.ReadAsStringAsync()
Dim json = JObject.Parse(cuerpo)
Console.WriteLine(json("dian")("status_description"))
var response = await client.GetAsync($"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/consultar");
var body = await response.Content.ReadAsStringAsync();
var res = JsonDocument.Parse(body).RootElement;
Console.WriteLine(res.GetProperty("dian").GetProperty("status_description"));
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/consultar`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const res = await response.json();
console.log(res.dian.status_description);
GET /documentos/{uuid}/xml requiere Bearer Token

Descargar XML firmado

Descarga el XML UBL 2.1 ya firmado con XAdES BES (incluye el CUFE, la firma digital, la cadena de certificados y los QR del Anexo Técnico). Devuelve application/xml en el body.

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../xml \
  -o factura.xml
<?php
$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/xml");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$xml = curl_exec($ch);
curl_close($ch);

file_put_contents('factura.xml', $xml);
Dim respuesta = Await cliente.GetAsync("https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/xml")
Dim xml = Await respuesta.Content.ReadAsStringAsync()
File.WriteAllText("factura.xml", xml)
var response = await client.GetAsync($"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/xml");
var xml = await response.Content.ReadAsStringAsync();
File.WriteAllText("factura.xml", xml);
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/xml`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const xml = await response.text();
// xml contiene el UBL firmado
GET /documentos/{uuid}/pdf representación gráfica

Descargar PDF

Descarga la representación gráfica del documento en PDF (el mismo que se adjunta al correo del cliente). Se genera on-demand — no está guardado en disco. Devuelve application/pdf.

Query opcional: ?download=1 fuerza descarga (attachment) en vez de apertura inline en el navegador.
# Descargar como archivo
curl -H "Authorization: Bearer YOUR_TOKEN" \
  "https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../pdf?download=1" \
  -o factura.pdf
<?php
$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/pdf?download=1");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$pdf = curl_exec($ch);
curl_close($ch);

file_put_contents('factura.pdf', $pdf);
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")
Dim respuesta = Await cliente.GetAsync(
    "https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/pdf?download=1")
Dim pdf = Await respuesta.Content.ReadAsByteArrayAsync()
File.WriteAllBytes("factura.pdf", pdf)
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");
var response = await client.GetAsync(
    $"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/pdf?download=1");
var pdf = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("factura.pdf", pdf);
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/pdf?download=1`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const blob = await response.blob();
// blob es un PDF — descargar/mostrar segun necesites
GET /documentos/{uuid}/zip ZIP enviado a DIAN

Descargar ZIP enviado a DIAN

Descarga el archivo ZIP que se envió a DIAN vía SendBillSync. Contiene el XML firmado empaquetado según el Anexo Técnico. Útil para auditoría interna o para reenviar manualmente a DIAN si fuera necesario. Devuelve application/zip.

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../zip \
  -o enviado_a_dian.zip
<?php
$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/zip");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$zip = curl_exec($ch);
curl_close($ch);

file_put_contents('enviado_a_dian.zip', $zip);
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")
Dim respuesta = Await cliente.GetAsync(
    "https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/zip")
Dim zip = Await respuesta.Content.ReadAsByteArrayAsync()
File.WriteAllBytes("enviado_a_dian.zip", zip)
var response = await client.GetAsync(
    $"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/zip");
var zip = await response.Content.ReadAsByteArrayAsync();
File.WriteAllBytes("enviado_a_dian.zip", zip);
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/zip`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const blob = await response.blob();
GET /documentos/{uuid}/respuesta-dian XML crudo devuelto por DIAN

Descargar respuesta DIAN cruda

Descarga el XML de respuesta que DIAN devolvió al enviar el documento (ApplicationResponse de SendBillSync). Contiene IsValid, StatusCode, StatusDescription, todos los ErrorMessages y el XmlDocumentKey.

Útil para debug cuando el estado_dian_mensaje resumido no da suficiente detalle sobre el rechazo. Aquí aparecen TODAS las reglas violadas con su descripción completa DIAN.
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../respuesta-dian \
  -o respuesta_dian.xml
<?php
$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/respuesta-dian");
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$xml = curl_exec($ch);
curl_close($ch);

file_put_contents('respuesta_dian.xml', $xml);
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")
Dim respuesta = Await cliente.GetAsync(
    "https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/respuesta-dian")
Dim xml = Await respuesta.Content.ReadAsStringAsync()
File.WriteAllText("respuesta_dian.xml", xml)
var response = await client.GetAsync(
    $"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/respuesta-dian");
var xml = await response.Content.ReadAsStringAsync();
File.WriteAllText("respuesta_dian.xml", xml);
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/respuesta-dian`, {
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const xml = await response.text();
POST /documentos/{uuid}/re-emitir re-emite doc rechazado con nuevo consecutivo

Re-emitir documento rechazado

Genera un NUEVO documento con nuevo consecutivo usando el request_json original guardado. El XML se regenera con el código actualizado (útil cuando el rechazo fue por un bug corregido de FacEPro). El documento original queda como referencia histórica.

Cuándo usarlo: únicamente cuando el rechazo original fue por un bug corregido en FacEPro (FAK24a por DV=0, FAU14 por PayableAmount con anticipos, CAD06 por clave técnica en NC, etc.). Los mismos datos del ERP ahora generan un XML correcto.

No usar si el rechazo fue por dato inválido del ERP (ej. FAK24 real por DV mal enviado). Corregir en el ERP y enviar como documento nuevo via POST /documentos.

Sin body — todos los datos vienen del request_json guardado del documento original.

curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../re-emitir
<?php
$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/re-emitir");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
if ($result['ok']) {
    echo 'Nuevo: ' . $result['reemplazo']['numero_completo'];
}
Dim cliente As New HttpClient()
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")

Dim resp = Await cliente.PostAsync(
    "https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/re-emitir",
    Nothing)
Dim body = Await resp.Content.ReadAsStringAsync()
Dim result = JObject.Parse(body)

If CBool(result("ok")) Then
    Console.WriteLine("Nuevo: " & result("reemplazo")("numero_completo"))
End If
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");

var resp = await client.PostAsync(
    $"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/re-emitir", null);
var body = await resp.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(body).RootElement;

if (result.GetProperty("ok").GetBoolean())
    Console.WriteLine("Nuevo: " + result.GetProperty("reemplazo").GetProperty("numero_completo"));
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/re-emitir`, {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
});
const result = await response.json();
if (result.ok) {
  console.log('Nuevo:', result.reemplazo.numero_completo);
}

Respuesta exitosa (HTTP 201)

{
  "ok": true,
  "reemplazo": {
    "tracking_id": "b8d47201-...",
    "numero_completo": "UPMO74",
    "estado": "aceptado",
    "cufe": "eab3...",
    "estado_dian_mensaje": "Procesado Correctamente.",
    "reemplaza": { "tracking_id": "5b3c...", "numero_completo": "UPMO73", "estado": "rechazado" }
  },
  "reemplazado": {
    "tracking_id": "5b3c1f42-...",
    "numero_completo": "UPMO73",
    "estado": "rechazado"
  },
  "mensaje": "Documento re-emitido como UPMO74 (estado: aceptado)."
}

Errores posibles (HTTP 422)

  • Solo funciona en documentos con estado='rechazado' o 'fallido'.
  • Requiere request_json guardado (docs muy antiguos no tienen — deben re-emitirse desde el ERP).
  • NO permite re-emitir dos veces el mismo documento (evita bucles).
POST /documentos/{uuid}/reenviar-correo reenvía el correo al adquirente

Re-enviar correo al adquirente

Fuerza un nuevo envío del correo de entrega DIAN al adquirente (formato Anexo 9.1: XML + attached_document + PDF). Útil cuando el cliente perdió el correo original, cambió de email, o el envío automático falló (SMTP timeout, mailbox lleno, etc.).

No re-emite el documento. El XML y el CUFE son los mismos — solo re-envía el correo. Si el documento no está en estado aceptado o enviado, no se puede re-enviar (porque no hay XML autorizado).

Body (opcional)

CampoTipoRequeridoDescripción
emailstringnoNuevo email destino. Si se envía, sobrescribe el email guardado en el documento. Si no, usa el existente.
mensajestringnoTexto custom para el body del correo (max 1000 chars).

Sin body también es válido — usa el email ya guardado del adquirente.

# Sin body (usa email guardado)
curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../reenviar-correo

# Con nuevo email
curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"nuevo@cliente.com"}' \
  https://facepro.cdpconsultores.com/api/v1/documentos/550e8400-.../reenviar-correo
<?php
$datos = [
    'email' => 'nuevo@cliente.com',   // opcional
];

$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos/{$trackingId}/reenviar-correo");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($datos));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$result = json_decode($response, true);

if ($result['ok']) {
    echo 'Enviado a: ' . $result['email_destino'];
}
Dim datos = New With {.email = "nuevo@cliente.com"}
Dim json = JsonConvert.SerializeObject(datos)
Dim contenido As New StringContent(json, Encoding.UTF8, "application/json")

cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")
Dim resp = Await cliente.PostAsync(
    "https://facepro.cdpconsultores.com/api/v1/documentos/" & trackingId & "/reenviar-correo",
    contenido)
Dim body = Await resp.Content.ReadAsStringAsync()
Dim result = JObject.Parse(body)

If CBool(result("ok")) Then
    Console.WriteLine("Enviado: " & result("email_destino"))
End If
var datos = new { email = "nuevo@cliente.com" };
var json = JsonSerializer.Serialize(datos);
var content = new StringContent(json, Encoding.UTF8, "application/json");

client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");
var resp = await client.PostAsync(
    $"https://facepro.cdpconsultores.com/api/v1/documentos/{trackingId}/reenviar-correo", content);
var body = await resp.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(body).RootElement;

if (result.GetProperty("ok").GetBoolean())
    Console.WriteLine("Enviado: " + result.GetProperty("email_destino"));
const response = await fetch(`https://facepro.cdpconsultores.com/api/v1/documentos/${trackingId}/reenviar-correo`, {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ email: 'nuevo@cliente.com' })
});
const result = await response.json();
if (result.ok) {
  console.log('Enviado a:', result.email_destino);
}

Respuesta exitosa (HTTP 200)

{
  "ok": true,
  "email_destino": "nuevo@cliente.com",
  "enviado_at": "2026-08-13T14:30:00-05:00",
  "intentos": 2,
  "mensaje": "Correo re-enviado correctamente a nuevo@cliente.com."
}

Errores posibles

  • 422: documento no está en estado aceptado/enviado.
  • 422: el documento no tiene email guardado y no se envió email en el body.
  • 502: SMTP falló al enviar (timeout, mailbox lleno, servidor caído). El campo error trae el detalle.
Cada llamada FUERZA un nuevo envío — no es idempotente. Usar con criterio (no en bucles). Cada envío incrementa intentos.
POST /notas-credito crea NC · permite_notas_credito

Emitir nota crédito

Igual a POST /documentos pero con dos campos extra obligatorios: factura_referencia (la factura que se acredita) y motivo.

Campos adicionales

CampoRequeridoDescripción
factura_referencia.numeroNúmero completo de la factura original (ej: SETP990000011)
factura_referencia.cufeCUFE de 96 caracteres hex de la factura original
factura_referencia.fechaFecha emisión de la factura original (YYYY-MM-DD)
motivo.codigo1, 2, 3, 4, 5 o 6 (ver tabla)
motivo.descripcionTexto libre describiendo el motivo

Ejemplo de request

{
  "factura_referencia": {
    "numero": "SETP990000011",
    "cufe":   "a1b2c3d4...96 hex chars...",
    "fecha":  "2026-05-10"
  },
  "motivo": {
    "codigo":      "2",
    "descripcion": "Anulación por error en valor unitario"
  },
  "adquiriente": { /* mismo cliente que la factura original */ },
  "lineas": [ /* lo que se acredita */ ]
}
curl -X POST https://facepro.cdpconsultores.com/api/v1/notas-credito \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "factura_referencia": {
      "numero": "SETP990000011",
      "cufe":   "a1b2c3...",
      "fecha":  "2026-05-10"
    },
    "motivo": { "codigo": "2", "descripcion": "Anulación" },
    "adquiriente": { ... },
    "lineas": [ ... ]
  }'
<?php
$datos = [
    'factura_referencia' => [
        'numero' => 'SETP990000011',
        'cufe'   => 'a1b2c3d4...',
        'fecha'  => '2026-05-10',
    ],
    'motivo' => [
        'codigo'      => '2',
        'descripcion' => 'Anulación por error en valor unitario',
    ],
    'adquiriente' => [ /* ... */ ],
    'lineas'      => [ /* ... */ ],
];

$ch = curl_init('https://facepro.cdpconsultores.com/api/v1/notas-credito');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($datos));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
Dim datos = New With {
    .factura_referencia = New With {
        .numero = "SETP990000011",
        .cufe   = "a1b2c3d4...",
        .fecha  = "2026-05-10"
    },
    .motivo = New With { .codigo = "2", .descripcion = "Anulación" },
    .adquiriente = New With { },
    .lineas = { }
}

Dim json = JsonConvert.SerializeObject(datos)
Dim contenido As New StringContent(json, Encoding.UTF8, "application/json")
Dim respuesta = Await cliente.PostAsync("https://facepro.cdpconsultores.com/api/v1/notas-credito", contenido)
var datos = new {
    factura_referencia = new {
        numero = "SETP990000011",
        cufe   = "a1b2c3d4...",
        fecha  = "2026-05-10"
    },
    motivo = new { codigo = "2", descripcion = "Anulación" },
    adquiriente = new { /* ... */ },
    lineas = new[] { /* ... */ }
};

var json = JsonSerializer.Serialize(datos);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://facepro.cdpconsultores.com/api/v1/notas-credito", content);
const datos = {
  factura_referencia: {
    numero: 'SETP990000011',
    cufe:   'a1b2c3d4...',
    fecha:  '2026-05-10'
  },
  motivo: { codigo: '2', descripcion: 'Anulación' },
  adquiriente: { /* ... */ },
  lineas: [ /* ... */ ]
};

const response = await fetch('https://facepro.cdpconsultores.com/api/v1/notas-credito', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(datos)
});
const result = await response.json();
POST /notas-debito crea ND · permite_notas_debito

Emitir nota débito

Estructura idéntica a la nota crédito pero con motivos distintos (1=Intereses, 2=Gastos por cobrar, 3=Cambio del valor, 4=Otros).

{
  "factura_referencia": {
    "numero": "SETP990000011",
    "cufe":   "a1b2c3d4...",
    "fecha":  "2026-05-10"
  },
  "motivo": {
    "codigo":      "1",
    "descripcion": "Intereses por mora"
  },
  "adquiriente": { ... },
  "lineas": [ ... ]
}
curl -X POST https://facepro.cdpconsultores.com/api/v1/notas-debito \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "factura_referencia": { "numero": "SETP990000011", "cufe": "...", "fecha": "2026-05-10" },
    "motivo": { "codigo": "1", "descripcion": "Intereses por mora" },
    "adquiriente": { ... },
    "lineas": [ ... ]
  }'
<?php
$datos = [
    'factura_referencia' => [ 'numero'=>'SETP990000011', 'cufe'=>'...', 'fecha'=>'2026-05-10' ],
    'motivo'             => [ 'codigo'=>'1', 'descripcion'=>'Intereses por mora' ],
    'adquiriente'        => [ /* ... */ ],
    'lineas'             => [ /* ... */ ],
];

$ch = curl_init('https://facepro.cdpconsultores.com/api/v1/notas-debito');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($datos));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer YOUR_TOKEN',
    'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
Dim datos = New With {
    .factura_referencia = New With { .numero = "SETP990000011", .cufe = "...", .fecha = "2026-05-10" },
    .motivo = New With { .codigo = "1", .descripcion = "Intereses por mora" },
    .adquiriente = New With { },
    .lineas = { }
}

Dim json = JsonConvert.SerializeObject(datos)
Dim contenido As New StringContent(json, Encoding.UTF8, "application/json")
Dim respuesta = Await cliente.PostAsync("https://facepro.cdpconsultores.com/api/v1/notas-debito", contenido)
var datos = new {
    factura_referencia = new { numero = "SETP990000011", cufe = "...", fecha = "2026-05-10" },
    motivo = new { codigo = "1", descripcion = "Intereses por mora" },
    adquiriente = new { /* ... */ },
    lineas = new[] { /* ... */ }
};

var json = JsonSerializer.Serialize(datos);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://facepro.cdpconsultores.com/api/v1/notas-debito", content);
const datos = {
  factura_referencia: { numero: 'SETP990000011', cufe: '...', fecha: '2026-05-10' },
  motivo: { codigo: '1', descripcion: 'Intereses por mora' },
  adquiriente: { /* ... */ },
  lineas: [ /* ... */ ]
};

const response = await fetch('https://facepro.cdpconsultores.com/api/v1/notas-debito', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_TOKEN', 'Content-Type': 'application/json' },
  body: JSON.stringify(datos)
});
const result = await response.json();
POST /documentos-soporte Documento Soporte Electrónico · DIAN Anexo DS v1.1

Emitir Documento Soporte Electrónico

Se emite cuando tu empresa COMPRA a una persona natural o jurídica no obligada a facturar electrónicamente (agricultores, taxistas, prestadores ocasionales, etc.). Tú generas el DS como soporte tributario para poder deducir el costo/gasto y descontar el IVA.

Diferencia clave vs Factura: aquí tú eres el ADQUIRENTE (CustomerParty en UBL), y la persona a la que le compras es el vendedor (SupplierParty). Por eso el JSON usa vendedor en vez de adquiriente.
Requisitos previos:
  • Habilitación DIAN para Documento Soporte Electrónico (habilitación aparte de FE).
  • Resolución de numeración de DS activa en tu tenant (prefijo típico DS, DSNP, etc.).
  • Si tienes varias resoluciones DS activas, envía factura.prefijo para elegir cuál usar.

Campos del vendedor (SupplierParty)

CampoTipoRequeridoDescripción
vendedor.tipo_personastring1=Jurídica, 2=Natural (típico 2)
vendedor.tipo_documentostring13=Cédula (típico), 22=CE, 31=NIT si es jurídica no obligada
vendedor.numero_documentostringSin puntos, sin guiones
vendedor.dvstringsi NITUn solo carácter (solo para tipo_documento 31)
vendedor.razon_socialstringNombre completo (Natural) o razón social (Jurídica)
vendedor.responsabilidad_fiscalstringnoTípico no obligado: O-23;O-47
vendedor.direccionstringnoDirección física del vendedor
vendedor.ciudadstringnoEj: MEDELLIN
vendedor.codigo_municipiostringnoDIAN 5 dígitos (ej: 05001)
vendedor.codigo_postalstringnoEj: 050001
vendedor.departamentostringnoNombre departamento
vendedor.codigo_departamentostringnoDIAN 2 dígitos (ej: 05)
vendedor.paisstringnoISO 2 letras (default CO)
Retenciones: lo típico en DS es que TÚ (adquirente) retengas al vendedor ReteIVA, ReteRenta o ReteICA. El bloque retenciones tiene la misma estructura que en facturas — códigos válidos 05, 06, 07.
Líneas: misma estructura que en factura (codigo, descripcion, cantidad, precio, iva_pct, opcionales descuento_pct, ica_pct, inc_pct, nota).

No aplican en DS: impuestos sectoriales Nivel 2 (bolsas, IBUA, ICUI, carbono, licor), propina, regalos, anticipos.

Ejemplo de request

{
  "factura": {
    "fecha": "2026-07-04",
    "forma_pago": "1",
    "metodo_pago": "10"
  },
  "vendedor": {
    "tipo_persona": "2",
    "tipo_documento": "13",
    "numero_documento": "71234567",
    "razon_social": "Juan Perez Gomez",
    "responsabilidad_fiscal": "O-23;O-47",
    "ciudad": "ARBELAEZ",
    "codigo_municipio": "25053",
    "departamento": "Cundinamarca",
    "codigo_departamento": "25",
    "pais": "CO"
  },
  "retenciones": [
    { "codigo": "05", "porcentaje": 15.00, "base_calculo": "iva" },
    { "codigo": "06", "porcentaje": 11.00, "base_calculo": "subtotal" }
  ],
  "lineas": [
    { "codigo": "AGRO-001", "descripcion": "Semillas certificadas",
      "cantidad": 1, "unidad": "P0",
      "precio": 2000000, "iva_pct": 5 }
  ]
}
curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @documento-soporte.json \
  https://facepro.cdpconsultores.com/api/v1/documentos-soporte
<?php
$datos = [
  'factura' => ['fecha' => '2026-07-04', 'forma_pago' => '1', 'metodo_pago' => '10'],
  'vendedor' => [
    'tipo_persona' => '2', 'tipo_documento' => '13',
    'numero_documento' => '71234567',
    'razon_social' => 'Juan Perez Gomez',
    'responsabilidad_fiscal' => 'O-23;O-47',
    'ciudad' => 'ARBELAEZ', 'codigo_municipio' => '25053',
    'departamento' => 'Cundinamarca', 'codigo_departamento' => '25',
    'pais' => 'CO',
  ],
  'retenciones' => [
    ['codigo' => '05', 'porcentaje' => 15.00, 'base_calculo' => 'iva'],
  ],
  'lineas' => [[
    'codigo' => 'AGRO-001', 'descripcion' => 'Semillas',
    'cantidad' => 1, 'unidad' => 'P0',
    'precio' => 2000000, 'iva_pct' => 5,
  ]],
];

$ch = curl_init("https://facepro.cdpconsultores.com/api/v1/documentos-soporte");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($datos));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  'Authorization: Bearer YOUR_TOKEN',
  'Content-Type: application/json',
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$result = json_decode($response, true);
Dim datos As New With {
    .factura = New With {.fecha = "2026-07-04", .forma_pago = "1", .metodo_pago = "10"},
    .vendedor = New With {
        .tipo_persona = "2", .tipo_documento = "13",
        .numero_documento = "71234567",
        .razon_social = "Juan Perez Gomez",
        .responsabilidad_fiscal = "O-23;O-47",
        .pais = "CO"
    },
    .retenciones = {New With {.codigo = "05", .porcentaje = 15.0, .base_calculo = "iva"}},
    .lineas = {New With {
        .codigo = "AGRO-001", .descripcion = "Semillas",
        .cantidad = 1, .unidad = "P0",
        .precio = 2000000, .iva_pct = 5
    }}
}

Dim contenido As New StringContent(
    JsonConvert.SerializeObject(datos), Encoding.UTF8, "application/json")
cliente.DefaultRequestHeaders.Authorization =
    New AuthenticationHeaderValue("Bearer", "YOUR_TOKEN")
Dim resp = Await cliente.PostAsync("https://facepro.cdpconsultores.com/api/v1/documentos-soporte", contenido)
Dim body = Await resp.Content.ReadAsStringAsync()
var datos = new {
  factura = new { fecha = "2026-07-04", forma_pago = "1", metodo_pago = "10" },
  vendedor = new {
    tipo_persona = "2", tipo_documento = "13",
    numero_documento = "71234567",
    razon_social = "Juan Perez Gomez",
    responsabilidad_fiscal = "O-23;O-47",
    pais = "CO"
  },
  retenciones = new[] {
    new { codigo = "05", porcentaje = 15.0, base_calculo = "iva" }
  },
  lineas = new[] {
    new {
      codigo = "AGRO-001", descripcion = "Semillas",
      cantidad = 1, unidad = "P0",
      precio = 2000000, iva_pct = 5
    }
  }
};

var json = JsonSerializer.Serialize(datos);
var content = new StringContent(json, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Authorization =
  new AuthenticationHeaderValue("Bearer", "YOUR_TOKEN");
var resp = await client.PostAsync("https://facepro.cdpconsultores.com/api/v1/documentos-soporte", content);
const datos = {
  factura: { fecha: '2026-07-04', forma_pago: '1', metodo_pago: '10' },
  vendedor: {
    tipo_persona: '2', tipo_documento: '13',
    numero_documento: '71234567',
    razon_social: 'Juan Perez Gomez',
    responsabilidad_fiscal: 'O-23;O-47',
    pais: 'CO'
  },
  retenciones: [{ codigo: '05', porcentaje: 15.0, base_calculo: 'iva' }],
  lineas: [{
    codigo: 'AGRO-001', descripcion: 'Semillas',
    cantidad: 1, unidad: 'P0',
    precio: 2000000, iva_pct: 5
  }]
};

const response = await fetch('https://facepro.cdpconsultores.com/api/v1/documentos-soporte', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(datos)
});
const result = await response.json();

Respuesta exitosa (HTTP 201)

{
  "ok": true,
  "documento": {
    "tracking_id": "a3f2c891-...",
    "numero_completo": "DS12",
    "tipo_documento": "documento_soporte",
    "cufe": "8ab4c7...",
    "estado": "aceptado",
    "estado_dian_codigo": "0",
    "estado_dian_mensaje": "Procesado Correctamente.",
    "totales": { "subtotal": 2000000, "iva": 100000, "total": 2100000 }
  }
}
GET /documentos-soporte y variantes /{uuid}, /consultar, /xml

Consultar Documentos Soporte

Los endpoints de lectura de DS funcionan exactamente igual que los de facturas — solo cambia la ruta base a /documentos-soporte/.

MétodoRutaUso
GET/documentos-soporteListar todos los DS del tenant (paginado, filtros: estado, desde, hasta)
GET/documentos-soporte/{uuid}Detalle de un DS por su tracking_id
GET/documentos-soporte/{uuid}/consultarRe-consultar estado en DIAN (útil si quedó pendiente)
GET/documentos-soporte/{uuid}/xmlDescargar el XML UBL firmado
La respuesta usa el mismo formato que las facturas (tracking_id, numero_completo, estado, cufe, etc.) con tipo_documento="documento_soporte".

Ojo: los datos del vendedor se persisten en las mismas columnas adquirente_*, así que la respuesta JSON sigue diciendo adquirente — el contenido son los datos del vendedor (el nombre del bloque en la respuesta es genérico, distinto al request donde sí se llama vendedor).
POST /eventos acuse · aceptación · reclamo

Emitir evento DIAN sobre una factura recibida

Permite emitir los 4 eventos DIAN (Anexo Técnico sección 8.5) sobre facturas que tu empresa recibió de un proveedor. Genera el ApplicationResponse XML, lo firma con XAdES BES, calcula el CUDE y lo envía a DIAN vía SendEventUpdateStatus (sincrónico).

Códigos de evento

CódigoEventoPara qué sirve
030Acuse de reciboConfirmas que recibiste la factura. Inicia conteo de 3 días para aceptación tácita.
032Recibo del bien o servicioConfirmas que recibiste el bien o servicio facturado.
033Aceptación expresaAceptas formalmente la factura. La factura se convierte en título valor negociable.
031ReclamoRechazas la factura. Requiere concepto_reclamo.

Cuerpo del request

CampoTipoRequeridoDescripción
codigo_eventostring030, 031, 032 o 033
concepto_reclamostringsi 03101=Inconsistencias, 02=Mercancía no entregada totalmente, 03=No entregada, 04=Servicio no prestado
factura.numerostringNúmero completo de la factura recibida (ej. FV-12345)
factura.cufestringCUFE de la factura, 96 caracteres hex
factura.fecha_emisiondateFecha de emisión YYYY-MM-DD
factura.tipo_doc_codestringno01=Factura (default), 91=NC, 92=ND
factura.emisor_nitstringNIT del proveedor (sin guiones)
factura.emisor_dvstringnoDV del NIT proveedor
factura.emisor_razon_socialstringRazón social del proveedor
persona.idstringCédula del firmante (solo números). Regla DIAN AAH13.
persona.nombrestringNombres del firmante. Regla DIAN AAH15.
persona.apellidostringApellidos del firmante. Regla DIAN AAH16.
persona.cargostringnoCargo (ej. "Administrador")
persona.areastringnoÁrea (ej. "Compras")

Ejemplo de request (030 Acuse de recibo)

{
  "codigo_evento": "030",
  "factura": {
    "numero": "FV-12345",
    "cufe": "8736be4825ce83da9327df...96 chars...",
    "fecha_emision": "2026-05-10",
    "tipo_doc_code": "01",
    "emisor_nit": "800123456",
    "emisor_dv": "1",
    "emisor_razon_social": "Llantas y Repuestos SA"
  },
  "persona": {
    "id": "71234567",
    "nombre": "Luis Alberto",
    "apellido": "Perez Gomez",
    "cargo": "Administrador",
    "area": "Compras"
  }
}

Respuesta 201 Created

{
  "ok": true,
  "data": {
    "uuid": "...",
    "codigo_evento": "030",
    "descripcion": "Acuse de Recibo de la Factura Electronica",
    "cude": "...96 chars hex...",
    "estado": "aceptado",
    "estado_dian_codigo": "00",
    "estado_dian_mensaje": "Procesado correctamente"
  }
}
Regla LGC62: Los eventos 033 (Aceptación expresa) y 034 (Tácita) solo se pueden emitir sobre facturas a crédito (forma_pago=2). DIAN rechaza con regla LGC62 si la factura es de contado.
GET /eventos

Listar eventos del tenant

Devuelve los eventos que tu empresa ha emitido o recibido, paginados de 20 en 20.

Query params (filtros)

ParamValores
direccionemitido | recibido
codigo_evento030 | 031 | 032 | 033
cufeCUFE específico
estadoaceptado | rechazado | enviado | fallido

También disponible GET /eventos/{uuid} para ver el detalle de un evento específico.

GET /api/consulta-acquirer/{tipo}/{numero} PÚBLICO — sin auth

Consultar NIT/cédula en RUT DIAN

Devuelve la razón social oficial y el email registrado de un contribuyente colombiano según su RUT DIAN. Ideal para autocompletar datos de un cliente al crear una factura o para validar que un NIT existe antes de emitir.

Endpoint público — sin autenticación. FacEPro firma la consulta a DIAN con un certificado operador central, así que TU aplicación no necesita configurar nada. Solo llama al URL.

Parámetros de ruta

ParámetroValores válidosEjemplo
tipo13=Cédula, 22=CE, 31=NIT, 41=Pasaporte, 42=Doc. extranjero, 50=NIT otro país31
numeroSolo dígitos. Para NIT: sin DV, sin puntos, sin guiones.901259449
# Consultar NIT (empresa)
curl "https://facepro.cdpconsultores.com/api/v1/../consulta-acquirer/31/901259449"

# Consultar cedula
curl "https://facepro.cdpconsultores.com/api/v1/../consulta-acquirer/13/71272149"
<?php
$tipo = '31';
$numero = '901259449';
$url = 'https://facepro.cdpconsultores.com/api/consulta-acquirer/' . $tipo . '/' . $numero;

$json = file_get_contents($url);
$data = json_decode($json, true);

if ($data['encontrado']) {
    echo 'Razon social: ' . $data['razon_social'] . PHP_EOL;
    echo 'Email: ' . $data['email'] . PHP_EOL;
}
Dim tipo As String = "31"
Dim numero As String = "901259449"
Dim url As String = "https://facepro.cdpconsultores.com/api/consulta-acquirer/" & tipo & "/" & numero

Dim cliente As New HttpClient()
Dim body = Await cliente.GetStringAsync(url)
Dim r = JObject.Parse(body)

If CBool(r("encontrado")) Then
    Console.WriteLine("Razon social: " & r("razon_social").ToString())
    Console.WriteLine("Email: " & r("email").ToString())
End If
using var client = new HttpClient();
var body = await client.GetStringAsync(
    $"https://facepro.cdpconsultores.com/api/consulta-acquirer/31/901259449");
var r = JsonDocument.Parse(body).RootElement;

if (r.GetProperty("encontrado").GetBoolean()) {
    Console.WriteLine("Razon social: " + r.GetProperty("razon_social"));
    Console.WriteLine("Email: " + r.GetProperty("email"));
}
const r = await fetch('https://facepro.cdpconsultores.com/api/consulta-acquirer/31/901259449');
const data = await r.json();

if (data.encontrado) {
  console.log('Razon social:', data.razon_social);
  console.log('Email:', data.email);
}

Respuesta encontrado (HTTP 200)

{
  "ok": true,
  "code": "100",
  "encontrado": true,
  "tipo_documento": "31",
  "numero_documento": "901259449",
  "razon_social": "UP MOTOS S.A.S",
  "email": "contacto@upmotos.com",
  "from_cache": false,
  "consultado_en": "produccion"
}

Respuesta no encontrado (HTTP 200)

{
  "ok": true,
  "code": "404",
  "encontrado": false,
  "message": "El adquirente No existe en la base de datos",
  "tipo_documento": "31",
  "numero_documento": "999999999",
  "razon_social": null,
  "email": null
}

Detalles importantes

  • Cache 12h por (tipo+número) — consultas repetidas del mismo NIT devuelven from_cache: true.
  • Rate limit 60/minuto por IP — evita scraping masivo.
  • Auditado — cada consulta se loguea con IP + user agent.
  • Encontrado depende del ambiente: en producción están todos los NITs colombianos reales.

Códigos de error

HTTPSignificadoAcción recomendada
200OKOperación exitosa (GET).
201CreatedDocumento creado (POST).
401UnauthenticatedToken inválido o ausente. Verifica el header Authorization.
403Forbidden / permiso no concedidoTu empresa no tiene habilitado este endpoint. Contacta a CDP.
404Not foundEl tracking_id no existe (o no pertenece a tu empresa).
422Datos inválidosRevisa el campo errors de la respuesta.
429Quota excedidaLlegaste al límite mensual/anual. Espera al próximo periodo o pide ampliación.
500Error internoEl cuerpo trae el detalle. Si persiste, reintenta más tarde.

Ejemplo: 422 Validación

{
  "ok": false,
  "error": "Datos invalidos",
  "errors": {
    "adquiriente.numero_documento": ["El número de documento es requerido."],
    "lineas.0.iva_pct": ["El IVA % debe ser un número entre 0 y 100."]
  }
}

Ejemplo: 429 Quota

{
  "ok": false,
  "error": "Cuota excedida para el periodo.",
  "codigo": "quota_excedida",
  "consumidos": 100,
  "limite": 100,
  "periodo": "mensual"
}

Ejemplo: 403 Permiso

{
  "ok": false,
  "error": "Tu empresa no tiene permitido emitir este tipo de documento.",
  "codigo": "permiso_no_concedido",
  "permiso": "permite_notas_credito"
}

Mejores prácticas

  • Guarda siempre el tracking_id y el cufe en tu sistema. Son tus identificadores estables; el numero_completo sirve para reportes humanos pero el cufe es lo que te exigirán para una nota crédito o auditoría.
  • Revisa estado en la respuesta. Solo aceptado significa que la DIAN validó la factura. Cualquier otro valor (rechazado, fallido, pendiente) requiere atención: lee estado_dian_mensaje y ultimo_error.
  • Usa /consultar para reintentar cuando el estado quedó pendiente por timeout. Llamarlo no genera un nuevo envío, solo re-pregunta a la DIAN por el zip_key original.
  • Implementa idempotencia en tu lado: antes de POST, verifica si ya enviaste esa venta. La DIAN rechaza duplicados (mismo prefijo + consecutivo).
  • Maneja el 429 con un retry-after. Si llegas a la cuota, frena los envíos automáticos y avisa al usuario humano para que pida ampliación.
  • Logs locales: guarda el JSON enviado y la respuesta completa al menos 90 días. Te servirá para soporte si hay un problema con la DIAN.
  • Pruebas: usa el ambiente habilitacion (configurado en el portal) hasta que tu integración sea estable. Cuando pases a produccion, los documentos van a tener efecto fiscal real.

¿Necesitas ayuda?

Escríbenos a soporte@cdpconsultores.com con el tracking_id y el JSON que enviaste. CDP Consultores · Rionegro, Antioquia, Colombia.