Documentation

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

  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 · json
{
"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 · bash
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 · bash
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) · bash
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 · bash
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[] }.