Tag Groups
Overview
Section titled “Overview”Tag groups are named groupings of product tags linked by membership. Staff save these groups for repeated analytics queries. Instead of manually selecting the same set of tags each time, users create a tag group (e.g., “Summer 2024 Collection”) and track its metrics over time via tag analytics.
Goal: Enable reusable, named tag groups for consistent performance tracking.
API endpoints
Section titled “API endpoints”Tag group CRUD (internal, not yet specced)
Section titled “Tag group CRUD (internal, not yet specced)”| Method | Path | Description |
|---|---|---|
| GET | /api/tag-groups | List all tag groups |
| POST | /api/tag-groups | Create or update a tag group (upsert by name) |
| DELETE | /api/tag-groups/:groupId | Delete a tag group |
Related specced endpoints
Section titled “Related specced endpoints”| Method | Path | Spec | Description |
|---|---|---|---|
| GET | /tags | product.yaml | List available tags for group membership |
| GET | /categories | product.yaml | List categories (tag groups rewrite uses product_category table) |
| GET | /api/tag-analytics/group-performance/:groupId | (not yet specced) | Metrics for a saved tag group |
Schema tables
Section titled “Schema tables”| Table | Module | Role |
|---|---|---|
product_category | Product | CRUD: tag group storage (rewrite target for Drizzle) |
product_tag | Product | Read: available tags for group membership |
Infrastructure
Section titled “Infrastructure”| Resource | Type | Purpose |
|---|---|---|
dashboard-api | CF Worker (internal) | CRUD endpoints for tag groups |
| PlanetScale (Hyperdrive) | Managed Postgres | Tag group + tag data storage |
Participants
Section titled “Participants”| Actor | Role |
|---|---|
| Staff user (Creative/Ops) | Creates, edits, and deletes tag groups |
| Dashboard API Worker | Serves CRUD endpoints |
| PlanetScale | Store for tag groups |
Create/update request
Section titled “Create/update request”// POST /api/tag-groups{ name: string, // Tag group name, e.g. "Summer 2024 Collection" tagNames: string[] // Tag identifiers, e.g. ["summer-2024", "floral", "tropical"]}The endpoint upserts — if a tag group with the same name already exists, it updates the member tags.
Response DTO
Section titled “Response DTO”interface TagGroup { id: string; // UUID name: string; // Group name tagNames: string[]; // Member tag identifiers}Database table
Section titled “Database table”| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
name | VARCHAR(255) | Tag group name |
tag_names | JSONB | Array of tag identifiers (membership relation) |
temporal_origin | TIMESTAMPTZ | Creation timestamp |
last_modified_at | TIMESTAMPTZ | Last update timestamp |
Relationship to tag analytics
Section titled “Relationship to tag analytics”Tag groups feed into the tag analytics group performance endpoint:
GET /api/tag-analytics/group-performance/:groupIdThis looks up the tag group’s member tags and runs the same aggregate metric query as the bulk performance endpoint.
Migration notes
Section titled “Migration notes”The current system uses Supabase RPCs (upsert_tag_group, get_tag_groups, delete_tag_group). In the rewrite, these become straightforward Drizzle CRUD operations on the product_category table.
Acceptance criteria
Section titled “Acceptance criteria”- Creating a tag group with a new name inserts a new record
- Creating a tag group with an existing name updates the member tags
- Deleting a tag group removes it and its analytics no longer resolve
- Tag group performance via analytics endpoint returns correct aggregated metrics