Skip to content

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 to

An 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 example order.amount. You segment by fields, and you can write a field of an Object tag by this name.
  • Object value. One object per user, for example "order": {"amount": 120, "status": "paid"}.
  • ObjectList value. 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 to

Pushwoosh 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 Amount and amount are 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.note exists, adding the field note to order fails with tag 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 to

Create and change object tags with the REST methods below. Use /json/1.3/addTag only for regular tags, because it can’t define fields.

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

Authentication

Anchor link to

Every 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_TOKEN

These methods manage the schema of object tags:

MethodPathDescription
POST/api/tagsCreate an object tag with its fields
GET/api/tags/{name}Get an object tag with its fields
GET/api/tagsList tags. An object tag comes with its fields, and the fields aren’t listed as separate tags
POST/api/tags/{name}/fieldsAdd 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 to

POST /api/tags

Request body
Anchor link to
NameTypeDescription
name*stringName of the object tag.
type*stringOBJECT for one object or OBJECT_LIST for a list of objects.
fields*arrayFields of the object, from 1 to 20.
fields[].name*stringField name. The field becomes the tag <name>.<fields[].name>.
fields[].type*stringField type: INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE, or VERSION.
Example
{
"name": "orders",
"type": "OBJECT_LIST",
"fields": [
{ "name": "amount", "type": "INTEGER" },
{ "name": "status", "type": "STRING" }
]
}
Response
{
"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 to

POST /api/tags/{name}/fields

Example: POST /api/tags/orders/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 to

DELETE /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 come in the format {"code": <n>, "message": "...", "details": [...]}. When you create an object tag, the 400 messages start with validate: .

HTTP statusMessageCause
400object tag and field names must contain only letters, digits and underscoresA name has another character.
400an object tag needs 1 to 20 fieldsNo fields or more than 20 fields on create.
400an object tag has at most 20 fieldsThe object tag already has 20 fields.
400field <name> is given twiceTwo fields have the same name, ignoring case.
400<object>.<field>: object and field names together cannot be longer than 64 charactersThe full field name is too long.
400<object>.<field>: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE or VERSIONUnsupported field type.
400an object tag needs at least one field; delete the object tag insteadYou tried to delete the last field.
400field 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.
403object tags are not enabled for the accountObject tags aren’t enabled for the account.
403a device API token cannot call this method, use an account API tokenThe request uses a Device API token.
404tag not foundGET or DELETE /api/tags/{name}: no such tag, the name is a field, or object tags aren’t enabled.
404object tag not found, field not foundField methods: no such object tag or field.
409tag already existsA tag with the object tag name exists.
409tag <object>.<field> already existsA tag with the field’s full name exists.

Write values

Anchor link to

Write 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:

Example: /json/1.3/setTags
{
"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 typeExample value
INTEGER120
STRING"paid"
LIST["gift", "express"]
DATE"2026-10-02 22:11" (UTC), "2026-10-02", or a Unix timestamp in seconds
BOOLEANtrue or false
PRICE19.95
VERSION"1.0.0"

Writing replaces the whole value

Anchor link to

Each write replaces the stored value of the object tag:

  • A field missing from the new value is deleted. A field sent as null is deleted too.
  • null, [], or "" deletes the whole value.
  • {} deletes all fields of an Object tag.
  • 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 to

Write 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 to

A 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 to

With 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>:

ResultCause
object tags are stored on the user onlyThe object tag isn’t User-specific.
cannot load the fields of the objectPushwoosh 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 mapsThe 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 listThe 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:

Response
{
"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 to

Set 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 to

A 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 statuses paid and new matches noteq "paid".
  • notin on a List field. No element contains any of the given values.
  • notin on 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"].
  • json and json_each_line. Fields of an Object tag come as separate tags named <object>.<field>. An ObjectList tag comes as one entry in tags with the elements in object_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, or version. 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.