# Notas débito y crédito [BETA]

<span>Bienvenido al </span>**módulo de notas**<span> de tu inmobiliaria. En esta sección centralizamos las conexiones para que tus plataformas externas ajusten o anulen una factura de venta sin volver a digitar el documento en la interfaz de nuby.</span>

**¿Qué puedes lograr en este módulo?**

**1. Ajustar la cartera:**<span> tu aplicación puede crear una </span>**nota de crédito**<span> para reducir el saldo de una factura o una </span>**nota de débito**<span> para aumentarlo. nuby calcula el IVA, las retenciones y el descuento de cada ítem, recalcula el total y actualiza el saldo de la factura en el acto. Si una nota de crédito deja el saldo en cero, la factura queda anulada por nota crédito.</span>

**2. Consulta sincronizada:**<span> podrás listar las notas de una factura o de un rango de fechas, filtrar por tipo de nota y obtener el detalle con sus ítems, impuestos y el estado y saldo resultantes de la factura.</span>

**3. Control sin sorpresas:**<span> la nota nace en </span>**borrador**<span> y la emisión a la DIAN la realiza un usuario en nuby. La API solo opera con facturas de venta activas: las facturas de contrato se rechazan de forma explícita, y cada operación es idempotente para que un reintento no duplique el documento.</span>

Explora las opciones a continuación para automatizar los ajustes de tu facturación y mantener tu cartera conciliada.

# Listar Notas [BETA]

<p class="callout warning">**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.</p>

Este servicio devuelve una **lista paginada de notas de crédito y débito** registradas en nuby, con sus datos principales. Puedes filtrar por factura, por tipo de nota y por rango de fechas.

Úsalo para conciliar las notas de una factura con tu plataforma, para construir un historial de ajustes o para verificar qué notas se crearon en un período.

<p class="callout info">**¿Para qué sirve este servicio?**  
Úsalo cuando necesites **recorrer las notas registradas en nuby**. Los casos de uso más frecuentes son:</p>

<div class="page-content" dir="auto" id="bkmrk-conciliar-las-notas-"><div dir="auto">- **Conciliar las notas de una factura** con tu plataforma, filtrando por `factura_id`.
- **Construir un historial de ajustes** separando las notas de crédito de las de débito con `tipo`.
- **Cerrar un período contable** revisando las notas creadas entre dos fechas con `desde` y `hasta`.
- **Recorrer resultados largos por páginas** con `page` y `page_size`.
- **Obtener el `nota_id`** para consultar el detalle solo de las notas que vas a mostrar.

---

  </div></div>#### **1. El Endpoint (La dirección web)**

##### **Método y dirección**

<div class="page-content" dir="auto" id="bkmrk-dato-valor-m%C3%A9todo-ge"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23%;"></col><col style="width: 77%;"></col></colgroup><thead><tr><th>Dato</th><th>Valor</th></tr></thead><tbody><tr><td>Método</td><td>`GET`</td></tr><tr><td>Recurso</td><td>`/notes/list`</td></tr><tr><td>Cuerpo de la petición</td><td>No se envía cuerpo</td></tr></tbody></table>

</div></div>```http
GET https://{{instancia}}/service/v2/public/notes/list
```

##### **¿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`.

<p class="callout warning">**Importante:** no incluyas `https://` ni barras diagonales (`/`) al final. La dirección se compone así: `https://` + tu instancia + `/service/v2/public/notes/list`.</p>

##### **Encabezados requeridos**

<div class="page-content" dir="auto" id="bkmrk-encabezado-valor-aut"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>Encabezado</th><th>Valor</th></tr></thead><tbody><tr><td>`Authorization`</td><td>`Bearer <token>`, con el token obtenido en el servicio **Login**.</td></tr></tbody></table>

</div></div>##### **Permiso requerido (scope): `catalogs:read`**

Este servicio es de **solo lectura** y exige el permiso `catalogs:read`. Si falta, la respuesta es `403` con `RECURSO_NO_AUTORIZADO`.

<p class="callout info">Si aún no tienes un token, consulta la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Login</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/login) del libro **API nuby V2**.</p>

<div class="page-content" dir="auto" id="bkmrk-"><div dir="auto">  </div></div>#### **2. La Petición (¿Qué debes enviarnos?)**

Todos son opcionales y se envían en la dirección web. Sin filtros, recibes la primera página con las últimas 50 notas.

<div class="page-content" dir="auto" id="bkmrk-par%C3%A1metro-tipo-reque"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 8%;"></col><col style="width: 9%;"></col><col style="width: 10%;"></col><col style="width: 51%;"></col></colgroup><thead><tr><th>Parámetro</th><th>Tipo</th><th>Requerido</th><th>Default</th><th>Descripción</th></tr></thead><tbody><tr><td>`factura_id`</td><td>entero</td><td>No</td><td>—</td><td>Devuelve solo las notas de esa factura.</td></tr><tr><td>`tipo`</td><td>texto</td><td>No</td><td>—</td><td>`credito` o `debito`.</td></tr><tr><td>`desde`</td><td>fecha</td><td>No</td><td>—</td><td>Fecha inicial en formato `AAAA-MM-DD` (inclusive).</td></tr><tr><td>`hasta`</td><td>fecha</td><td>No</td><td>—</td><td>Fecha final en formato `AAAA-MM-DD` (inclusive).</td></tr><tr><td>`page`</td><td>entero</td><td>No</td><td>`1`</td><td>Número de página, empezando en 1.</td></tr><tr><td>`page_size`</td><td>entero</td><td>No</td><td>`50`</td><td>Registros por página. Máximo `1000`.</td></tr></tbody></table>

</div></div><p class="callout warning">**Importante:** `page`, `page_size` y `factura_id` deben ser enteros positivos; `desde` no puede ser mayor que `hasta`. Un valor inválido produce `400`.</p>

<p class="callout info">**Los filtros se combinan.** Si envías `factura_id` y `tipo`, deben cumplirse ambos.</p>

##### **Ejemplos de dirección**

```http
GET /notes/list?factura_id=456
GET /notes/list?tipo=credito&desde=2026-10-01&hasta=2026-10-31
GET /notes/list?page=2&page_size=20
```

##### **Validaciones de parámetros**

Los parámetros se validan antes de ejecutar la consulta. Si uno no cumple su regla, la respuesta es `400 VALIDATION_ERROR` y no se devuelve ningún dato:

<div class="page-content" dir="auto" id="bkmrk-par%C3%A1metro-regla-mens"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Parámetro</th><th>Regla</th><th>Mensaje `400`</th></tr></thead><tbody><tr><td>`page`</td><td>Entero positivo. `0`, valores negativos o texto no son válidos.</td><td>`El parámetro "page" debe ser un número positivo.`</td></tr><tr><td>`page_size`</td><td>Entero positivo, con `1000` como máximo.</td><td>`El parámetro "page_size" debe ser un número positivo.`</td></tr><tr><td>`factura_id`</td><td>Entero positivo. Si apunta a una factura de contrato, la respuesta es `409 FACTURA_CONTRATO_NO_SOPORTADA`.</td><td>`VALIDATION_ERROR` con el mensaje del parámetro.</td></tr><tr><td>`tipo`</td><td>Solo admite `credito` o `debito`.</td><td>`VALIDATION_ERROR`: el valor no está permitido.</td></tr><tr><td>`desde`, `hasta`</td><td>Fecha en formato `AAAA-MM-DD`.</td><td>`VALIDATION_ERROR` con el mensaje del parámetro.</td></tr><tr><td>Rango de fechas</td><td>`desde` no puede ser mayor que `hasta`.</td><td>`VALIDATION_ERROR` por rango inválido.</td></tr></tbody></table>

</div></div>##### **Cuándo usar cada filtro**

<div class="page-content" dir="auto" id="bkmrk-filtro-caso-de-uso-p"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Filtro</th><th>Caso de uso</th><th>Precisión</th></tr></thead><tbody><tr><td>`factura_id`</td><td>Conciliar todas las notas de una factura concreta.</td><td>Exacta: devuelve solo las notas de esa factura.</td></tr><tr><td>`tipo`</td><td>Separar los ajustes que reducen el saldo de los que lo aumentan.</td><td>Exacta: `credito` o `debito`.</td></tr><tr><td>`desde` / `hasta`</td><td>Cerrar un período contable o revisar los ajustes de un mes.</td><td>Por rango de fechas, con ambos extremos incluidos.</td></tr><tr><td>`factura_id` + `tipo`</td><td>Ver solo las notas de crédito o solo las de débito de una factura.</td><td>Exacta: los filtros se combinan con `AND`.</td></tr><tr><td>`page` / `page_size`</td><td>Recorrer un resultado largo sin traer todo de una vez.</td><td>No filtra: solo controla el tamaño y la posición de la página.</td></tr></tbody></table>

  </div></div>#### **3. La Respuesta (¿Qué te entregaremos?)**

```json
{
  "status": 200,
  "message": null,
  "body": [
    {
      "nota_id": 789,
      "factura_id": 456,
      "tipo": "credito",
      "consecutivo": 1024,
      "fecha": "2026-10-21",
      "valor_total": 59500,
      "estado_id": 5,
      "clasificacion": 1,
      "concepto_id": 6,
      "observaciones": "Ajuste acordado"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 50,
    "total": 1,
    "total_pages": 1
  },
  "filters_applied": {
    "factura_id": 456,
    "tipo": "credito"
  }
}
```

##### **Variantes de respuesta**

El envelope cambia según los filtros enviados y según haya o no coincidencias. Estos son los tres casos que debes manejar en tu integración:

##### **Con filtros**

```json
{
  "status": 200,
  "message": null,
  "body": [
    {
      "nota_id": 789,
      "factura_id": 456,
      "tipo": "credito",
      "consecutivo": 1024,
      "fecha": "2026-10-21",
      "valor_total": 59500,
      "estado_id": 5,
      "clasificacion": 1,
      "concepto_id": 6,
      "observaciones": "Ajuste acordado"
    }
  ],
  "pagination": { "page": 1, "page_size": 50, "total": 1, "total_pages": 1 },
  "filters_applied": { "factura_id": 456, "tipo": "credito" }
}
```

##### **Sin filtros**

Cuando no envías ningún filtro, `filters_applied` es `null` y recibes la primera página de todas las notas de venta, ordenadas por fecha descendente.

```json
{
  "status": 200,
  "message": null,
  "body": [
    {
      "nota_id": 789,
      "factura_id": 456,
      "tipo": "credito",
      "consecutivo": 1024,
      "fecha": "2026-10-21",
      "valor_total": 59500,
      "estado_id": 5,
      "clasificacion": 1,
      "concepto_id": 6,
      "observaciones": "Ajuste acordado"
    },
    {
      "nota_id": 790,
      "factura_id": 457,
      "tipo": "debito",
      "consecutivo": 1025,
      "fecha": "2026-10-20",
      "valor_total": 20000,
      "estado_id": 5,
      "clasificacion": 1,
      "concepto_id": 8,
      "observaciones": "Cargo adicional"
    }
  ],
  "pagination": { "page": 1, "page_size": 50, "total": 2, "total_pages": 1 },
  "filters_applied": null
}
```

