Tags
Add text, number, boolean and date attributes to devices and users, then filter audiences by type.
Tags describe a device or a user. Use them to build audiences. Notification tags are different: they categorize campaigns for message history and statistics.
Choose a scope and a type
Device tags belong to one installation. Set them through a mobile SDK or the device API.
User tags belong to an external_id, within one app and live/test mode. Set them from your
backend; all devices identified with that external ID use the same profile. A profile can exist
before a device registers. See shared user tags.
Each scope has four independent maps:
| Map | Value | Example |
|---|---|---|
tags | Text | "plan": "premium" |
number_tags | Integer or decimal number | "total_spent": 49.95 |
boolean_tags | JSON boolean | "has_subscription": false |
date_tags | ISO 8601 timestamp with a timezone | "expires_at": "2026-10-01T12:00:00.000Z" |
The same key may exist in several maps. tags.expires_at and date_tags.expires_at are
independent: writing or removing one never changes the other. There is no type inference
from strings. "12" is text, not a number; "false" is text, not a boolean.
Number tags accept integers and decimals, including negative values. NaN and infinities are rejected.
Date tags represent instants. The API validates them and stores canonical UTC timestamps with milliseconds. A date without a time or timezone is rejected. Birthdays and recurring calendar anniversaries are not supported as date-only types.
Set device tags with an SDK
These methods require SDK 0.4.0 or later. These methods queue changes and synchronize them with device registration. Pending changes survive restarts and network retries. Acknowledged tags are not replayed during unrelated metadata updates.
React Native
Carillon.setTag('plan', 'premium');
Carillon.setTagNumber('total_spent', 49.95);
Carillon.setTagBoolean('has_subscription', false);
Carillon.setTagDate('expires_at', new Date('2026-10-01T12:00:00Z'));setTagDate requires a valid JavaScript Date; it does not accept a string or timestamp number.
setTagNumber rejects NaN and infinities.
Swift
Carillon.setTag("plan", "premium")
Carillon.setTagNumber("total_spent", 49.95)
Carillon.setTagBoolean("has_subscription", false)
Carillon.setTagDate("expires_at", Date(timeIntervalSince1970: 1_790_856_000))The date method takes Foundation Date. Numeric values must be finite.
Kotlin
Carillon.setTag("plan", "premium")
Carillon.setTagNumber("total_spent", 49.95)
Carillon.setTagBoolean("has_subscription", false)
Carillon.setTagDate("expires_at", java.util.Date(1_790_856_000_000L))The date method takes java.util.Date. Numeric values must be finite.
All three SDKs provide removeTag, removeTagNumber, removeTagBoolean and removeTagDate,
each taking the key. setTags merges a map of strings; null values (nil in Swift) remove text tags.
After synchronization, open Devices in the dashboard and select the device to inspect its
Device tags and, when an external ID is linked, User tags. You can also read device tags
through GET /v1/devices/{id}.
Write tags from your backend
Use a secret key with devices:write for device tags, or users:write for user tags.
Mobile keys can only set device tags through their registration calls; they cannot write user profiles.
curl -X PATCH https://api.carillon.dev/v1/devices/DEVICE_ID \
-H "Authorization: Bearer $CARILLON_SECRET_KEY" \
-H 'Content-Type: application/json' \
-d '{
"tags": { "plan": "premium" },
"number_tags": { "purchase_count": 12 },
"boolean_tags": { "has_subscription": false },
"date_tags": { "expires_at": "2026-10-01T12:00:00Z" }
}'For a user, send the same body to PATCH /v1/users/usr_123/tags. It creates the profile if
necessary. Both endpoints return 200 and the resulting maps. POST /v1/devices accepts the
same optional maps when registering a device.
Only supplied keys change. Omitted maps and keys remain unchanged. A null entry removes one key from its map:
{ "date_tags": { "expires_at": null } }A typed map set to null clears that entire type: { "number_tags": null }. Each map is
limited to 50 keys and 4096 UTF-8 JSON bytes after merging. Keys contain 1–64 characters;
text values contain at most 256 characters. Invalid types, timestamps or limits return 400.
The whole request is atomic: if one map is invalid, no map is updated.
Backend tag patches do not refresh device activity or registration metadata. Deleting a user profile removes all its tag maps without deleting or detaching its devices.
Filter an audience
In the dashboard, choose Device tag or User tag, then choose a tag. The selector shows each key with its type in parentheses; selecting it sets the available comparisons automatically. Suggestions come from reachable live devices and their associated user profiles. You may also enter a key before any device has reported it.
| Type | Operators |
|---|---|
| Text | Equals, one of, none of |
| Number | Equals, greater than, at least, less than, at most, between |
| Boolean | True or false |
| Date | Before, after, between, within the last/next N days, more than N days ago |
| Every type | Is set, is not set |
A missing boolean is not false. A missing number is not zero. Value comparisons exclude missing
typed tags; use exists: false to target their absence. Same-name values in other maps do not count.
Create or preview audiences using these filter fields:
| Scope | Text | Number | Boolean | Date |
|---|---|---|---|---|
| Device | tag | number_tag | boolean_tag | date_tag |
| User | user_tag | user_number_tag | user_boolean_tag | user_date_tag |
{
"name": "Renewing subscribers",
"definition": {
"filters": [
{ "field": "user_boolean_tag", "key": "has_subscription", "value": true },
{ "field": "user_number_tag", "key": "purchase_count", "gte": 3 },
{ "field": "user_date_tag", "key": "expires_at", "within_next_days": 7 }
]
}
}Send this to POST /v1/apps/APP_ID/audiences using a management token. Preview its definition
at POST /v1/apps/APP_ID/audiences/preview first. All filters must match.
Number filters accept exactly one of value, gt, gte, lt, lte, between: [start, end],
or exists. Boolean filters accept value or exists. Date filters accept before, after,
between: [start, end], within_last_days, within_next_days, older_than_days, or exists.
Ranges include both bounds; before/after and greater/less comparisons are strict. Relative windows use whole days of 24 hours, from 1 to 3650, evaluated when the audience is resolved. The last N days excludes future dates; the next N days excludes past dates. Saving an audience does not freeze the clock. These filters select recipients; they do not schedule a campaign.
Use tags through MCP
The MCP tools expose device_tags_read, device_tags_patch, user_tags_read and
user_tags_patch for live data. Supply app_id, then device_id or external_id, and the maps
to merge. Each call checks your organization membership. They never send notifications.
audience_preview and audience_create accept the same typed filters as the API. Preview an
audience before saving or sending to it.