Skip to main content

list-anomalies

dci list-anomalies [flags]

Returns a list of detected anomalies. Anomalies can be sorted by startTime, severityLevel, or costOfAnomaly using sortBy and sortOrder. By default they are sorted by startTime in descending order (most recent first). The sort order is total and reproducible: ties on the selected field are broken by startTime descending and then by anomaly id ascending, so the same result set always paginates in the same order. The notifications array is always present on each anomaly item; it is empty unless includeNotifications=true is supplied. Pagination stability: sortBy, sortOrder, filter, minCreationTime, and maxCreationTime must be held constant across a pagination sequence. A pageToken presented with any of them changed is rejected with 400.

CLI default view

dci list-anomalies presents a curated table by default:

  • Columns: service (from serviceName), severity (from severityLevel), anomaly cost (from costOfAnomaly), platform, status, and started (UTC) (from startTime).
  • started (UTC) always stays in UTC, regardless of timezone settings: it labels the anomaly's usage window (a UTC billing bucket), so a local-time rendering would relabel which day was anomalous — see Timestamps and timezones.
  • The server's sort order is kept — control it with --sort-by (startTime, severityLevel, costOfAnomaly) and --sort-order (asc or desc).
  • --filter keys: serviceName, billingAccount, platform, severityLevel (values information, warning, critical); combine terms with |.
  • --output json and explicit -C/--fields selections return the raw fields documented below.

Examples

# The most recent anomalies, ten per page.
dci list-anomalies --max-results 10 --sort-by startTime --sort-order desc
# Critical anomalies only, most expensive first (severity values are lowercase).
dci list-anomalies --filter severityLevel:critical --sort-by costOfAnomaly --sort-order desc
# Anomalies whose usage started on or after 1 August 2026 00:00 UTC (epoch milliseconds).
dci list-anomalies --min-creation-time 1785542400000
# Every page as JSON, for scripts.
dci list-anomalies --all --output json
# Browse in the full-screen viewer; Enter prints the selected anomaly's id.
dci list-anomalies -M interactive

Flags

FlagTypeDefaultExampleDescription
--min-creation-timeinteger (int64)Inclusive lower bound on the anomaly's usage start time, in milliseconds since the POSIX epoch. Despite the name, this filters the anomaly's usage start time, not the time the anomaly document was created.
--max-creation-timeinteger (int64)Inclusive upper bound on the anomaly's usage start time, in milliseconds since the POSIX epoch. Despite the name, this filters the anomaly's usage start time, not the time the anomaly document was created.
--filterstringseverityLevel:criticalAn expression for filtering the results. The syntax is key:value. Multiple criteria can be combined using a pipe |. See Filters. Available filter keys: serviceName, billingAccount, platform, severityLevel. severityLevel values must be lowercase: information, warning, critical. An unrecognised key, or a segment that is not key:value, is rejected with 400 rather than ignored.
--sort-bystring"startTime"A field by which the results will be sorted. Defaults to startTime. One of: "startTime", "severityLevel", "costOfAnomaly".
--sort-orderstringSpecifies the sort direction; accepts asc for ascending (lowest to highest) or desc for descending (highest to lowest). One of: "asc", "desc".
--max-resultsinteger (int64)The maximum number of results to return in a single page. If omitted, all anomalies matching the filters and time window are returned in a single page.
--page-tokenstringPage token, returned by a previous call, to request the next page of results
--include-notificationsbooleanfalseInclude anomaly notifications from the subcollection. Defaults to false.

Every command also accepts the CLI-wide flags for output shaping — see Output formats and Table output options.

Output

OK - The request succeeded.

By default dci renders the result as a table. Use --output json to get the full structure described below — see Output formats.