##### **Lista vacía**

No es un error: el servicio responde `200`, `body` vacío y un mensaje informativo. En este ejemplo sí se enviaron filtros, por eso `filters_applied` no es `null`.

```json
{
  "status": 200,
  "message": "No se encontraron notas con los filtros aplicados.",
  "body": [],
  "pagination": { "page": 1, "page_size": 50, "total": 0, "total_pages": 0 },
  "filters_applied": {
    "tipo": "debito",
    "desde": "2026-09-01",
    "hasta": "2026-09-30"
  }
}
```

##### **Campos de cada nota**

<div class="page-content" dir="auto" id="bkmrk-campo-descripci%C3%B3n-no"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>Campo</th><th>Descripción</th></tr></thead><tbody><tr><td>`nota_id`</td><td>Identificador interno de la nota. Úsalo para consultar el detalle.</td></tr><tr><td>`factura_id`</td><td>Factura anotada.</td></tr><tr><td>`tipo`</td><td>`credito` o `debito`.</td></tr><tr><td>`consecutivo`</td><td>Número de la nota.</td></tr><tr><td>`fecha`</td><td>Fecha de la nota.</td></tr><tr><td>`valor_total`</td><td>Valor total de la nota.</td></tr><tr><td>`estado_id`</td><td>`5` = borrador.</td></tr><tr><td>`clasificacion`</td><td>`1` = vinculante.</td></tr><tr><td>`concepto_id`</td><td>Concepto aplicado a la nota.</td></tr><tr><td>`observaciones`</td><td>Observaciones registradas.</td></tr></tbody></table>

</div></div>##### **Campos de paginación**

<div class="page-content" dir="auto" id="bkmrk-campo-significado-ca"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Campo</th><th>Significado</th><th>Caso sin resultados</th></tr></thead><tbody><tr><td>`page`</td><td>Número de página devuelta, empezando en `1`.</td><td>Conserva la página solicitada (`1` por defecto).</td></tr><tr><td>`page_size`</td><td>Cantidad de registros por página efectivamente aplicada.</td><td>Conserva el tamaño solicitado (`50` por defecto).</td></tr><tr><td>`total`</td><td>Total de notas que cumplen los filtros.</td><td>`0`.</td></tr><tr><td>`total_pages`</td><td>Total de páginas disponibles para ese resultado.</td><td>`0`. Úsalo como condición de fin al recorrer páginas.</td></tr></tbody></table>

</div></div>##### **`filters_applied` y `message`**

<div class="page-content" dir="auto" id="bkmrk-sin-filtros%2C-filters"><div dir="auto">- Sin filtros, `filters_applied` es `null`.
- Con filtros, es un objeto con los filtros usados.
- Con resultados, `message` es `null`.
- Sin resultados, `message` es `No se encontraron notas con los filtros aplicados.` y `body` es `[]`.

</div></div>##### **Campos del envelope**

<div class="page-content" dir="auto" id="bkmrk-campo-contenido-cu%C3%A1n"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Campo</th><th>Contenido</th><th>Cuándo cambia</th></tr></thead><tbody><tr><td>`status`</td><td>`200` en una lectura exitosa.</td><td>Los errores usan su propio código HTTP y agregan `error_code`.</td></tr><tr><td>`message`</td><td>Texto informativo o `null`.</td><td>Deja de ser `null` cuando la lista viene vacía.</td></tr><tr><td>`body`</td><td>Arreglo de notas con los campos del listado.</td><td>Es `[]` cuando no hay coincidencias.</td></tr><tr><td>`pagination`</td><td>Objeto con `page`, `page_size`, `total` y `total_pages`.</td><td>`total` y `total_pages` pasan a `0` sin resultados.</td></tr><tr><td>`filters_applied`</td><td>Objeto con los filtros usados, o `null`.</td><td>Es `null` solo cuando no enviaste ningún filtro.</td></tr></tbody></table>

</div></div><p class="callout info">**Una lista vacía no es un error.** Se responde `200`; revisa `pagination.total` para confirmar que no hay coincidencias.</p>

##### **Cómo interpretar el resultado**

<div class="page-content" dir="auto" id="bkmrk-se%C3%B1al-en-la-respuest"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 14%;"></col><col style="width: 52%;"></col></colgroup><thead><tr><th>Señal en la respuesta</th><th>Qué significa</th><th>Qué hacer</th></tr></thead><tbody><tr><td>`body` con notas y `message: null`</td><td>La consulta encontró coincidencias.</td><td>Procesa cada nota y, si hay más páginas, avanza con `page`.</td></tr><tr><td>`body: []` y `total: 0`</td><td>Ninguna nota cumple los filtros enviados.</td><td>Revisa los filtros; no lo trates como un fallo técnico.</td></tr><tr><td>`filters_applied: null`</td><td>No se aplicó ningún filtro.</td><td>Confirma que enviaste los parámetros en la dirección web.</td></tr><tr><td>`400 VALIDATION_ERROR`</td><td>Un parámetro no cumple su regla y la consulta no se ejecutó.</td><td>Corrige el parámetro que indica el mensaje y repite.</td></tr><tr><td>`409 FACTURA_CONTRATO_NO_SOPORTADA`</td><td>El `factura_id` pertenece a una factura de contrato.</td><td>Gestiona esas notas desde el flujo de contratos; no reintentes igual.</td></tr></tbody></table>

</div></div><p class="callout warning">**Orden:** las notas se devuelven por fecha descendente y, a igualdad de fecha, por identificador descendente. El orden es estable entre páginas.</p>

<div class="page-content" dir="auto" id="bkmrk--1"><div dir="auto">  </div></div>#### **4. Seguridad y Posibles Errores**

##### **Reglas y exclusiones**

La API solo expone notas de **facturas de venta**:

<div class="page-content" dir="auto" id="bkmrk-el-listado-nunca-inc"><div dir="auto">- El listado **nunca** incluye notas de facturas de contrato, aunque no envíes `factura_id`.
- Si filtras por una factura de contrato, la respuesta es `409 FACTURA_CONTRATO_NO_SOPORTADA` con `warnings[]`.

</div></div>La nota se devuelve con el estado que tiene en nuby. Las notas creadas por la API están en **borrador** hasta que un usuario las emite en la interfaz.

##### **Errores del contrato de la operación**

<div class="page-content" dir="auto" id="bkmrk-http-error_code-cu%C3%A1n"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>HTTP</th><th>`error_code`</th><th>Cuándo ocurre y cómo resolverlo</th></tr></thead><tbody><tr><td>`400`</td><td>`VALIDATION_ERROR`</td><td>Algún parámetro tiene un valor inválido. Revisa `page`, `page_size`, `factura_id`, `tipo`, `desde` o `hasta`.</td></tr><tr><td>`401`</td><td>—</td><td>Falta el token o expiró. Vuelve a consumir **Login**.</td></tr><tr><td>`403`</td><td>`RECURSO_NO_AUTORIZADO`</td><td>Al cliente OAuth le falta `catalogs:read`.</td></tr><tr><td>`409`</td><td>`FACTURA_CONTRATO_NO_SOPORTADA`</td><td>Filtraste por una factura de contrato. Incluye `warnings[]`.</td></tr><tr><td>`500`</td><td>`INTERNAL_ERROR`</td><td>Fallo interno al consultar. Reintenta; si persiste, contacta a soporte.</td></tr></tbody></table>

</div></div>##### **Errores frecuentes al paginar**

<div class="page-content" dir="auto" id="bkmrk-situaci%C3%B3n-qu%C3%A9-ocurre"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 14%;"></col><col style="width: 52%;"></col></colgroup><thead><tr><th>Situación</th><th>Qué ocurre</th><th>Cómo corregir</th></tr></thead><tbody><tr><td>Envías `page=0` o `page=abc`</td><td>`400 VALIDATION_ERROR`; la consulta no se ejecuta.</td><td>Usa un entero desde `1`.</td></tr><tr><td>Envías `page_size=0` o un valor negativo</td><td>`400 VALIDATION_ERROR`.</td><td>Usa un entero positivo, con `1000` como máximo.</td></tr><tr><td>Recorres páginas hasta recibir una vacía</td><td>Puedes hacer una petición de más o detenerte antes de tiempo.</td><td>Recorre hasta `pagination.total_pages`.</td></tr><tr><td>Esperas que una lista vacía traiga `filters_applied: null`</td><td>`filters_applied` refleja los filtros enviados, no el resultado.</td><td>Revisa `body` y `pagination.total` para detectar la lista vacía.</td></tr></tbody></table>

</div></div><p class="callout danger">**Atención:** una factura de contrato no es un error de parámetro: el servicio la rechaza explícitamente para no exponer notas de ese flujo.</p>

<div class="page-content" dir="auto" id="bkmrk--2"><div dir="auto">  ---

</div></div>#### **5. Ejemplos de integración**

<div class="page-content" dir="auto" id="bkmrk-curl-token%3D%22tu_token"><div dir="auto"><details><summary>cURL</summary>

```bash
TOKEN="TU_TOKEN_AQUI"
INSTANCIA="tu-inmobiliaria.nuby.app"

# Notas de una factura
curl -G "https://${INSTANCIA}/service/v2/public/notes/list" \
  --data-urlencode "factura_id=456" \
  -H "Authorization: Bearer ${TOKEN}"

# Notas de crédito de octubre, página 1 de 20
curl -G "https://${INSTANCIA}/service/v2/public/notes/list" \
  --data-urlencode "tipo=credito" \
  --data-urlencode "desde=2026-10-01" \
  --data-urlencode "hasta=2026-10-31" \
  --data-urlencode "page=1" \
  --data-urlencode "page_size=20" \
  -H "Authorization: Bearer ${TOKEN}"
```

</details><details><summary>PHP</summary>

```php
<?php

$instancia = 'tu-inmobiliaria.nuby.app';
$token = 'TU_TOKEN_AQUI';

$query = http_build_query([
    'factura_id' => 456,
    'page' => 1,
    'page_size' => 50,
]);

$url = "https://{$instancia}/service/v2/public/notes/list?{$query}";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer {$token}",
]);

$respuesta = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$datos = json_decode($respuesta, true);

echo "HTTP: {$codigo}\n";
echo "Total: " . ($datos['pagination']['total'] ?? 0) . "\n";

foreach ($datos['body'] as $nota) {
    echo $nota['nota_id'] . ' - ' . $nota['tipo'] . ' - ' . $nota['valor_total'] . "\n";
}
```

