Create one business mapping in a viewpoint.
Rules- how the match is defined
Two levels, and the nesting is the whole grammar:
rules[] → OR between rules
└ filters[] → AND between filters inside one rule
So rules: [A, B] matches a row satisfying either rule, while a rule holding two filters matches only rows satisfying both. There is no third level and no explicit operator between the levels - the shape is the operator.
{"name": "Platform - EU production", "rules": [ { "filters": [ { "field": "service", "operator": "IS", "values": ["Amazon Elastic Compute Cloud"] }, { "field": "region", "operator": "STARTS_WITH", "values": "eu-" } ] }, { "filters": [ { "field": "customtags", "prefix": "team", "operator": "IS", "values": ["platform"] } ] } ]}
Reads as: (service is EC2 and region starts with eu-) or (tag team is platform).
Operators
| Operator | values | Meaning |
|---|---|---|
IS | array | Exact match against any value in the list. |
IS_NOT | array | Matches nothing in the list. |
LIKE | string | Substring, case-insensitive. |
LIKE_CASE_S | string | Substring, case-sensitive. |
NOT_LIKE | string | Negated substring, case-insensitive. |
NOT_LIKE_CASE_S | string | Negated substring, case-sensitive. |
STARTS_WITH | string | Prefix match. |
ENDS_WITH | string | Suffix match. |
GTE | string | Greater than or equal — for the numeric dimensions. |
Table Fields
The set is per cloud, and it is narrower than the Cost & Usage dimension list - a dimension you can group a report by is not necessarily one a mapping can be built from.
| Cloud | Fields |
|---|---|
| AWS | region, linkedaccid, service, division, customtags, virtualcustomtags, subviewscustomtags, payeraccount, operation, familytype, instancetype, chargetype, zonetag, resource, usagetype, and the Kubernetes four: k8snamespace, k8slabels, k8scluster, k8snodegroup |
| AZURE | linkedaccid, resourcegroup, region, service, operation, payeraccount, division, customtags, virtualcustomtags, resource, usagetype |
| GCP | region, linkedaccid, project, service, operation, familytype, instancetype, division, customtags, virtualcustomtags, resource |
NOTE: The Kubernetes fields are listed under AWS but apply wherever the account has K8s data; pair them with k8s: true.
Split (optional)
A split mapping does not keep the cost its rules collect. It collects the cost and then redistributes every dollar of it onto other mappings, leaving itself at zero. This is how shared cost is charged out: a "Shared infrastructure" mapping catches the cluster bill and pushes it onto the teams.
end split: true and a splitOptions object. The two fields every split needs:
| Field | Type | Notes |
|---|---|---|
splitOptions.mappings | string[] | Required, min 1. The uuids of the target mappings — not their names. Get them from GET /api/v2/business-mapping/viewpoints/:viewpointId/mappings. Targets must already exist; this route does not resolve or verify them. |
splitOptions.splitType | string | Required. How the cost divides. See below. |
Split types
| Type | Accepted here | How the share is computed | Extra fields |
|---|---|---|---|
EQUAL | yes | Every target gets 1 / n, regardless of its own size. The simplest and the default the UI starts from. | — |
RATIO | yes | Proportional to each target's own cost in the same time bucket. A target twice the size absorbs twice the shared cost. When the targets sum to zero in a bucket, it degrades to an equal split rather than dividing by zero. | — |
CUSTOM | yes | Fixed percentages you set per target. | customDivision |
RATIO_MONTHLY | no — 400 | Like RATIO, but the ratio is averaged over the months covering the bucket rather than recomputed per bucket, so the allocation stays steady instead of moving with daily noise. Stored and honoured; simply not reachable through this route. | — |
METRIC | no — 400 | Shares follow a Unit Economics metric or KPI (seats, requests, GB) instead of cost. Stored and honoured; not reachable through this route. Refused outright on AI-provider accounts. | kpiConfig |
Mapping types
The flags above are not variants of one thing - they pick which kind of mapping you are creating, and each resolves differently at query time.
| Type | Set by | What it produces |
|---|---|---|
| Rule mapping | nothing extra — the default | One bucket. Rows matching the rules land in it and stay. |
| Split mapping | split: true + splitOptions | One bucket that empties itself into other mappings. Ends at zero by design. |
| Group By mapping | is_group_by: true + group_by_field | Many buckets from one definition — one per distinct value of the field, named <value>____<mapping name> - the four underscores are the separator the split pipeline reads back apart. One rule, a bucket per linked account or per team tag, without writing them out. Add group_by_field_key for a two-level field. Eligible fields are narrower than the rule fields: linkedaccid, region, service, familytype, operation, customtags, virtualcustomtags, accounttags, governance_tags_keys, plus payeraccount and cloudprovider on multi-cloud. |
| Telemetry mapping | is_telemetry: true + telemetryConfig | Allocation driven by a Unit Economics pipeline metric rather than by cost. Mutually exclusive with split and is_group_by — a rule the in-app route enforces and this one does not. |
| Kubernetes mapping | k8s: true (+ k8s_config) | Rules resolve against the Kubernetes dataset, which is what makes k8snamespace, k8scluster, k8snodegroup and k8slabels meaningful. |
| Global mapping | a different endpoint | Defined once in the account library and attached to many viewpoints. Created via POST /api/v2/business-mapping/global-mappings and attached with PUT …/viewpoints/:viewpointId/global-mappings/:mappingId. Same body, but split is refused. |
