> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ugps.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Campos personalizados

> Leer, buscar y actualizar los datos propios de tus activos, uno por uno o de a 100.

Los **campos personalizados** son datos que tu empresa define para todos sus activos: un
código de inventario, un centro de costo, un área. Cada campo tiene una etiqueta (lo que se ve
en la plataforma) y un **nombre interno** fijo, que es la clave que usa la API:

| Etiqueta | Nombre interno | Tipo |
| - | - | - |
| Código de inventario | `codigo_de_inventario` | `text` |
| Área | `area` | `select` |
| Arrendado | `arrendado` | `boolean` |

Esta guía recorre el flujo típico de una integración que sincroniza activos con su propio
sistema (un ERP o un sistema de mantenimiento) usando el código de inventario como llave.
Todos los ejemplos usan un [API token](https://docs.ugps.io/autenticacion.md) en `$ATLAS_TOKEN`.

> [!NOTE]
>
> Los campos los crea un administrador de tu empresa en **Configuración › Campos de activos**. Leer
> valores requiere el permiso `access_tracking` o `access_fleet_assets`; escribirlos,
> `access_fleet_assets`. Un API token tiene los permisos del rol de su dueño.

## 1. Listar las definiciones

```bash theme={null}
curl https://api.ugps.io/api/asset-custom-fields \
  -H "Authorization: Bearer $ATLAS_TOKEN"
```

```json theme={null}
{
  "status": "success",
  "data": [
    {
      "id": "6727d4b2c1a8e3f0a9b1c2d3",
      "label": "Código de inventario",
      "name": "codigo_de_inventario",
      "type": "text",
      "groupId": "6727d4b2c1a8e3f0a9b1c2d0",
      "order": 0,
      "options": [],
      "rules": { "required": true, "unique": true, "maxLength": 20 },
      "status": "active"
    },
    {
      "id": "6727d4b2c1a8e3f0a9b1c2d4",
      "label": "Área",
      "name": "area",
      "type": "select",
      "groupId": "6727d4b2c1a8e3f0a9b1c2d0",
      "order": 1,
      "options": [
        { "value": "bodega", "label": "Bodega", "status": "active" },
        { "value": "taller_mecanico", "label": "Taller mecánico", "status": "active" }
      ],
      "rules": {},
      "status": "active"
    }
  ]
}
```

* `name` es la clave del valor en el activo. No cambia aunque cambie la etiqueta.
* `type` define el formato del valor: `text` y `select` son texto (en un desplegable va el
  `value` de la opción, no su etiqueta), `number` es número, `date` es `YYYY-MM-DD` y
  `boolean` es `true`/`false`.
* `rules.unique` (**Valor único**) garantiza que dos activos de la empresa no tengan el mismo
  valor. Compara sin distinguir mayúsculas, tildes ni espacios de los extremos: `asm-001 ` y
  `ASM-001` son el mismo valor. Solo un campo con Valor único sirve para identificar activos.

Por defecto se listan los campos activos; `?status=all` incluye los archivados.

## 2. Leer los valores de los activos

Cada activo trae `customFields` con los valores de los campos activos, bajo su nombre
interno. Un campo sin valor no aparece.

```bash theme={null}
curl https://api.ugps.io/api/assets \
  -H "Authorization: Bearer $ATLAS_TOKEN"
```

```json theme={null}
{
  "status": "success",
  "data": [
    {
      "_id": "6650f1a2c9d84e0012345678",
      "name": "Camioneta 01",
      "plate": "RLPD21",
      "customFields": { "codigo_de_inventario": "ASM-001", "area": "bodega" }
    }
  ]
}
```

## 3. Buscar por un campo

`customFields.<nombre interno>=<valor>` filtra la lista por igualdad. Varios filtros se
combinan con «y».

```bash theme={null}
curl "https://api.ugps.io/api/assets?customFields.codigo_de_inventario=asm-001" \
  -H "Authorization: Bearer $ATLAS_TOKEN"
```

El texto se compara sin mayúsculas ni tildes; número, fecha, opción y booleano, exactos.

## 4. Leer y actualizar un activo por su código

Con `idField`, el segmento de la ruta deja de ser el id del activo y pasa a ser el valor
buscado. `idField` acepta `plate`, `name` o el nombre interno de un campo con Valor único.

```bash theme={null}
curl "https://api.ugps.io/api/assets/ASM-001?idField=codigo_de_inventario" \
  -H "Authorization: Bearer $ATLAS_TOKEN"
```

Para actualizar, usá `PATCH`: cambia **solo lo que envíes**. En `customFields`, las claves
enviadas se escriben, `null` o `""` borran el valor y las ausentes no se tocan.

```bash theme={null}
curl -X PATCH "https://api.ugps.io/api/assets/ASM-001?idField=codigo_de_inventario" \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "customFields": { "area": "taller_mecanico" } }'
```

La respuesta es el activo actualizado. Cada cambio queda en el Registro de actividades de la
empresa, con el canal «llave API».

> [!TIP]
>
> Preferí `PATCH` a `PUT`: el `PUT` reemplaza el activo completo y exige que queden todos los campos
> obligatorios; el `PATCH` solo falla si vacía uno.

## 5. Cargar en lote

`POST /api/assets/batch/update` actualiza hasta **100 activos** por llamada. Cada entrada
identifica un activo por `id`, o por `idField` + `idValue`, y trae en `data` el mismo cuerpo
que el `PATCH`. No crea activos.

Empezá con `"dryRun": true`: valida todas las filas **sin guardar nada** y devuelve el mismo
resultado que tendría la carga real.

```bash theme={null}
curl -X POST https://api.ugps.io/api/assets/batch/update \
  -H "Authorization: Bearer $ATLAS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dryRun": true,
    "entries": [
      { "idField": "plate", "idValue": "RLPD21", "data": { "customFields": { "codigo_de_inventario": "ASM-001" } } },
      { "idField": "plate", "idValue": "KTXR45", "data": { "customFields": { "codigo_de_inventario": "ASM-001" } } },
      { "idField": "plate", "idValue": "ZZZZ99", "data": { "customFields": { "codigo_de_inventario": "ASM-003" } } },
      { "idField": "codigo_de_inventario", "idValue": "ASM-010", "data": { "customFields": { "area": "taller_mecanico" } } }
    ]
  }'
```

La respuesta es siempre `200` con un resultado por entrada, en el mismo orden (`index`), y un
resumen:

```json theme={null}
{
  "status": "success",
  "data": {
    "dryRun": true,
    "summary": { "total": 4, "updated": 2, "failed": 2 },
    "results": [
      {
        "index": 0,
        "status": "updated",
        "asset": { "id": "6650f1a2c9d84e0012345678", "name": "Camioneta 01", "plate": "RLPD21" }
      },
      {
        "index": 1,
        "status": "conflict",
        "asset": { "id": "6650f1a2c9d84e0012345679", "name": "Camioneta 02", "plate": "KTXR45" },
        "error": {
          "code": "ASSET_CUSTOM_FIELD_VALUE_CONFLICT",
          "message": "Ya lo tiene el activo Camioneta 01 (RLPD21). El campo «Código de inventario» no admite valores repetidos.",
          "details": {
            "fieldName": "codigo_de_inventario",
            "fieldLabel": "Código de inventario",
            "value": "ASM-001",
            "asset": { "id": "6650f1a2c9d84e0012345678", "name": "Camioneta 01", "plate": "RLPD21" }
          }
        }
      },
      {
        "index": 2,
        "status": "notFound",
        "error": { "code": "ASSET_NOT_FOUND", "message": "Activo no encontrado" }
      },
      {
        "index": 3,
        "status": "updated",
        "asset": { "id": "6650f1a2c9d84e001234567a", "name": "Grúa 2", "plate": null }
      }
    ]
  }
}
```

| `status` | Qué pasó |
| - | - |
| `updated` | Se actualizó (con `dryRun`, se actualizaría). |
| `notFound` | Ningún activo de tu empresa coincide. |
| `ambiguous` | Más de un activo coincide (patente o nombre repetidos): `details.assets` los lista. |
| `invalid` | La entrada o sus datos no son válidos: sin identificador, `idField` no admitido, tipo, regla. |
| `conflict` | Un valor único ya lo tiene otro activo: `details.asset` dice cuál. |

Cómo leerlo:

* Una fila que falla **no detiene a las demás**. Para reintentar, reenviá solo las filas que
  no salieron `updated`.
* Las filas se procesan en orden: un valor único repetido **dentro del mismo lote** falla en la
  segunda fila que lo trae (en el ejemplo, la fila 1 choca con la fila 0).
* Cuando el resultado te conforma, mandá el mismo lote sin `dryRun` (o con `false`). Cada fila
  actualizada queda en el Registro de actividades como una edición del activo.
* La respuesta es `400` solo si la petición en sí es inválida: más de 100 entradas, ninguna, un
  `id` mal formado o una forma incorrecta. En ese caso no se procesa ninguna fila.

## Errores

En las operaciones de un activo (`PATCH`, `PUT`, `POST`) y en el campo `error` de cada fila del
lote, los errores de campos personalizados traen `details` para que tu integración sepa qué
corregir:

| Status | `code` | `details` |
| - | - | - |
| `400` | `ASSET_CUSTOM_FIELD_VALUE_INVALID` | `key` (el nombre interno) y, si aplica, `rule` (`maxLength`, `min`, …); o `missingFields` si se vacía un obligatorio. |
| `400` | `ASSET_ID_FIELD_INVALID` | — (`idField` no es `plate`, `name` ni un campo activo con Valor único). |
| `404` | `ASSET_NOT_FOUND` | — (ningún activo coincide con el id o el `idField`). |
| `409` | `ASSET_ID_FIELD_AMBIGUOUS` | `idField`, `value` y `assets` (cada uno con `id`, `name`, `plate`). |
| `409` | `ASSET_CUSTOM_FIELD_VALUE_CONFLICT` | `fieldName`, `fieldLabel`, `value` y `asset` (el activo que ya tiene el valor). |

Ejemplo de un valor que no cumple una regla:

```json theme={null}
{
  "status": "failed",
  "code": "ASSET_CUSTOM_FIELD_VALUE_INVALID",
  "message": "«codigo_de_inventario» (Código de inventario) admite hasta 20 caracteres (maxLength)",
  "details": { "key": "codigo_de_inventario", "rule": "maxLength" },
  "requestId": "b2f8c9e0-…"
}
```

Una clave que no es un campo de tu empresa, o que es de un campo archivado, responde `400` con
`details.key`: la API no descarta valores en silencio. El resto del contrato de errores está en
[Códigos de error](https://docs.ugps.io/errores.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.