</details><details><summary>Python</summary>

```python
import requests

instancia = "tu-inmobiliaria.nuby.app"
token = "TU_TOKEN_AQUI"
url = f"https://{instancia}/service/v2/public/notes/list"

headers = {
    "Authorization": f"Bearer {token}",
}

pagina = 1
total_paginas = 1

while pagina <= total_paginas:
    respuesta = requests.get(
        url,
        headers=headers,
        params={"tipo": "credito", "desde": "2026-10-01", "hasta": "2026-10-31",
                "page": pagina, "page_size": 50},
        timeout=30,
    )

    if respuesta.status_code != 200:
        print(respuesta.status_code, respuesta.json())
        break

    datos = respuesta.json()
    total_paginas = datos["pagination"]["total_pages"]

    for nota in datos["body"]:
        print(nota["nota_id"], nota["tipo"], nota["valor_total"])

    pagina += 1
```

</details><details><summary>JavaScript (Fetch API)</summary>

```javascript
const instancia = 'tu-inmobiliaria.nuby.app';
const token = 'TU_TOKEN_AQUI';

const params = new URLSearchParams({
  factura_id: '456',
  page: '1',
  page_size: '50'
});

const url = `https://${instancia}/service/v2/public/notes/list?${params}`;

const respuesta = await fetch(url, {
  headers: { 'Authorization': `Bearer ${token}` }
});

const data = await respuesta.json();

if (respuesta.ok) {
  console.log(`Página ${data.pagination.page} de ${data.pagination.total_pages}`);
  data.body.forEach(n => console.log(n.nota_id, n.tipo, n.valor_total));
} else {
  console.error(`${respuesta.status} ${data.error_code}: ${data.message}`);
}
```

</details><details><summary>Power Query M (Excel / Power BI)</summary>

Power Query consulta una página por vez. El paso `pagination` te permite parametrizar la página y combinar varias con `Table.Combine` si necesitas el historial completo.

```m
let
    // 1. Configura tus datos de acceso
    instancia = "tu-inmobiliaria.nuby.app",
    token = "TU_TOKEN_AQUI",

    // 2. Prepara la dirección con los filtros y la página
    url = "https://" & instancia & "/service/v2/public/notes/list?factura_id=456&page=1&page_size=1000",

    // 3. Envía la petición GET
    response = Web.Contents(url, [
        Headers = [
            #"Authorization" = "Bearer " & token
        ]
    ]),

    // 4. Decodifica el envelope
    jsonResponse = Json.Document(response),
    body = jsonResponse[body],
    pagination = jsonResponse[pagination],

    // 5. Convierte el listado en tabla
    tabla = Table.FromList(body, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expandido = Table.ExpandRecordColumn(tabla, "Column1",
        {"nota_id", "factura_id", "tipo", "consecutivo", "fecha", "valor_total",
         "estado_id", "clasificacion", "concepto_id", "observaciones"},
        {"NotaID", "FacturaID", "Tipo", "Consecutivo", "Fecha", "ValorTotal",
         "EstadoID", "Clasificacion", "ConceptoID", "Observaciones"}),
    tipos = Table.TransformColumnTypes(expandido, {
        {"NotaID", Int64.Type},
        {"FacturaID", Int64.Type},
        {"Consecutivo", Int64.Type},
        {"Fecha", type date},
        {"ValorTotal", type number}
    }),
    totalPaginas = pagination[total_pages]
in
    tipos
```

</details></div></div>#### **6. Buenas prácticas y límites conocidos**

<div class="page-content" dir="auto" id="bkmrk-filtra-por-factura_i"><div dir="auto">- **Filtra por `factura_id`** cuando busques las notas de un documento concreto.
- **Usa `desde` y `hasta` en lugar de paginar meses completos.**
- **Recorre hasta `total_pages`**, no hasta una página vacía.
- **No uses la lista como detalle:** los ítems y el saldo de la factura están en la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Buscar Nota por Código</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/buscar-nota-por-codigo).
- **Guarda el `nota_id`** para evitar búsquedas repetidas.

</div></div>##### **Límites del listado**

<div class="page-content" dir="auto" id="bkmrk-solo-expone-notas-de"><div dir="auto">- Solo expone notas de **facturas de venta**; las de contrato nunca aparecen.
- No devuelve los ítems ni el saldo de la factura: eso está en el detalle de la nota.
- El estado corresponde al que tiene la nota en nuby; las creadas por la API están en borrador.

</div></div><p class="callout info">Si necesitas crear una nota, revisa la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Crear Nota</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/crear-nota).</p>

# Buscar Nota por Código [BETA]

<p class="callout warning">**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.</p>

Este servicio devuelve el **detalle completo de una nota** de crédito o débito a partir de su identificador interno en nuby (`nota_id`): cabecera, ítems con sus impuestos y retenciones, y el estado y saldo actuales de la factura anotada.

Úsalo después de crear una nota, o cuando ya guardaste su `nota_id` y necesitas verificarla sin recorrer el listado.

<p class="callout info">**¿Para qué sirve este servicio?**  
Úsalo cuando necesites **ver el detalle de una nota** sin recorrer el listado. Los casos de uso más frecuentes son:</p>

<div class="page-content" dir="auto" id="bkmrk-verificar-una-nota-r"><div dir="auto">- **Verificar una nota recién creada.** La creación ya devuelve el `nota_id`; con este servicio confirmas su contenido.
- **Mostrar el detalle del ajuste en tu plataforma**: cabecera e ítems con sus impuestos y retenciones.
- **Auditar los montos que calculó nuby** ítem por ítem: IVA, retenciones y descuento.
- **Confirmar el estado y el saldo de la factura anotada** antes de emitir el borrador en la interfaz de nuby.

---

  </div></div>#### **1. El Endpoint (La dirección web)**

##### **Método y dirección**

<div class="page-content" dir="auto" id="bkmrk-dato-valor-m%C3%A9todo-ge"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23%;"></col><col style="width: 77%;"></col></colgroup><thead><tr><th>Dato</th><th>Valor</th></tr></thead><tbody><tr><td>Método</td><td>`GET`</td></tr><tr><td>Recurso</td><td>`/notes/{nota_id}`</td></tr><tr><td>Cuerpo de la petición</td><td>No se envía cuerpo</td></tr></tbody></table>

</div></div>```http
GET https://{{instancia}}/service/v2/public/notes/{nota_id}
```

##### **¿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`.

<p class="callout warning">**Importante:** no incluyas `https://` ni barras diagonales (`/`) al final. La dirección se compone así: `https://` + tu instancia + `/service/v2/public/notes/` + el identificador de la nota.</p>

##### **Encabezados requeridos**

<div class="page-content" dir="auto" id="bkmrk-encabezado-valor-aut"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>Encabezado</th><th>Valor</th></tr></thead><tbody><tr><td>`Authorization`</td><td>`Bearer <token>`, con el token obtenido en el servicio **Login**.</td></tr></tbody></table>

</div></div>##### **Permiso requerido (scope): `catalogs:read`**

Este servicio es de **solo lectura** y exige el permiso `catalogs:read`. Si falta, la respuesta es `403` con `RECURSO_NO_AUTORIZADO`.

<p class="callout info">Si aún no tienes un token, consulta la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Login</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/login) del libro **API nuby V2**.</p>

<div class="page-content" dir="auto" id="bkmrk-"><div dir="auto">  </div></div>#### **2. La Petición (¿Qué debes enviarnos?)**

Este servicio **no requiere cuerpo**. Solo necesitas el encabezado de autorización y el identificador de la nota en la dirección.

##### **Parámetros de la ruta (path)**

<div class="page-content" dir="auto" id="bkmrk-par%C3%A1metro-tipo-reque"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 8%;"></col><col style="width: 9%;"></col><col style="width: 61%;"></col></colgroup><thead><tr><th>Parámetro</th><th>Tipo</th><th>Requerido</th><th>Descripción</th></tr></thead><tbody><tr><td>`nota_id`</td><td>entero</td><td>Sí</td><td>Identificador interno de la nota. Debe ser un número: la dirección solo acepta dígitos. Va en la dirección, después del segmento `/notes/`.</td></tr></tbody></table>

</div></div>##### **¿De dónde saco el `nota_id`?**

<div class="page-content" dir="auto" id="bkmrk-de-la-creaci%C3%B3n-de-la"><div dir="auto">- **De la creación de la nota** (`POST /notes`): la respuesta lo devuelve en `body.nota_id`.
- **Del listado** (`GET /notes/list`): cada fila del `body` lo trae en `nota_id`.

</div></div>##### **Ejemplos de petición**

```http
GET https://tu-inmobiliaria.nuby.app/service/v2/public/notes/789
```

<div class="page-content" dir="auto" id="bkmrk--1"><div dir="auto">  </div></div>#### **3. La Respuesta (¿Qué te entregaremos?)**

Si la nota existe y pertenece a una factura de venta, el sistema devuelve el envelope y el cuerpo con el detalle de la nota:

```json
{
  "status": 200,
  "message": null,
  "body": {
    "nota_id": 789,
    "factura_id": 456,
    "tipo": "credito",
    "consecutivo": 1024,
    "fecha": "2026-10-21",
    "resolucion_id": 3,
    "valor_total": 59500,
    "estado_id": 5,
    "moneda": "COP",
    "tasa_cambio": 1,
    "observaciones": "Ajuste acordado",
    "clasificacion": 1,
    "tercero_id": 123,
    "concepto_id": 6,
    "items": [
      {
        "producto_id": 159,
        "descripcion": "Ajuste plan X",
        "cantidad": 1,
        "valor_unitario": 50000,
        "por_iva": 19,
        "iva": 9500,
        "por_retencion": 0,
        "retencion": 0,
        "por_reteiva": 0,
        "reteiva": 0,
        "por_reteica": 0,
        "reteica": 0,
        "por_descuento": 0,
        "descuento": 0
      }
    ],
    "factura_estado_id": 1,
    "factura_saldo": 150000
  },
  "pagination": null,
  "filters_applied": null
}
```

<p class="callout info">**Nota sobre el envelope:** es una consulta de un solo registro, así que `pagination` y `filters_applied` siempre vienen en `null`.</p>

##### **Cabecera de la nota**

