Tags
Tags are free-form labels that can be attached to any ZeyOS entity record (contacts, accounts, transactions, tickets, etc.). Unlike ExtData, which stores structured key-value data, tags are simple strings used for categorization, filtering, and workflow routing. A single record can carry any number of tags, and the same tag name can appear on records across different entity types.
Common uses for tags include:
- Classification — labeling contacts as "VIP", "Partner", or "Prospect"
- Workflow state — marking records as "Needs Review" or "Approved"
- Filtering — selecting subsets of records in queries and exports
- Segmentation — grouping records for reports or bulk operations
Adding tags
Use tags:add to attach one or more tags to a record. Specify the entity type and record ID, then list the tag names as tags:name children:
<tags:add entity="contacts" id="$contactId">
<tags:name>VIP</tags:name>
<tags:name>Newsletter</tags:name>
</tags:add>
Adding a tag that already exists on the record is a no-op — it will not create duplicates.
Tags inside db:set
When creating or updating records with db:set, you can include tags:name children directly alongside db:data and extdata:data:
<db:set entity="contacts" var="contactId">
<db:data field="firstname">Ada</db:data>
<db:data field="lastname">Lovelace</db:data>
<tags:name>Customer</tags:name>
<tags:name>A-Class</tags:name>
</db:set>
This sets the record's tags as part of the same operation. Note that db:set replaces all existing tags with the ones you specify (like tags:reset), whereas tags:add appends to the existing tag set.
Removing tags
Use tags:remove to detach specific tags from a record:
<tags:remove entity="contacts" id="$contactId">
<tags:name>Prospect</tags:name>
</tags:remove>
Replacing all tags
Use tags:reset to remove all existing tags and replace them with a new set:
<tags:reset entity="contacts" id="$contactId">
<tags:name>Customer</tags:name>
<tags:name>Active</tags:name>
</tags:reset>
After this call, the record has exactly two tags — regardless of what was there before.
Reading tags
Listing tags on a record
Use tags:list to retrieve all tag names for a specific record as an array:
<tags:list var="tags" entity="contacts" id="$contactId" />
<foreach var="tags" var_value="tag">
<output>$tag&n;</output>
</foreach>
Checking if tags exist
Use tags:exists to check whether a record has any tags at all:
<tags:exists var="hasTags" entity="contacts" id="$contactId" />
<if value1="$hasTags">
<output>This contact has tags.</output>
</if>
To check for a specific tag, combine tags:list with an array search:
<tags:list var="tags" entity="contacts" id="$contactId" />
<array:search var="tags" var_result="found">VIP</array:search>
<if value1="$found" func="!=">
<output>Contact is a VIP.</output>
</if>
Passing tags via variable
All tag commands accept var_names to supply tag names from an array variable instead of inline tags:name children:
<array var="tagList">
<item>Customer</item>
<item>Priority</item>
</array>
<tags:add entity="contacts" id="$contactId" var_names="tagList" />
Filtering queries by tags
db:intags — SQL-level tag filtering
Use db:intags inside db:select to filter results to records that carry specific tags:
<db:select var_result="vips" type="assoc">
<db:fields>
<db:field>c.firstname</db:field>
<db:field>c.lastname</db:field>
<db:field>c.email</db:field>
</db:fields>
<db:table alias="c">contacts</db:table>
<db:intags entity="contacts" field="c.ID">
<tags:name>VIP</tags:name>
</db:intags>
</db:select>
You can also pass tag names from a variable:
<array var="filterTags">
<item>VIP</item>
<item>Partner</item>
</array>
<db:select var_result="results" type="assoc">
<db:fields>
<db:field>c.firstname</db:field>
<db:field>c.lastname</db:field>
</db:fields>
<db:table alias="c">contacts</db:table>
<db:intags entity="contacts" field="c.ID" var="filterTags" />
</db:select>
Tag joins — db:innertags and db:lefttags
For more complex queries, you can join on the tags table directly. Use db:innertags to include only records with a specific tag, or db:lefttags to include all records while making the tag data available for conditional logic:
<db:select var_result="customers" type="assoc">
<db:fields>
<db:field>c.firstname</db:field>
<db:field>c.lastname</db:field>
</db:fields>
<db:table alias="c">contacts</db:table>
<db:join>
<db:innertags alias="t" entity="contacts" name="Customer" field="c.ID" />
</db:join>
</db:select>
db:innertags reference → | db:lefttags reference →
Practical patterns
Tag-based workflow routing
<tags:list var="tags" entity="tickets" id="$ticketId" />
<!-- Check if the ticket needs escalation -->
<array:search var="tags" var_result="isUrgent">Urgent</array:search>
<if value1="$isUrgent" func="!=">
<!-- Escalation logic -->
<tags:add entity="tickets" id="$ticketId">
<tags:name>Escalated</tags:name>
</tags:add>
</if>
Bulk tagging from a query
<db:select var_result="overdue" type="assoc">
<db:fields>
<db:field>ID</db:field>
</db:fields>
<db:table>transactions</db:table>
<db:is field="status">0</db:is>
<db:is field="duedate" func="<">$DATENOW</db:is>
</db:select>
<foreach var="overdue" var_value="row">
<tags:add entity="transactions" id="$row.ID">
<tags:name>Overdue</tags:name>
</tags:add>
</foreach>