# API des tags d'objet (bêta)

Les tags d'objet stockent des données structurées sur un utilisateur dans un seul tag. Un tag de type `Object` contient un objet avec des champs nommés, comme la dernière commande `order` avec son `amount` et son `status`. Un tag de type `ObjectList` contient un tableau de tels objets, comme toutes les commandes `orders`. Vous définissez les champs une seule fois, écrivez et lisez les valeurs via l'API des tags, et créez des segments selon les valeurs des champs.

<Aside type="caution" title="Bêta">
Les tags d'objet sont en bêta et disponibles sur demande. Contactez le [Support Pushwoosh](https://help.pushwoosh.com/hc/en-us/requests/new) pour les activer sur votre compte. Pendant la bêta, les tags d'objet fonctionnent uniquement via l'API :

- Le Panneau de Contrôle ne propose aucun écran pour les tags d'objet.
- Les champs des tags d'objet ne peuvent pas être insérés dans le contenu des messages.
- Customer Journey ne prend pas en charge les tags d'objet.
</Aside>

## Fonctionnement des tags d'objet

Un tag d'objet possède un schéma et une valeur. Le schéma est le nom du tag et une liste de champs, chacun avec son propre type scalaire. La valeur est stockée dans les champs, séparément pour chaque utilisateur.

- **Champ.** Chaque champ est un tag nommé `<object>.<field>`, par exemple `order.amount`. Vous segmentez selon les champs, et vous pouvez écrire un champ d'un tag `Object` par ce nom.
- **Valeur `Object`.** Un objet par utilisateur, par exemple `"order": {"amount": 120, "status": "paid"}`.
- **Valeur `ObjectList`.** Un tableau d'objets avec les mêmes champs, par exemple `"orders": [{"amount": 120, "status": "paid"}, {"amount": 50}]`.

Les tags d'objet sont toujours User-specific : la valeur appartient à l'utilisateur, et non à un seul appareil, et elle est copiée sur tous les appareils de l'utilisateur. Consultez l'[API de Tags](/fr/developer/api-reference/tags/) pour les tags classiques.

## Règles du schéma

Pushwoosh vérifie ces règles lorsque vous créez un tag d'objet ou ajoutez un champ :

- Les noms d'objets et de champs ne contiennent que des lettres latines, des chiffres et `_`.
- Le nom complet du champ `<object>.<field>` comporte 64 caractères au maximum.
- Un tag d'objet comporte de 1 à 20 champs. Les noms de champs ne sont pas sensibles à la casse : `Amount` et `amount` désignent le même champ.
- Un champ a l'un de ces types : `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE`, `VERSION`. Les champs de fuseau horaire et les objets imbriqués ne sont pas pris en charge.
- Un champ ne peut pas prendre un nom déjà porté par un autre tag. Si un tag `order.note` existe, l'ajout du champ `note` à `order` échoue avec `tag order.note already exists`.
- Un tag d'objet ne peut pas perdre son dernier champ. Supprimez plutôt le tag d'objet.
- Un champ ou un tag d'objet en cours d'utilisation ne peut pas être supprimé. L'erreur indique où il est utilisé, par exemple `field is in use: segment Big buyers, in-app Spring sale`. Les segments, les in-apps, les campagnes Customer Journey en cours, les presets, les modèles d'e-mail et les autres éléments qui font référence au tag comptent comme des utilisations.

## Gérer le schéma

Créez et modifiez les tags d'objet avec les méthodes REST ci-dessous. Utilisez `/json/1.3/addTag` uniquement pour les tags classiques, car cette méthode ne peut pas définir de champs.

### URL de base

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

### Authentification