<div class="page-content" dir="auto" id="bkmrk-campo-tipo-puede-ser"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 8%;"></col><col style="width: 11%;"></col><col style="width: 59%;"></col></colgroup><thead><tr><th>Campo</th><th>Tipo</th><th>Puede ser `null`</th><th>Descripción</th></tr></thead><tbody><tr><td>`nota_id`</td><td>entero</td><td>No</td><td>Identificador interno de la nota.</td></tr><tr><td>`factura_id`</td><td>entero</td><td>No</td><td>Factura anotada.</td></tr><tr><td>`tipo`</td><td>texto</td><td>No</td><td>`credito` o `debito`.</td></tr><tr><td>`consecutivo`</td><td>entero</td><td>No</td><td>Número de la nota.</td></tr><tr><td>`fecha`</td><td>fecha</td><td>No</td><td>Fecha de la nota en formato `AAAA-MM-DD`.</td></tr><tr><td>`resolucion_id`</td><td>entero</td><td>Sí</td><td>Resolución heredada de la factura; puede ser `null` si la factura no tiene resolución.</td></tr><tr><td>`valor_total`</td><td>número</td><td>No</td><td>Valor total de la nota, calculado desde el detalle.</td></tr><tr><td>`estado_id`</td><td>entero</td><td>No</td><td>`5` = borrador.</td></tr><tr><td>`moneda`</td><td>texto</td><td>No</td><td>Moneda heredada de la factura (por ejemplo, `COP`).</td></tr><tr><td>`tasa_cambio`</td><td>número</td><td>No</td><td>Tasa heredada de la factura; es `1` cuando la moneda es la local.</td></tr><tr><td>`observaciones`</td><td>texto</td><td>No</td><td>Observaciones registradas; puede venir vacía si no se enviaron.</td></tr><tr><td>`clasificacion`</td><td>entero</td><td>No</td><td>`1` = vinculante.</td></tr><tr><td>`tercero_id`</td><td>entero</td><td>Sí</td><td>Tercero de la factura; puede ser `null` si la factura no lo tiene.</td></tr><tr><td>`concepto_id`</td><td>entero</td><td>No</td><td>Concepto aplicado a la nota.</td></tr></tbody></table>

</div></div>##### **Campos de cada ítem**

Cada ítem trae la base y los porcentajes junto con los montos ya calculados por nuby:

<div class="page-content" dir="auto" id="bkmrk-campo-tipo-descripci"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 8%;"></col><col style="width: 14%;"></col><col style="width: 56%;"></col></colgroup><thead><tr><th>Campo</th><th>Tipo</th><th>Descripción</th><th>Puede ser `null`</th></tr></thead><tbody><tr><td>`producto_id`</td><td>entero</td><td>Producto o servicio del ítem.</td><td>No</td></tr><tr><td>`descripcion`</td><td>texto</td><td>Descripción del ajuste.</td><td>No (puede venir vacía)</td></tr><tr><td>`cantidad`</td><td>entero</td><td>Cantidad del ítem.</td><td>No</td></tr><tr><td>`valor_unitario`</td><td>número</td><td>Base unitaria enviada por el consumidor.</td><td>No</td></tr><tr><td>`por_iva`</td><td>número</td><td>Porcentaje de IVA aplicado.</td><td>No</td></tr><tr><td>`iva`</td><td>número</td><td>IVA calculado: `base × por_iva / 100`.</td><td>No</td></tr><tr><td>`por_retencion`</td><td>número</td><td>Porcentaje de retención en la fuente.</td><td>No</td></tr><tr><td>`retencion`</td><td>número</td><td>Retención calculada sobre la base.</td><td>No</td></tr><tr><td>`por_reteiva`</td><td>número</td><td>Porcentaje de reteIVA.</td><td>No</td></tr><tr><td>`reteiva`</td><td>número</td><td>ReteIVA calculado sobre el IVA.</td><td>No</td></tr><tr><td>`por_reteica`</td><td>número</td><td>Porcentaje de reteICA.</td><td>No</td></tr><tr><td>`reteica`</td><td>número</td><td>ReteICA calculado sobre la base.</td><td>No</td></tr><tr><td>`por_descuento`</td><td>número</td><td>Porcentaje de descuento.</td><td>No</td></tr><tr><td>`descuento`</td><td>número</td><td>Descuento calculado sobre la base.</td><td>No</td></tr></tbody></table>

</div></div>##### **Cómo se compone el valor total**

<div class="page-content" dir="auto" id="bkmrk-monto-f%C3%B3rmula-se-cal"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Monto</th><th>Fórmula</th><th>Se calcula sobre</th></tr></thead><tbody><tr><td>`iva`</td><td>`base × por_iva / 100`</td><td>La base del ítem (`cantidad × valor_unitario`).</td></tr><tr><td>`retencion`</td><td>`base × por_retencion / 100`</td><td>La base del ítem.</td></tr><tr><td>`reteiva`</td><td>`iva × por_reteiva / 100`</td><td>El IVA calculado.</td></tr><tr><td>`reteica`</td><td>`base × por_reteica / 100`</td><td>La base del ítem.</td></tr><tr><td>`descuento`</td><td>`base × por_descuento / 100`</td><td>La base del ítem.</td></tr></tbody></table>

</div></div>El total del ítem suma la base y el IVA, y resta la retención, el reteIVA, el reteICA y el descuento. El `valor_total` de la nota es la suma de los totales de sus ítems, en la moneda original y con redondeo a dos decimales.

##### **Estado y saldo de la factura**

`factura_estado_id` indica el estado actual de la factura (`1` activa, `6` anulada por nota crédito) y `factura_saldo` su saldo después de las notas.

##### **Variantes de respuesta**

El mismo endpoint devuelve una nota de crédito, una de débito o un rechazo de factura de contrato. Los tres casos cambian el contenido del `body` y, en el último, el estado HTTP:

##### **Nota de crédito**

```json
{
  "status": 200,
  "message": null,
  "body": {
    "nota_id": 789,
    "factura_id": 456,
    "tipo": "credito",
    "consecutivo": 1024,
    "fecha": "2026-10-21",
    "resolucion_id": 3,
    "valor_total": 59500,
    "estado_id": 5,
    "moneda": "COP",
    "tasa_cambio": 1,
    "observaciones": "Ajuste acordado",
    "clasificacion": 1,
    "tercero_id": 123,
    "concepto_id": 6,
    "items": [
      {
        "producto_id": 159,
        "descripcion": "Ajuste plan X",
        "cantidad": 1,
        "valor_unitario": 50000,
        "por_iva": 19,
        "iva": 9500,
        "por_retencion": 0,
        "retencion": 0,
        "por_reteiva": 0,
        "reteiva": 0,
        "por_reteica": 0,
        "reteica": 0,
        "por_descuento": 0,
        "descuento": 0
      }
    ],
    "factura_estado_id": 1,
    "factura_saldo": 150000
  },
  "pagination": null,
  "filters_applied": null
}
```

La nota de crédito reduce el saldo de la factura. Si el saldo llega a cero, `factura_estado_id` pasa a `6`.

##### **Nota de débito**

```json
{
  "status": 200,
  "message": null,
  "body": {
    "nota_id": 790,
    "factura_id": 456,
    "tipo": "debito",
    "consecutivo": 1025,
    "fecha": "2026-10-21",
    "resolucion_id": 3,
    "valor_total": 20000,
    "estado_id": 5,
    "moneda": "COP",
    "tasa_cambio": 1,
    "observaciones": "Cargo adicional acordado",
    "clasificacion": 1,
    "tercero_id": 123,
    "concepto_id": 8,
    "items": [
      {
        "producto_id": 159,
        "descripcion": "Cargo adicional octubre",
        "cantidad": 1,
        "valor_unitario": 20000,
        "por_iva": 0,
        "iva": 0,
        "por_retencion": 0,
        "retencion": 0,
        "por_reteiva": 0,
        "reteiva": 0,
        "por_reteica": 0,
        "reteica": 0,
        "por_descuento": 0,
        "descuento": 0
      }
    ],
    "factura_estado_id": 1,
    "factura_saldo": 170000
  },
  "pagination": null,
  "filters_applied": null
}
```

La nota de débito aumenta el saldo. La factura conserva el estado `1` mientras siga ACTIVA.

##### **Nota de una factura de contrato (409)**

Cuando la nota pertenece a una factura de contrato, nuby no devuelve sus datos: responde `409` con el código estable y la advertencia estructurada. El `scope` siempre es `solo_facturas_de_venta`.

```json
{
  "success": false,
  "status": 409,
  "message": "La factura 456 fue generada desde un contrato de arrendamiento. Esta API solo admite facturas de venta; las facturas de contrato se gestionan en el flujo de contratos de nuby.",
  "error_code": "FACTURA_CONTRATO_NO_SOPORTADA",
  "warnings": [
    {
      "code": "FACTURA_CONTRATO_NO_SOPORTADA",
      "factura_id": 456,
      "scope": "solo_facturas_de_venta"
    }
  ]
}
```

##### **Estados que puedes recibir**

<div class="page-content" dir="auto" id="bkmrk-campo-valor-signific"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Campo</th><th>Valor</th><th>Significado</th></tr></thead><tbody><tr><td>`estado_id`</td><td>`5`</td><td>Borrador. Es el estado de toda nota creada por esta API.</td></tr><tr><td>`factura_estado_id`</td><td>`1`</td><td>Factura ACTIVA; el saldo todavía es mayor que cero.</td></tr><tr><td>`factura_estado_id`</td><td>`6`</td><td>Factura anulada por nota crédito porque el saldo llegó a cero.</td></tr></tbody></table>

</div></div>##### **Campos que no expone el detalle**

El detalle se limita a la nota y al estado y saldo de la factura. Estos datos no forman parte de la respuesta y debes consultarlos en otro servicio:

<div class="page-content" dir="auto" id="bkmrk-dato-por-qu%C3%A9-no-apar"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 14%;"></col><col style="width: 52%;"></col></colgroup><thead><tr><th>Dato</th><th>Por qué no aparece</th><th>Dónde consultarlo</th></tr></thead><tbody><tr><td>Nombre y documento del tercero</td><td>Solo se devuelve `tercero_id`.</td><td>**Buscar Tercero por Código**.</td></tr><tr><td>Número, fecha y valor total de la factura</td><td>La nota solo informa `factura_estado_id` y `factura_saldo`.</td><td>**Ver Detalle de una Factura**.</td></tr><tr><td>Ítems de la factura original</td><td>El arreglo `items[]` corresponde a la nota, no a la factura.</td><td>**Ver Detalle de una Factura**.</td></tr><tr><td>Emisión DIAN de la nota (`cufe`, `dian_id`)</td><td>La API crea y consulta el borrador; la emisión es un paso posterior en nuby.</td><td>Interfaz de nuby.</td></tr><tr><td>Contactos, archivos y documentos del tercero</td><td>No forman parte de esta API.</td><td>Módulo de terceros de nuby.</td></tr></tbody></table>

</div></div><p class="callout warning">**Importante:** los campos sin valor vienen en `null`, no se omiten. Verifica `null` antes de usar un dato en tu interfaz.</p>

<div class="page-content" dir="auto" id="bkmrk--2"><div dir="auto">  </div></div>#### **4. Seguridad y Posibles Errores**

