Tag Management Guide
Overview
Tags provide a flexible, flat labelling system for products in the Operations module. Unlike hierarchical categories that enforce a single tree structure, tags let you apply multiple cross-cutting labels to any product - enabling rich filtering, search facets, and promotional groupings without altering the category tree.
Each tag carries a name, a URL-safe slug, an optional description, a hex colour code for UI display, and a simple active/inactive flag. Tags support merge operations for consolidating duplicates and bulk endpoints for efficient catalog maintenance.
Tag Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | ULID string | Auto-generated | Unique identifier, e.g. 01hwxyz123abc456def789ghi0 |
name | string | Yes | Display name (max 100 characters, unique per tenant) |
slug | string | Yes | URL-safe identifier (lowercase letters, numbers, hyphens only; max 100 characters; unique per tenant) |
description | string | No | Optional descriptive text (max 500 characters) |
color | string | No | Hex colour code, e.g. #3B82F6 (defaults to #3B82F6) |
is_active | boolean | No | Active status flag (defaults to true) |
created_at | ISO 8601 datetime | Auto-generated | Record creation timestamp |
updated_at | ISO 8601 datetime | Auto-generated | Record last-modified timestamp |
Tag Lifecycle
Tags follow a simple active/inactive lifecycle identical to brands:
| Status | Description | Effect on Products |
|---|---|---|
| Active | Tag is available for use | Products can be tagged with this label |
| Inactive | Tag is suspended from use | Existing product associations are preserved; new associations are blocked |
Deactivation vs. Deletion
Deactivating a tag is non-destructive. Products already tagged retain the association. Use deactivation when a tag is no longer relevant but historical data should be preserved. Use deletion only when the tag was created in error and has no product associations.
API Endpoints
All tag endpoints are scoped under /api/v1/operations/tags. Every request requires a valid Bearer token.
List Tags
Endpoint: GET /api/v1/operations/tags
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Filter by name or slug (partial match) |
is_active | boolean | Filter by active status (true or false) |
has_products | boolean | Filter to tags with (true) or without (false) associated products |
color | string | Filter by hex colour code |
sort_by | string | Sort field: name, created_at, updated_at (default: name) |
sort_direction | string | asc or desc (default: asc) |
page | integer | Page number (default: 1) |
per_page | integer | Results per page (default: 15) |
Example Request:
GET /api/v1/operations/tags?is_active=true&sort_by=name&sort_direction=asc&per_page=25Response (200 OK):
{
"success": true,
"message": "Tags retrieved successfully",
"data": [
{
"id": "01hwxyz123abc456def789ghi0",
"name": "Electronics",
"slug": "electronics",
"description": "Products related to electronic devices and components",
"color": "#3B82F6",
"is_active": true,
"created_at": "2025-10-01T08:00:00Z",
"updated_at": "2025-11-15T14:30:00Z"
},
{
"id": "01hwxyz789def012ghi345jkl6",
"name": "Seasonal",
"slug": "seasonal",
"description": "Seasonal and limited-availability products",
"color": "#F59E0B",
"is_active": true,
"created_at": "2025-10-12T11:20:00Z",
"updated_at": "2025-10-12T11:20:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 24,
"last_page": 1
}
}Create a Tag
Endpoint: POST /api/v1/operations/tags
Request Body:
{
"name": "Electronics",
"slug": "electronics",
"description": "Products related to electronic devices and components",
"color": "#3B82F6",
"is_active": true
}Required Fields:
name- Display name of the tag (max 100 characters, unique)slug- URL-safe identifier (lowercase letters, numbers, and hyphens only; unique)
Optional Fields:
description- Free-text description (max 500 characters)color- Hex colour code (format:#RRGGBB); defaults to#3B82F6is_active- Defaults totrue
Response (201 Created):
{
"success": true,
"message": "Tag created successfully",
"data": {
"id": "01hwxyz123abc456def789ghi0",
"name": "Electronics",
"slug": "electronics",
"description": "Products related to electronic devices and components",
"color": "#3B82F6",
"is_active": true,
"created_at": "2025-12-17T10:30:00Z",
"updated_at": "2025-12-17T10:30:00Z"
},
"meta": {}
}Error Response (422 Unprocessable Entity):
{
"success": false,
"message": "The given data was invalid.",
"data": null,
"meta": {
"errors": {
"name": ["A tag with this name already exists"],
"slug": ["Tag slug must contain only lowercase letters, numbers, and hyphens"]
}
}
}Get Tag Details
Endpoint: GET /api/v1/operations/tags/{id}
Example:
GET /api/v1/operations/tags/01hwxyz123abc456def789ghi0Response (200 OK):
{
"success": true,
"message": "Tags retrieved successfully",
"data": {
"id": "01hwxyz123abc456def789ghi0",
"name": "Electronics",
"slug": "electronics",
"description": "Products related to electronic devices and components",
"color": "#3B82F6",
"is_active": true,
"created_at": "2025-10-01T08:00:00Z",
"updated_at": "2025-11-15T14:30:00Z"
},
"meta": {}
}Error Response (404 Not Found):
{
"success": false,
"message": "Tag not found",
"data": null,
"meta": {}
}Update a Tag
Endpoint: PUT /api/v1/operations/tags/{id}
All fields are optional - send only the fields you want to change.
Request Body:
{
"name": "Home Appliances",
"slug": "home-appliances",
"description": "Household and kitchen appliances",
"color": "#10B981"
}Response (200 OK):
{
"success": true,
"message": "Tag updated successfully",
"data": {
"id": "01hwxyz123abc456def789ghi0",
"name": "Home Appliances",
"slug": "home-appliances",
"description": "Household and kitchen appliances",
"color": "#10B981",
"is_active": true,
"updated_at": "2025-12-17T14:00:00Z"
},
"meta": {}
}Delete a Tag
Endpoint: DELETE /api/v1/operations/tags/{id}
Before Deleting
Deletion is permanent. If the tag has associated products, consider deactivating instead to preserve historical data. Products will lose the tag association upon deletion.
Response (200 OK):
{
"success": true,
"message": "Tag deleted successfully",
"data": null,
"meta": {}
}Bulk Operations
Bulk Activate
Endpoint: POST /api/v1/operations/tags/bulk-activate
Request Body:
{
"tag_ids": [
"01hwxyz123abc456def789ghi0",
"01hwxyz789def012ghi345jkl6",
"01hwxyzabc123def456ghi789j0"
]
}Response (200 OK):
{
"success": true,
"message": "Tags activated successfully",
"data": {
"processed": 3,
"successful": 3,
"failed": 0
},
"meta": {}
}Bulk Deactivate
Endpoint: POST /api/v1/operations/tags/bulk-deactivate
Request Body:
{
"tag_ids": [
"01hwxyz123abc456def789ghi0",
"01hwxyz789def012ghi345jkl6"
]
}Response (200 OK):
{
"success": true,
"message": "Tags deactivated successfully",
"data": {
"processed": 2,
"successful": 2,
"failed": 0
},
"meta": {}
}Bulk Delete
Endpoint: POST /api/v1/operations/tags/bulk-delete
Irreversible Operation
Bulk delete is permanent. Review tag-product associations before issuing a bulk delete.
Request Body:
{
"tag_ids": [
"01hwxyz123abc456def789ghi0",
"01hwxyz789def012ghi345jkl6"
]
}Response (200 OK):
{
"success": true,
"message": "Bulk delete completed",
"data": {
"processed": 2,
"successful": 2,
"failed": 0
},
"meta": {}
}Merge Tags
Endpoint: POST /api/v1/operations/tags/merge
Merge one or more source tags into a target tag. All product associations from source tags are transferred to the target. Source tags are removed after the merge.
Request Body:
{
"source_tag_ids": [
"01hwxyz123abc456def789ghi0",
"01hwxyz789def012ghi345jkl6"
],
"target_tag_id": "01hwxyzabc123def456ghi789j0"
}| Field | Type | Required | Description |
|---|---|---|---|
source_tag_ids | array of ULID strings | Yes | Tags to merge from (will be removed) |
target_tag_id | ULID string | Yes | Tag to merge into (will receive all product associations) |
Response (200 OK):
{
"success": true,
"message": "Tags merged successfully",
"data": {
"source_tag_ids": [
"01hwxyz123abc456def789ghi0",
"01hwxyz789def012ghi345jkl6"
],
"target_tag_id": "01hwxyzabc123def456ghi789j0"
},
"meta": {}
}When to Merge
Merge is useful when duplicate tags accumulate over time (e.g. "Electronics" and "Electronic" both exist). Consolidating into a single tag simplifies filtering and avoids confusion. Always verify which tag has the most product associations and use that as the target.
Tag Statistics
Endpoint: GET /api/v1/operations/tags/statistics
Returns aggregate counts for the tag catalog. Use this endpoint for dashboard widgets or catalog health checks.
Response (200 OK):
{
"success": true,
"message": "Tags retrieved successfully",
"data": {
"total": 24,
"active": 20,
"inactive": 4,
"with_products": 18,
"without_products": 6
},
"meta": {}
}Export Tags
Endpoint: POST /api/v1/operations/tags/export
Exports tag data in the requested format. The response is the exported file content with an appropriate Content-Disposition header for browser download.
Request Body:
{
"format": "csv"
}| Parameter | Type | Description |
|---|---|---|
format | string | Export format: csv or json |
Business Scenarios
Scenario 1: Organising a Product Catalog with Tags
Context: Your product catalog has grown and products need multiple cross-cutting labels beyond their primary category. You want to tag products by material, season, target audience, and promotional status.
Workflow:
- Create a set of tags for each dimension (material, season, etc.)
- Assign tags to products via the product create/update endpoints
- Use the list tags endpoint to verify coverage
- Run statistics to check for orphaned tags
API Calls:
# 1. Create material tags
POST /api/v1/operations/tags
{ "name": "Cotton", "slug": "cotton", "color": "#8B5CF6" }
POST /api/v1/operations/tags
{ "name": "Polyester", "slug": "polyester", "color": "#6366F1" }
# 2. Create seasonal tags
POST /api/v1/operations/tags
{ "name": "Summer 2026", "slug": "summer-2026", "color": "#F59E0B" }
# 3. Verify with statistics
GET /api/v1/operations/tags/statisticsScenario 2: Consolidating Duplicate Tags
Context: Multiple team members have created overlapping tags ("Electronics", "Electronic", "Elec"). You need to merge them into a single canonical tag.
Workflow:
- Search for duplicates
- Identify the canonical tag (most product associations)
- Merge duplicates into the canonical tag
- Verify the merge
API Calls:
# 1. Find all electronics-related tags
GET /api/v1/operations/tags?search=electr
# 2. Merge duplicates into the canonical tag
POST /api/v1/operations/tags/merge
{
"source_tag_ids": [
"01hwxyz789def012ghi345jkl6",
"01hwxyzabc123def456ghi789j0"
],
"target_tag_id": "01hwxyz123abc456def789ghi0"
}
# 3. Verify
GET /api/v1/operations/tags/statisticsScenario 3: Seasonal Tag Cleanup
Context: End-of-season tags from the previous year are cluttering the catalog. Some have products, others are empty.
Workflow:
- List inactive or past-season tags
- Export the list for review
- Deactivate tags still linked to products
- Bulk delete empty tags
API Calls:
# 1. Find seasonal tags
GET /api/v1/operations/tags?search=2025&has_products=false
# 2. Export for audit trail
POST /api/v1/operations/tags/export
{ "format": "csv" }
# 3. Bulk delete empty tags
POST /api/v1/operations/tags/bulk-delete
{
"tag_ids": [
"01hwxyzaaa111bbb222ccc333d0",
"01hwxyzeee555fff666ggg777h0"
]
}Best Practices
1. Slug Consistency
Always provide explicit slugs when creating tags. Use lowercase letters, numbers, and hyphens only. A consistent naming convention (e.g. material-cotton, season-summer-2026) makes programmatic filtering and search integration predictable.
2. Colour Coding
Assign distinct colours to tag groups so they are visually distinguishable in the UI. For example, use blue shades for material tags, amber for seasonal tags, and green for promotional tags. The colour is purely cosmetic and has no functional effect.
3. Merge Before Delete
When duplicates are discovered, always merge rather than deleting one tag and manually re-tagging products. The merge endpoint atomically transfers all product associations, ensuring no products lose their labels.
4. Use Statistics for Health Checks
Run GET /api/v1/operations/tags/statistics regularly. A growing without_products count signals tags that were created speculatively and never used - clean them up to keep the catalog tidy.
5. Prefer Tags over Deeply Nested Categories
When a product classification does not naturally fit a hierarchical tree (e.g. "Eco-Friendly", "Best Seller", "Clearance"), use tags instead of creating parallel category trees. Tags are flat, additive, and can be combined freely.
Integration Points
With Products
Tags are associated with products at the product level. When creating or updating a product, reference tags by their ULID. Deactivating a tag does not cascade to products - existing associations are preserved. Refer to the Product Management Guide for details on tag fields in product requests.
With Brands
Tags and brands serve complementary purposes. Brands identify provenance (who made it), while tags classify attributes and usage (what it is, when it is relevant). A product typically has one brand but multiple tags.
With Product Categories
Categories provide the primary tree-based navigation structure. Tags provide secondary, cross-cutting classification. Use categories for "where does this product live in the catalog" and tags for "what characteristics does this product have." See the Category Management Guide for category operations.
Troubleshooting
Tag Creation Fails with Name Conflict
Error: "A tag with this name already exists"
Cause: Another tag in the same tenant already uses the supplied name.
Solution:
- Search for the conflicting tag:
GET /api/v1/operations/tags?search={name} - If the existing tag is the same concept, consider merging
- If the tags are distinct, choose a more specific name
Slug Validation Error
Error: "Tag slug must contain only lowercase letters, numbers, and hyphens"
Cause: The slug contains uppercase letters, spaces, or special characters.
Solution: Use only a-z, 0-9, and - in slugs. Convert spaces to hyphens and strip special characters before submitting.
Merge Fails
Error: Merge operation returns an error.
Cause: One of the source tag IDs or the target tag ID does not exist.
Solution:
- Verify all IDs by fetching each tag individually
- Ensure no IDs are duplicated between source and target
- Retry with corrected IDs
Related Documentation
- Product Management Guide - Associating tags with products
- Brand Management Guide - Complementary brand labelling
- Category Management Guide - Hierarchical product organisation
- Operations Module Overview - Full list of Operations module capabilities