# Contratos

<span>Bienvenido al </span>**archivador legal digital**<span> de tu inmobiliaria. En esta sección agrupamos todas las opciones de conexión para que tus plataformas externas interactúen con los </span>**acuerdos de arrendamiento**.

**¿Cuál es el propósito de este módulo?**<span> Facilitar la sincronización de tu ecosistema. Aquí encontrarás las herramientas necesarias para que tus sistemas consulten y extraigan automáticamente los detalles de cualquier contrato, eliminando por completo la necesidad de realizar búsquedas manuales o revisar documentos físicos en nuestra plataforma.</span>

Explora el listado de abajo para descubrir cómo consultar términos, información financiera, estados y fechas de tus inmuebles alquilados.

# Listar Contratos

Permite obtener la lista paginada de todos los contratos de arrendamiento registrados en el sistema. Cada elemento incluye la información completa del contrato: propiedad asociada, propietarios, inquilinos, valores económicos, fechas, estado y observaciones.

<p class="callout info">**¿Para qué sirve este servicio?**  
Úsalo para sincronizar el inventario de contratos con tu sistema externo, generar reportes de gestión inmobiliaria, alimentar dashboards con información contractual o consultar el estado general de los contratos administrados.</p>

---

#### **1. El Endpoint (La dirección web)**

Apunta tu sistema a la siguiente dirección. Recuerda reemplazar <span style="color: rgb(241, 196, 15);">**{{instancia}}**</span> por la dirección web completa que utilizas para ingresar a tu plataforma.

```http
GET https://{{instancia}}/service/v2/public/contracts/list
```

<p class="callout info">**¿Qué debes colocar en {{instancia}}?**  
Es muy sencillo: corresponde a la **dirección web principal** que utilizas a diario para ingresar a tu plataforma (incluyendo la terminación `.nuby.app` o `.arrendasoft.co`).  
Por ejemplo, si para entrar a tu sistema escribes `inmobiliaria.nuby.app` o `inmobiliaria.arrendasoft.co` en tu navegador, esa será exactamente tu instancia. Solo asegúrate de no incluir el "https://" ni barras diagonales ("/") al final.</p>

#### **2. La Petición (¿Qué debes enviarnos?)**

Este servicio no requiere cuerpo en la petición. Envía los encabezados requeridos y opcionalmente los parámetros de paginación en la URL:

<table border="1" id="bkmrk-m%C3%A9todo-get-content-t" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20%;"></col><col style="width: 80%;"></col></colgroup><tbody><tr><td>**Método**</td><td>GET</td></tr><tr><td>**Content-Type**</td><td>application/json</td></tr><tr><td>**Authorization**</td><td>**Bearer token**, Token obtenido al consumir el servicio [Login](https://docs.nuby.ai/books/api-nuby-v2/page/login "Login")</td></tr></tbody></table>