FieldTypeDescription
anomaliesarray of objectAnomalies in this page. Always an array; empty ([]) when there are no matching anomalies.
anomalies[].attributionstringAttribution ID.
anomalies[].billingAccountstringBilling account ID.
anomalies[].costOfAnomalynumber (double)Excess cost over and above the expected normal cost.
anomalies[].actualCostnumber (double)Observed (actual) cost of the anomaly.
anomalies[].expectedMaxCostnumber (double)Maximum cost within the expected normal range.
anomalies[].idstring
anomalies[].platformstringCloud Provider name.
anomalies[].scopestringThe anomaly's project or account identifier as reported by the provider. For providers whose billing grain is not a cloud project — a user or an organization, for example — this is that identifier, so treat it as an opaque id and use entityLabel/entityName for presentation.
anomalies[].entityLabelstringConnector-declared name for what scope identifies, for example "Project", "Account" or "User". Absent when the provider publishes no display profile.
anomalies[].entityNamestringHuman-readable value for scope when the provider publishes one — for example a user's email address where scope is an opaque user id. Absent when unavailable.
anomalies[].providerDisplayNamestringConnector-declared display name for the provider, for example "Anthropic (Analytics API)". Absent when the provider publishes no display profile.
anomalies[].serviceNamestringService name.
anomalies[].severityLevelstringSeverity level: information, warning, or critical.
anomalies[].startTimeinteger (int64)Usage start time of the anomaly.
anomalies[].endTimeintegerEnd of the anomaly.
anomalies[].deactivationReasonstringWhy the anomaly stopped being active. reverted means the cost returned inside the expected normal range; expired means the anomaly was deactivated without the cost returning inside that range; unknown means the reason could not be determined. Null while the anomaly is still active. One of: "reverted", "expired", "unknown".
anomalies[].timeFramestringTimeframe: Daily or Hourly
anomalies[].top3SKUsarray of objectArray of SKU entries contributing to an anomaly.
anomalies[].top3SKUs[].costnumber (double)
anomalies[].top3SKUs[].namestring
anomalies[].monitorLevelstringWhether the anomaly was detected on a single SKU (sku) or at the level of a whole service (service). One of: "service", "sku".
anomalies[].resourceDataarray of objectArray of resources contributing to an anomaly.
anomalies[].resourceData[].costnumber (double)
anomalies[].resourceData[].resourceIdstring
anomalies[].resourceData[].skuDescriptionstring
anomalies[].resourceData[].operationstringFor anomalies related to AWS S3
anomalies[].resourceData[].labelsarray of objectLabels (also known as cost-allocation tags) present on this resource during the anomaly; each entry reports the label's key, its value, and the resource's cost tagged with that key/value pair. Cloud providers use different names for the same concept; GCP uses "labels", AWS uses "cost-allocation tags", and Azure uses "tags". We refer to all of these as labels.
anomalies[].resourceData[].labels[].keystringThe label/tag key.
anomalies[].resourceData[].labels[].valuestringThe label/tag value.
anomalies[].resourceData[].labels[].costnumber (double)The resource's cost tagged with this key/value pair; typically equal to the resource's cost, since labels/tags usually cover all of its spend.
anomalies[].statusstringOne of: "active", "inactive".
anomalies[].acknowledgedbooleanHas the anomaly been acknowledged
anomalies[].acknowledgedAtstring (date-time)When the anomaly was first acknowledged
anomalies[].acknowledgedBystringEmail of the user who first acknowledged the anomaly
anomalies[].notificationsarray of objectChronologically ordered notification dispatch events.
anomalies[].notifications[].timestampstring (date-time)Dispatch timestamp in RFC3339 UTC.
anomalies[].notifications[].channelstringDispatch channel. One of: "email", "slack", "msteams".
anomalies[].linkedAnomaliesarray of stringIDs of the other related anomalies in the same service around same time. Always the complete group: the filters, time window, and pagination of the request that returned this anomaly do not narrow it, so an ID here may not appear among the anomalies of that same response.
pageTokenstringOpaque token for the next page. Omitted when there is no next page.
rowCountinteger (int64)Number of items in this page (anomalies.length). This is not the total count across all pages; the total across the full filtered result set is totalCount.
truncatedbooleantrue when filtered anomalies remain beyond this page. Derived from the same "rows remain after the last returned row" check as pageToken, so the two are never contradictory: pageToken is present if and only if truncated is true.
totalCountinteger (int64)Total number of anomalies matching the filters and time window, across all pages. This is a per-request snapshot: under concurrent writes, the value can differ between pages fetched during the same pagination sequence.
totalCountExactbooleanWhether totalCount is exact. Always true for this operation, because totalCount is computed over the fully materialised filtered result set.
anomalySummaryobjectAnomaly-specific summary. countBySeverity and totalCostOfAnomaly cover the complete filtered result set across all pages, not the returned page. Anomalies whose severity cannot be determined (legacy documents) are counted in totalCount and totalCostOfAnomaly but in none of the three severity buckets in countBySeverity, so the three values can sum to less than totalCount. Computed as of this request; under concurrent writes, values may differ between pages fetched during the same pagination sequence.
anomalySummary.countBySeverityobjectCount of matching anomalies per severity level. All three keys are always present, with zero counts included.
anomalySummary.countBySeverity.informationinteger (int64)
anomalySummary.countBySeverity.warninginteger (int64)
anomalySummary.countBySeverity.criticalinteger (int64)
anomalySummary.totalCostOfAnomalynumber (double)Sum of costOfAnomaly across all matching anomalies, in USD, rounded to cents. Every matching anomaly contributes, linked ones included, so a single cost spike described at more than one level of detail contributes once per anomaly describing it.
Raw JSON schema
{
"type": "object",
"description": "List of detected cloud cost anomalies.",
"required": [
"anomalies",
"rowCount",
"truncated",
"totalCount",
"totalCountExact",
"anomalySummary"
],
"properties": {
"anomalies": {
"type": "array",
"description": "Anomalies in this page. Always an array; empty (`[]`) when there are no matching anomalies.",
"items": {
"required": [
"attribution",
"billingAccount",
"costOfAnomaly",
"platform",
"scope",
"serviceName",
"severityLevel",
"startTime",
"timeFrame",
"top3SKUs",
"monitorLevel",
"notifications",
"linkedAnomalies"
],
"type": "object",
"description": "Detailed information about a detected anomaly. The `notifications` array is always present; list responses return an empty array unless `includeNotifications=true` is requested.",
"properties": {
"attribution": {
"type": "string",
"description": "Attribution ID."
},
"billingAccount": {
"type": "string",
"description": "Billing account ID."
},
"costOfAnomaly": {
"type": "number",
"description": "Excess cost over and above the expected normal cost.",
"format": "double"
},
"actualCost": {
"type": "number",
"format": "double",
"nullable": true,
"description": "Observed (actual) cost of the anomaly."
},
"expectedMaxCost": {
"type": "number",
"format": "double",
"nullable": true,
"description": "Maximum cost within the expected normal range."
},
"id": {
"type": "string"
},
"platform": {
"type": "string",
"description": "Cloud Provider name."
},
"scope": {
"type": "string",
"description": "The anomaly's project or account identifier as reported by the provider. For providers whose billing grain is not a cloud project — a user or an organization, for example — this is that identifier, so treat it as an opaque id and use `entityLabel`/`entityName` for presentation."
},
"entityLabel": {
"type": "string",
"description": "Connector-declared name for what `scope` identifies, for example \"Project\", \"Account\" or \"User\". Absent when the provider publishes no display profile."
},
"entityName": {
"type": "string",
"description": "Human-readable value for `scope` when the provider publishes one — for example a user's email address where `scope` is an opaque user id. Absent when unavailable."
},
"providerDisplayName": {
"type": "string",
"description": "Connector-declared display name for the provider, for example \"Anthropic (Analytics API)\". Absent when the provider publishes no display profile."
},
"serviceName": {
"type": "string",
"description": "Service name."
},
"severityLevel": {
"type": "string",
"description": "Severity level: `information`, `warning`, or `critical`."
},
"startTime": {
"type": "integer",
"description": "Usage start time of the anomaly.",
"format": "int64"
},
"endTime": {
"type": "integer",
"nullable": true,
"description": "End of the anomaly."
},
"deactivationReason": {
"type": "string",
"nullable": true,
"description": "Why the anomaly stopped being active. `reverted` means the cost returned inside the expected normal range; `expired` means the anomaly was deactivated without the cost returning inside that range; `unknown` means the reason could not be determined. Null while the anomaly is still active.",
"enum": [
"reverted",
"expired",
"unknown"
]
},
"timeFrame": {
"type": "string",
"description": "Timeframe: Daily or Hourly"
},
"top3SKUs": {
"type": "array",
"description": "Array of SKU entries contributing to an anomaly.",
"items": {
"type": "object",
"description": "SKU-level information contributing to an anomaly.",
"properties": {
"cost": {
"type": "number",
"format": "double"
},
"name": {
"type": "string"
}
}
}
},
"monitorLevel": {
"type": "string",
"description": "Whether the anomaly was detected on a single SKU (`sku`) or at the level of a whole service (`service`).",
"enum": [
"service",
"sku"
]
},
"resourceData": {
"type": "array",
"description": "Array of resources contributing to an anomaly.",
"items": {
"type": "object",
"description": "Resource-specific contribution to an anomaly.",
"properties": {
"cost": {
"type": "number",
"format": "double"
},
"resourceId": {
"type": "string"
},
"skuDescription": {
"type": "string"
},
"operation": {
"description": "For anomalies related to AWS S3",
"type": "string"
},
"labels": {
"type": "array",
"description": "Labels (also known as cost-allocation tags) present on this resource during the anomaly; each entry reports the label's key, its value, and the resource's cost tagged with that key/value pair.\nCloud providers use different names for the same concept; GCP uses \"labels\", AWS uses \"cost-allocation tags\", and Azure uses \"tags\". We refer to all of these as labels.",
"items": {
"type": "object",
"description": "A single label (a.k.a. cost-allocation tag) on the resource, paired with the resource's cost tagged with this key/value pair.",
"properties": {
"key": {
"type": "string",
"description": "The label/tag key."
},
"value": {
"type": "string",
"description": "The label/tag value."
},
"cost": {
"type": "number",
"format": "double",
"description": "The resource's cost tagged with this key/value pair; typically equal to the resource's cost, since labels/tags usually cover all of its spend."
}
}
}
}
}
}
},
"status": {
"type": "string",
"nullable": true,
"enum": [
"active",
"inactive"
]
},
"acknowledged": {
"description": "Has the anomaly been acknowledged",
"type": "boolean"
},
"acknowledgedAt": {
"description": "When the anomaly was first acknowledged",
"type": "string",
"format": "date-time",
"nullable": true
},
"acknowledgedBy": {
"description": "Email of the user who first acknowledged the anomaly",
"type": "string",
"nullable": true
},
"notifications": {
"type": "array",
"description": "Chronologically ordered notification dispatch events.",
"items": {
"type": "object",
"description": "A successful notification dispatch for an anomaly.\nThis records that the API/worker sent the notification, not that delivery was confirmed.",
"required": [
"timestamp",
"channel"
],
"properties": {
"timestamp": {
"type": "string",
"format": "date-time",
"description": "Dispatch timestamp in RFC3339 UTC."
},
"channel": {
"type": "string",
"description": "Dispatch channel.",
"enum": [
"email",
"slack",
"msteams"
]
}
}
}
},
"linkedAnomalies": {
"type": "array",
"description": "IDs of the other related anomalies in the same service around same time. Always the complete group: the filters, time window, and pagination of the request that returned this anomaly do not narrow it, so an ID here may not appear among the anomalies of that same response.",
"items": {
"type": "string"
}
}
}
}
},
"pageToken": {
"type": "string",
"description": "Opaque token for the next page. Omitted when there is no next page."
},
"rowCount": {
"type": "integer",
"format": "int64",
"description": "Number of items in this page (`anomalies.length`). This is not the total count across all pages; the total across the full filtered result set is `totalCount`."
},
"truncated": {
"type": "boolean",
"description": "`true` when filtered anomalies remain beyond this page. Derived from the same \"rows remain after the last returned row\" check as `pageToken`, so the two are never contradictory: `pageToken` is present if and only if `truncated` is `true`."
},
"totalCount": {
"type": "integer",
"format": "int64",
"description": "Total number of anomalies matching the filters and time window, across all pages. This is a per-request snapshot: under concurrent writes, the value can differ between pages fetched during the same pagination sequence."
},
"totalCountExact": {
"type": "boolean",
"description": "Whether `totalCount` is exact. Always `true` for this operation, because `totalCount` is computed over the fully materialised filtered result set."
},
"anomalySummary": {
"type": "object",
"description": "Anomaly-specific summary. `countBySeverity` and `totalCostOfAnomaly` cover the complete filtered result set across all pages, not the returned page. Anomalies whose severity cannot be determined (legacy documents) are counted in `totalCount` and `totalCostOfAnomaly` but in none of the three severity buckets in `countBySeverity`, so the three values can sum to less than `totalCount`. Computed as of this request; under concurrent writes, values may differ between pages fetched during the same pagination sequence.",
"required": [
"countBySeverity",
"totalCostOfAnomaly"
],
"properties": {
"countBySeverity": {
"type": "object",
"description": "Count of matching anomalies per severity level. All three keys are always present, with zero counts included.",
"required": [
"information",
"warning",
"critical"
],
"properties": {
"information": {
"type": "integer",
"format": "int64"
},
"warning": {
"type": "integer",
"format": "int64"
},
"critical": {
"type": "integer",
"format": "int64"
}
}
},
"totalCostOfAnomaly": {
"type": "number",
"format": "double",
"description": "Sum of `costOfAnomaly` across all matching anomalies, in USD, rounded to cents. Every matching anomaly contributes, linked ones included, so a single cost spike described at more than one level of detail contributes once per anomaly describing it."
}
}
}
}
}

Errors

On failure, dci prints a single error message — with a hint when one is available — and exits with a typed code your scripts can branch on. See Errors and exit codes for the full contract.

HTTP status to exit code mapping
HTTP statusExit codeError codeMeaning
40030VALIDATION_ERRORThe arguments or request body were rejected. Review the command's flags and payload.
40110AUTHENTICATION_FAILEDNot signed in, or the API token is invalid. Run dci login or check DCI_API_KEY.
40311PERMISSION_DENIEDThe DoiT user or the active customer context does not have access.
40420RESOURCE_NOT_FOUNDThe requested resource does not exist. Check the identifier argument.

Aliases: listanomalies