Documentación de la API de Wardian
Integra facturación electrónica, inventarios y más. Todas las peticiones se autentican con una API Key.
- Base URL:
//qa.wardian.com.co/dist/api/ - Formato: JSON
- Autenticación: header
X-API-Key
Autenticación
Envía tu API Key en el header X-API-Key en cada petición. Si falta o es inválida, la API responde 401 Unauthorized.
X-API-Key: wrd_tu_api_key_aqui
1 Generar tu API Key
En tu panel, ve a Software → Documentación → API Keys y presiona Generar API Key. Cópiala y guárdala; solo se muestra completa al crearla.
2 Primer request
Toda petición lleva el header X-API-Key. Para POST, envía el cuerpo en JSON con Content-Type: application/json. Las respuestas son JSON.
const res = await fetch("//qa.wardian.com.co/dist/api/products/data_list.php", {
headers: { "X-API-Key": "wrd_tu_api_key_aqui" }
});
const data = await res.json();
const res = await fetch("//qa.wardian.com.co/dist/api/third/create.php", {
method: "POST",
headers: {
"X-API-Key": "wrd_tu_api_key_aqui",
"Content-Type": "application/json"
},
body: JSON.stringify({ /* campos del endpoint */ })
});
const data = await res.json();
Códigos de error
| Código | Significado |
|---|---|
200 | OK |
400 | Petición inválida (faltan datos) |
401 | API Key inválida o ausente |
404 | Recurso no encontrado |
500 | Error interno |
Relaciones (catálogos)
Varios campos de productos, terceros y de la factura estándar no son valores libres: referencian catálogos (IDs de la DIAN / DANE). Obtén los valores válidos antes de crear.
Catálogos globales (datos de referencia, públicos). Usa table=all para traerlos todos.
| Campo que lo usa | table= | Descripción |
|---|---|---|
unit_measure_id | unidades | Unidades de medida DIAN |
standard_code_id | codigos_estandar | Códigos estándar de adopción |
tribute_id | tributos | Tributos (IVA, INC, etc.) |
identification_document_id | documentos_identidad | Tipos de documento (CC, NIT…) |
municipalities_code | municipios | Municipios (DANE) |
country_code | paises | Países |
| — | responsabilidades_iva, regimen_iva, autorretenciones, bancos | Otros catálogos disponibles |
const res = await fetch("//qa.wardian.com.co/dist/api/references/tables.php?table=unidades");
const data = await res.json();
{
"success": true,
"data": [
{ "id": 70, "code": "94", "name": "unidad" }
]
}
Catálogos con endpoint propio (requieren X-API-Key)
| Campo | Endpoint | Notas |
|---|---|---|
categoria_id | api/settings/categories/list.php | Categorías del usuario |
numbering_range_id | api/numbering_ranges/list.php | Resoluciones / rangos de numeración |
payment_method_code | api/pos/list_payment_methods.php | Métodos de pago DIAN |
tax_rate | — | Valor directo: 0, 5, 8, 19 |
api/invoice/generate.php) usa estas mismas relaciones en su customer (identification_document_id, tribute_id, municipalities_code) y en cada ítem (unit_measure_id, standard_code_id, tax_rate), más numbering_range_id y payment_method_code.Productos
unit_measure_id, standard_code_id, tax_rate, categoria_id y tribute_id referencian catálogos. Consulta Relaciones / Catálogos para obtener los valores válidos. Son las mismas relaciones que usa la factura estándar.Lista los productos del usuario dueño de la API Key. Acepta paginación: ?pagina=1&search=texto.
{
"success": true,
"data": [
{ "id": 123, "code_reference": "SKU-001", "name": "Camiseta", "price": "50000.00", "tax_rate": "19.00" }
]
}
Crea un producto. Se envía como multipart/form-data (permite imágenes en files[]).
| Campo | Descripción | |
|---|---|---|
type | req | Tipo de producto |
code_reference | req | Código / referencia único |
name | req | Nombre del producto |
price | req | Precio de venta |
unit_measure_id | req | Unidad de medida (id DIAN) |
standard_code_id | req | Código estándar (id) |
tax_rate, cost_price, wholesale_price | opc | Impuesto, costo, precio mayorista |
categoria_id, details, requiere_stock, stock | opc | Categoría, detalles, control de stock |
contenido_presentacion, lote, fecha_vencimiento, cums_codigo | opc | Presentación, lote, vencimiento, CUMS |
files[] | opc | Imágenes del producto |
{
"type": "Producto",
"code_reference": "SKU-001",
"name": "Camiseta",
"price": "50000",
"unit_measure_id": "70",
"standard_code_id": "1",
"tax_rate": "19",
"categoria_id": "5"
}
{
"status": "success",
"message": "Producto registrado exitosamente",
"data": { "producto_id": 123, "code_reference": "SKU-001", "name": "Camiseta" }
}
multipart/form-data (campo files[]) en vez de JSON.Edita un producto existente. Mismos campos que crear, más:
| Campo | Descripción | |
|---|---|---|
id | req | ID del producto a editar |
{ "id": "123", "name": "Camiseta Premium", "price": "60000" }
{ "status": "success", "message": "Producto actualizado exitosamente" }
Terceros
Lista los terceros (clientes/proveedores) del usuario. Acepta ?pagina=1&search=texto.
{
"success": true,
"data": [
{ "id": 45, "identification": "901234567", "company_name": "ACME SAS", "email": "info@acme.co" }
]
}
Crea un tercero.
| Campo | Descripción | |
|---|---|---|
typeSelector | req | Tipo (Persona / Empresa) |
identification_document_id | req | Tipo de documento (id DIAN) |
identification | req | Número de identificación |
address, email, phone | req | Dirección, correo, teléfono |
tribute_id | req | Responsabilidad tributaria (id) |
dv, company_name, name, last_name, trade_name | opc | DV, razón social, nombres, nombre comercial |
ciiu, country_code, municipalities_code | opc | CIIU, país, municipio (DANE) |
{
"typeSelector": "Empresa",
"identification_document_id": "31",
"identification": "901234567",
"email": "info@acme.co",
"phone": "3001234567",
"address": "Calle 1 # 2-3",
"tribute_id": "21"
}
{
"status": "success",
"message": "Tercero creado exitosamente",
"data": { "tercero_id": 45, "name": "ACME SAS", "identification": "901234567", "email": "info@acme.co", "phone": "3001234567" }
}
Edita un tercero. Mismos campos que crear, más:
| Campo | Descripción | |
|---|---|---|
tercero_id | req | ID del tercero a editar |
{ "tercero_id": "45", "email": "nuevo@acme.co", "phone": "3009999999" }
{ "status": "success", "message": "Tercero actualizado exitosamente" }
Categorías
Lista las categorías de productos del usuario.
{
"success": true,
"data": [
{ "id": 5, "nombre": "Ropa", "descripcion": "Prendas de vestir", "color": "#CFFF00", "activo": 1 }
]
}
Crea una categoría de productos.
| Campo | Descripción | |
|---|---|---|
nombre | req | Nombre de la categoría |
descripcion, color, icono, activo | opc | Descripción, color, ícono, estado |
configuracion_puc_activa, cuenta_ingreso_venta, cuenta_costo_venta, cuenta_inventario, cuenta_compra… | opc | Cuentas contables PUC (si se activa la config) |
{ "nombre": "Ropa", "descripcion": "Prendas de vestir", "color": "#CFFF00", "activo": "1" }
{
"status": "success",
"message": "Categoría creada exitosamente",
"data": { "categoria_id": 5, "nombre": "Ropa", "descripcion": "Prendas de vestir", "color": "#CFFF00" }
}
Edita una categoría. Mismos campos que crear, más:
| Campo | Descripción | |
|---|---|---|
categoria_id | req | ID de la categoría a editar |
{ "categoria_id": "5", "nombre": "Ropa y Calzado" }
{ "status": "success", "message": "Categoría actualizada exitosamente" }
Crear factura estándar
id) y los productos, y ten a mano el numbering_range_id (ver Catálogos). La factura se envía a la DIAN y responde con el número y el CUFE.Genera una factura electrónica de venta estándar (multipart/form-data). Responde JSON con el resultado DIAN.
Cabecera
| Campo | Descripción | |
|---|---|---|
customer | req | ID del tercero (de api/third) |
numbering_range_id | req | Rango de numeración / resolución |
payment_form | req | 1 Contado · 2 Crédito |
payment_method_code | req | Método de pago (catálogo) |
observation | opc | Nota / observación |
payment_due_date | opc | Fecha de vencimiento (si crédito) |
company | opc | Nombre del emisor (para el correo/PDF) |
reference_code, cuenta_bancaria_id, cost_center_id | opc | Referencia, cuenta bancaria, centro de costo |
currency_code, currency_value, currency_date | opc | Moneda (por defecto COP) |
Ítems — products[]
| Campo | Descripción | |
|---|---|---|
id | req | ID del producto (de api/products) |
code_reference, name | req | Código y nombre del ítem |
quantity, price | req | Cantidad y precio unitario |
tax_rate | req | % IVA: 0, 5, 8, 19 |
unit_measure_id, standard_code_id, tribute_id | req | Catálogos (ver Relaciones) |
discount_rate, is_excluded | opc | % descuento, excluido de IVA (0/1) |
ret_fuente_rate, ret_iva_rate, ret_ica_rate | opc | Retenciones (si el cliente retiene) |
Ejemplo
{
"customer": "45",
"numbering_range_id": "502",
"payment_form": "1",
"payment_due_date": "2026-07-02",
"payment_method_code": "10",
"observation": "Gracias por su compra",
"products": [
{
"id": "123",
"code_reference": "SKU-001",
"name": "Camiseta",
"quantity": "2",
"price": "50000",
"tax_rate": "19",
"unit_measure_id": "70",
"standard_code_id": "1",
"tribute_id": "1",
"discount_rate": "0",
"is_excluded": "0"
}
]
}
{
"status": "success",
"message": "Factura creada exitosamente",
"data": {
"invoice_id": 47014,
"bill_number": "SETP990000227",
"cufe": "7b4a382a9a751cc88156a47f2eac24c7987353d1...",
"code_reference": "INV6a46c29683f15fdc6"
}
}
payment_due_date (una fecha, aun en contado) y en cada ítem discount_rate e is_excluded con valor (ej. 0). La DIAN rechaza campos vacíos/nulos con "Datos de la factura incompletos".Crear factura por mandato
operation_type 11) permite facturar por cuenta de terceros mandantes. Es idéntica a la factura estándar, pero cada ítem indica el mandante por el que se factura. La DIAN responde con el número y el CUFE.Genera una factura electrónica de venta por mandato. Responde JSON con el resultado DIAN.
Cabecera
Mismos campos que la factura estándar (customer, numbering_range_id, payment_form, payment_method_code, observation, payment_due_date, company, moneda…). El operation_type se fija internamente en 11.
Ítems — products[]
Mismos campos que la factura estándar (id, code_reference, name, quantity, price, tax_rate, catálogos, retenciones…) más los datos del mandante por el que se factura ese ítem:
| Campo | Descripción | |
|---|---|---|
mandate_identification | req | Identificación (NIT/CC) del tercero mandante. Si va vacío, el ítem se factura sin mandato. |
mandate_identification_document_id | req | Tipo de documento del mandante (catálogo documentos_identidad) |
mandate_dv | opc | Dígito de verificación del mandante (si aplica) |
Ejemplo
{
"customer": "45",
"numbering_range_id": "502",
"payment_form": "1",
"payment_due_date": "2026-07-02",
"payment_method_code": "10",
"observation": "Facturación por mandato",
"products": [
{
"id": "123",
"code_reference": "SKU-001",
"name": "Canon de arrendamiento",
"quantity": "1",
"price": "1000000",
"tax_rate": "0",
"unit_measure_id": "70",
"standard_code_id": "1",
"tribute_id": "1",
"discount_rate": "0",
"is_excluded": "0",
"mandate_identification": "901234567",
"mandate_identification_document_id": "31",
"mandate_dv": "8"
}
]
}
{
"status": "success",
"message": "Factura de mandato creada exitosamente",
"data": {
"invoice_id": 47021,
"code_reference": "INV6a46c29683f15fdc6",
"bill_number": "SETP990000228",
"api_status": "Created",
"cufe": "8c5b493b0b862dd99267b58a3fbd35d8098464e2...",
"comprobante_id": 90312
}
}
payment_due_date y en cada ítem discount_rate e is_excluded con valor. Para facturar por mandato, cada ítem debe incluir mandate_identification (y su mandate_identification_document_id) del mandante.Crear tiquete POS
Factura de Venta POS) a la DIAN. Mismo formato de request que la factura estándar; usa el rango de numeración POS de tu cuenta.Factura de Venta POS. Si no envías numbering_range_id, se detecta automáticamente el rango POS activo del titular.Genera un tiquete POS electrónico. Responde JSON con el resultado DIAN.
Cabecera
| Campo | Descripción | |
|---|---|---|
customer | req | ID del tercero (de api/third) |
payment_form | req | 1 Contado · 2 Crédito |
payment_method_code | opc | Método de pago DIAN (por defecto 10) |
numbering_range_id | opc | Rango POS. Si se omite, se auto-detecta el rango Factura de Venta POS activo |
observation | opc | Nota (por defecto "Tiquete POS Electrónico") |
payment_due_date, municipality_id, tip_amount, seller_id | opc | Vencimiento, municipio, propina, vendedor |
Ítems — products[]
| Campo | Descripción | |
|---|---|---|
id | req | ID del producto (de api/products) |
code_reference, name | req | Código y nombre del ítem |
quantity, price, discount_rate | req | Cantidad, precio unitario, % descuento |
tax_rate | req | % IVA: 0, 5, 8, 19 |
unit_measure_id, standard_code_id, tribute_id | req | Catálogos (ver Relaciones) |
is_excluded, requiere_stock, withholding_tax_rate | opc | Excluido de IVA (0/1), control de stock, retención |
Ejemplo
{
"customer": "8",
"payment_form": "1",
"payment_method_code": "10",
"observation": "Venta POS",
"products": [
{
"id": "72707",
"code_reference": "ENVIO-SHOPIFY",
"name": "Envío - Estándar",
"quantity": "1",
"price": "4000",
"tax_rate": "0",
"discount_rate": "0",
"unit_measure_id": "70",
"standard_code_id": "1",
"tribute_id": "22",
"is_excluded": "0",
"requiere_stock": "0"
}
]
}
{
"status": "success",
"message": "Factura POS creada exitosamente",
"data": {
"invoice_id": 47016,
"bill_number": "EPOS71",
"cufe": "20764680cd9bd7b8d62c72442e012887c912cfc8...",
"api_status": "Created",
"dian_response": { "bill": { "number": "EPOS71", "cufe": "2076...", "total": 4000, "id": "347619492" } }
}
}
requiere_stock=1 dentro de la transacción. Envía numbering_range_id explícito si manejas varios rangos POS por sucursal.Crear factura RIPS (salud)
rips_id) creado desde el módulo de RIPS. Los datos clínicos (paciente/servicios) se toman de ese paquete, no se envían en el request.Genera una factura electrónica RIPS. Responde JSON con el resultado DIAN.
Cabecera
| Campo | Descripción | |
|---|---|---|
rips_id | req | ID del paquete RIPS validado. De él se cargan pacientes y servicios de salud. |
invoice_type | req | Debe ser "rips" |
customer | req | ID del tercero (pagador — normalmente la EPS/entidad) |
numbering_range_id | req | Rango de numeración / resolución |
payment_form | req | 1 Contado · 2 Crédito |
payment_method_code | req | Método de pago (catálogo) |
payment_due_date | req | Fecha de vencimiento (una fecha, aun en contado) |
company | req | Nombre del emisor (requerido para la notificación por correo) |
observation | opc | Nota / observación |
cuenta_bancaria_id, cost_center_id, moneda | opc | Cuenta bancaria, centro de costo, divisa |
products) y los datos clínicos (health_data) se derivan automáticamente del paquete rips_id. No es necesario enviarlos.Ejemplo
{
"rips_id": "30",
"invoice_type": "rips",
"customer": "8",
"numbering_range_id": "100",
"payment_form": "1",
"payment_method_code": "10",
"payment_due_date": "2026-07-02",
"company": "NEXO CONTABLE",
"observation": "Servicios de salud - paquete RIPS"
}
{
"status": "success",
"message": "Factura creada exitosamente",
"data": {
"invoice_id": 47021,
"code_reference": "INV6a46da95177b095bb",
"bill_number": "SETP990000231",
"api_status": "Created",
"cufe": "ad3a694a73e1a211f585d259c329c160031dbe2f...",
"dian_response": { "bill": { "number": "SETP990000231", "total": 7350000 } }
}
}
rips_id debe estar en estado validado y pertenecer a tu cuenta; (2) la cuenta debe tener credenciales DIAN configuradas; (3) envía payment_due_date (la DIAN rechaza con "Datos de la factura incompletos" si falta) y company (requerido por la notificación por correo). Los ítems y datos clínicos salen del paquete RIPS.El flujo FEV-RIPS
RIPS = Registro Individual de Prestación de Servicios de Salud. FEV-RIPS es la pareja del RIPS con la factura electrónica de venta (FEV) en salud.
rips_id), después se factura. No hay un endpoint que reciba un RIPS crudo y lo facture en un solo paso.1. POST api/rips_invoice/rips.php → registra el RIPS (paquete_id)
2. GET api/rips_invoice/get_rips_for_invoice.php?rips_id=N → arma datos de factura
3. POST api/invoice/generate_rips.php → factura ante la DIAN (CUFE) + valida ante MinSalud (CUV)
CUV ≠ CUFE (el punto que más confunde)
- CUFE — lo asigna la DIAN al timbrar la factura. Identifica la factura.
- CUV — lo asigna MinSalud (MUV) al validar el RIPS. Identifica la validación.
numFactura) — no por el CUFE.Estados del paquete
| estado | Significado |
|---|---|
pendiente | Validó localmente; falta su factura. En producción los RIPS sin factura quedan aquí hasta facturarse. |
validado | CUV real de MinSalud. |
rechazado | El MUV lo rechazó (ver mensaje_respuesta con resultadosValidacion). |
error | Falló la llamada al validador. |
Estructura del payload RIPS
Forma del payload que registras en Registrar paquete RIPS. Sigue la estructura oficial Res. 2275/2023. Es también lo que genera Wardian al facturar desde un evento clínico.
Raíz — sin factura vs. con factura
La raíz es excluyente: un paquete se reporta como nota sin factura o con factura, nunca ambos.
{
"numDocumentoIdObligado": "900123456",
"numFactura": null,
"numNota": "00001234",
"tipoNota": "RS",
"usuarios": [ /* … */ ]
}
{
"numDocumentoIdObligado": "900123456",
"numFactura": "SETP990000001",
"numNota": null,
"tipoNota": null,
"usuarios": [ /* … */ ]
}
cufe (o al facturar el paquete), la plataforma resuelve la FEV localmente y reescribe la raíz a numFactura = número de esa factura, con tipoNota/numNota = null. Para notas crédito/débito coexisten numFactura + tipoNota (NC/ND) + numNota.Usuario
Cada entrada de usuarios[] es un paciente con sus servicios. Códigos de residencia según la guía oficial (país Colombia = 170).
{
"consecutivo": 1,
"tipoDocumentoIdentificacion": "CC",
"numDocumentoIdentificacion": "1234567890",
"fechaNacimiento": "1985-06-15",
"codSexo": "M",
"codPaisResidencia": "170",
"codMunicipioResidencia": "11001", // DANE — de pacientes.municipio_id (o el tercero)
"codZonaTerritorialResidencia": "02", // 02 = urbana
"incapacidad": "NO", // "SI" | "NO"
"codPaisOrigen": "170",
"tipoUsuario": "01",
"servicios": { "consultas": [ … ], "procedimientos": [ … ] }
}
codMunicipioResidencia es null si el paciente no tiene municipio DANE cargado (columna pacientes.municipio_id, con respaldo al tercero enlazado). El validador oficial lo exige — cárgalo desde la ficha del paciente.Servicios — consultas vs. procedimientos
Wardian clasifica cada servicio en su bloque oficial según el tipo de evento: Consulta/Control → consultas[]; el resto (incl. Procedimiento, Urgencia) → procedimientos[]. Difieren en algunos campos:
{
"codPrestador": "800100123456",
"fechaInicioAtencion": "2024-02-20 10:30:00",
"numAutorizacion": "0000000",
"codConsulta": "890201",
"modalidadGrupoServicioTecSal": "01",
"grupoServicios": "01",
"codServicio": 890,
"finalidadTecnologiaSalud": "11",
"causaMotivoAtencion": "38",
"codDiagnosticoPrincipal": "I10",
"codDiagnosticoRelacionado1": null,
"codDiagnosticoRelacionado2": null,
"codDiagnosticoRelacionado3": null,
"tipoDiagnosticoPrincipal": "02",
"tipoDocumentoIdentificacion": "CC",
"numDocumentoIdentificacion": "1234567890",
"vrServicio": 50000,
"conceptoRecaudo": "05",
"valorPagoModerador": 0,
"numFEVPagoModerador": null,
"consecutivo": 1
}
{
"codPrestador": "800100123456",
"fechaInicioAtencion": "2024-02-20 14:00:00",
"idMIPRES": null,
"numAutorizacion": "0000000",
"codProcedimiento": "880201",
"modalidadGrupoServicioTecSal": "01",
"grupoServicios": "01",
"codServicio": 890,
"viaIngresoServicioSalud": "01",
"finalidadTecnologiaSalud": "11",
"codDiagnosticoPrincipal": "I10",
"codDiagnosticoRelacionado": null,
"codComplicacion": null,
"tipoDocumentoIdentificacion": "CC",
"numDocumentoIdentificacion": "1234567890",
"vrServicio": 500000,
"conceptoRecaudo": "05",
"valorPagoModerador": 0,
"numFEVPagoModerador": null,
"consecutivo": 1
}
numFEVPagoModerador = número de la FEV en todo ítem con valorPagoModerador > 0, en cualquiera de los bloques.causaMotivoAtencion (38) y tipoDiagnosticoPrincipal (02) son valores por defecto hasta que se capture el dato clínico real (sobreescribibles por cuenta vía rips_configuracion). Los bloques urgencias, hospitalizacion, medicamentos, otrosServicios y recienNacidos aún no se generan (requieren datos que el modelo clínico no almacena).conceptoRecaudo (campos C18/P17), presente en consultas y procedimientos. El Anexo no define tipoPagoModerador — es un alias del mismo concepto. Como el MUV (nivel 1) valida con esquema estricto y rechaza claves desconocidas, se emite únicamente conceptoRecaudo.Registrar paquete RIPS
paquete_id (= rips_id) que usarás para facturar. Aquí no se crea FEV.Cuerpo
| Campo | Descripción | |
|---|---|---|
payload | req | El JSON RIPS completo — ver Estructura del payload (numDocumentoIdObligado, usuarios[] con servicios.consultas/servicios.procedimientos, …). |
usuario_id_rips | opc | Dueño del RIPS. Por defecto, quien envía. Un principal puede enviar por sus subusuarios. |
cufe | opc | CUFE de una FEV ya emitida. Si se envía, el RIPS se convierte a modo con factura (la raíz numFactura toma el número de esa factura) y se valida así. El CUFE se resuelve localmente contra tus facturas; no se escribe dentro del RIPS. |
{
"usuario_id_rips": 29,
"payload": {
"numDocumentoIdObligado": "900123456",
"numNota": "00001234",
"tipoNota": "RS",
"usuarios": [ { "...": "ver Estructura del payload" } ]
}
}
{
"status": "ok",
"cuv": "a1b2c3...",
"paquete_id": 30,
"usuario_id_rips": 29,
"usuario_id_envio": 29
}
WARDIAN- es simulado (validador inalcanzable) y solo ocurre fuera de producción. La opción cufe requiere la estructura moderna usuarios[].Datos para facturar
Solo devuelve paquetes con cuv no vacío.
{
"status": "success",
"data": {
"rips_id": 30,
"rips_cuv": "a1b2c3...",
"rips_cufe": null,
"numbering_range_id": 100,
"invoice_type": "rips",
"health_data": { "provider_code": "...", "patient": { "...": "" } },
"items": [ { "code_references": "890201", "name": "Consulta", "price": 50000 } ]
},
"suggested_customer": { "id": 8, "name": "EPS ..." }
}
rips_cufe (tomado del payload_fev) es null hasta que el RIPS se factura. Pasa data a Crear factura RIPS.Revalidar / con factura
Cuerpo
| Campo | Descripción | |
|---|---|---|
rips_id | req | ID del paquete a revalidar. |
cufe | opc | Si se envía, convierte el paquete a modo con factura (raíz numFactura = número de esa FEV, resuelta localmente) antes de reenviar. El payload convertido se persiste. |
{ "rips_id": 30, "cufe": "ad3a694a73e1..." }
{
"status": "success",
"message": "RIPS regenerado correctamente con CUV real",
"cuv": "a1b2c3...",
"estado": "validado",
"is_simulated": false
}
404 si no existe una factura con ese CUFE en tu cuenta. Sin cufe, revalida el paquete tal cual (sin cambiar a con factura).Crear documento soporte
id), los ítems y el numbering_range_id del rango de documento soporte. Se envía a la DIAN y responde con el número y el CUDS.Genera un documento soporte electrónico. Acepta application/x-www-form-urlencoded o multipart/form-data. Responde JSON con el resultado DIAN. El documento soporte no lleva IVA (compra a no obligado).
Cabecera
| Campo | Descripción | |
|---|---|---|
customer | req | ID del proveedor (tercero, de api/third) |
numbering_range_id | req | Rango de numeración de documento soporte |
payment_form | req | 1 Contado · 2 Crédito |
payment_method_code | req | Método de pago (catálogo) |
issue_date | opc | Fecha de emisión (por defecto hoy) |
payment_due_date | opc | Fecha de vencimiento (si crédito) |
observation | opc | Nota / observación |
cuenta_bancaria_id, cuenta_cxp_codigo, cost_center_id | opc | Cuenta bancaria (pago), CxP (crédito), centro de costo |
save_draft | opc | 1 = guardar como Borrador (no se envía a la DIAN) |
Ítems — products[]
| Campo | Descripción | |
|---|---|---|
code_reference, name | req | Código y nombre del ítem |
quantity, price | req | Cantidad y precio unitario |
discount_rate | req | % descuento (ej. 0) |
unit_measure_id, standard_code_id | req | Catálogos (ver Relaciones) |
ret_fuente_rate, ret_ica_rate, withholding_config_id | opc | Retenciones (ReteFuente / ReteICA) |
Ejemplo
{
"customer": "48",
"numbering_range_id": "100",
"payment_form": "1",
"payment_method_code": "47",
"issue_date": "2026-07-14",
"observation": "Compra a proveedor no obligado",
"products": [
{
"code_reference": "SERV-01",
"name": "Servicio de mantenimiento",
"quantity": "1",
"price": "500000",
"discount_rate": "0",
"unit_measure_id": "70",
"standard_code_id": "1",
"ret_fuente_rate": "4"
}
]
}
{
"status": "success",
"success": true,
"data": {
"document_id": 618,
"code_reference": "INV6a46c29683f15fdc6",
"number": "DS-1",
"cuds": "a1b2c3d4e5f6...",
"api_status": "Created"
}
}
{
"status": "success",
"success": true,
"message": "Borrador guardado",
"data": { "document_id": 618, "estado": "Borrador" }
}
save_draft=1 el documento queda en Borrador (no va a la DIAN) para revisar/emitir después desde el panel. Un borrador se puede convertir en documento soporte emitido.Qué es el RDA
El flujo
| # | Paso | Dónde |
|---|---|---|
| 1 | Cargar las credenciales que emitió MinSalud (por prestador y por entorno) | Ajustes → RDA (IHCE) |
| 2 | Registrar el profesional con documento y registro RETHUS | Ajustes → Prestadores (es_profesional=1) |
| 3 | Completar una atención clínica con diagnóstico CIE-10 | Historia clínica → Eventos |
| 4 | Se construye el Bundle, se valida local y se envía | automático si auto_enviar está activo, o POST manual |
| 5 | IHCE devuelve el identificador del RDA, que se persiste | Historia clínica → RDA (IHCE) |
Lo que hay que saber del contrato
| Aspecto | Valor |
|---|---|
| Estructura | Bundle con type: "document". entry[0] debe ser la Composition, y solo puede haber una. |
| Referencias | Ids planos, sin # y sin fullUrl: "subject": {"reference": "CC-80189301"}. Personas usan TipoDoc-NumDoc; la IPS, su código de habilitación pelado. |
| Tipo de documento | Composition.type = LOINC 51845-6 (Outpatient Consult note) para el RDA ambulatorio. |
| Secciones | Nueve obligatorias. Las que no tengan datos igual se envían, con emptyReason = nilknown. |
| Obligatorios en el Bundle | Composition, Patient, Practitioner y DocumentReference son 1..1. La Organization de la IPS es 0..1. |
| Transporte | OAuth2 client_credentials contra Azure AD. Cada llamada lleva Authorization: Bearer, Ocp-Apim-Subscription-Key y Content-Type: application/fhir+json. |
| Duplicados | IHCE responde 409 si coinciden Encounter.subject + period + serviceProvider + participant. Wardian lo detecta localmente antes de enviar. |
client_id, el client_secret y la Ocp-Apim-Subscription-Key al asignar credenciales. Se solicitan por la Mesa de Servicios del micrositio IHCE.11450-4, desde el CIE-10 del evento) y Pagadores (48768-6, si el paciente tiene código EAPB). Las otras siete viajan con emptyReason porque su perfil exige datos que el sistema aún no captura de forma codificada. El Bundle es estructuralmente válido en cualquier caso.47519-4) solo existe en los RDA de hospitalización y urgencias; en consulta externa el CUPS viaja en Encounter.serviceType.Credenciales IHCE
Cuerpo
| Campo | Descripción | |
|---|---|---|
entorno | req | qa | preproduccion | produccion |
base_url | req | La URL que emitió MinSalud. Debe ser https://. |
tenant_id | req | Tenant de Azure AD contra el que se pide el token. |
client_id | req | |
client_secret | req | Obligatorio al crear. Al editar, envíalo vacío para conservar el guardado. |
subscription_key | req | El Ocp-Apim-Subscription-Key. Mismo criterio que el secreto. |
scope | req | Scope OAuth2, normalmente api://…/.default. |
prestador_id | opc | IPS a la que pertenece la credencial, si el tenant opera varias. |
auto_enviar | opc | 0 por defecto. Con 1, cada atención completada dispara el envío. |
op_enviar_ambulatorio | opc | Ruta de la operación. Configurable porque el Manual documenta menos operaciones que la guía FHIR. |
{
"entorno": "qa",
"base_url": "https://<la-que-emitio-minsalud>",
"tenant_id": "00000000-0000-0000-0000-000000000000",
"client_id": "<client-id>",
"client_secret": "<client-secret>",
"subscription_key": "<ocp-apim-subscription-key>",
"scope": "api://rda/.default",
"auto_enviar": 0
}
POST .../rda_credenciales/test.php con {"entorno":"qa"} pide un token real a Azure AD. Un success confirma tenant/client/secret/scope, pero no valida la subscription key ni la URL base: eso se prueba en el primer envío.GET .../rda_credenciales/list.php devuelve las credenciales del tenant con los secretos enmascarados, y POST .../delete.php con {"id":N} elimina una.Previsualizar Bundle
{
"evento_id": 4242,
"entorno": "qa",
"valido": true,
"errores": [],
"bundle": {
"resourceType": "Bundle",
"type": "document",
"identifier": { "value": "…uuid…" },
"entry": [
{ "resource": { "resourceType": "Composition", "id": "Composition-0" } },
{ "resource": { "resourceType": "Patient", "id": "CC-1000200300" } },
{ "resource": { "resourceType": "Practitioner", "id": "CC-79123456" } },
{ "resource": { "resourceType": "Organization", "id": "230010255201" } },
{ "resource": { "resourceType": "Location", "id": "230010255201-01" } },
{ "resource": { "resourceType": "Encounter", "id": "Encounter-0" } },
{ "resource": { "resourceType": "DocumentReference", "id": "DocumentReference-0" } },
{ "resource": { "resourceType": "Condition", "id": "Condition-0" } }
]
}
}
Encounter.diagnosis es 1..4), sin profesional registrado, o sin código de habilitación en la IPS.Enviar RDA
borrador con los errores.Cuerpo
| Campo | Descripción | |
|---|---|---|
evento_id | req | Evento clínico a reportar. |
entorno | opc | qa por defecto. |
forzar | opc | Reenvía aunque el contenido sea idéntico a un envío ya aceptado. |
confirmar_produccion | opc | Obligatorio si entorno es produccion. Salvaguarda contra envíos accidentales. |
{
"estado": "aceptado",
"paquete_id": 17,
"http_code": 200,
"rda_id": "RDA-000123",
"mensaje": "RDA aceptado por IHCE.",
"errores": []
}
Estados y códigos
| Estado | HTTP | Significado |
|---|---|---|
aceptado | 200 | IHCE devolvió el identificador del RDA. |
duplicado | 200 | El encuentro ya se había reportado con el mismo contenido. No se reenvía. |
enviado | 202 | Respondió 200 pero sin identificador reconocible. |
borrador | 422 | No pasó la validación local. No se envió. |
rechazado | 422 | IHCE devolvió 400. Los issue del OperationOutcome vienen en errores. |
error | 502 | Fallo de red o HTTP inesperado. Reintentable. |
dist/cron/rda_retry_pending.php reintenta solo error y enviado, con espera creciente (5/15/60/240 min) y techo de 5 intentos. Nunca reintenta rechazado ni borrador: son defectos de contenido y reintentarlos da el mismo 400.Consultar envíos
Devuelve los paquetes del tenant, el conteo por estado y un bloque aptitud con lo que falta para que los envíos pasen la validación de registros de MinSalud (EVOL, RETHUS, REPS, DIVIPOLA).
{
"paquetes": [
{ "id": 17, "estado": "aceptado", "entorno": "qa", "rda_id": "RDA-000123",
"fecha_evento": "2026-07-20", "diagnostico_codigo": "J00", "intentos": 1 }
],
"por_estado": { "aceptado": 12, "rechazado": 1 },
"aptitud": {
"pacientes_total": 340,
"pacientes_sin_fecha_nac": 0,
"pacientes_sin_sexo_biologico": 12,
"profesionales": 3,
"ips_con_habilitacion": 1,
"eventos_completados": 512,
"eventos_sin_diagnostico": 4,
"credenciales": { "qa": "lista", "preproduccion": "sin configurar", "produccion": "sin configurar" }
}
}
list.php no devuelve el Bundle: es información clínica y pesa. Para el detalle completo (Bundle enviado, OperationOutcome y bitácora) usa GET .../rda/data.php?id=17.