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
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
Verifica el token con
GET /meConfirma que tu token responde y que la empresa asociada es la correcta antes de enviar facturas reales.
-
3
Envía tu primera factura con
POST /documentosRecibirás un
tracking_id, elcufey 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ódigo | Descripción |
|---|---|
1 | Persona Jurídica |
2 | Persona Natural |
Tipo de documento tipo_documento
| Código | Descripción |
|---|---|
11 | Registro civil |
12 | Tarjeta de identidad |
13 | Cédula de ciudadanía |
21 | Tarjeta de extranjería |
22 | Cédula de extranjería |
31 | NIT |
41 | Pasaporte |
42 | Documento extranjero |
50 | NIT otro país |
Responsabilidad fiscal responsabilidad_fiscal
| Código | Descripción |
|---|---|
O-13 | Gran contribuyente |
O-15 | Autorretenedor |
O-23 | Agente de retención IVA |
O-47 | Régimen simple de tributación |
R-99-PN | No responsable (lo más común para personas naturales y régimen simplificado) |
Motivo nota crédito motivo.codigo (NC)
| Código | Descripción |
|---|---|
1 | Devolución parcial de bienes y/o no aceptación parcial del servicio |
2 | Anulación de factura electrónica |
3 | Rebaja o descuento parcial o total |
4 | Ajuste de precios |
5 | Otros |
6 | Devolución total de bienes y/o no aceptación total del servicio |
Motivo nota débito motivo.codigo (ND)
| Código | Descripción |
|---|---|
1 | Intereses |
2 | Gastos por cobrar |
3 | Cambio del valor |
4 | Otros |
Unidad de medida unidad (línea)
| Código | Descripción |
|---|---|
94 | Unidad (default) |
EA | Each (cada uno) |
KGM | Kilogramo |
LTR | Litro |
MTR | Metro |
HUR | Hora |
Catálogo completo en el Anexo Técnico 1.9 DIAN.
/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"
}
/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);
/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
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
factura.fecha | date | no | Fecha emisión YYYY-MM-DD (default: hoy) |
factura.hora | string | no | Hora HH:mm:ss-05:00 (default: ahora) |
adquiriente.tipo_persona | string | sí | 1=Jurídica, 2=Natural |
adquiriente.tipo_documento | string | sí | Ver tabla de tipos de documento |
adquiriente.numero_documento | string | sí | NIT sin DV, o número de cédula |
adquiriente.dv | string | no | Dígito verificación NIT (solo si tipo_documento = 31) |
adquiriente.razon_social | string | sí | Nombre de la empresa o persona |
adquiriente.responsabilidad_fiscal | string | sí | Ej: R-99-PN |
adquiriente.email | string | no | Email para notificación DIAN |
adquiriente.direccion | string | no | Dirección física |
adquiriente.codigo_municipio | string | no | Código DANE municipio (ej: 05001) |
adquiriente.codigo_departamento | string | no | Código DANE depto (ej: 05) |
lineas[] | array | sí | Mínimo 1, máximo 200 ítems |
lineas[].codigo | string | sí | Código interno del producto |
lineas[].descripcion | string | sí | Descripción del bien o servicio |
lineas[].cantidad | number | sí | Cantidad (admite decimales) |
lineas[].unidad | string | no | Unidad de medida (default: 94) |
lineas[].precio | number | sí | Precio unitario sin IVA |
lineas[].iva_pct | number | sí | Porcentaje IVA (0, 5, 19) |
lineas[].descuento_pct | number | no | Descuento % sobre la línea |
lineas[].descuento_valor | number | no | Alternativa: 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);
}
/documentos
requiere Bearer Token
Listar documentos
Lista paginada de documentos del tenant.
Query params
| Parámetro | Tipo | Descripción |
|---|---|---|
estado | string | Filtrar por estado: aceptado, rechazado, pendiente, fallido |
desde | date | Fecha emisión >= (YYYY-MM-DD) |
hasta | date | Fecha emisión <= (YYYY-MM-DD) |
per_page | int | Items por página (default 20, max 100) |
page | int | Nú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}`);
});
/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);
/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);
/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
/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
| Campo | Requerido | Descripción |
|---|---|---|
factura_referencia.numero | sí | Número completo de la factura original (ej: SETP990000011) |
factura_referencia.cufe | sí | CUFE de 96 caracteres hex de la factura original |
factura_referencia.fecha | sí | Fecha emisión de la factura original (YYYY-MM-DD) |
motivo.codigo | sí | 1, 2, 3, 4, 5 o 6 (ver tabla) |
motivo.descripcion | sí | Texto 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();
/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
| HTTP | Significado | Acción recomendada |
|---|---|---|
200 | OK | Operación exitosa (GET). |
201 | Created | Documento creado (POST). |
401 | Unauthenticated | Token inválido o ausente. Verifica el header Authorization. |
403 | Forbidden / permiso no concedido | Tu empresa no tiene habilitado este endpoint. Contacta a CDP. |
404 | Not found | El tracking_id no existe (o no pertenece a tu empresa). |
422 | Datos inválidos | Revisa el campo errors de la respuesta. |
429 | Quota excedida | Llegaste al límite mensual/anual. Espera al próximo periodo o pide ampliación. |
500 | Error interno | El 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_idy elcufeen tu sistema. Son tus identificadores estables; elnumero_completosirve para reportes humanos pero elcufees lo que te exigirán para una nota crédito o auditoría. -
Revisa
estadoen la respuesta. Soloaceptadosignifica que la DIAN validó la factura. Cualquier otro valor (rechazado,fallido,pendiente) requiere atención: leeestado_dian_mensajeyultimo_error. -
Usa
/consultarpara reintentar cuando el estado quedópendientepor timeout. Llamarlo no genera un nuevo envío, solo re-pregunta a la DIAN por elzip_keyoriginal. - 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 aproduccion, 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.