Tenant tags
What they do
Free-text labels you can attach to customer tenants to organize them in the dashboard and filter the customer list. A tenant carries an array of tags stored on the public.tenant.tags column; the dashboard's filter bar accepts a tag query and narrows the visible rows to tenants whose tag array contains that value.
Tags are workspace-local - every MSP defines their own conventions. Common patterns:
- Tier:
tier-1,tier-2,tier-3 - Vertical:
healthcare,legal,manufacturing - Lifecycle:
onboarding,production,winding-down - On-call rotation:
oncall-mondays,oncall-tuesdays
There is no schema for what tags mean. Pick a convention with your team and stick to it.
When to use them
- Dashboard organization. Once you cross ~20 tenants the dashboard becomes a sea of rows. Tag by tier and you can filter to "show me my tier-1 customers only" in one click.
- On-call routing. Tag tenants by which on-call admin handles them and the filter becomes a personal worklist.
- Bulk operations. Tag tenants slated for the same baseline upgrade with
baseline-v2026-rolloutand the bulk-change picker lets you select all of them by tag instead of clicking 30 checkboxes. - Reporting. "How many critical alerts came from our tier-1 customers this month?" - Slice the alerts list by tag.
Adding tags
- Tenant detail → Settings → General.
- Tags field. Type a tag and press Enter (or comma) to add. Click the X on a chip to remove.
- Click Save.
Tags are saved via a server action that goes through the normal requireAtLeast('admin') gate and audits the change as tenant.tags_updated with the before/after arrays.
Filtering the customer list
- Dashboard or Tenants page → click the Tags filter pill.
- Pick from the dropdown (lists every tag in use across the workspace) or type to add a new filter.
- The visible list narrows to tenants whose
tagsarray contains the picked value.
Multiple tag filters compose as AND - a tenant must carry every selected tag to appear. To OR, use a single multi-select picker entry.
The filter state is encoded in the URL (?tag=tier-1&tag=healthcare), so you can bookmark a personal worklist.
Limits and rules
| Rule | Why |
|---|---|
| Max 10 tags per tenant | Hard cap - keeps the row UI legible and the GIN index small |
| Max 40 chars per tag | Hard cap - long tags break the chip layout and are usually mistakes |
| Lowercase, kebab-case suggested | UI normalizes display; the column itself preserves whatever you typed |
| No empty tags | The form trims and rejects empty strings |
Validation happens server-side too - a malformed array (e.g. via the API) is rejected with invalid_tags. The UI surfaces the same generic error code; details go to the audit log.
How the filter performs at scale
The tenant_tags_idx is a GIN index on the tags array column. Queries of the shape where 'tier-1' = any(tags) (the dashboard's actual filter) hit the GIN and stay fast even at 1000+ tenants. You won't hit a performance ceiling from tag filtering before you hit limits on other dashboard data.
Troubleshooting
- My new tag doesn't appear in the filter dropdown. The dropdown caches the workspace's distinct tag list for 60 seconds. Refresh after a minute, or just type the tag manually.
- "Tag too long" error. 40-character cap. Trim it.
- "Too many tags" error. 10-tag cap. Drop one before adding another.
- Tags vanish after a restore. Tag changes go through the audit log; the column is part of the standard backup. If you genuinely lost them, the audit log has the last-set value - copy-paste back in.
- Other admins can see my tags. Yes - tags are workspace-level, not user-level. Treat them as shared metadata. For personal worklists use the URL-encoded filter (bookmark it) instead.
- Bulk select by tag is missing a tenant. Bulk operations also require the tenant to be in
importedconsent state. A tagged tenant inpendingordisconnectedis excluded from bulk dry-runs even if it matches the tag filter.
Related
- Daily workflows for the dashboard-triage flow that benefits most from tag filtering.
- Change management → Bulk changes for the bulk picker that consumes tag filters.