Smart Tags
Virtual tags derived from tagging policies you define — each policy enforces a tag key and builds its value from the resource's own fields. Applied for cost attribution only, never written back to the cloud; reviewable and reconciled on every refresh.
Smart tags are virtual tags ZopNight derives from tagging policies you define. Each policy enforces a tag key and derives its value from the resource's own fields, so the result is reproducible and cheap to re-run. Smart tags are applied inside ZopNight for cost attribution only; they are never written back to the cloud provider.
How derivation works
- You create a tagging policy: an optional
selector(which resources it applies to — by provider, type, region, …) plus atagspec (the key, and the value built from resource fields). - The recommender resolves each resource against its policies. For a match, it derives the value:
fields → join → standardize(e.g.res.provider+res.regionjoined by-, lowercased →gcp-us-central1). - Derived tags land as pending. They are never auto-applied to cost — you accept the ones you want (or revoke later).
pending = derived − accepted. There is no "rejected" state.
Value source fields are restricted to stable resource attributes — res.type, res.region, res.provider, res.instance_type, res.name. Empty parts are skipped so a partially-absent multi-field value never produces separator garbage; an all-empty value omits the tag (there is no literal fallback).
Lifecycle & reconciliation
Tags are re-derived per resource on each resource.discovered event and across the org on a full refresh (POST /recommendations/refresh). Every re-derivation reconciles your accepted tags: an accepted tag is kept only if it is still derived with the same value, otherwise it is dropped — so disabling a policy or changing its value automatically removes the now-unsourced tag on the next refresh. When a resource is deleted in the cloud, its smart-tag row is removed.
List Smart Tags
/smart-tagsPaginated per-resource derived/accepted tags for the current org, plus org-wide pending/accepted counts for the tab badges.
Query Parameters
| Parameter | Description |
|---|---|
status | Lifecycle filter: pending | accepted. Omit for all. |
provider | Cloud provider filter: aws | gcp | azure. |
page | Page number (default: 1). |
size | Items per page (default: 20, max: 100). |
{
"data": {
"items": [
{
"resourceUid": "//artifactregistry.googleapis.com/projects/acme/locations/us-central1/repositories/web",
"resourceName": "web",
"resourceType": "artifact-registry",
"provider": "gcp",
"cloudAccountId": "acc_1",
"derived": { "cost-allocation": "gcp-us-central1", "cloud-platform": "gcp" },
"accepted": { "cloud-platform": "gcp" },
"pending": { "cost-allocation": "gcp-us-central1" }
}
],
"hasMore": false,
"limit": 20,
"counts": { "pending": 132, "accepted": 6 }
}
}Accept or Revoke
/smart-tagsPromote pending tags into cost attribution (accept) or remove them (revoke). Body-based so resourceUid may contain slashes (e.g. GCP //... paths).
Send accepted: true to accept or accepted: false to revoke. Name the keys with keys: [...], or pass all: true (accept only) to act on every derived key. A mutation with no keys and all unset is rejected — it would otherwise silently no-op.
curl -X PATCH https://zopnight.com/api/smart-tags \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"resourceUid": "//artifactregistry.googleapis.com/projects/acme/locations/us-central1/repositories/web",
"keys": ["cost-allocation"],
"accepted": true
}'curl -X PATCH https://zopnight.com/api/smart-tags \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "resourceUid": "i-0abc123def456", "all": true, "accepted": true }'curl -X PATCH https://zopnight.com/api/smart-tags \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"resourceUid": "i-0abc123def456",
"keys": ["cost-allocation"],
"accepted": false
}'Tagging Policies
Tagging policies are stored in the generic policy store and managed through the standard policy CRUD endpoints with domain: "tagging". The recommender serves the form vocabulary and validates a spec before you create it.
/policiesCreate a tagging policy (domain=tagging).
curl -X POST https://zopnight.com/api/policies \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"domain": "tagging",
"targetType": "organisation",
"name": "cost-allocation",
"spec": {
"selector": { "all": [{ "attr": "res.provider", "op": "eq", "values": ["gcp"] }] },
"tag": {
"key": "cost-allocation",
"value": { "fields": ["res.provider", "res.region"], "join": "-", "standardize": true }
}
},
"enabled": true
}'/tagging/policies/catalogForm vocabulary: selectable condition facets, operators, and the allowlisted value-source fields.
/tagging/policies/validationsValidate a tagging spec before create. Returns { valid, errors[] }.