<div class="page-content" dir="auto" id="bkmrk-http-error_code-cu%C3%A1n"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>HTTP</th><th>`error_code`</th><th>Cuándo ocurre y cómo resolverlo</th></tr></thead><tbody><tr><td>`401`</td><td>—</td><td>Falta el token o expiró. Vuelve a consumir **Login**.</td></tr><tr><td>`403`</td><td>`RECURSO_NO_AUTORIZADO`</td><td>Al cliente OAuth le falta `catalogs:read`.</td></tr><tr><td>`404`</td><td>`NOTA_NO_ENCONTRADA`</td><td>No existe una nota con ese identificador. Verifica el `nota_id` o búscala en **Listar Notas**.</td></tr><tr><td>`409`</td><td>`FACTURA_CONTRATO_NO_SOPORTADA`</td><td>La nota pertenece a una factura de contrato; esa información no se expone por esta API. Incluye `warnings[]`.</td></tr><tr><td>`500`</td><td>`INTERNAL_ERROR`</td><td>Fallo interno al consultar. Reintenta; si persiste, contacta a soporte.</td></tr></tbody></table>

</div></div>##### **Ejemplo de nota inexistente (404)**

```json
{
  "success": false,
  "status": 404,
  "message": "No se encontró la nota con el código solicitado.",
  "error_code": "NOTA_NO_ENCONTRADA"
}
```

<p class="callout warning">**Ojo con el identificador:** `nota_id` es el número interno de la nota, no el consecutivo ni el número de la factura. Para buscar las notas de una factura usa `/notes/list?factura_id=...`.</p>

##### **Errores frecuentes y solución**

<div class="page-content" dir="auto" id="bkmrk-c%C3%B3digo-causa-qu%C3%A9-hac"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 14%;"></col><col style="width: 52%;"></col></colgroup><thead><tr><th>Código</th><th>Causa</th><th>Qué hacer</th></tr></thead><tbody><tr><td>`401`</td><td>No enviaste el token o ya expiró.</td><td>Vuelve a consumir **Login** y repite la consulta con el token nuevo.</td></tr><tr><td>`403 RECURSO_NO_AUTORIZADO`</td><td>Al cliente OAuth le falta el permiso `catalogs:read`.</td><td>Solicita el scope a tu administrador en nuby.</td></tr><tr><td>`404 NOTA_NO_ENCONTRADA`</td><td>El `nota_id` no existe en nuby.</td><td>Verifica el identificador o búscalo con **Listar Notas**.</td></tr><tr><td>`409 FACTURA_CONTRATO_NO_SOPORTADA`</td><td>La nota pertenece a una factura de contrato.</td><td>Gestiona el ajuste en el flujo de contratos y revisa `warnings[]`.</td></tr><tr><td>`500 INTERNAL_ERROR`</td><td>Fallo interno al consultar.</td><td>Reintenta; si persiste, contacta a soporte con el `nota_id` consultado.</td></tr></tbody></table>

  ---

</div></div>#### **5. Ejemplos de integración**

<div class="page-content" dir="auto" id="bkmrk-curl-token%3D%22tu_token"><div dir="auto"><details><summary>cURL</summary>

```bash
TOKEN="TU_TOKEN_AQUI"
INSTANCIA="tu-inmobiliaria.nuby.app"
NOTA_ID=789

curl "https://${INSTANCIA}/service/v2/public/notes/${NOTA_ID}" \
  -H "Authorization: Bearer ${TOKEN}"
```

</details><details><summary>PHP</summary>

```php
<?php

$instancia = 'tu-inmobiliaria.nuby.app';
$token = 'TU_TOKEN_AQUI';
$notaId = 789;

$url = "https://{$instancia}/service/v2/public/notes/{$notaId}";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer {$token}",
]);

$respuesta = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$datos = json_decode($respuesta, true);

if ($codigo === 200) {
    $nota = $datos['body'];
    echo "{$nota['tipo']} {$nota['consecutivo']} - total {$nota['valor_total']}\n";
    echo "Factura {$nota['factura_id']}: estado {$nota['factura_estado_id']}, saldo {$nota['factura_saldo']}\n";

    foreach ($nota['items'] as $item) {
        echo "{$item['descripcion']} - {$item['cantidad']} x {$item['valor_unitario']}\n";
    }
} else {
    echo "HTTP {$codigo}: " . $datos['message'] . "\n";
}
```

</details><details><summary>Python</summary>

```python
import requests

instancia = "tu-inmobiliaria.nuby.app"
token = "TU_TOKEN_AQUI"
nota_id = 789
url = f"https://{instancia}/service/v2/public/notes/{nota_id}"

headers = {
    "Authorization": f"Bearer {token}",
}

respuesta = requests.get(url, headers=headers, timeout=30)

if respuesta.status_code == 200:
    nota = respuesta.json()["body"]
    print(f"{nota['tipo']} {nota['consecutivo']} — total {nota['valor_total']}")
    print(f"Factura {nota['factura_id']}: estado {nota['factura_estado_id']}, saldo {nota['factura_saldo']}")

    for item in nota["items"]:
        print(item["descripcion"], item["cantidad"], item["valor_unitario"])
else:
    print(respuesta.status_code, respuesta.json())
```

</details><details><summary>JavaScript (Fetch API)</summary>

```javascript
const instancia = 'tu-inmobiliaria.nuby.app';
const token = 'TU_TOKEN_AQUI';
const notaId = 789;

const url = `https://${instancia}/service/v2/public/notes/${notaId}`;

const respuesta = await fetch(url, {
  headers: { 'Authorization': `Bearer ${token}` }
});

const data = await respuesta.json();

