update_alert
Update an existing cost alert. Look up alertId via search / list_alerts / get / create_alert. All fields except alertId (and slug) are optional — omit a field to keep the stored value. Most edits (name, description, tagIds, condition, dedup) need nothing else. tagIds replaces the full list: existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created. Omit keeps stored tags; [] clears them. Explorer (queries + period) and notification dest are wholesale groups: if you send any field in the group, send the full group, written from scratch exactly as for create_alert — the stored queries from get are not a valid input. Inside the explorer group, omit scopeId to keep the alert's saved scope (send null to clear it); it cannot be sent on its own. scopeId is the same id as list_teams, stored on the alert (read it from get) and not merged into the stored queries. Do not send aggBy: the stored grain is always Day. Setting condition on a legacy threshold alert that has no dedup config succeeds but the response may include warning: pass dedup too (or in a follow-up call) to avoid notifying on every firing evaluation. Sharing is not this tool — use get_object_permissions then set_object_permissions with resourceKind "costAlert".
EXAMPLE: rename → { alertId, name: "New name" }
EXAMPLE: dest switch to Slack → { alertId, notificationChannel: "SLACK", slackChannelId: "C01ABC" }
EXAMPLE: condition change → { alertId, condition: "a > 2000" }
EXAMPLE: narrow the monitored series (scope kept) → { alertId, queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_provider in ["AWS"]" }], datePreset: "TRAILING_90_DAYS" }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Explicit window end (inclusive, YYYY-MM-DD). Use with from instead of datePreset. | |
| from | No | Explicit window start (YYYY-MM-DD). Use with to instead of datePreset. | |
| name | No | Display name for the alert. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| dedup | No | Deduplication config controlling how often a still-firing group notifies. The window is per groupBy value; delivery stays one message listing newly eligible groups. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days. | |
| emails | No | Email addresses (required if EMAIL) | |
| tagIds | No | Replaces the full tag list. Omit to keep stored tags; pass [] to clear. Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). | |
| alertId | Yes | Alert ID from search, list_alerts, get, or create_alert. | |
| queries | No | Replaces the monitored series wholesale, and `condition` references these `name`s. Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. Write them from scratch: the stored `queries` returned by the `get` tool are a different, read-only shape. | |
| scopeId | No | Team scope id (list_teams), stored on the alert, not merged into query filters. A change still needs full queries plus period. Omit (with queries+period) to keep the saved scope; null clears it. Do not send scopeId alone. | |
| condition | No | Alerts v3 firing rule: a single boolean expression over the query names (`name` field of each query). Supports arithmetic (+ - * /), comparisons (> >= < <= == !=), logical and/or/not, parentheses, and these window functions: rollingSum(a, N, UNIT) (trailing sum over the last N units, UNIT ∈ DAY|WEEK|MONTH, inclusive of today), weekToDateSum(a) (Monday-to-date), monthToDateSum(a) (1st-of-month-to-date), and timeShift(a, N, UNIT) (value shifted back N units; may wrap a window function). Examples: `a > 1000`, `rollingSum(a, 7, DAY) > 1000`, `(a - timeShift(a, 1, DAY)) / timeShift(a, 1, DAY) > 0.2`, `a > 10000 or rollingSum(a, 7, DAY) > 50000`. | |
| datePreset | No | Official date preset (same DatePreset as dashboards/reports, e.g. MTD, LAST_MONTH, TRAILING_30_DAYS). Prefer this over hand-computed from/to when a preset matches. Mutually exclusive with from/to. | |
| description | No | Alert description. | |
| slackChannelId | No | Slack target id (required if SLACK): a channel id (C…) to post to a channel, or a Slack user id (U…) to deliver a direct message to that user. Use list_available_destinations to discover both channels and the signed-in user's DM. | |
| teamsChannelId | No | Teams channel ID (required if TEAMS) | |
| notificationChannel | No | Notification channel |