Skip to main content Skip to content

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.

4 min read Last updated: 

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

  1. You create a tagging policy: an optional selector (which resources it applies to — by provider, type, region, …) plus a tag spec (the key, and the value built from resource fields).
  2. The recommender resolves each resource against its policies. For a match, it derives the value: fields → join → standardize (e.g. res.provider + res.region joined by -, lowercased → gcp-us-central1).
  3. Derived tags land as pending. They are never auto-applied to cost — you accept the ones you want (or revoke later).
  4. 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

GET /smart-tags

Paginated per-resource derived/accepted tags for the current org, plus org-wide pending/accepted counts for the tab badges.

Query Parameters

ParameterDescription
statusLifecycle filter: pending | accepted. Omit for all.
providerCloud provider filter: aws | gcp | azure.
pagePage number (default: 1).
sizeItems per page (default: 20, max: 100).
Response
{
"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

PATCH /smart-tags

Promote 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.

Accept specific keys
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
}'
Accept every derived key on a resource
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 }'
Revoke (back to pending)
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.

POST /policies

Create a tagging policy (domain=tagging).

Request
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
}'
GET /tagging/policies/catalog

Form vocabulary: selectable condition facets, operators, and the allowlisted value-source fields.

POST /tagging/policies/validations

Validate a tagging spec before create. Returns { valid, errors[] }.

Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001 · zero-trust· 30% average cloud cost cut· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001 · zero-trust· 30% average cloud cost cut· 4 platforms · 1 console·