if (respuesta.ok) {
  const nota = data.body;
  console.log(`${nota.tipo} ${nota.consecutivo} — total ${nota.valor_total}`);
  console.log(`Factura ${nota.factura_id}: estado ${nota.factura_estado_id}, saldo ${nota.factura_saldo}`);
  nota.items.forEach(i => console.log(i.descripcion, i.cantidad, i.valor_unitario));
} else {
  console.error(`${respuesta.status} ${data.error_code}: ${data.message}`);
}
```

</details><details><summary>Power Query M (Excel / Power BI)</summary>

El detalle se modela como una tabla de cabecera y otra de ítems. Power Query consulta una vez y expande ambas partes del `body`:

```m
let
    // 1. Configura tus datos de acceso
    instancia = "tu-inmobiliaria.nuby.app",
    token = "TU_TOKEN_AQUI",
    notaId = "789",

    // 2. Prepara la dirección de la petición
    url = "https://" & instancia & "/service/v2/public/notes/" & notaId,

    // 3. Envía la petición GET
    response = Web.Contents(url, [
        Headers = [
            #"Authorization" = "Bearer " & token
        ]
    ]),

    // 4. Decodifica la respuesta JSON
    jsonResponse = Json.Document(response),
    body = jsonResponse[body],

    // 5. Convierte la cabecera en tabla
    cabecera = Table.FromList({body}, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    columnas = Table.ExpandRecordColumn(cabecera, "Column1",
        {"nota_id", "factura_id", "tipo", "consecutivo", "fecha", "valor_total",
         "estado_id", "concepto_id", "factura_estado_id", "factura_saldo"},
        {"NotaID", "FacturaID", "Tipo", "Consecutivo", "Fecha", "ValorTotal",
         "EstadoID", "ConceptoID", "FacturaEstadoID", "FacturaSaldo"}),
    tipos = Table.TransformColumnTypes(columnas, {
        {"NotaID", Int64.Type},
        {"FacturaID", Int64.Type},
        {"Consecutivo", Int64.Type},
        {"Fecha", type date},
        {"ValorTotal", type number},
        {"FacturaSaldo", type number}
    }),

    // 6. Expande los ítems en una tabla aparte
    items = Table.FromList(body[items], Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    itemsExpandidos = Table.ExpandRecordColumn(items, "Column1",
        {"producto_id", "descripcion", "cantidad", "valor_unitario", "por_iva", "iva",
         "por_retencion", "retencion", "por_reteiva", "reteiva",
         "por_reteica", "reteica", "por_descuento", "descuento"},
        {"ProductoID", "Descripcion", "Cantidad", "ValorUnitario", "PorIVA", "IVA",
         "PorRetencion", "Retencion", "PorReteIVA", "ReteIVA",
         "PorReteICA", "ReteICA", "PorDescuento", "Descuento"})
in
    tipos
```

</details></div></div>#### **6. Buenas prácticas y límites conocidos**

##### **Recomendaciones**

<div class="page-content" dir="auto" id="bkmrk-consulta-el-detalle-"><div dir="auto">- **Consulta el detalle solo cuando lo necesites.** La creación ya devuelve el `nota_id` y el saldo resultante.
- **Usa `factura_saldo` y `factura_estado_id`** para reflejar el estado real de la cartera sin consultar la factura.
- **Trata el `404` como un caso de negocio**, no como una falla técnica: el identificador no existe en nuby.
- **Recuerda que los campos ausentes vienen en `null`**; no supongas cadenas vacías.

</div></div>##### **Límites**

<div class="page-content" dir="auto" id="bkmrk-solo-devuelve-notas-"><div dir="auto">- Solo devuelve notas de **facturas de venta**; las de contrato se rechazan con `409`.
- El detalle no incluye la emisión DIAN de la nota: la API solo crea y consulta el borrador.
- No incluye datos del tercero más allá de su identificador: para eso usa los servicios de terceros.

</div></div>El detalle no admite paginación ni filtros: el `nota_id` identifica un único registro. Si necesitas recorrer varias notas, usa **Listar Notas** y consulta el detalle solo de las que vayas a mostrar. Así evitas una petición por cada nota del período.

<p class="callout info">Para crear una nota, revisa la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Crear Nota</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/crear-nota). Para listar notas, revisa [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Listar Notas</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/listar-notas).</p>

# Crear Nota [BETA]

<p class="callout warning">**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.</p>

Este servicio registra una **nota de crédito** o una **nota de débito** sobre una factura de venta de nuby. La nota se guarda en **borrador** y actualiza el saldo de la factura en el acto: una nota de crédito lo reduce y puede anular la factura si llega a cero; una nota de débito lo aumenta.

Es el servicio que necesitas cuando tu plataforma debe ajustar o anular una factura emitida desde nuby, sin volver a digitar el documento en la interfaz.

<p class="callout info">**¿Para qué sirve este servicio?**  
Úsalo cuando necesites **ajustar una factura de venta** desde tu plataforma sin volver a digitar el documento en nuby. Los casos de uso más frecuentes son:</p>

<div class="page-content" dir="auto" id="bkmrk-anular-una-factura-p"><div dir="auto">- **Anular una factura por nota de crédito.** Cuando el ajuste iguala el saldo, la factura queda anulada por nota crédito.
- **Registrar un cargo adicional posterior** con una nota de débito, por ejemplo un cobro que no estaba incluido en la factura original.
- **Replicar en nuby los ajustes que originas en tu plataforma**, con una `referencia_externa` que evita crear la nota dos veces.
- **Dejar la nota lista para emitir.** La API crea el borrador; un usuario lo emite a la DIAN desde nuby.

---

  </div></div>#### **1. El Endpoint (La dirección web)**

##### **Método y dirección**

<div class="page-content" dir="auto" id="bkmrk-dato-valor-m%C3%A9todo-po"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23%;"></col><col style="width: 77%;"></col></colgroup><thead><tr><th>Dato</th><th>Valor</th></tr></thead><tbody><tr><td>Método</td><td>`POST`</td></tr><tr><td>Recurso</td><td>`/notes`</td></tr><tr><td>Cuerpo de la petición</td><td>Sí: el JSON de la nota que se describe en **La Petición**</td></tr></tbody></table>

</div></div>```http
POST https://{{instancia}}/service/v2/public/notes
```

##### **¿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`.

<p class="callout warning">**Importante:** no incluyas `https://` ni barras diagonales (`/`) al final. La dirección se compone así: `https://` + tu instancia + `/service/v2/public/notes`.</p>

##### **Encabezados requeridos**

<div class="page-content" dir="auto" id="bkmrk-encabezado-valor-con"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>Encabezado</th><th>Valor</th></tr></thead><tbody><tr><td>`Content-Type`</td><td>`application/json`</td></tr><tr><td>`Authorization`</td><td>`Bearer <token>`, con el token obtenido en el servicio **Login**.</td></tr></tbody></table>

</div></div>##### **Permiso requerido (scope): `notes:create`**

El cliente OAuth debe tener habilitado `notes:create`. Si el token es válido pero falta el permiso, la respuesta es `403` con el código `RECURSO_NO_AUTORIZADO`.

<p class="callout info">Si aún no tienes un token, consulta la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Login</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/login) del libro **API nuby V2**.</p>

<div class="page-content" dir="auto" id="bkmrk-"><div dir="auto">  </div></div>#### **2. ¿Qué hace este servicio?**

La nota se crea reutilizando la misma lógica de la interfaz de nuby: se asigna su consecutivo, se guarda su detalle y se recalcula el saldo de la factura. La API **no reimplementa** numeración, contabilidad ni DIAN.

##### **Qué SÍ hace**

<div class="page-content" dir="auto" id="bkmrk-crea-la-nota-en-esta"><div dir="auto">- Crea la nota en estado **borrador** y le asigna consecutivo.
- Reduce el saldo de la factura si es nota de crédito, o lo aumenta si es nota de débito.
- Deja la factura en estado **6 (anulada por nota crédito)** cuando el saldo queda en cero.
- Resuelve por ti el concepto y los datos que hereda de la factura.

</div></div>##### **Qué NO hace**

<div class="page-content" dir="auto" id="bkmrk-no-env%C3%ADa-la-nota-a-l"><div dir="auto">- **No envía la nota a la DIAN.** La nota queda en borrador; la emisión la realiza un usuario en nuby.
- **No edita ni anula** notas existentes.
- **No crea notas no vinculantes.** Toda nota creada por esta API queda vinculada a su factura.
- **No anota facturas de contrato** (inquilino o propietario): esas se gestionan en el flujo de contratos.

</div></div><p class="callout warning">**Importante:** la factura debe estar en estado **ACTIVA (facturada)**. Una factura en borrador o anulada no admite notas y la API responde `409 FACTURA_NO_ANOTABLE`.</p>

<div class="page-content" dir="auto" id="bkmrk--1"><div dir="auto">  </div></div>#### **3. La Petición (¿Qué debes enviarnos?)**

##### **Campos obligatorios**

<div class="page-content" dir="auto" id="bkmrk-campo-tipo-requerido"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 8%;"></col><col style="width: 9%;"></col><col style="width: 61%;"></col></colgroup><thead><tr><th>Campo</th><th>Tipo</th><th>Requerido</th><th>Descripción</th></tr></thead><tbody><tr><td>`referencia_externa`</td><td>texto</td><td>Sí</td><td>Identificador único de **tu** operación. Es la llave de idempotencia: un reintento no crea una segunda nota.</td></tr><tr><td>`factura_id`</td><td>entero</td><td>Sí</td><td>Identificador interno de la factura de venta que se va a anotar.</td></tr><tr><td>`tipo`</td><td>texto</td><td>Sí</td><td>`credito` para reducir el saldo o `debito` para aumentarlo.</td></tr><tr><td>`items[]`</td><td>arreglo</td><td>Sí</td><td>Al menos un ítem con `producto_id`, `cantidad` entero mayor que cero y `valor_unitario` distinto de cero.</td></tr></tbody></table>

</div></div>`fecha` es opcional: si no la envías, se usa la fecha de hoy en formato `AAAA-MM-DD`.

##### **Campos opcionales**

<div class="page-content" dir="auto" id="bkmrk-grupo-campos-cabecer"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 78%;"></col></colgroup><thead><tr><th>Grupo</th><th>Campos</th></tr></thead><tbody><tr><td>**Cabecera**</td><td>`concepto_id`, `observaciones`, `por_iva`, `por_retencion`, `fecha_ini`, `fecha_fin`</td></tr><tr><td>**Ítem**</td><td>`descripcion`, `por_iva`, `por_retencion`, `por_reteiva`, `por_reteica`, `por_descuento`</td></tr></tbody></table>

</div></div>##### **Datos que hereda de la factura**

No necesitas enviarlos y, de hecho, no se aceptan si difieren: `resolucion_id`, `tercero_id`, `moneda` y `tasa_cambio` se toman de la factura. Si envías una moneda o una tasa distinta, la respuesta es `422 CAMPO_NO_PERMITIDO`: el saldo se calcula sin conversión y un valor distinto lo descuadraría.

##### **Campos que el consumidor no puede fijar**

La nota hereda de la factura los datos que garantizan la coherencia del saldo y de la numeración. Si envías uno de estos campos con valor, la respuesta es `422 CAMPO_NO_PERMITIDO` y la nota no se crea:

<div class="page-content" dir="auto" id="bkmrk-campo-por-qu%C3%A9-no-lo-"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 14%;"></col><col style="width: 52%;"></col></colgroup><thead><tr><th>Campo</th><th>Por qué no lo puedes fijar</th><th>Respuesta</th></tr></thead><tbody><tr><td>`clasificacion`</td><td>El servidor fija `1` (vinculante). La API no crea notas no vinculantes.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`tercero_id`</td><td>Se hereda de la factura anotada. Un tercero distinto descuadraría la cartera.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`resolucion_id`</td><td>Se hereda de la factura; la nota no tiene resolución propia.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`tipo_factura`</td><td>Lo determina la factura anotada, que debe ser de venta.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`consecutivo`</td><td>Lo asigna nuby al crear la nota.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`estado_id`</td><td>La nota nace en `5` (borrador); la emisión es un paso posterior en nuby.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`dian_id`, `cufe`</td><td>La emisión a la DIAN no forma parte de esta API.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`valor_total`</td><td>Se recalcula desde los ítems y sus porcentajes.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`creadopor`</td><td>Lo registra nuby con el consumidor autenticado.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`nota_id`</td><td>Lo asigna nuby; enviarlo sugiere una edición que la API no soporta.</td><td>`422 CAMPO_NO_PERMITIDO`</td></tr><tr><td>`moneda`, `tasa_cambio`</td><td>Se heredan de la factura. Solo se toleran si coinciden exactamente con ella.</td><td>`422 CAMPO_NO_PERMITIDO` si difieren</td></tr><tr><td>Montos calculados: `iva`, `retencion`, `reteiva`, `reteica`, `descuento`</td><td>El servidor los deriva de `valor_unitario` y de los porcentajes del ítem.</td><td>Se ignoran; el total se recalcula</td></tr></tbody></table>

</div></div>##### **Concepto de la nota**

Si omites `concepto_id`, nuby aplica el concepto por defecto del tipo. El concepto se resuelve contra los `target` y `codigo` de la maestra:

<div class="page-content" dir="auto" id="bkmrk-tipo-de-nota-target-"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 14%;"></col><col style="width: 50%;"></col></colgroup><thead><tr><th>Tipo de nota</th><th>`target`</th><th>`codigo`</th><th>Cuándo aplica</th></tr></thead><tbody><tr><td>**Nota de crédito**</td><td>`1`</td><td>`'2'`</td><td>Se omite `concepto_id` y `tipo` es `credito`. Corresponde a la anulación de la factura electrónica.</td></tr><tr><td>**Nota de débito**</td><td>`2`</td><td>`'4'`</td><td>Se omite `concepto_id` y `tipo` es `debito`. Corresponde al concepto "Otros".</td></tr></tbody></table>

</div></div>Si envías un `concepto_id`, nuby no aplica el default: valida que el concepto exista y que su `target` corresponda al tipo de nota. Si no existe o no corresponde, la respuesta es `422 CONCEPTO_NO_DISPONIBLE` y no se crea la nota.

##### **Montos: tú envías tasas, nuby calcula**

Por cada ítem envía `valor_unitario` y los porcentajes. El servidor calcula el IVA, las retenciones y el descuento, y recalcula el total de la nota a partir del detalle. Si envías montos ya calculados, se ignoran.

<div class="page-content" dir="auto" id="bkmrk-monto-f%C3%B3rmula-%28porce"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>Monto</th><th>Fórmula (porcentaje sobre 100)</th><th>Ejemplo con base `50000`</th></tr></thead><tbody><tr><td>`iva`</td><td>`base × por_iva`</td><td>`50000 × 19 % = 9500`</td></tr><tr><td>`retencion`</td><td>`base × por_retencion`</td><td>`50000 × 0 % = 0`</td></tr><tr><td>`reteiva`</td><td>`iva × por_reteiva`</td><td>`9500 × 0 % = 0`</td></tr><tr><td>`reteica`</td><td>`base × por_reteica`</td><td>`50000 × 0 % = 0`</td></tr><tr><td>`descuento`</td><td>`base × por_descuento`</td><td>`50000 × 0 % = 0`</td></tr></tbody></table>

</div></div>El total del ítem se obtiene al sumar la base y el IVA, y restar la retención, el reteIVA, el reteICA y el descuento. Con el ejemplo anterior, el total de la nota es `59500`. Todos los montos se calculan en la moneda original de la factura y se redondean a dos decimales.

<p class="callout warning">**Importante:** el IVA de un ítem requiere que el producto tenga configurado su tipo de IVA en nuby. Si no lo tiene, la operación falla y no se crea la nota.</p>

##### **Ejemplo: nota de crédito total**

Cuando el total de la nota de crédito iguala el saldo de la factura, el saldo queda en `0` y la factura pasa al estado `6` (anulada por nota crédito). La respuesta lo confirma en `factura_estado_id` y `factura_saldo`.

```json
{
  "referencia_externa": "NOTE-000046",
  "factura_id": 456,
  "tipo": "credito",
  "fecha": "2026-10-21",
  "observaciones": "Anulación total acordada",
  "items": [
    {
      "producto_id": 159,
      "descripcion": "Anulación total factura 456",
      "cantidad": 1,
      "valor_unitario": 100000,
      "por_iva": 19,
      "por_retencion": 0,
      "por_reteiva": 0,
      "por_reteica": 0,
      "por_descuento": 0
    }
  ]
}
```

<p class="callout info">La nota calcula `iva = 100000 × 19 % = 19000` y un total de `119000`. Si el saldo de la factura es exactamente `119000`, la respuesta devuelve `factura_saldo: 0` y `factura_estado_id: 6`.</p>

##### **Ejemplo: nota de débito**

La nota de débito aumenta el saldo. Úsala para cobrar un ajuste posterior, por ejemplo un cargo no incluido en la factura original. La factura debe seguir ACTIVA.

```json
{
  "referencia_externa": "NOTE-000047",
  "factura_id": 456,
  "tipo": "debito",
  "fecha": "2026-10-21",
  "observaciones": "Cargo adicional acordado",
  "items": [
    {
      "producto_id": 159,
      "descripcion": "Cargo adicional octubre",
      "cantidad": 1,
      "valor_unitario": 20000,
      "por_iva": 0,
      "por_retencion": 0,
      "por_reteiva": 0,
      "por_reteica": 0,
      "por_descuento": 0
    }
  ]
}
```

<p class="callout info">Si el saldo era `119000`, después de esta nota de débito será `139000`. La factura conserva el estado `1` (ACTIVA).</p>

##### **Ejemplo completo**

```json
{
  "referencia_externa": "NOTE-000045",
  "factura_id": 456,
  "tipo": "credito",
  "concepto_id": null,
  "fecha": "2026-10-21",
  "observaciones": "Ajuste acordado",
  "items": [
    {
      "producto_id": 159,
      "descripcion": "Ajuste plan X",
      "cantidad": 1,
      "valor_unitario": 50000,
      "por_iva": 19,
      "por_retencion": 0,
      "por_reteiva": 0,
      "por_reteica": 0,
      "por_descuento": 0
    }
  ]
}
```

<div class="page-content" dir="auto" id="bkmrk--2"><div dir="auto">  </div></div>#### **4. Cómo afecta el saldo de la factura**

##### **Escenario → resultado en la factura**

<div class="page-content" dir="auto" id="bkmrk-escenario-resultado-"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25%;"></col><col style="width: 75%;"></col></colgroup><thead><tr><th>Escenario</th><th>Resultado en la factura</th></tr></thead><tbody><tr><td>**Nota de crédito parcial**</td><td>El saldo se reduce en el valor de la nota. La factura sigue ACTIVA (`1`).</td></tr><tr><td>**Nota de crédito total**</td><td>El saldo llega a `0` y la factura queda en estado **6** (anulada por nota crédito).</td></tr><tr><td>**Nota de débito**</td><td>El saldo aumenta en el valor de la nota. La factura sigue ACTIVA (`1`).</td></tr><tr><td>**Factura en borrador**</td><td>`409 FACTURA_NO_ANOTABLE`. No se crea la nota ni se modifica el saldo.</td></tr><tr><td>**Factura de contrato**</td><td>`409 FACTURA_CONTRATO_NO_SOPORTADA` con `warnings[]`. No se crea la nota ni se modifica el saldo.</td></tr><tr><td>**Factura inexistente**</td><td>`404 FACTURA_NO_ENCONTRADA`. Verifica el `factura_id` antes de reintentar.</td></tr></tbody></table>

</div></div><p class="callout info">La nota se crea **en borrador** y aun así afecta el saldo, porque nuby recalcula la cartera al guardarla. La emisión a la DIAN es un paso posterior y no cambia el saldo.</p>

<p class="callout warning">**Ojo con el estado de la factura:** solo se anotan facturas **ACTIVAS**. Si la factura ya fue anulada o está en borrador, la respuesta es `409 FACTURA_NO_ANOTABLE`.</p>

<div class="page-content" dir="auto" id="bkmrk--3"><div dir="auto">  </div></div>#### **5. La Respuesta (¿Qué te entregaremos?)**

Si la nota se crea correctamente, la respuesta es un JSON con el resultado de la operación y el estado en que queda la factura anotada:

```json
{
  "success": true,
  "status": 201,
  "message": "Nota creada en borrador.",
  "body": {
    "nota_id": 789,
    "tipo": "credito",
    "consecutivo": 1024,
    "estado_id": 5,
    "factura_id": 456,
    "factura_estado_id": 1,
    "factura_saldo": 150000
  },
  "idempotent_replay": false
}
```

##### **Estructura de la respuesta**

<div class="page-content" dir="auto" id="bkmrk-campo-tipo-descripci"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 8%;"></col><col style="width: 70%;"></col></colgroup><thead><tr><th>Campo</th><th>Tipo</th><th>Descripción</th></tr></thead><tbody><tr><td>`nota_id`</td><td>entero</td><td>Identificador interno de la nota. Guárdalo para consultarla después.</td></tr><tr><td>`tipo`</td><td>texto</td><td>`credito` o `debito`.</td></tr><tr><td>`consecutivo`</td><td>entero</td><td>Número asignado a la nota.</td></tr><tr><td>`estado_id`</td><td>entero</td><td>`5` = borrador.</td></tr><tr><td>`factura_estado_id`</td><td>entero</td><td>Estado de la factura después de la nota (`1` activa, `6` anulada por nota crédito).</td></tr><tr><td>`factura_saldo`</td><td>número</td><td>Saldo de la factura después de la nota.</td></tr><tr><td>`idempotent_replay`</td><td>booleano</td><td>`false` en la primera respuesta; `true` cuando se recupera una solicitud anterior.</td></tr></tbody></table>

</div></div>##### **Escenario de reintento (replay)**

Si repites la petición con la misma `referencia_externa` y el mismo contenido, nuby no crea una segunda nota: devuelve la respuesta original con `idempotent_replay` en `true`.

```json
{
  "success": true,
  "status": 201,
  "message": "Nota creada en borrador.",
  "body": {
    "nota_id": 789,
    "tipo": "credito",
    "consecutivo": 1024,
    "estado_id": 5,
    "factura_id": 456,
    "factura_estado_id": 1,
    "factura_saldo": 150000
  },
  "idempotent_replay": true
}
```

<p class="callout info">Para ver el detalle completo de la nota, consulta la página [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Buscar Nota por Código</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/buscar-nota-por-codigo).</p>

<div class="page-content" dir="auto" id="bkmrk--4"><div dir="auto">  </div></div>#### **6. Idempotencia y reintentos**

La escritura exige `referencia_externa`. nuby recuerda qué respondió para esa referencia, así que un reintento no duplica la nota.

<div class="page-content" dir="auto" id="bkmrk-situaci%C3%B3n-qu%C3%A9-respon"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 66%;"></col></colgroup><thead><tr><th>Situación</th><th>Qué responde nuby</th></tr></thead><tbody><tr><td>Primera vez que se usa la referencia</td><td>Procesa y responde `201`.</td></tr><tr><td>Misma referencia y mismo contenido</td><td>La respuesta original con `idempotent_replay: true`.</td></tr><tr><td>Misma referencia con otro contenido</td><td>`409 IDEMPOTENCY_PAYLOAD_MISMATCH`.</td></tr><tr><td>Solicitud en proceso</td><td>`409 IDEMPOTENCY_IN_PROGRESS`.</td></tr><tr><td>Solicitud fallida</td><td>Se permite reintentar, incluso con datos corregidos.</td></tr></tbody></table>

</div></div><p class="callout warning">**Importante:** un error `422` de validación **no consume** la referencia. Puedes corregir el payload y reintentar con la misma `referencia_externa`.</p>

<p class="callout info">**Token renovado:** el contenido interno que agrega la autenticación no forma parte de la comparación de idempotencia. Puedes renovar el token y reintentar sin conflicto.</p>

<div class="page-content" dir="auto" id="bkmrk--5"><div dir="auto">  </div></div>#### **7. Seguridad y Posibles Errores**

<div class="page-content" dir="auto" id="bkmrk-http-error_code-cu%C3%A1n"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 14%;"></col><col style="width: 64%;"></col></colgroup><thead><tr><th>HTTP</th><th>`error_code`</th><th>Cuándo ocurre y cómo resolverlo</th></tr></thead><tbody><tr><td>`400`</td><td>`VALIDATION_ERROR`</td><td>El cuerpo no es un objeto JSON. Envía JSON válido con `Content-Type: application/json`.</td></tr><tr><td>`401`</td><td>—</td><td>Falta el token o expiró. Vuelve a consumir **Login**.</td></tr><tr><td>`403`</td><td>`RECURSO_NO_AUTORIZADO`</td><td>Al cliente OAuth le falta `notes:create`.</td></tr><tr><td>`404`</td><td>`FACTURA_NO_ENCONTRADA`</td><td>El `factura_id` no existe en nuby.</td></tr><tr><td>`409`</td><td>`FACTURA_CONTRATO_NO_SOPORTADA`</td><td>La factura es de contrato; se gestiona en el flujo de contratos. Incluye `warnings[]`.</td></tr><tr><td>`409`</td><td>`FACTURA_NO_ANOTABLE`</td><td>La factura no está ACTIVA (borrador, anulada o pagada).</td></tr><tr><td>`409`</td><td>`IDEMPOTENCY_IN_PROGRESS` / `IDEMPOTENCY_PAYLOAD_MISMATCH`</td><td>Reintento con la misma referencia en curso o con contenido distinto.</td></tr><tr><td>`422`</td><td>`VALIDATION_ERROR`</td><td>Falta un obligatorio o un dato no es válido. Revisa `errors` por campo.</td></tr><tr><td>`422`</td><td>`CAMPO_NO_PERMITIDO`</td><td>Intentaste fijar un campo del sistema o una moneda/tasa distinta a la factura.</td></tr><tr><td>`422`</td><td>`FACTURACION_ELECTRONICA_DESHABILITADA`</td><td>La facturación electrónica está apagada en nuby. No se puede crear la nota por API.</td></tr><tr><td>`422`</td><td>`CONCEPTO_NO_DISPONIBLE`</td><td>El `concepto_id` no existe o no corresponde al tipo de nota.</td></tr><tr><td>`422`</td><td>`DATOS_NOTA_INVALIDOS`</td><td>Los datos no pasaron la validación de nuby. Revisa el mensaje.</td></tr><tr><td>`500`</td><td>`INTERNAL_ERROR`</td><td>Fallo interno. La solicitud queda fallida y es reintentable; si persiste, contacta a soporte.</td></tr></tbody></table>

</div></div>##### **Errores de validación frecuentes**

<div class="page-content" dir="auto" id="bkmrk-situaci%C3%B3n-mensaje-%2F-"><div dir="auto"><table style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 34%;"></col><col style="width: 14%;"></col><col style="width: 52%;"></col></colgroup><thead><tr><th>Situación</th><th>Mensaje / `error_code`</th><th>Cómo corregir</th></tr></thead><tbody><tr><td>Falta `referencia_externa` o viene vacía</td><td>`422 VALIDATION_ERROR`</td><td>Envía una referencia única y no vacía. Sin ella no se aplica idempotencia.</td></tr><tr><td>Falta `factura_id`, `tipo` o `items[]`</td><td>`422 VALIDATION_ERROR`</td><td>Incluye la factura, un tipo válido (`credito` o `debito`) y al menos un ítem.</td></tr><tr><td>Un ítem no tiene `producto_id`, o su `cantidad` o `valor_unitario` no es válido</td><td>`422 VALIDATION_ERROR`</td><td>Usa `cantidad` entero mayor que cero y `valor_unitario` distinto de cero.</td></tr><tr><td>La factura no existe</td><td>`404 FACTURA_NO_ENCONTRADA`</td><td>Verifica el `factura_id` con el detalle o el listado de facturas de venta.</td></tr><tr><td>La factura es de contrato</td><td>`409 FACTURA_CONTRATO_NO_SOPORTADA`</td><td>Anula o ajusta desde el flujo de contratos. Revisa `warnings[].code`, `factura_id` y `scope`.</td></tr><tr><td>La factura no está ACTIVA</td><td>`409 FACTURA_NO_ANOTABLE`</td><td>Solo se anotan facturas en estado `1`. Emite o descarta el borrador antes de anotar.</td></tr><tr><td>La facturación electrónica está apagada</td><td>`422 FACTURACION_ELECTRONICA_DESHABILITADA`</td><td>Pide a tu administrador que habilite `ENABLE_FACT_ELECT = 1` en nuby.</td></tr><tr><td>El concepto no existe o no corresponde al tipo</td><td>`422 CONCEPTO_NO_DISPONIBLE`</td><td>Omite `concepto_id` para usar el default, o envía uno del `target` correcto.</td></tr><tr><td>Enviaste un campo reservado al sistema</td><td>`422 CAMPO_NO_PERMITIDO`</td><td>Quita `clasificacion`, `tercero_id`, `resolucion_id`, `consecutivo`, `estado_id`, `valor_total`, `nota_id` y los demás de la lista.</td></tr><tr><td>La moneda o la tasa no coincide con la factura</td><td>`422 CAMPO_NO_PERMITIDO`</td><td>Omite `moneda` y `tasa_cambio`, o envía exactamente los de la factura.</td></tr><tr><td>El producto no tiene configurado su tipo de IVA o los porcentajes no son válidos</td><td>`422 DATOS_NOTA_INVALIDOS`</td><td>Revisa la configuración del producto en nuby y envía porcentajes válidos por ítem.</td></tr></tbody></table>

</div></div><p class="callout danger">**Atención:** cuando la respuesta es `<span style="color: rgb(52, 73, 94);">409</span>` o `<span style="color: rgb(52, 73, 94);">422</span>`, nuby no deja una nota a medias. La operación se revierte completa.</p>

<div class="page-content" dir="auto" id="bkmrk--6"><div dir="auto">  ---

</div></div>#### **8. Ejemplos de integración**

<div class="page-content" dir="auto" id="bkmrk-curl-token%3D%22tu_token"><div dir="auto"><details><summary>cURL</summary>

```bash
TOKEN="TU_TOKEN_AQUI"
INSTANCIA="tu-inmobiliaria.nuby.app"

curl -X POST "https://${INSTANCIA}/service/v2/public/notes" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -d '{
    "referencia_externa": "NOTE-000045",
    "factura_id": 456,
    "tipo": "credito",
    "fecha": "2026-10-21",
    "observaciones": "Ajuste acordado",
    "items": [
      {
        "producto_id": 159,
        "descripcion": "Ajuste plan X",
        "cantidad": 1,
        "valor_unitario": 50000,
        "por_iva": 19,
        "por_retencion": 0,
        "por_reteiva": 0,
        "por_reteica": 0,
        "por_descuento": 0
      }
    ]
  }'
```

</details><details><summary>PHP</summary>

```php
<?php

$instancia = 'tu-inmobiliaria.nuby.app';
$token = 'TU_TOKEN_AQUI';
$url = "https://{$instancia}/service/v2/public/notes";

$payload = [
    'referencia_externa' => 'NOTE-000045',
    'factura_id' => 456,
    'tipo' => 'credito',
    'fecha' => '2026-10-21',
    'observaciones' => 'Ajuste acordado',
    'items' => [
        [
            'producto_id' => 159,
            'descripcion' => 'Ajuste plan X',
            'cantidad' => 1,
            'valor_unitario' => 50000,
            'por_iva' => 19,
            'por_retencion' => 0,
            'por_reteiva' => 0,
            'por_reteica' => 0,
            'por_descuento' => 0,
        ],
    ],
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    "Authorization: Bearer {$token}",
]);

$respuesta = curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP: {$codigo}\n";
print_r(json_decode($respuesta, true));
```

</details><details><summary>Python</summary>

```python
import requests

instancia = "tu-inmobiliaria.nuby.app"
token = "TU_TOKEN_AQUI"
url = f"https://{instancia}/service/v2/public/notes"

payload = {
    "referencia_externa": "NOTE-000045",
    "factura_id": 456,
    "tipo": "credito",
    "fecha": "2026-10-21",
    "observaciones": "Ajuste acordado",
    "items": [
        {
            "producto_id": 159,
            "descripcion": "Ajuste plan X",
            "cantidad": 1,
            "valor_unitario": 50000,
            "por_iva": 19,
            "por_retencion": 0,
            "por_reteiva": 0,
            "por_reteica": 0,
            "por_descuento": 0,
        }
    ],
}

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {token}",
}

respuesta = requests.post(url, json=payload, headers=headers, timeout=30)

if respuesta.status_code == 201:
    datos = respuesta.json()
    print(f"Nota {datos['body']['nota_id']} — saldo factura: {datos['body']['factura_saldo']}")
else:
    print(respuesta.status_code, respuesta.json())
```

</details><details><summary>JavaScript (Fetch API)</summary>

```javascript
const instancia = 'tu-inmobiliaria.nuby.app';
const token = 'TU_TOKEN_AQUI';
const url = `https://${instancia}/service/v2/public/notes`;

const payload = {
  referencia_externa: 'NOTE-000045',
  factura_id: 456,
  tipo: 'credito',
  fecha: '2026-10-21',
  items: [{
    producto_id: 159,
    descripcion: 'Ajuste plan X',
    cantidad: 1,
    valor_unitario: 50000,
    por_iva: 19,
    por_retencion: 0,
    por_reteiva: 0,
    por_reteica: 0,
    por_descuento: 0
  }]
};

const respuesta = await fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${token}`
  },
  body: JSON.stringify(payload)
});

const data = await respuesta.json();

if (respuesta.ok) {
  console.log(`Nota ${data.body.nota_id} — saldo factura: ${data.body.factura_saldo}`);
} else {
  console.error(`${respuesta.status} ${data.error_code}: ${data.message}`, data.errors ?? {});
}
```

</details><details><summary>Power Query M (Excel / Power BI)</summary>

Power Query construye el cuerpo con `Json.FromValue` y lo envía con `Web.Contents`. La respuesta se convierte en una tabla de una fila. Si la petición falla, `Web.Contents` genera una excepción que puedes capturar con `try ... otherwise`.

```m
let
    // 1. Configura tus datos de acceso
    instancia = "tu-inmobiliaria.nuby.app",
    token = "TU_TOKEN_AQUI",

    // 2. Prepara la dirección de la petición
    url = "https://" & instancia & "/service/v2/public/notes",

    // 3. Construye el cuerpo JSON
    cuerpo = Json.FromValue([
        referencia_externa = "NOTE-000045",
        factura_id = 456,
        tipo = "credito",
        fecha = "2026-10-21",
        observaciones = "Ajuste acordado",
        items = {
            [
                producto_id = 159,
                descripcion = "Ajuste plan X",
                cantidad = 1,
                valor_unitario = 50000,
                por_iva = 19,
                por_retencion = 0,
                por_reteiva = 0,
                por_reteica = 0,
                por_descuento = 0
            ]
        }
    ]),

    // 4. Envía la petición POST
    response = Web.Contents(url, [
        Headers = [
            #"Content-Type" = "application/json",
            #"Authorization" = "Bearer " & token
        ],
        Content = cuerpo
    ]),

    // 5. Decodifica la respuesta JSON
    jsonResponse = Json.Document(response),
    body = jsonResponse[body],

    // 6. Convierte el resultado en tabla
    tabla = Table.FromList({body}, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expandido = Table.ExpandRecordColumn(tabla, "Column1",
        {"nota_id", "tipo", "consecutivo", "estado_id", "factura_id", "factura_estado_id", "factura_saldo"},
        {"NotaID", "Tipo", "Consecutivo", "EstadoID", "FacturaID", "FacturaEstadoID", "FacturaSaldo"})
in
    expandido
```

</details></div></div>#### **9. Buenas prácticas y límites conocidos**

##### **Recomendaciones**

<div class="page-content" dir="auto" id="bkmrk-verifica-el-estado-d"><div dir="auto">- **Verifica el estado de la factura antes de anotar:** debe estar ACTIVA.
- **Usa una referencia externa por operación** y no la reutilices para otra nota.
- **Guarda el `nota_id` y el `factura_saldo`** de la respuesta: te ahorran una consulta posterior.
- **Envía los porcentajes por ítem**: los de cabecera no se usan.
- **Trata el `422` como reintentable tras corregir**: la referencia no se consume.

</div></div>##### **Límites**

<div class="page-content" dir="auto" id="bkmrk-la-nota-queda-en-bor"><div dir="auto">- La nota queda en borrador: la emisión a la DIAN no forma parte de esta API.
- No hay edición ni anulación de notas por API.
- No se admiten notas no vinculantes.
- `fecha_ini` y `fecha_fin` se aceptan pero no se persisten en la nota.
- No se valida el rango 0–100 de los porcentajes; envía valores válidos.

</div></div><p class="callout info">Para consultar notas ya registradas, revisa las páginas [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Listar Notas</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/listar-notas) y [*<span class="tw-font-bold tw-text-arrBlue hover:tw-text-nubyBlue tw-cursor-pointer">Buscar Nota por Código</span>*](https://docs.nuby.ai/books/api-nuby-v2/page/buscar-nota-por-codigo).</p>