# API объектных тегов (бета)

Объектные теги хранят структурированные данные о пользователе в одном теге. Тег типа `Object` содержит один объект с именованными полями, например последний заказ `order` с полями `amount` и `status`. Тег типа `ObjectList` содержит массив таких объектов, например все заказы `orders`. Вы один раз описываете поля, записываете и читаете значения через API тегов и строите сегменты по значениям полей.

<Aside type="caution" title="Бета">
Объектные теги находятся в бета-версии и доступны по запросу. Чтобы включить их для вашего аккаунта, обратитесь в [службу поддержки Pushwoosh](https://help.pushwoosh.com/hc/en-us/requests/new). Во время бета-версии объектные теги работают только через API:

- В Control Panel нет экрана для объектных тегов.
- Поля объектных тегов нельзя вставить в содержимое сообщения.
- Customer Journey не поддерживает объектные теги.
</Aside>

## Как работают объектные теги

У объектного тега есть схема и значение. Схема — это имя тега и список полей, у каждого из которых свой скалярный тип. Значение хранится в полях, отдельно для каждого пользователя.

- **Поле.** Каждое поле — это тег с именем `<object>.<field>`, например `order.amount`. По полям строятся сегменты, а поле тега `Object` можно записать по этому имени.
- **Значение `Object`.** Один объект на пользователя, например `"order": {"amount": 120, "status": "paid"}`.
- **Значение `ObjectList`.** Массив объектов с одинаковыми полями, например `"orders": [{"amount": 120, "status": "paid"}, {"amount": 50}]`.

Объектные теги всегда User-specific: значение принадлежит пользователю, а не отдельному устройству, и копируется на все устройства пользователя. Об обычных тегах см. [API тегов](/ru/developer/api-reference/tags/).

## Правила схемы

Pushwoosh проверяет эти правила, когда вы создаете объектный тег или добавляете поле:

- Имена объектов и полей содержат только латинские буквы, цифры и `_`.
- Полное имя поля `<object>.<field>` не длиннее 64 символов.
- У объектного тега от 1 до 20 полей. Имена полей нечувствительны к регистру, поэтому `Amount` и `amount` — одно и то же поле.
- Поле имеет один из типов: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE`, `VERSION`. Поля с часовым поясом и вложенные объекты не поддерживаются.
- Поле не может получить имя, которое уже есть у другого тега. Если существует тег `order.note`, добавление поля `note` в `order` завершается ошибкой `tag order.note already exists`.
- Объектный тег не может лишиться последнего поля. Вместо этого удалите сам объектный тег.
- Поле или объектный тег, который используется, нельзя удалить. В ошибке перечислено, где он используется, например `field is in use: segment Big buyers, in-app Spring sale`. Использованиями считаются сегменты, In-App, запущенные кампании Customer Journey, пресеты, шаблоны email и другие объекты, которые ссылаются на тег.

## Управление схемой

Создавайте и изменяйте объектные теги с помощью REST-методов ниже. Используйте `/json/1.3/addTag` только для обычных тегов, потому что он не умеет описывать поля.

### Базовый URL

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

### Аутентификация

Каждый запрос должен содержать заголовок `Authorization` с вашим [Server API token](/ru/developer/api-reference/api-access-token/#server-api-token). На запрос с Device API token возвращается HTTP 403 `a device API token cannot call this method, use an account API token`.

```
Authorization: Api YOUR_API_TOKEN
```

### Эндпоинты

Эти методы управляют схемой объектных тегов:

| Метод | Путь | Описание |
| :---- | :---- | :---- |
| `POST` | `/api/tags` | Создать объектный тег с полями |
| `GET` | `/api/tags/{name}` | Получить объектный тег с полями |
| `GET` | `/api/tags` | Получить список тегов. Объектный тег возвращается вместе со своими `fields`, а поля не выводятся как отдельные теги |
| `POST` | `/api/tags/{name}/fields` | Добавить поле |
| `DELETE` | `/api/tags/{name}/fields/{field}` | Удалить поле |
| `DELETE` | `/api/tags/{name}` | Удалить объектный тег со всеми полями |

`POST /api/tags` и оба метода для полей возвращают объектный тег после изменения, обернутый в `{"tag": {...}}`. `DELETE /api/tags/{name}` возвращает `{}`.

Если объектные теги не включены для вашего аккаунта, `POST /api/tags` с объектным типом и оба метода для полей завершаются ошибкой HTTP 403 `object tags are not enabled for the account`. `GET /api/tags/{name}` и `DELETE /api/tags/{name}` возвращают HTTP 404 `tag not found`, а `GET /api/tags` не выводит объектные теги. Без включенных объектных тегов создать такой тег нельзя, поэтому и записывать значения некуда.

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

### Создание объектного тега

`POST` `/api/tags`

##### Тело запроса

| Имя | Тип | Описание |
| :---- | :---- | :---- |
| name\* | string | Имя объектного тега. |
| type\* | string | `OBJECT` для одного объекта или `OBJECT_LIST` для списка объектов. |
| fields\* | array | Поля объекта, от 1 до 20. |
| fields[].name\* | string | Имя поля. Поле становится тегом `<name>.<fields[].name>`. |
| fields[].type\* | string | Тип поля: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE` или `VERSION`. |

```json title="Пример"
{
  "name": "orders",
  "type": "OBJECT_LIST",
  "fields": [
    { "name": "amount", "type": "INTEGER" },
    { "name": "status", "type": "STRING" }
  ]
}
```

```json title="Ответ"
{
  "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" }
    ]
  }
}
```

Тег создается как User-specific независимо от значения `user_specific`. `GET /api/tags/{name}` возвращает тот же объект без обертки `tag`. Поля отсортированы по имени.

### Добавление поля

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

```json title="Пример: POST /api/tags/orders/fields"
{
  "field": { "name": "note", "type": "STRING" }
}
```

Новое поле можно сразу записывать в составе полного значения тега. Добавление поля завершается ошибкой, если у объектного тега уже 20 полей или если существует тег с полным именем поля, см. [Ошибки](#errors).

### Удаление поля

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

Например, `DELETE /api/tags/orders/fields/note` удаляет поле `orders.note`. Нельзя удалить последнее поле или поле, которое используется, см. [Ошибки](#errors).

<span id="errors" />

### Ошибки

Ошибки приходят в формате `{"code": <n>, "message": "...", "details": [...]}`. При создании объектного тега сообщения с кодом 400 начинаются с `validate: `.

| HTTP-статус | Сообщение | Причина |
| :---- | :---- | :---- |
| 400 | `object tag and field names must contain only letters, digits and underscores` | В имени есть другой символ. |
| 400 | `an object tag needs 1 to 20 fields` | При создании нет полей или их больше 20. |
| 400 | `an object tag has at most 20 fields` | У объектного тега уже 20 полей. |
| 400 | `field <name> is given twice` | У двух полей одинаковое имя без учета регистра. |
| 400 | `<object>.<field>: object and field names together cannot be longer than 64 characters` | Полное имя поля слишком длинное. |
| 400 | `<object>.<field>: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE or VERSION` | Неподдерживаемый тип поля. |
| 400 | `an object tag needs at least one field; delete the object tag instead` | Попытка удалить последнее поле. |
| 400 | `field is in use: ...`, `tag is in use: ...` | Поле или объектный тег используется в сегментах, In-App, кампаниях Customer Journey или других объектах. |
| 403 | `object tags are not enabled for the account` | Объектные теги не включены для аккаунта. |
| 403 | `a device API token cannot call this method, use an account API token` | В запросе используется Device API token. |
| 404 | `tag not found` | `GET` или `DELETE /api/tags/{name}`: такого тега нет, имя принадлежит полю или объектные теги не включены. |
| 404 | `object tag not found`, `field not found` | Методы для полей: такого объектного тега или поля нет. |
| 409 | `tag already exists` | Существует тег с именем объектного тега. |
| 409 | `tag <object>.<field> already exists` | Существует тег с полным именем поля. |

## Запись значений

Записывайте значения объектных тегов с помощью [`/setTags`](/ru/developer/api-reference/device-api/#settags) и [`/bulkSetTags`](/ru/developer/api-reference/audience-api/#bulksettags). У этих методов своя аутентификация: `/setTags` принимает Device API token в заголовке `Authorization: Token`, а `/bulkSetTags` — токен доступа к API в `auth`. Передавайте значение `Object` как JSON-объект, а значение `ObjectList` — как массив объектов:

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

Если вы задаете значение по HWID, оно сохраняется у пользователя этого устройства.

Каждое поле принимает значение в формате своего типа:

| Тип поля | Пример значения |
| :---- | :---- |
| `INTEGER` | `120` |
| `STRING` | `"paid"` |
| `LIST` | `["gift", "express"]` |
| `DATE` | `"2026-10-02 22:11"` (UTC), `"2026-10-02"` или Unix-время в секундах |
| `BOOLEAN` | `true` или `false` |
| `PRICE` | `19.95` |
| `VERSION` | `"1.0.0"` |

### Запись заменяет все значение

Каждая запись заменяет сохраненное значение объектного тега:

- Поле, которого нет в новом значении, удаляется. Поле, переданное как `null`, тоже удаляется.
- `null`, `[]` или `""` удаляет все значение.
- `{}` удаляет все поля тега `Object`.
- Ключ, который не является полем объекта, пропускается.
- Если значение поля не соответствует типу поля, все значение тега отклоняется, а сохраненное значение остается прежним.

`/setTags` не сообщает об отклоненном значении в ответе. Проверьте результат через [`/getTags`](#read-values) или используйте `/bulkSetTags`, который возвращает результат по каждому тегу.

### Запись одного поля Object

Записывайте отдельное поле тега `Object` по его полному имени. `increment` и другие операции [`/setTags`](/ru/developer/api-reference/device-api/#settags) работают с полями:

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

Поле тега `ObjectList` нельзя записать отдельно, потому что непонятно, какой элемент изменить. `/setTags` игнорирует такое значение. Вместо этого передайте весь список.

### Объектные теги не создаются автоматически

Значение с объектом или массивом объектов для несуществующего тега пропускается. Автоматическое создание тегов не создает объектные теги, поэтому сначала создайте схему через [`POST /api/tags`](#create-an-object-tag).

### Результаты bulkSetTags

С `detailed=true` [статус `/bulkSetTags`](/ru/developer/api-reference/audience-api/#bulksettags-status) возвращает для объектного тега один из этих результатов в дополнение к `ok`, `tag not found` и `wrong value. expect :<type>`:

| Результат | Причина |
| :---- | :---- |
| `object tags are stored on the user only` | Объектный тег не User-specific. |
| `cannot load the fields of the object` | Pushwoosh не удалось прочитать поля объектного тега. |
| `wrong value. expect an object (a map) or, for a list of objects, an array of maps` | Значение не подходит объектному тегу или значение поля не соответствует его типу. |
| `a field of a list of objects cannot be set on its own, set the whole list` | Значение задается для поля тега `ObjectList`. |

<span id="read-values" />

## Чтение значений

[`/getTags`](/ru/developer/api-reference/device-api/#gettags) возвращает значение `Object` как JSON-объект, а значение `ObjectList` — как массив объектов:

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

Значения полей возвращаются в `/getTags` так же, как значения обычных тегов: Date — как Unix-время в секундах, Price — как строка, например `"120.00"`, а Boolean — как `1` или `0`.

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

## Сегментация по полям

Задавайте условия сегмента по полям, а не по самому объектному тегу. В [языке сегментации](/ru/developer/api-reference/segmentation-filters-api/segmentation-language/) поле — это тег с именем через точку, и к нему применяются операторы его собственного типа:

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

Условие по самому объектному тегу завершается ошибкой `object tag has no value of its own, use one of its fields`.

### Условия по ObjectList

Условие по полю тега `ObjectList` выполняется для пользователя, если хотя бы один элемент списка удовлетворяет условию. Условия по полям одного списка, объединенные через AND, должны выполняться для одного и того же элемента:

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

В этот сегмент попадают пользователи, у которых есть хотя бы один заказ на 100 или больше, и при этом оплаченный. Пользователь с одним крупным неоплаченным заказом и одним небольшим оплаченным в сегмент не попадает. Условия, объединенные через OR, не обязаны выполняться для одного и того же элемента. Условия по двум разным спискам проверяются для каждого списка отдельно.

Некоторые операторы работают для поля тега `ObjectList` иначе:

- **`notset`.** Ни у одного элемента списка нет значения в этом поле. Пользователь без списка тоже подходит.
- **`noteq`.** Хотя бы у одного элемента есть значение в этом поле, и оно отличается от заданного. Элементы без этого поля не учитываются. Пользователь с заказами в статусах `paid` и `new` подходит под `noteq "paid"`.
- **`notin` для поля типа List.** Ни один элемент не содержит ни одного из заданных значений.
- **`notin` для поля другого типа.** Хотя бы у одного элемента в этом поле есть значение, которого нет среди заданных.

`notset` и `notin` для поля типа List не привязаны к тому же элементу, что и другие условия, объединенные через AND.

## Экспорт сегментов с объектными тегами

[`/exportSegment`](/ru/developer/api-reference/segmentation-filters-api/#exportsegment) экспортирует поля объектных тегов:

- **CSV.** Каждое поле — отдельный столбец с именем `<object>.<field>`, а для самих объектных тегов столбца нет. Поле ObjectList содержит все различные значения по всем элементам в виде JSON-массива, так же как тег типа List: `["120","50"]`.
- **`json` и `json_each_line`.** Поля тега `Object` выводятся как отдельные теги с именем `<object>.<field>`. Тег `ObjectList` выводится одной записью в `tags` с элементами в `object_list.items`. Каждый элемент перечисляет свои поля по коротким именам, а каждое поле хранит значение под ключом, названным по его типу: `integer`, `string`, `list`, `date`, `boolean`, `price` или `version`. Значения Integer выводятся как строки:

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

Если `tagsList` указывает объектный тег, экспортируются все его поля.