Object tags API (beta)
Object tags store structured data about a user in one tag. A tag of the Object type holds one object with named fields, such as the last order with its amount and status. A tag of the ObjectList type holds an array of such objects, such as all orders. You define the fields once, write and read values through the tag API, and build segments by field values.
How object tags work
Anchor link toAn object tag has a schema and a value. The schema is the tag name and a list of fields, each with its own scalar type. The value is stored in the fields, separately for each user.
- Field. Each field is a tag named
<object>.<field>, for exampleorder.amount. You segment by fields, and you can write a field of anObjecttag by this name. Objectvalue. One object per user, for example"order": {"amount": 120, "status": "paid"}.ObjectListvalue. An array of objects with the same fields, for example"orders": [{"amount": 120, "status": "paid"}, {"amount": 50}].
Object tags are always User-specific, which means the value belongs to the user, not to a single device, and is copied to all of the user’s devices. See Tags API for regular tags.
Schema rules
Anchor link toPushwoosh checks these rules when you create an object tag or add a field:
- Object and field names contain only Latin letters, digits, and
_. - The full field name
<object>.<field>is 64 characters or shorter. - An object tag has from 1 to 20 fields. Field names are case-insensitive, so
Amountandamountare the same field. - A field has one of these types:
INTEGER,STRING,LIST,DATE,BOOLEAN,PRICE,VERSION. Timezone fields and nested objects aren’t supported. - A field can’t take a name that another tag already has. If a tag
order.noteexists, adding the fieldnotetoorderfails withtag order.note already exists. - An object tag can’t lose its last field. Delete the object tag instead.
- A field or an object tag that is in use can’t be deleted. The error lists where it is used, for example
field is in use: segment Big buyers, in-app Spring sale. Segments, in-apps, running Customer Journey campaigns, presets, email templates, and other items that reference the tag count as usages.
Manage the schema
Anchor link toCreate and change object tags with the REST methods below. Use /json/1.3/addTag only for regular tags, because it can’t define fields.
Base URL
Anchor link tohttps://rpc-api.svc-nue.pushwoosh.comAuthentication
Anchor link toEvery request must include an Authorization header with your Server API token. A Device API token gets HTTP 403 a device API token cannot call this method, use an account API token.
Authorization: Api YOUR_API_TOKENEndpoints
Anchor link toThese methods manage the schema of object tags:
| Method | Path | Description |
|---|---|---|
POST | /api/tags | Create an object tag with its fields |
GET | /api/tags/{name} | Get an object tag with its fields |
GET | /api/tags | List tags. An object tag comes with its fields, and the fields aren’t listed as separate tags |
POST | /api/tags/{name}/fields | Add a field |
DELETE | /api/tags/{name}/fields/{field} | Delete a field |
DELETE | /api/tags/{name} | Delete an object tag with all its fields |
POST /api/tags and both field methods return the object tag after the change, wrapped in {"tag": {...}}. DELETE /api/tags/{name} returns {}.
If object tags aren’t enabled for your account, POST /api/tags with an object type and both field methods fail with HTTP 403 object tags are not enabled for the account. GET /api/tags/{name} and DELETE /api/tags/{name} return HTTP 404 tag not found, and GET /api/tags doesn’t list object tags. Without object tags enabled you can’t create one, so there is nothing to write values to.
Create an object tag
Anchor link toPOST /api/tags
Request body
Anchor link to| Name | Type | Description |
|---|---|---|
| name* | string | Name of the object tag. |
| type* | string | OBJECT for one object or OBJECT_LIST for a list of objects. |
| fields* | array | Fields of the object, from 1 to 20. |
| fields[].name* | string | Field name. The field becomes the tag <name>.<fields[].name>. |
| fields[].type* | string | Field type: INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE, or VERSION. |
{ "name": "orders", "type": "OBJECT_LIST", "fields": [ { "name": "amount", "type": "INTEGER" }, { "name": "status", "type": "STRING" } ]}{ "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" } ] }}The tag is created as User-specific regardless of the user_specific value. GET /api/tags/{name} returns the same object without the tag wrapper. Fields are sorted by name.
Add a field
Anchor link toPOST /api/tags/{name}/fields
{ "field": { "name": "note", "type": "STRING" }}You can write the new field right away as part of the whole tag value. Adding a field fails if the object tag already has 20 fields or if a tag with the full field name exists, see Errors.
Delete a field
Anchor link toDELETE /api/tags/{name}/fields/{field}
For example, DELETE /api/tags/orders/fields/note deletes the field orders.note. You can’t delete the last field or a field that is in use, see Errors.
Errors
Anchor link toErrors come in the format {"code": <n>, "message": "...", "details": [...]}. When you create an object tag, the 400 messages start with validate: .
| HTTP status | Message | Cause |
|---|---|---|
| 400 | object tag and field names must contain only letters, digits and underscores | A name has another character. |
| 400 | an object tag needs 1 to 20 fields | No fields or more than 20 fields on create. |
| 400 | an object tag has at most 20 fields | The object tag already has 20 fields. |
| 400 | field <name> is given twice | Two fields have the same name, ignoring case. |
| 400 | <object>.<field>: object and field names together cannot be longer than 64 characters | The full field name is too long. |
| 400 | <object>.<field>: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE or VERSION | Unsupported field type. |
| 400 | an object tag needs at least one field; delete the object tag instead | You tried to delete the last field. |
| 400 | field is in use: ..., tag is in use: ... | The field or the object tag is used in segments, in-apps, Customer Journey campaigns, or other items. |
| 403 | object tags are not enabled for the account | Object tags aren’t enabled for the account. |
| 403 | a device API token cannot call this method, use an account API token | The request uses a Device API token. |
| 404 | tag not found | GET or DELETE /api/tags/{name}: no such tag, the name is a field, or object tags aren’t enabled. |
| 404 | object tag not found, field not found | Field methods: no such object tag or field. |
| 409 | tag already exists | A tag with the object tag name exists. |
| 409 | tag <object>.<field> already exists | A tag with the field’s full name exists. |
Write values
Anchor link toWrite object tag values with /setTags and /bulkSetTags. These methods keep their own authentication: /setTags takes a Device API token in the Authorization: Token header, and /bulkSetTags takes the API access token in auth. Send an Object value as a JSON object and an ObjectList value as an array of objects:
{ "request": { "application": "XXXXX-XXXXX", "userId": "user_42", "tags": { "order": { "amount": 120, "status": "paid" }, "orders": [ { "amount": 120, "status": "paid" }, { "amount": 50 } ] } }}If you set the value by HWID, it is stored on the user of that device.
Each field takes a value in the format of its type:
| Field type | Example value |
|---|---|
INTEGER | 120 |
STRING | "paid" |
LIST | ["gift", "express"] |
DATE | "2026-10-02 22:11" (UTC), "2026-10-02", or a Unix timestamp in seconds |
BOOLEAN | true or false |
PRICE | 19.95 |
VERSION | "1.0.0" |
Writing replaces the whole value
Anchor link toEach write replaces the stored value of the object tag:
- A field missing from the new value is deleted. A field sent as
nullis deleted too. null,[], or""deletes the whole value.{}deletes all fields of anObjecttag.- A key that isn’t a field of the object is skipped.
- If a field value doesn’t match the field type, the whole value of the tag is rejected and the stored value stays as it was.
/setTags doesn’t report a rejected value in its response. Check the result with /getTags or use /bulkSetTags, which reports a result for each tag.
Write one field of an Object
Anchor link toWrite a single field of an Object tag by its full name. increment and the other operations of /setTags work on fields:
"tags": { "order.status": "shipped", "order.amount": { "operation": "increment", "value": 5 }}A field of an ObjectList tag can’t be written on its own, because it isn’t clear which element to change. /setTags ignores such a value. Send the whole list instead.
Object tags aren’t created automatically
Anchor link toA value with an object or an array of objects for a tag that doesn’t exist is skipped. Tag auto-creation doesn’t create object tags, so create the schema first with POST /api/tags.
bulkSetTags results
Anchor link toWith detailed=true, /bulkSetTags status returns one of these results for an object tag, in addition to ok, tag not found, and wrong value. expect :<type>:
| Result | Cause |
|---|---|
object tags are stored on the user only | The object tag isn’t User-specific. |
cannot load the fields of the object | Pushwoosh couldn’t read the fields of the object tag. |
wrong value. expect an object (a map) or, for a list of objects, an array of maps | The value doesn’t fit the object tag, or a field value doesn’t match its type. |
a field of a list of objects cannot be set on its own, set the whole list | The value is set on a field of an ObjectList tag. |
Read values
Anchor link to/getTags returns an Object value as a JSON object and an ObjectList value as an array of objects:
{ "status_code": 200, "status_message": "OK", "response": { "result": { "order": { "amount": 120, "status": "paid" }, "orders": [ { "amount": 120, "status": "paid" }, { "amount": 50 } ] } }}Field values are returned the same way as regular tag values in /getTags: a Date as a Unix timestamp in seconds, a Price as a string such as "120.00", and a Boolean as 1 or 0.
Segment by fields
Anchor link toSet segment conditions on fields, not on the object tag itself. In the segmentation language, a field is a tag with a dotted name, and it takes the operators of its own type:
AT("XXXXX-XXXXX", "order.amount", gte, 100)A condition on the object tag itself fails with object tag has no value of its own, use one of its fields.
Conditions on an ObjectList
Anchor link toA condition on a field of an ObjectList tag matches a user when at least one element of the list fits the condition. Conditions on fields of the same list joined with AND must match the same element:
AT("XXXXX-XXXXX", "orders.amount", gte, 100) * AT("XXXXX-XXXXX", "orders.status", eq, "paid")This segment holds users with at least one order that is both 100 or more and paid. A user with one large unpaid order and one small paid order doesn’t match. Conditions joined with OR don’t have to match the same element. Conditions on two different lists are checked on each list separately.
Some operators work differently on a field of an ObjectList tag:
notset. No element of the list has a value in this field. A user without the list matches too.noteq. At least one element has a value in this field, and that value differs from the given one. Elements without the field don’t count. A user with orders in statusespaidandnewmatchesnoteq "paid".notinon a List field. No element contains any of the given values.notinon a field of another type. At least one element has a value in this field that isn’t among the given values.
notset and notin on a List field aren’t tied to the same element as the other conditions joined with AND.
Export segments with object tags
Anchor link to/exportSegment exports the fields of object tags:
- CSV. Each field is a separate column named
<object>.<field>, and object tags themselves get no column. A field of an ObjectList holds all distinct values across the elements as a JSON array, the same way as a List tag:["120","50"]. jsonandjson_each_line. Fields of anObjecttag come as separate tags named<object>.<field>. AnObjectListtag comes as one entry intagswith the elements inobject_list.items. Each element lists its fields with short names, and each field carries its value under a key named after its type:integer,string,list,date,boolean,price, orversion. Integer values come as strings:
{ "name": "orders", "object_list": { "items": [ { "fields": [{ "name": "amount", "integer": "120" }, { "name": "status", "string": "paid" }] }, { "fields": [{ "name": "amount", "integer": "50" }] } ] }}If tagsList names an object tag, all of its fields are exported.