<p class="callout warning">**¿Aún no tienes tu Token de acceso?**  
Para consumir este servicio necesitas un Token vigente. Consulta el servicio de [Login](https://docs.nuby.ai/books/api-nuby-v2/page/login) para aprender cómo obtenerlo.</p>

**Parámetros de consulta (query string):**

<table border="1" id="bkmrk-par%C3%A1metro-tipo-reque" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 10%;"></col><col style="width: 10%;"></col><col style="width: 12%;"></col><col style="width: 46%;"></col></colgroup><thead><tr><th>Parámetro</th><th>Tipo</th><th>Requerido</th><th>Por defecto</th><th>Descripción</th></tr></thead><tbody><tr><td>`page`</td><td>integer</td><td>No</td><td>1</td><td>Número de página de resultados que se desea recuperar. Debe ser un número positivo.</td></tr><tr><td>`page_size`</td><td>integer</td><td>No</td><td>10</td><td>Número máximo de contratos por página. Debe ser un número positivo. Valor máximo permitido: **1000**.</td></tr></tbody></table>

**Ejemplos de petición:**

```http
GET https://mi-inmobiliaria.nuby.app/service/v2/public/contracts/list
GET https://mi-inmobiliaria.nuby.app/service/v2/public/contracts/list?page=2
GET https://mi-inmobiliaria.nuby.app/service/v2/public/contracts/list?page_size=50
GET https://mi-inmobiliaria.nuby.app/service/v2/public/contracts/list?page=3&page_size=100
```

#### **3. La Respuesta (¿Qué te entregaremos?)**

Si tu Token es válido, el sistema te devolverá un objeto JSON con el estado de la respuesta, el listado de contratos y la información de paginación. La respuesta se verá similar a esta:

```json
{
    "status": 200,
    "message": null,
    "body": [
        {
            "contrato_id": 45,
            "consecutivo": "45",
            "propiedad_id": 312,
            "propiedad": "312 - Cll 78 # 12 - 34 Apto 501",
            "estrato_propiedad": "Cuatro",
            "propietario": "[1] 52498731 - ANDREA MILENA CASTRO ROJAS",
            "propietarios_id": "18",
            "inquilino": "[1] 80213654 - CARLOS EDUARDO PINEDA VARGAS",
            "inquilinos_id": "42",
            "valor_canon_individual": "2850000",
            "canon_total": 2850000,
            "porcentaje_comision": "10.00 %",
            "periodicidad": "Mensual",
            "escenario": "CANON DE ARRENDAMIENTO SIN IVA, CON RETEFUENTE 3.5%, SIN RETEIVA, SIN RETEICA",
            "estado": "Activo",
            "estado_id": 1,
            "fecha_inicio": "2024-06-01",
            "fecha_fin": "2025-05-31",
            "fecha_terminacion": null,
            "observaciones": "Contrato renovado automáticamente por un año adicional.",
            "uso": "Vivienda",
            "fecha_creacion": "2024-05-28 14:32:10",
            "creado_por": "Sandra"
        },
        {
            "contrato_id": 46,
            "consecutivo": "46",
            "propiedad_id": 589,
            "propiedad": "589 - Cra 45 # 90 - 12 Local 3",
            "estrato_propiedad": "Comercial",
            "propietario": "[1] 900456789 - INVERSIONES HORIZONTE S.A.S.",
            "propietarios_id": "65",
            "inquilino": "[1] 1098745231 - JULIANA PATRICIA RÍOS MENDOZA",
            "inquilinos_id": "73",
            "valor_canon_individual": "4500000",
            "canon_total": 4500000,
            "porcentaje_comision": "8.50 %",
            "periodicidad": "Mensual",
            "escenario": "CANON DE ARRENDAMIENTO CON IVA 19%, CON RETEFUENTE 3.5%, CON RETEIVA 15%, SIN RETEICA",
            "estado": "Activo",
            "estado_id": 1,
            "fecha_inicio": "2023-09-01",
            "fecha_fin": "2025-08-31",
            "fecha_terminacion": null,
            "observaciones": null,
            "uso": "Comercial",
            "fecha_creacion": "2023-08-25 09:15:44",
            "creado_por": "Administrador"
        },
        {
            "contrato_id": 12,
            "consecutivo": "12",
            "propiedad_id": 104,
            "propiedad": "104 - Cll 23 # 56 - 78",
            "estrato_propiedad": "Tres",
            "propietario": "[1] 71654321 - HÉCTOR FABIO MORENO AGUILAR",
            "propietarios_id": "31",
            "inquilino": "[1] 43876123 - DIANA MARCELA OSPINA VELÁSQUEZ",
            "inquilinos_id": "55",
            "valor_canon_individual": "1200000",
            "canon_total": 1200000,
            "porcentaje_comision": "12.00 %",
            "periodicidad": "Mensual",
            "escenario": "CANON DE ARRENDAMIENTO SIN IVA, SIN RETEFUENTE, SIN RETEIVA, SIN RETEICA",
            "estado": "Terminado",
            "estado_id": 2,
            "fecha_inicio": "2022-01-15",
            "fecha_fin": "2023-01-14",
            "fecha_terminacion": "2022-11-30",
            "observaciones": "Terminación anticipada por mutuo acuerdo.",
            "uso": "Vivienda",
            "fecha_creacion": "2021-12-20 16:40:33",
            "creado_por": "Sandra"
        }
    ],
    "pagination": {
        "total_records": 347,
        "total_pages": 116,
        "current_page": 1,
        "page_size": 3,
        "current_page_records": 3,
        "has_next_page": true,
        "has_previous_page": false
    }
}
```

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

<table border="1" id="bkmrk-campo-tipo-descripci" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22%;"></col><col style="width: 12%;"></col><col style="width: 66%;"></col></colgroup><thead><tr><th>Campo</th><th>Tipo</th><th>Descripción</th></tr></thead><tbody><tr><td>`status`</td><td>integer</td><td>Código del estado de la respuesta. `200` para exitoso, `400` para error de validación, `500` para error interno.</td></tr><tr><td>`message`</td><td>string | null</td><td>Cuando la respuesta es exitosa vale `null`. Si no se encuentran contratos, contiene el mensaje informativo. Si hay error, describe el problema.</td></tr><tr><td>`body`</td><td>array</td><td>Listado de contratos devueltos. Arreglo vacío `[]` si no hay resultados.</td></tr><tr><td>`pagination`</td><td>object</td><td>Información de paginación para recorrer los resultados.</td></tr></tbody></table>

##### **Campos de cada contrato (body)**

Cada elemento dentro del arreglo `body` contiene las siguientes claves:

<table border="1" id="bkmrk-campo-tipo-descripci-1" 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>Tipo</th><th>Descripción</th></tr></thead><tbody><tr><td>`contrato_id`</td><td>integer</td><td>Identificador único del contrato en el sistema.</td></tr><tr><td>`consecutivo`</td><td>string</td><td>Número consecutivo interno del contrato.</td></tr><tr><td>`propiedad_id`</td><td>integer</td><td>Identificador de la propiedad asociada al contrato.</td></tr><tr><td>`propiedad`</td><td>string</td><td>Etiqueta descriptiva de la propiedad en formato `{id} - {dirección}`.</td></tr><tr><td>`estrato_propiedad`</td><td>string</td><td>Estrato socioeconómico asignado a la propiedad (ej. "Tres", "Cuatro", "Comercial").</td></tr><tr><td>`propietario`</td><td>string</td><td>Cadena con el consecutivo del detalle, documento y nombre completo del propietario o propietarios. Formato: `[n] documento - NOMBRE`. Si hay varios, van separados por coma.</td></tr><tr><td>`propietarios_id`</td><td>string</td><td>ID(s) del/los propietario(s) en el sistema. Si hay varios, van separados por coma.</td></tr><tr><td>`inquilino`</td><td>string</td><td>Cadena con el consecutivo del detalle, documento y nombre completo del inquilino o inquilinos. Mismo formato que `propietario`.</td></tr><tr><td>`inquilinos_id`</td><td>string</td><td>ID(s) del/los inquilino(s) en el sistema. Si hay varios, van separados por coma.</td></tr><tr><td>`valor_canon_individual`</td><td>string</td><td>Valor del canon por cada detalle del contrato. Si hay varios detalles, los valores van separados por coma.</td></tr><tr><td>`canon_total`</td><td>number | null</td><td>Valor total del canon de arrendamiento del contrato.</td></tr><tr><td>`porcentaje_comision`</td><td>string</td><td>Porcentaje de comisión de la inmobiliaria con formato `"X.XX %"`.</td></tr><tr><td>`periodicidad`</td><td>string</td><td>Frecuencia de pago del canon (ej. "Mensual").</td></tr><tr><td>`escenario`</td><td>string</td><td>Descripción del escenario tributario y de facturación aplicado al contrato.</td></tr><tr><td>`estado`</td><td>string</td><td>Estado textual del contrato. Valores posibles: `Activo`, `Terminado`, `En Construcción`.</td></tr><tr><td>`estado_id`</td><td>integer</td><td>Identificador numérico del estado: `1` = Activo, `2` = Terminado, `3` = En Construcción.</td></tr><tr><td>`fecha_inicio`</td><td>string</td><td>Fecha de inicio del contrato en formato `YYYY-MM-DD`.</td></tr><tr><td>`fecha_fin`</td><td>string</td><td>Fecha de finalización prevista del contrato en formato `YYYY-MM-DD`.</td></tr><tr><td>`fecha_terminacion`</td><td>string | null</td><td>Fecha de terminación efectiva del contrato (si aplica). `null` si el contrato sigue vigente.</td></tr><tr><td>`observaciones`</td><td>string | null</td><td>Observaciones o notas asociadas al contrato. `null` si no hay.</td></tr><tr><td>`uso`</td><td>string</td><td>Tipo de uso de la propiedad: `Vivienda` o `Comercial`.</td></tr><tr><td>`fecha_creacion`</td><td>string</td><td>Fecha y hora de creación del registro en formato `YYYY-MM-DD HH:MM:SS`.</td></tr><tr><td>`creado_por`</td><td>string</td><td>Nombre del usuario que creó el contrato en el sistema.</td></tr></tbody></table>

##### **Campos de paginación (pagination)**

El objeto `pagination` contiene las siguientes claves:

<table border="1" id="bkmrk-campo-tipo-descripci-2" 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>Tipo</th><th>Descripción</th></tr></thead><tbody><tr><td>`total_records`</td><td>integer</td><td>Total de contratos registrados en el sistema.</td></tr><tr><td>`total_pages`</td><td>integer</td><td>Cantidad total de páginas disponibles (total de registros dividido por el tamaño de página).</td></tr><tr><td>`current_page`</td><td>integer</td><td>Número de la página actual consultada.</td></tr><tr><td>`page_size`</td><td>integer</td><td>Tamaño de la página (cantidad de registros solicitados por consulta).</td></tr><tr><td>`current_page_records`</td><td>integer</td><td>Cantidad de registros efectivamente devueltos en la página actual.</td></tr><tr><td>`has_next_page`</td><td>boolean</td><td>`true` si existen páginas posteriores que se pueden consultar.</td></tr><tr><td>`has_previous_page`</td><td>boolean</td><td>`true` si existen páginas anteriores que se pueden consultar.</td></tr></tbody></table>

<p class="callout info">**Nota sobre contratos sin resultados**  
Si no existen contratos registrados en el sistema, la respuesta tendrá HTTP `200` con `body` vacío (`[]`), un `message` informativo y los valores de paginación en cero.</p>

---

#### **4. Seguridad y Posibles Errores**

<p class="callout danger">**¡Tu pase tiene fecha de caducidad!**</p>

Por medidas de seguridad, el `token` que te entregamos **solo dura 1 hora**. Una vez transcurrido ese tiempo, el pase expirará y el sistema te bloqueará el acceso devolviéndote un error `401`. Cuando esto ocurra, tu sistema simplemente debe volver a consumir el servicio de [Login](https://docs.nuby.ai/books/api-nuby-v2/page/login) para pedir un pase nuevo y continuar trabajando.

Así se ve el error que te devolverá el sistema cuando tu token se haya vencido o intentes usar un pase inválido:

```json
{
    "statusCode": 401,
    "error": {
        "type": "SERVER_ERROR",
        "description": "JWT Token expired."
    }
}
```

Si alguno de los parámetros de paginación no cumple con las validaciones, el sistema devolverá un error `400` con la estructura estándar. Por ejemplo:

```json
{
    "status": 400,
    "message": "El parámetro \"page_size\" debe ser un número positivo.",
    "body": null,
    "pagination": null
}
```

Posibles errores:

<table border="1" id="bkmrk-c%C3%B3digo-http-descripc" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12%;"></col><col style="width: 88%;"></col></colgroup><thead><tr><th>Código HTTP</th><th>Descripción</th></tr></thead><tbody><tr><td>**400**</td><td>**Token faltante o inválido.** Posibles causas:  
— No se envió el encabezado `Authorization`. Mensaje: `"JWT Token required."`  
— El encabezado no tiene el formato `Bearer {token}`. Mensaje: `"JWT Token not send."`  
— El token no fue encontrado en el sistema. Mensaje: `"JWT Token not found."`  
  
**Parámetros inválidos.** Posibles causas:  
— `page` no es numérico o no es positivo.  
— `page_size` no es numérico o no es positivo.</td></tr><tr><td>**401**</td><td>El token ha expirado. Debes generar uno nuevo consumiendo el servicio de **Login**. Mensaje: `"JWT Token expired."`</td></tr><tr><td>**403**</td><td>El cliente OAuth no tiene el scope necesario para esta operación. Para endpoints GET se requiere el scope `read`. Mensaje: `"Insufficient scope. Required: 'read', granted: '{scope_actual}'."`</td></tr><tr><td>**500**</td><td>Error interno del servidor al procesar la consulta de contratos.</td></tr></tbody></table>

---

#### **5. Ejemplos de integración**

Aquí tienes ejemplos de código listos para que tus desarrolladores los adapten a tu plataforma:

<details id="bkmrk-curl-%23-listar-contra"><summary>cURL</summary>

```bash
# Listar contratos con paginación por defecto
curl -X GET "https://{{instancia}}/service/v2/public/contracts/list" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN_AQUI"

# Listar contratos página 3 con 100 registros por página
curl -X GET "https://{{instancia}}/service/v2/public/contracts/list?page=3&page_size=100" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_TOKEN_AQUI"
```

</details><details id="bkmrk-php-%3C%3Fphp-%24instance-"><summary>PHP</summary>

```php
<?php

$instance = 'tu_instancia'; // Reemplaza con tu instancia real
$token = 'TU_TOKEN_AQUI'; // Token obtenido del servicio Login

// Parámetros de paginación
$page = 1;
$page_size = 50;

$queryParams = http_build_query([
    'page' => $page,
    'page_size' => $page_size
]);

$url = "https://{$instance}/service/v2/public/contracts/list?{$queryParams}";

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

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if (curl_errno($ch)) {
    echo 'Error: ' . curl_error($ch);
} else {
    echo "Código de estado HTTP: " . $http_code . "\n";

    $info = json_decode($response, true);
    $body = $info['body'] ?? [];

    if ($http_code === 200 && !empty($body)) {
        $pagination = $info['pagination'];
        echo "Contratos encontrados: {$pagination['total_records']} (página {$pagination['current_page']} de {$pagination['total_pages']})\n";

        foreach ($body as $contrato) {
            echo "  [{$contrato['consecutivo']}] {$contrato['propiedad']} - {$contrato['estado']} - Canon: \${$contrato['canon_total']}\n";
        }
    } else {
        echo "Mensaje: " . ($info['message'] ?? 'Sin resultados') . "\n";
    }
}
curl_close($ch);

?>
```

</details><details id="bkmrk-python-import-reques"><summary>Python</summary>

```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. Prepara la dirección y los parámetros
url = f"https://{instancia}/service/v2/public/contracts/list"

params = {
    "page": 1,
    "page_size": 50
}

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

# 3. Envía la petición GET y procesa la respuesta
try:
    response = requests.get(url, params=params, headers=headers)

    print(f"Código de estado HTTP: {response.status_code}")

    if response.status_code == 200:
        info = response.json()
        body = info.get('body', [])
        pagination = info.get('pagination', {})

        if body:
            print(f"Contratos: {pagination['total_records']} total (página {pagination['current_page']} de {pagination['total_pages']})")
            for contrato in body:
                print(f"  [{contrato['consecutivo']}] {contrato['propiedad']} - {contrato['estado']} - Canon: ${contrato['canon_total']}")
        else:
            print(f"Mensaje: {info.get('message', 'Sin resultados')}")
    else:
        print("Error al consultar los contratos.")
        print(f"Detalle del error: {response.text}")

except Exception as e:
    print(f"Ocurrió un error de conexión: {e}")

```

</details><details id="bkmrk-javascript-%2F%2F-1.-con"><summary>JavaScript</summary>

```javascript
// 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. Prepara la dirección con parámetros
const params = new URLSearchParams({
    page: 1,
    page_size: 50
});

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

// 3. Función para consultar los contratos
async function consultarContratos() {
    try {
        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${token}`
            }
        });

        console.log(`Código de estado HTTP: ${response.status}`);

        if (response.ok) {
            const info = await response.json();
            const body = info.body || [];
            const pagination = info.pagination || {};

            if (body.length > 0) {
                console.log(`Contratos: ${pagination.total_records} total (página ${pagination.current_page} de ${pagination.total_pages})`);
                body.forEach(contrato => {
                    console.log(`  [${contrato.consecutivo}] ${contrato.propiedad} - ${contrato.estado} - Canon: $${contrato.canon_total}`);
                });
            } else {
                console.log(`Mensaje: ${info.message || 'Sin resultados'}`);
            }
        } else {
            console.log('Error al consultar los contratos.');
            const errorData = await response.text();
            console.log(`Detalle del error: ${errorData}`);
        }
    } catch (error) {
        console.error(`Ocurrió un error de conexión: ${error}`);
    }
}

// 4. Ejecutamos la función
consultarContratos();

```

</details><details id="bkmrk-power-query-m-%28excel"><summary>Power Query M (Excel / Power BI)</summary>

```powerquery
let
    // 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. Prepara la dirección de la petición con parámetros
    url = "https://" & instancia & "/service/v2/public/contracts/list?page=1&page_size=1000",

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

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

    // 5. Convierte el listado en tabla
    tabla = Table.FromList(body, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expandido = Table.ExpandRecordColumn(tabla, "Column1",
        {"contrato_id", "consecutivo", "propiedad", "estrato_propiedad", "propietario", "inquilino", "canon_total", "porcentaje_comision", "periodicidad", "estado", "fecha_inicio", "fecha_fin", "fecha_terminacion", "uso", "creado_por"},
        {"ContratoID", "Consecutivo", "Propiedad", "Estrato", "Propietario", "Inquilino", "Canon", "Comision", "Periodicidad", "Estado", "FechaInicio", "FechaFin", "FechaTerminacion", "Uso", "CreadoPor"})
in
    expandido
```

</details>