# API de tags de objeto (beta)

Los tags de objeto almacenan datos estructurados sobre un usuario en un solo tag. Un tag de tipo `Object` contiene un objeto con campos con nombre, como el último `order` con su `amount` y su `status`. Un tag de tipo `ObjectList` contiene un array de esos objetos, como todos los `orders`. Defines los campos una vez, escribes y lees los valores a través de la API de tags y creas segmentos según los valores de los campos.

<Aside type="caution" title="Beta">
Los tags de objeto están en beta y disponibles bajo solicitud. Contacta con el [Soporte de Pushwoosh](https://help.pushwoosh.com/hc/en-us/requests/new) para habilitarlos en tu cuenta. Durante la beta, los tags de objeto solo funcionan a través de la API:

- El Panel de Control no tiene ninguna pantalla para los tags de objeto.
- Los campos de los tags de objeto no se pueden insertar en el contenido de los mensajes.
- Customer Journey no admite tags de objeto.
</Aside>

## Cómo funcionan los tags de objeto

Un tag de objeto tiene un esquema y un valor. El esquema es el nombre del tag y una lista de campos, cada uno con su propio tipo escalar. El valor se almacena en los campos, por separado para cada usuario.

- **Campo.** Cada campo es un tag con el nombre `<object>.<field>`, por ejemplo `order.amount`. Segmentas por campos, y puedes escribir un campo de un tag `Object` usando este nombre.
- **Valor `Object`.** Un objeto por usuario, por ejemplo `"order": {"amount": 120, "status": "paid"}`.
- **Valor `ObjectList`.** Un array de objetos con los mismos campos, por ejemplo `"orders": [{"amount": 120, "status": "paid"}, {"amount": 50}]`.

Los tags de objeto siempre son User-specific, lo que significa que el valor pertenece al usuario, no a un único dispositivo, y se copia en todos los dispositivos del usuario. Consulta la [API de Tags](/es/developer/api-reference/tags/) para los tags normales.

## Reglas del esquema

Pushwoosh comprueba estas reglas cuando creas un tag de objeto o añades un campo:

- Los nombres de objetos y campos contienen solo letras latinas, dígitos y `_`.
- El nombre completo del campo `<object>.<field>` tiene 64 caracteres o menos.
- Un tag de objeto tiene de 1 a 20 campos. Los nombres de los campos no distinguen entre mayúsculas y minúsculas, por lo que `Amount` y `amount` son el mismo campo.
- Un campo tiene uno de estos tipos: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE`, `VERSION`. No se admiten campos de zona horaria ni objetos anidados.
- Un campo no puede tomar un nombre que ya tenga otro tag. Si existe un tag `order.note`, añadir el campo `note` a `order` falla con `tag order.note already exists`.
- Un tag de objeto no puede perder su último campo. En su lugar, elimina el tag de objeto.
- No se puede eliminar un campo o un tag de objeto que esté en uso. El error indica dónde se usa, por ejemplo `field is in use: segment Big buyers, in-app Spring sale`. Cuentan como usos los segmentos, los in-apps, las campañas de Customer Journey en ejecución, los presets, las plantillas de email y otros elementos que hacen referencia al tag.

## Gestionar el esquema

Crea y modifica tags de objeto con los métodos REST que se indican a continuación. Usa `/json/1.3/addTag` solo para tags normales, porque no puede definir campos.

### URL base

```
https://rpc-api.svc-nue.pushwoosh.com
```

### Autenticación

Cada solicitud debe incluir un encabezado `Authorization` con tu [token de API de servidor](/es/developer/api-reference/api-access-token/#server-api-token). Un token de API de dispositivo recibe HTTP 403 `a device API token cannot call this method, use an account API token`.

```
Authorization: Api YOUR_API_TOKEN
```

### Endpoints

Estos métodos gestionan el esquema de los tags de objeto:

| Método | Ruta | Descripción |
| :---- | :---- | :---- |
| `POST` | `/api/tags` | Crear un tag de objeto con sus campos |
| `GET` | `/api/tags/{name}` | Obtener un tag de objeto con sus campos |
| `GET` | `/api/tags` | Listar tags. Un tag de objeto viene con sus `fields`, y los campos no se listan como tags separados |
| `POST` | `/api/tags/{name}/fields` | Añadir un campo |
| `DELETE` | `/api/tags/{name}/fields/{field}` | Eliminar un campo |
| `DELETE` | `/api/tags/{name}` | Eliminar un tag de objeto con todos sus campos |

`POST /api/tags` y ambos métodos de campos devuelven el tag de objeto después del cambio, envuelto en `{"tag": {...}}`. `DELETE /api/tags/{name}` devuelve `{}`.

Si los tags de objeto no están habilitados en tu cuenta, `POST /api/tags` con un tipo de objeto y ambos métodos de campos fallan con HTTP 403 `object tags are not enabled for the account`. `GET /api/tags/{name}` y `DELETE /api/tags/{name}` devuelven HTTP 404 `tag not found`, y `GET /api/tags` no lista los tags de objeto. Sin los tags de objeto habilitados no puedes crear ninguno, así que no hay nada en lo que escribir valores.

<span id="create-an-object-tag" />

### Crear un tag de objeto

`POST` `/api/tags`

##### Cuerpo de la solicitud

| Nombre | Tipo | Descripción |
| :---- | :---- | :---- |
| name\* | string | Nombre del tag de objeto. |
| type\* | string | `OBJECT` para un objeto u `OBJECT_LIST` para una lista de objetos. |
| fields\* | array | Campos del objeto, de 1 a 20. |
| fields[].name\* | string | Nombre del campo. El campo se convierte en el tag `<name>.<fields[].name>`. |
| fields[].type\* | string | Tipo del campo: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE` o `VERSION`. |

```json title="Ejemplo"
{
  "name": "orders",
  "type": "OBJECT_LIST",
  "fields": [
    { "name": "amount", "type": "INTEGER" },
    { "name": "status", "type": "STRING" }
  ]
}
```

```json title="Respuesta"
{
  "tag": {
    "name": "orders",
    "type": "OBJECT_LIST",
    "system": false,
    "user_specific": true,
    "has_right_ace_modify": true,
    "fields": [
      { "name": "amount", "type": "INTEGER" },
      { "name": "status", "type": "STRING" }
    ]
  }
}
```

El tag se crea como User-specific independientemente del valor de `user_specific`. `GET /api/tags/{name}` devuelve el mismo objeto sin el envoltorio `tag`. Los campos se ordenan por nombre.

### Añadir un campo

`POST` `/api/tags/{name}/fields`

```json title="Ejemplo: POST /api/tags/orders/fields"
{
  "field": { "name": "note", "type": "STRING" }
}
```

Puedes escribir el nuevo campo de inmediato como parte del valor completo del tag. Añadir un campo falla si el tag de objeto ya tiene 20 campos o si existe un tag con el nombre completo del campo, consulta [Errores](#errors).

### Eliminar un campo

`DELETE` `/api/tags/{name}/fields/{field}`

Por ejemplo, `DELETE /api/tags/orders/fields/note` elimina el campo `orders.note`. No puedes eliminar el último campo ni un campo que esté en uso, consulta [Errores](#errors).

<span id="errors" />

### Errores

Los errores tienen el formato `{"code": <n>, "message": "...", "details": [...]}`. Cuando creas un tag de objeto, los mensajes 400 empiezan por `validate: `.

| Estado HTTP | Mensaje | Causa |
| :---- | :---- | :---- |
| 400 | `object tag and field names must contain only letters, digits and underscores` | Un nombre tiene otro carácter. |
| 400 | `an object tag needs 1 to 20 fields` | Ningún campo o más de 20 campos al crear. |
| 400 | `an object tag has at most 20 fields` | El tag de objeto ya tiene 20 campos. |
| 400 | `field <name> is given twice` | Dos campos tienen el mismo nombre, sin distinguir mayúsculas y minúsculas. |
| 400 | `<object>.<field>: object and field names together cannot be longer than 64 characters` | El nombre completo del campo es demasiado largo. |
| 400 | `<object>.<field>: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE or VERSION` | Tipo de campo no admitido. |
| 400 | `an object tag needs at least one field; delete the object tag instead` | Intentaste eliminar el último campo. |
| 400 | `field is in use: ...`, `tag is in use: ...` | El campo o el tag de objeto se usa en segmentos, in-apps, campañas de Customer Journey u otros elementos. |
| 403 | `object tags are not enabled for the account` | Los tags de objeto no están habilitados en la cuenta. |
| 403 | `a device API token cannot call this method, use an account API token` | La solicitud usa un token de API de dispositivo. |
| 404 | `tag not found` | `GET` o `DELETE /api/tags/{name}`: no existe ese tag, el nombre es un campo o los tags de objeto no están habilitados. |
| 404 | `object tag not found`, `field not found` | Métodos de campos: no existe ese tag de objeto o campo. |
| 409 | `tag already exists` | Existe un tag con el nombre del tag de objeto. |
| 409 | `tag <object>.<field> already exists` | Existe un tag con el nombre completo del campo. |

## Escribir valores

Escribe los valores de los tags de objeto con [`/setTags`](/es/developer/api-reference/device-api/#settags) y [`/bulkSetTags`](/es/developer/api-reference/audience-api/#bulksettags). Estos métodos mantienen su propia autenticación: `/setTags` recibe un token de API de dispositivo en el encabezado `Authorization: Token`, y `/bulkSetTags` recibe el token de acceso a la API en `auth`. Envía un valor `Object` como un objeto JSON y un valor `ObjectList` como un array de objetos:

```json title="Ejemplo: /json/1.3/setTags"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "userId": "user_42",
    "tags": {
      "order": { "amount": 120, "status": "paid" },
      "orders": [
        { "amount": 120, "status": "paid" },
        { "amount": 50 }
      ]
    }
  }
}
```

Si estableces el valor por HWID, se almacena en el usuario de ese dispositivo.

Cada campo recibe un valor en el formato de su tipo:

| Tipo de campo | Valor de ejemplo |
| :---- | :---- |
| `INTEGER` | `120` |
| `STRING` | `"paid"` |
| `LIST` | `["gift", "express"]` |
| `DATE` | `"2026-10-02 22:11"` (UTC), `"2026-10-02"` o un timestamp Unix en segundos |
| `BOOLEAN` | `true` o `false` |
| `PRICE` | `19.95` |
| `VERSION` | `"1.0.0"` |

### Escribir reemplaza el valor completo

Cada escritura reemplaza el valor almacenado del tag de objeto:

- Un campo que falta en el nuevo valor se elimina. Un campo enviado como `null` también se elimina.
- `null`, `[]` o `""` elimina el valor completo.
- `{}` elimina todos los campos de un tag `Object`.
- Una clave que no es un campo del objeto se omite.
- Si el valor de un campo no coincide con el tipo del campo, se rechaza el valor completo del tag y el valor almacenado se mantiene como estaba.

`/setTags` no informa de un valor rechazado en su respuesta. Comprueba el resultado con [`/getTags`](#read-values) o usa `/bulkSetTags`, que informa de un resultado para cada tag.

### Escribir un campo de un Object

Escribe un único campo de un tag `Object` por su nombre completo. `increment` y las demás operaciones de [`/setTags`](/es/developer/api-reference/device-api/#settags) funcionan con los campos:

```json
"tags": {
  "order.status": "shipped",
  "order.amount": { "operation": "increment", "value": 5 }
}
```

Un campo de un tag `ObjectList` no se puede escribir por separado, porque no está claro qué elemento cambiar. `/setTags` ignora ese valor. En su lugar, envía la lista completa.

### Los tags de objeto no se crean automáticamente

Un valor con un objeto o un array de objetos para un tag que no existe se omite. La creación automática de tags no crea tags de objeto, así que primero crea el esquema con [`POST /api/tags`](#create-an-object-tag).

### Resultados de bulkSetTags

Con `detailed=true`, el [estado de `/bulkSetTags`](/es/developer/api-reference/audience-api/#bulksettags-status) devuelve uno de estos resultados para un tag de objeto, además de `ok`, `tag not found` y `wrong value. expect :<type>`:

| Resultado | Causa |
| :---- | :---- |
| `object tags are stored on the user only` | El tag de objeto no es User-specific. |
| `cannot load the fields of the object` | Pushwoosh no pudo leer los campos del tag de objeto. |
| `wrong value. expect an object (a map) or, for a list of objects, an array of maps` | El valor no encaja con el tag de objeto, o el valor de un campo no coincide con su tipo. |
| `a field of a list of objects cannot be set on its own, set the whole list` | El valor se establece en un campo de un tag `ObjectList`. |

<span id="read-values" />

## Leer valores

[`/getTags`](/es/developer/api-reference/device-api/#gettags) devuelve un valor `Object` como un objeto JSON y un valor `ObjectList` como un array de objetos:

```json title="Respuesta"
{
  "status_code": 200,
  "status_message": "OK",
  "response": {
    "result": {
      "order": { "amount": 120, "status": "paid" },
      "orders": [
        { "amount": 120, "status": "paid" },
        { "amount": 50 }
      ]
    }
  }
}
```

Los valores de los campos se devuelven igual que los valores de los tags normales en `/getTags`: un Date como timestamp Unix en segundos, un Price como cadena, por ejemplo `"120.00"`, y un Boolean como `1` o `0`.

<span id="segment-by-fields" />

## Segmentar por campos

Establece las condiciones del segmento sobre los campos, no sobre el propio tag de objeto. En el [lenguaje de segmentación](/es/developer/api-reference/segmentation-filters-api/segmentation-language/), un campo es un tag con un nombre con puntos, y admite los operadores de su propio tipo:

```
AT("XXXXX-XXXXX", "order.amount", gte, 100)
```

Una condición sobre el propio tag de objeto falla con `object tag has no value of its own, use one of its fields`.

### Condiciones sobre un ObjectList

Una condición sobre un campo de un tag `ObjectList` coincide con un usuario cuando al menos un elemento de la lista cumple la condición. Las condiciones sobre campos de la misma lista unidas con AND deben coincidir con el mismo elemento:

```
AT("XXXXX-XXXXX", "orders.amount", gte, 100) * AT("XXXXX-XXXXX", "orders.status", eq, "paid")
```

Este segmento contiene usuarios con al menos un pedido que es a la vez de 100 o más y pagado. Un usuario con un pedido grande sin pagar y un pedido pequeño pagado no coincide. Las condiciones unidas con OR no tienen que coincidir con el mismo elemento. Las condiciones sobre dos listas diferentes se comprueban en cada lista por separado.

Algunos operadores funcionan de forma distinta sobre un campo de un tag `ObjectList`:

- **`notset`.** Ningún elemento de la lista tiene un valor en este campo. Un usuario sin la lista también coincide.
- **`noteq`.** Al menos un elemento tiene un valor en este campo, y ese valor es distinto del indicado. Los elementos sin el campo no cuentan. Un usuario con pedidos en los estados `paid` y `new` coincide con `noteq "paid"`.
- **`notin` sobre un campo List.** Ningún elemento contiene ninguno de los valores indicados.
- **`notin` sobre un campo de otro tipo.** Al menos un elemento tiene un valor en este campo que no está entre los valores indicados.

`notset` y `notin` sobre un campo List no están ligados al mismo elemento que las demás condiciones unidas con AND.

## Exportar segmentos con tags de objeto

[`/exportSegment`](/es/developer/api-reference/segmentation-filters-api/#exportsegment) exporta los campos de los tags de objeto:

- **CSV.** Cada campo es una columna separada con el nombre `<object>.<field>`, y los propios tags de objeto no tienen columna. Un campo de un ObjectList contiene todos los valores distintos de los elementos como un array JSON, igual que un tag List: `["120","50"]`.
- **`json` y `json_each_line`.** Los campos de un tag `Object` vienen como tags separados con el nombre `<object>.<field>`. Un tag `ObjectList` viene como una entrada en `tags` con los elementos en `object_list.items`. Cada elemento lista sus campos con nombres cortos, y cada campo lleva su valor bajo una clave con el nombre de su tipo: `integer`, `string`, `list`, `date`, `boolean`, `price` o `version`. Los valores enteros vienen como cadenas:

```json
{
  "name": "orders",
  "object_list": {
    "items": [
      { "fields": [{ "name": "amount", "integer": "120" }, { "name": "status", "string": "paid" }] },
      { "fields": [{ "name": "amount", "integer": "50" }] }
    ]
  }
}
```

Si `tagsList` nombra un tag de objeto, se exportan todos sus campos.