Tag detail
Everything a single tag is attached to — filter by entity type, bulk-detach, and edit the tag — backed by the polymorphic tag_links join.
Tag detail
The tag detail view at /tags/{id} lists every entity a single tag is attached to, with per-entity-type filtering, bulk detach, and a quick edit affordance. It is backed by the /v1/tags API (api/routes/tags.py) and two tenant-scoped tables: tags (the catalog) and tag_links (one row per attachment).
Live surface
Tag CRUD, attach/detach, and the links listing are all real, enforced endpoints. Tags are a genuine cross-entity primitive, not a mock.
The data model
A tag is a small catalog row — name, slug, color, description — unique per tenant on slug. Attachments live in tag_links, whose entity_id is a VARCHAR(64) so the one join table handles both integer-PK and string-PK entities. The uniqueness constraint uq_tag_links_attach on (tenant_id, tag_id, entity_type, entity_id) makes attaching idempotent — re-attaching the same entity returns the existing link with already_attached: true rather than duplicating it.
What the detail view shows
GET /v1/tags/{id}/links returns the attachments, newest first, with a total count and pagination (limit / offset). Pass entity_type to filter to a single kind. The view renders each link with the right icon and a deep link into that record — accounts, contacts, opportunities, sequences, and campaigns among the recognized types.
# Every entity this tag touches
curl -s "$PACT/v1/tags/42/links?limit=100" \
-H "Authorization: Bearer $TOKEN"
# Just the contacts
curl -s "$PACT/v1/tags/42/links?entity_type=contact" \
-H "Authorization: Bearer $TOKEN"
Attaching and detaching
- 1
Attach an entity
POST /v1/tags/{id}/attachwith{ "entity_type": "...", "entity_id": "..." }. Idempotent: a duplicate returns the existing link flaggedalready_attached. - 2
Detach individually or in bulk
DELETE /v1/tags/{id}/attachwith the same body removes one link. The detail view supports bulk-detach across the filtered set. - 3
Edit the tag itself
PATCH /v1/tags/{id}updates name, color, or description. The catalog row is shared by every attachment, so an edit recolors the tag everywhere at once.
Deleting a tag
DELETE /v1/tags/{id} removes the tag and all of its tag_links in one transaction — deletion cascades to the attachments, so a deleted tag leaves no dangling links. Every mutation is tenant-scoped: tenant_id is derived from the auth context on every route, never from the request body.