Catálogos de Facturación [BETA]
Funcionalidad en desarrollo y pruebas: este servicio está disponible en el ambiente de pruebas y todavía no debe usarse en producción. El funcionamiento, los campos y los mensajes de error pueden cambiar antes del despliegue definitivo.
Estos servicios devuelven los catálogos que necesitas para crear facturas y notas en nuby: resoluciones de venta, conceptos de nota, productos con sus impuestos y estados de factura. Así evitas escribir identificadores fijos en tu plataforma.
Los cuatro son de solo lectura: no crean, no modifican y no consumen referencias de idempotencia.
¿Para qué sirve este servicio?
Úsalos cuando necesites traducir los identificadores de nuby a datos legibles, o al revés, sin mantener copias propias de la configuración. Los casos de uso más frecuentes son:
- Elegir la resolución de venta que vas a enviar en
resolucion_idal crear o editar una factura. - Obtener el
concepto_idde las notas de crédito y de las notas débito que vas a crear. - Tomar los impuestos del producto (
tipo_iva_id,por_ivay sus equivalentes de retención y reteICA) y enviarlos enitems[]de una factura o de una nota. - Traducir el
estado_idde una factura al significado que ves en la interfaz de nuby. - Evitar identificadores fijos en tu plataforma: los conceptos y productos dependen de la configuración de tu instancia.
1. El Endpoint (La dirección web)
Método y direcciones
| Método | Recurso | Catálogo que devuelve |
|---|---|---|
GET |
/invoices/sales/resolutions |
Resoluciones de venta |
GET |
/masters/note-concepts |
Conceptos de nota |
GET |
/masters/products |
Productos e impuestos |
GET |
/masters/invoice-statuses |
Estados de factura |
Ninguno de los cuatro recibe cuerpo en la petición ni exige referencia_externa: no son idempotentes y no escriben en la base.
GET https://{{instancia}}/service/v2/public/invoices/sales/resolutions
GET https://{{instancia}}/service/v2/public/masters/note-concepts
GET https://{{instancia}}/service/v2/public/masters/products
GET https://{{instancia}}/service/v2/public/masters/invoice-statuses
¿Qué debo colocar en {{instancia}}?
La dirección web principal con la que ingresas a tu plataforma, incluyendo la terminación .nuby.app o .arrendasoft.co, sin https:// y sin barras diagonales al final. Si entras a nuby escribiendo inmobiliaria.nuby.app en el navegador, esa es tu instancia.
Importante: no incluyas https:// ni barras diagonales (/) al final. La dirección se compone así: https:// + tu instancia + /service/v2/public/ + la ruta del catálogo.
Encabezados requeridos
| Encabezado | Valor |
|---|---|
Authorization |
Bearer <token>, con el token obtenido en el servicio Login. |
Los cuatro servicios exigen el encabezado Authorization.
Permiso requerido (scope): catalogs:read
Los cuatro servicios exigen el permiso catalogs:read. Si falta, la respuesta es 403 con RECURSO_NO_AUTORIZADO.
¿Por qué catalogs:read y no un permiso de facturas? Las lecturas de la API usan catalogs:read porque el cliente nuby-clientes no tiene un scope de lectura de facturas y exigirlo dejaría los endpoints inutilizables. En tu cliente OAuth los permisos de consulta disponibles son catalogs:read, invoices:create, invoices:update, notes:create y third-parties:create; leer se autoriza con catalogs:read.
Si aún no tienes un token, consulta la página Login del libro API nuby V2.
2. La Petición (¿Qué debes enviarnos?)
Los catálogos se consultan con GET y sin cuerpo: lo único que puedes enviar son parámetros de consulta en la dirección. Todos son opcionales; si no envías ninguno, el servicio devuelve la primera página del catálogo completo.
Resoluciones de venta — GET /invoices/sales/resolutions
GET https://{{instancia}}/service/v2/public/invoices/sales/resolutions
Devuelve las resoluciones de facturación de venta, es decir, las que puedes enviar en resolucion_id al crear o editar una factura. Las resoluciones reservadas para facturas de contrato (inquilino y propietario) no aparecen en esta lista.
Usa este servicio, no el anterior. El endpoint GET /invoices/resolutions sigue existiendo por compatibilidad, pero puede devolver resoluciones de contrato, y la API de venta las rechaza con 422 RESOLUCION_CONTRATO_NO_PERMITIDA.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
activa |
texto | No | SI o NO. |
tipo_doc_id |
entero | No | Tipo de documento de la resolución. |
fact_electronica |
texto | No | SI o NO. |
resolucion_para |
entero | No | 1 factura o 2 documento soporte. |
fecha_desde / fecha_hasta |
fecha | No | Rango sobre la fecha de la resolución (AAAA-MM-DD). |
fecha_vigencia_desde / fecha_vigencia_hasta |
fecha | No | Rango sobre la vigencia (AAAA-MM-DD). |
page |
entero | No | Número de página, empezando en 1. Por defecto 1. |
page_size |
entero | No | Registros por página. Por defecto 10. Máximo 1000. |
Conceptos de nota — GET /masters/note-concepts
GET https://{{instancia}}/service/v2/public/masters/note-concepts?target=2
Devuelve los conceptos que puedes enviar en concepto_id al crear una nota. El campo target indica el tipo: 1 = nota crédito y 2 = nota débito.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
target |
entero | No | 1 nota crédito o 2 nota débito. Sin filtro devuelve todos. |
page |
entero | No | Número de página, empezando en 1. Por defecto 1. |
page_size |
entero | No | Registros por página. Por defecto 50. Máximo 1000. |
Productos e impuestos — GET /masters/products
GET https://{{instancia}}/service/v2/public/masters/products?search=plan&estado=1
Devuelve los productos que puedes usar en items[] de una factura o nota, con los porcentajes de IVA, retención en la fuente, reteIVA y reteICA configurados.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
search |
texto | No | Busca coincidencias en el nombre del producto y en su código. |
estado |
entero | No | 1 activos, 0 inactivos. Sin filtro devuelve todos. |
page |
entero | No | Número de página, empezando en 1. Por defecto 1. |
page_size |
entero | No | Registros por página. Por defecto 50. Máximo 1000. |
Estados de factura — GET /masters/invoice-statuses
GET https://{{instancia}}/service/v2/public/masters/invoice-statuses
Devuelve el significado de cada estado_id que aparece en las respuestas de factura. No recibe parámetros: entrega el catálogo completo.
Parámetros inválidos: si page, page_size, activa, tipo_doc_id, fact_electronica, resolucion_para, una fecha, target o estado no tienen un valor válido, la respuesta es 400 con VALIDATION_ERROR. page y page_size deben ser enteros mayores que cero.
3. La Respuesta (¿Qué te entregaremos?)
Los cuatro catálogos responden con el mismo envelope de las lecturas de la API: el estado, el mensaje, el cuerpo con los registros y el bloque de paginación.
Estructura principal de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
status |
integer | Código del estado de la respuesta. 200 cuando la consulta fue exitosa. |
message |
string | null cuando la consulta fue exitosa. |
body |
array | Registros del catálogo consultado. En un error, este campo no existe. |
pagination |
object | Resumen de la página: total_records, total_pages, current_page, page_size, current_page_records, has_next_page y has_previous_page. |
filters_applied |
object | Filtros que aplicó el servicio (por ejemplo {"target": 2}). Llega en null cuando la consulta no llevaba filtros. |
Resoluciones de venta
Cada elemento de body describe una resolución de venta:
{
"status": 200,
"message": null,
"body": [
{
"resolucion_id": 1,
"tipo_doc_id": 1,
"tipo_doc": "Factura electronica de venta",
"resolucion_numero": "18760000001",
"consecutivo_iniciar": 1,
"consecutivo_inicial": 1,
"consecutivo_final": 5000,
"fecha": "2026-01-01",
"fecha_vigencia": "2027-01-01",
"prefijo": "FE",
"dias_vencimiento": 30,
"activa": "SI",
"fact_electronica": "SI",
"resolucion_para": 1
}
],
"pagination": {
"total_records": 1,
"total_pages": 1,
"current_page": 1,
"page_size": 10,
"current_page_records": 1,
"has_next_page": false,
"has_previous_page": false
},
"filters_applied": null
}
El prefijo y el dias_vencimiento son los que nuby usa al crear la factura si no envías fecha_vencimiento.
Conceptos de nota
Cada elemento de body trae el id que se envía en concepto_id, su tipo (target), el código y el nombre:
{
"status": 200,
"message": null,
"body": [
{ "id": 9, "target": 2, "codigo": "2", "nombre": "Gastos por cobrar", "estado": 1 },
{ "id": 10, "target": 2, "codigo": "4", "nombre": "Otros", "estado": 1 }
],
"pagination": { "total_records": 2, "total_pages": 1, "current_page": 1, "page_size": 50, "current_page_records": 2, "has_next_page": false, "has_previous_page": false },
"filters_applied": { "target": 2 }
}
Si no envías concepto_id al crear una nota, nuby aplica el concepto por defecto del tipo. Envíalo solo cuando necesites un concepto específico.
Productos e impuestos
Cada elemento de body describe un producto con sus datos y los porcentajes configurados:
{
"producto_id": 159,
"producto": "Plan X",
"descripcion": "Plan administrativo",
"codigo": "PLAN-X",
"valor_unitario": 100000,
"tipo_iva_id": 1,
"por_iva": 19,
"tipo_retencion_id": 1,
"por_retencion": 2.5,
"tipo_reteiva_id": null,
"por_reteiva": null,
"tipo_reteica_id": null,
"por_reteica": null,
"estado": 1
}
Los porcentajes no se calculan solos. Al crear la factura o la nota envía tipo_iva_id y por_iva (y sus equivalentes de retención) tomados de este catálogo. Si el producto trae null, envía null y el impuesto no se aplica.
Estados de factura
Cada elemento de body empareja el estado_id con su nombre, ordenado por estado_id:
estado_id |
Estado | Qué significa |
|---|---|---|
| 1 | Facturado | Emitida con consecutivo y enviada a la DIAN. |
| 2 | Anulado | Anulada. |
| 3 | Pagada | Saldo en cero. |
| 4 | Contabilizada | Contabilidad cerrada. |
| 5 | Borrador | Creada por la API, editable, aún sin número legal. |
| 6 | Anulada por nota crédito | Una nota crédito total la dejó en saldo cero. |
| 7 | Anulado por nota ajuste | Anulada por un ajuste. |
Puedes consultar este catálogo una vez y guardarlo en tu sistema: los estados son fijos.
4. Seguridad y Posibles Errores
Tu pase tiene fecha de caducidad
Por medidas de seguridad, el token que te entregamos solo dura 1 hora. Una vez transcurrido ese tiempo, el sistema te bloquea el acceso con un error 401 y tu sistema debe volver a consumir el servicio de Login para pedir un pase nuevo.
Las causas de rechazo de los catálogos ya documentadas son estas:
| Código HTTP | error_code |
Cuándo ocurre |
|---|---|---|
400 |
VALIDATION_ERROR |
Un parámetro tiene un valor inválido (por ejemplo page=0, target=3 o una fecha mal formada). |
401 |
— | Falta el encabezado Authorization o el token no es válido. |
403 |
RECURSO_NO_AUTORIZADO |
El token no tiene el permiso catalogs:read. |
500 |
INTERNAL_ERROR |
Fallo interno al consultar la base. Reintenta más tarde. |
Un parámetro inválido rechaza la consulta completa con 400 VALIDATION_ERROR. Corrige el valor y repite la petición.
Así se ve el rechazo por permisos:
{
"success": false,
"status": 403,
"message": "El consumidor no tiene habilitado el recurso solicitado.",
"error_code": "RECURSO_NO_AUTORIZADO"
}
5. Ejemplos de integración
Los ejemplos consultan el catálogo de resoluciones de venta; la misma forma de llamada sirve para los otros tres catálogos, cambiando solo la ruta. Tratan el caso feliz, el rechazo por parámetro inválido (400 VALIDATION_ERROR) y el rechazo por permisos (403 RECURSO_NO_AUTORIZADO).
cURL
TOKEN="TU_TOKEN_AQUI"
INSTANCIA="mi-inmobiliaria.nuby.app"
# 1. Primera página de resoluciones activas
curl --silent --show-error --write-out "\nHTTP %{http_code}\n" \
"https://${INSTANCIA}/service/v2/public/invoices/sales/resolutions?activa=SI&page=1&page_size=10" \
-H "Authorization: Bearer ${TOKEN}"
# 2. Un parámetro inválido devuelve 400 VALIDATION_ERROR
curl --silent --show-error --write-out "\nHTTP %{http_code}\n" \
"https://${INSTANCIA}/service/v2/public/invoices/sales/resolutions?page=0" \
-H "Authorization: Bearer ${TOKEN}"
PHP
<?php
$instancia = 'mi-inmobiliaria.nuby.app'; // Tu dirección web completa
$token = 'TU_TOKEN_AQUI'; // Token obtenido del servicio Login
$query = http_build_query([
'activa' => 'SI',
'page' => 1,
'page_size' => 10,
]);
$url = "https://{$instancia}/service/v2/public/invoices/sales/resolutions?{$query}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
]);
$respuesta = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$datos = json_decode($respuesta, true);
if ($httpCode === 200) {
foreach ($datos['body'] as $resolucion) {
echo "{$resolucion['prefijo']} {$resolucion['resolucion_numero']} - "
. "{$resolucion['consecutivo_inicial']} a {$resolucion['consecutivo_final']}\n";
}
echo "Total: {$datos['pagination']['total_records']}\n";
} elseif ($httpCode === 400) {
echo "Parámetro inválido ({$datos['error_code']}): {$datos['message']}\n";
} elseif ($httpCode === 403) {
echo "Sin permiso catalogs:read ({$datos['error_code']}): {$datos['message']}\n";
} else {
echo "Error HTTP {$httpCode}: {$datos['message']}\n";
}
Python
import requests
# 1. Configura tus datos de acceso
instancia = "mi-inmobiliaria.nuby.app" # Reemplaza con tu dirección web completa
token = "TU_TOKEN_AQUI" # Token obtenido del servicio Login
# 2. Envía la petición con los filtros como parámetros
url = f"https://{instancia}/service/v2/public/invoices/sales/resolutions"
headers = {"Authorization": f"Bearer {token}"}
params = {"activa": "SI", "page": 1, "page_size": 10}
respuesta = requests.get(url, headers=headers, params=params, timeout=30)
# 3. Procesa la respuesta
if respuesta.status_code == 200:
datos = respuesta.json()
for resolucion in datos["body"]:
print(resolucion["prefijo"], resolucion["resolucion_numero"])
print(f"Total: {datos['pagination']['total_records']}")
elif respuesta.status_code in (400, 403):
error = respuesta.json()
print(f"Rechazo {error['error_code']}: {error['message']}")
else:
print(f"Error HTTP {respuesta.status_code}: {respuesta.text}")
JavaScript (Fetch API)
// 1. Configura tus datos de acceso
const instancia = 'mi-inmobiliaria.nuby.app'; // Reemplaza con tu dirección web completa
const token = 'TU_TOKEN_AQUI'; // Token obtenido del servicio Login
// 2. Función que consulta el catálogo de resoluciones de venta
async function listarResoluciones(filtros = {}) {
const params = new URLSearchParams({
activa: 'SI',
page: '1',
page_size: '10',
...filtros,
});
const url = `https://${instancia}/service/v2/public/invoices/sales/resolutions?${params}`;
const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` },
});
const datos = await response.json().catch(() => ({}));
if (!response.ok) {
console.error(`Error HTTP ${response.status} ${datos.error_code ?? ''}: ${datos.message ?? ''}`);
return [];
}
console.log(`Total de resoluciones: ${datos.pagination.total_records}`);
return datos.body;
}
// 3. Consume el catálogo
listarResoluciones().then((resoluciones) => {
resoluciones.forEach((r) => console.log(`${r.prefijo} ${r.resolucion_numero}`));
});
Los otros tres catálogos se consumen igual, con el mismo encabezado Authorization y el mismo permiso catalogs:read; solo cambia la ruta: GET https://{{instancia}}/service/v2/public/masters/note-concepts para los conceptos de nota (con el filtro opcional target), GET https://{{instancia}}/service/v2/public/masters/products para los productos e impuestos (con search y estado) y GET https://{{instancia}}/service/v2/public/masters/invoice-statuses para los estados de factura (sin parámetros).
6. Buenas prácticas y límites conocidos
Recomendaciones
- Consulta los catálogos una vez y guárdalos. Cambian poco y son de solo lectura.
- Usa
/invoices/sales/resolutionspara elegir la resolución de una factura de venta; el endpoint anterior puede devolver resoluciones de contrato. - Toma los impuestos de
/masters/productsy envíalos enitems[]; nuby no los adivina. - Recorre hasta
total_pages, no hasta una página vacía. - No envíes
page_sizemayor a 1000: el servidor lo recorta.
Límites
- Los catálogos solo devuelven datos de facturas de venta.
- No exponen datos de contacto de terceros ni información contable.
- Los estados de factura son fijos; los conceptos y productos dependen de la configuración de tu instancia.
Si necesitas crear una factura, revisa la página Crear Factura Borrador.