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.

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"
}

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)
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.responsabilidad_fiscalstringEj: R-99-PN
adquiriente.emailstringnoEmail para notificación DIAN
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

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.

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
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();

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.