Chaque requête doit inclure un en-tête `Authorization` avec votre [jeton API Serveur](/fr/developer/api-reference/api-access-token/#server-api-token). Un jeton API Appareil reçoit HTTP 403 `a device API token cannot call this method, use an account API token`.

```
Authorization: Api YOUR_API_TOKEN
```

### Points de terminaison

Ces méthodes gèrent le schéma des tags d'objet :

| Méthode | Chemin | Description |
| :---- | :---- | :---- |
| `POST` | `/api/tags` | Créer un tag d'objet avec ses champs |
| `GET` | `/api/tags/{name}` | Obtenir un tag d'objet avec ses champs |
| `GET` | `/api/tags` | Lister les tags. Un tag d'objet est renvoyé avec ses `fields`, et les champs ne sont pas listés comme des tags distincts |
| `POST` | `/api/tags/{name}/fields` | Ajouter un champ |
| `DELETE` | `/api/tags/{name}/fields/{field}` | Supprimer un champ |
| `DELETE` | `/api/tags/{name}` | Supprimer un tag d'objet avec tous ses champs |

`POST /api/tags` et les deux méthodes de champs renvoient le tag d'objet après la modification, encapsulé dans `{"tag": {...}}`. `DELETE /api/tags/{name}` renvoie `{}`.

Si les tags d'objet ne sont pas activés pour votre compte, `POST /api/tags` avec un type objet et les deux méthodes de champs échouent avec HTTP 403 `object tags are not enabled for the account`. `GET /api/tags/{name}` et `DELETE /api/tags/{name}` renvoient HTTP 404 `tag not found`, et `GET /api/tags` ne liste pas les tags d'objet. Sans les tags d'objet activés, vous ne pouvez pas en créer, il n'y a donc rien dans quoi écrire des valeurs.

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

### Créer un tag d'objet

`POST` `/api/tags`

##### Corps de la requête

| Nom | Type | Description |
| :---- | :---- | :---- |
| name\* | string | Nom du tag d'objet. |
| type\* | string | `OBJECT` pour un objet ou `OBJECT_LIST` pour une liste d'objets. |
| fields\* | array | Champs de l'objet, de 1 à 20. |
| fields[].name\* | string | Nom du champ. Le champ devient le tag `<name>.<fields[].name>`. |
| fields[].type\* | string | Type du champ : `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE` ou `VERSION`. |

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

```json title="Réponse"
{
  "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" }
    ]
  }
}
```

Le tag est créé comme User-specific, quelle que soit la valeur de `user_specific`. `GET /api/tags/{name}` renvoie le même objet sans l'enveloppe `tag`. Les champs sont triés par nom.

### Ajouter un champ

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

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

Vous pouvez écrire le nouveau champ immédiatement dans le cadre de la valeur complète du tag. L'ajout d'un champ échoue si le tag d'objet a déjà 20 champs ou si un tag portant le nom complet du champ existe, voir [Erreurs](#errors).

### Supprimer un champ

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

Par exemple, `DELETE /api/tags/orders/fields/note` supprime le champ `orders.note`. Vous ne pouvez pas supprimer le dernier champ ni un champ en cours d'utilisation, voir [Erreurs](#errors).

<span id="errors" />

### Erreurs

Les erreurs sont renvoyées au format `{"code": <n>, "message": "...", "details": [...]}`. Lorsque vous créez un tag d'objet, les messages 400 commencent par `validate: `.

| Statut HTTP | Message | Cause |
| :---- | :---- | :---- |
| 400 | `object tag and field names must contain only letters, digits and underscores` | Un nom contient un autre caractère. |
| 400 | `an object tag needs 1 to 20 fields` | Aucun champ ou plus de 20 champs à la création. |
| 400 | `an object tag has at most 20 fields` | Le tag d'objet a déjà 20 champs. |
| 400 | `field <name> is given twice` | Deux champs portent le même nom, sans tenir compte de la casse. |
| 400 | `<object>.<field>: object and field names together cannot be longer than 64 characters` | Le nom complet du champ est trop long. |
| 400 | `<object>.<field>: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE or VERSION` | Type de champ non pris en charge. |
| 400 | `an object tag needs at least one field; delete the object tag instead` | Vous avez tenté de supprimer le dernier champ. |
| 400 | `field is in use: ...`, `tag is in use: ...` | Le champ ou le tag d'objet est utilisé dans des segments, des in-apps, des campagnes Customer Journey ou d'autres éléments. |
| 403 | `object tags are not enabled for the account` | Les tags d'objet ne sont pas activés pour le compte. |
| 403 | `a device API token cannot call this method, use an account API token` | La requête utilise un jeton API Appareil. |
| 404 | `tag not found` | `GET` ou `DELETE /api/tags/{name}` : ce tag n'existe pas, le nom est un champ, ou les tags d'objet ne sont pas activés. |
| 404 | `object tag not found`, `field not found` | Méthodes de champs : ce tag d'objet ou ce champ n'existe pas. |
| 409 | `tag already exists` | Un tag portant le nom du tag d'objet existe. |
| 409 | `tag <object>.<field> already exists` | Un tag portant le nom complet du champ existe. |

## Écrire des valeurs

Écrivez les valeurs des tags d'objet avec [`/setTags`](/fr/developer/api-reference/device-api/#settags) et [`/bulkSetTags`](/fr/developer/api-reference/audience-api/#bulksettags). Ces méthodes conservent leur propre authentification : `/setTags` prend un jeton API Appareil dans l'en-tête `Authorization: Token`, et `/bulkSetTags` prend le jeton d'accès API dans `auth`. Envoyez une valeur `Object` sous forme d'objet JSON et une valeur `ObjectList` sous forme de tableau d'objets :

```json title="Exemple : /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 vous définissez la valeur par HWID, elle est stockée sur l'utilisateur de cet appareil.

Chaque champ prend une valeur au format de son type :

| Type de champ | Exemple de valeur |
| :---- | :---- |
| `INTEGER` | `120` |
| `STRING` | `"paid"` |
| `LIST` | `["gift", "express"]` |
| `DATE` | `"2026-10-02 22:11"` (UTC), `"2026-10-02"` ou un timestamp Unix en secondes |
| `BOOLEAN` | `true` ou `false` |
| `PRICE` | `19.95` |
| `VERSION` | `"1.0.0"` |

### L'écriture remplace toute la valeur

Chaque écriture remplace la valeur stockée du tag d'objet :

- Un champ absent de la nouvelle valeur est supprimé. Un champ envoyé avec `null` est également supprimé.
- `null`, `[]` ou `""` supprime toute la valeur.
- `{}` supprime tous les champs d'un tag `Object`.
- Une clé qui n'est pas un champ de l'objet est ignorée.
- Si la valeur d'un champ ne correspond pas au type du champ, toute la valeur du tag est rejetée et la valeur stockée reste inchangée.

`/setTags` ne signale pas une valeur rejetée dans sa réponse. Vérifiez le résultat avec [`/getTags`](#read-values) ou utilisez `/bulkSetTags`, qui renvoie un résultat pour chaque tag.

### Écrire un champ d'un Object

Écrivez un seul champ d'un tag `Object` par son nom complet. `increment` et les autres opérations de [`/setTags`](/fr/developer/api-reference/device-api/#settags) fonctionnent sur les champs :

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

Un champ d'un tag `ObjectList` ne peut pas être écrit seul, car on ne sait pas quel élément modifier. `/setTags` ignore une telle valeur. Envoyez plutôt la liste entière.

### Les tags d'objet ne sont pas créés automatiquement

Une valeur contenant un objet ou un tableau d'objets pour un tag qui n'existe pas est ignorée. La création automatique de tags ne crée pas de tags d'objet : créez donc d'abord le schéma avec [`POST /api/tags`](#create-an-object-tag).

### Résultats de bulkSetTags

Avec `detailed=true`, le [statut de `/bulkSetTags`](/fr/developer/api-reference/audience-api/#bulksettags-status) renvoie l'un de ces résultats pour un tag d'objet, en plus de `ok`, `tag not found` et `wrong value. expect :<type>` :

| Résultat | Cause |
| :---- | :---- |
| `object tags are stored on the user only` | Le tag d'objet n'est pas User-specific. |
| `cannot load the fields of the object` | Pushwoosh n'a pas pu lire les champs du tag d'objet. |
| `wrong value. expect an object (a map) or, for a list of objects, an array of maps` | La valeur ne correspond pas au tag d'objet, ou la valeur d'un champ ne correspond pas à son type. |
| `a field of a list of objects cannot be set on its own, set the whole list` | La valeur est définie sur un champ d'un tag `ObjectList`. |

<span id="read-values" />

## Lire des valeurs

[`/getTags`](/fr/developer/api-reference/device-api/#gettags) renvoie une valeur `Object` sous forme d'objet JSON et une valeur `ObjectList` sous forme de tableau d'objets :

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

Les valeurs des champs sont renvoyées de la même manière que les valeurs des tags classiques dans `/getTags` : un Date sous forme de timestamp Unix en secondes, un Price sous forme de chaîne, par exemple `"120.00"`, et un Boolean sous forme de `1` ou `0`.

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

## Segmenter selon les champs

Définissez les conditions de segment sur les champs, et non sur le tag d'objet lui-même. Dans le [langage de segmentation](/fr/developer/api-reference/segmentation-filters-api/segmentation-language/), un champ est un tag dont le nom contient un point, et il accepte les opérateurs de son propre type :

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

Une condition sur le tag d'objet lui-même échoue avec `object tag has no value of its own, use one of its fields`.

### Conditions sur un ObjectList

Une condition sur un champ d'un tag `ObjectList` correspond à un utilisateur lorsqu'au moins un élément de la liste remplit la condition. Les conditions sur des champs de la même liste reliées par AND doivent correspondre au même élément :

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

Ce segment contient les utilisateurs ayant au moins une commande à la fois d'un montant de 100 ou plus et payée. Un utilisateur avec une grosse commande non payée et une petite commande payée ne correspond pas. Les conditions reliées par OR n'ont pas à correspondre au même élément. Les conditions sur deux listes différentes sont vérifiées sur chaque liste séparément.

Certains opérateurs fonctionnent différemment sur un champ d'un tag `ObjectList` :

- **`notset`.** Aucun élément de la liste n'a de valeur dans ce champ. Un utilisateur sans la liste correspond également.
- **`noteq`.** Au moins un élément a une valeur dans ce champ, et cette valeur diffère de celle indiquée. Les éléments sans ce champ ne comptent pas. Un utilisateur avec des commandes aux statuts `paid` et `new` correspond à `noteq "paid"`.
- **`notin` sur un champ List.** Aucun élément ne contient l'une des valeurs indiquées.
- **`notin` sur un champ d'un autre type.** Au moins un élément a dans ce champ une valeur qui ne figure pas parmi les valeurs indiquées.

`notset` et `notin` sur un champ List ne sont pas liés au même élément que les autres conditions reliées par AND.

## Exporter des segments avec des tags d'objet

[`/exportSegment`](/fr/developer/api-reference/segmentation-filters-api/#exportsegment) exporte les champs des tags d'objet :

- **CSV.** Chaque champ est une colonne distincte nommée `<object>.<field>`, et les tags d'objet eux-mêmes n'ont pas de colonne. Un champ d'un ObjectList contient toutes les valeurs distinctes des éléments sous forme de tableau JSON, de la même manière qu'un tag List : `["120","50"]`.
- **`json` et `json_each_line`.** Les champs d'un tag `Object` sont renvoyés comme des tags distincts nommés `<object>.<field>`. Un tag `ObjectList` est renvoyé comme une seule entrée dans `tags`, avec les éléments dans `object_list.items`. Chaque élément liste ses champs avec leurs noms courts, et chaque champ porte sa valeur sous une clé nommée d'après son type : `integer`, `string`, `list`, `date`, `boolean`, `price` ou `version`. Les valeurs entières sont renvoyées sous forme de chaînes :

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

Si `tagsList` nomme un tag d'objet, tous ses champs sont exportés.