Overview
Tags let you organize contacts into groups. Each tag belongs to a workspace and can have a name, color, and emoji. Contacts can have multiple tags, and you can filter your contact list by one or more tags at once. The relationship between contacts and tags is many-to-many, managed through acontact_tags junction table. Tag assignment is idempotent — adding a tag that’s already assigned is a no-op.
Creating Tags
Create a tag via the UI or the API:nameis required (1–100 characters)colorandemojiare optional
Updating & Deleting Tags
- Update —
PUT /api/tags/:idaccepts partial updates (name, color, emoji) - Delete —
DELETE /api/tags/:idpermanently removes the tag and all its contact associations (via cascade)
Assigning Tags to Contacts
Tags are assigned and removed through dedicated endpoints:onConflictDoNothing — if the contact already has that tag, the request succeeds silently. This makes the operation safe to retry without side effects.
Both the tag and the contact must exist and belong to the same workspace, otherwise the API returns a 404.
Multi-Tag Filtering
When listing contacts, you can filter by multiple tags at once by passing a comma-separated list of tag IDs:contacts with contact_tags using inArray(contactTags.tag_id, tagIds).
To avoid double-counting contacts that have multiple matching tags, the total count uses COUNT(DISTINCT contacts.id) instead of a plain COUNT(*).
Tags in Exports
When creating an export, you can optionally passtagIds to export only contacts that match those tags. See the Exports guide for details.
Data Model
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
workspace_id | UUID | Workspace scope |
name | VARCHAR(100) | Tag display name |
color | VARCHAR(20) | Optional color identifier |
emoji | VARCHAR(10) | Optional emoji |
contact_tags junction table has a composite unique constraint on (contact_id, tag_id) to prevent duplicate assignments.