# Settings reference

> Every workspace setting a customer can set — key, shape, default and its platform variable, who may set it, where the portal edits it, and what it changes. API-only keys are marked.

_Reference · Updated 2026-10-28 · Describes release 0.1.164_

Every setting a workspace can change, grouped by the endpoint that writes it. Each row gives the stored key, its shape, the default (and the platform variable a platform administrator sets it with, where one exists), the permission that may change it, the portal card that edits it — or **API only** — and what it changes. The procedures live in [Agents and guardrails](/help/agents-and-guardrails.md); this page is the lookup table.

## How settings are layered {#how-settings-are-layered}

1. **Platform default.** A platform administrator sets it through the environment variable named in the tables (`AIOPS_…`, the upper-case form of the key) or, for a few, the ops console. It applies to every workspace that has not set its own value.
2. **Workspace override.** The workspace's own value in its settings. For the JSON-boolean switches, `true` or `false` is an override and `null` removes it — the workspace follows the platform default again. A string such as `"false"` or `"off"` is refused rather than read two ways.
3. **Per-pillar override.** A few agent fields (the model, the schedule, the system prompt, the extra approval gates) live on the pillar agent, not the workspace.

Three endpoints write workspace settings, each with its own permission:

| Endpoint | Permission | What it takes |
|---|---|---|
| `PATCH /api/aiops/{tenant_id}` with `{"settings": {…}}` | `members.manage` | Any key except the protected ones; keys merge one by one, each key sent replaces that key whole, and `null` restores the default. The MCP tool `patch_tenant` rides the same rules. |
| `PATCH /api/aiops/{tenant_id}/guardrails` | `members.manage` (`GET` is open to every member) | The fields of the Guardrails, Intake rules and Notifications pages (the field names below), stored under the keys given. A merge-patch: only present fields change. |
| `PATCH /api/aiops/{tenant_id}/pillars/{pillar}` | `incidents.act`; `pillars.manage` when the body touches `approval_required` or `toolsets` | The pillar agent fields. |

Keys **protected** from `PATCH /api/aiops/{tenant_id}` (refused with `400 settings key(s) managed via dedicated endpoints: …`): `execution_mode`, `byo_llm`, `model_tiers`, `pii_salt`, `webhook_token_hash`, `smtp`, `workspace_scope`, `aws_external_id`, `lab_box_of`, `cloud_runtime`, `sso_trusted_issuers`, `builtin_detection`, `aiops_compliance_muted`. Each has its own endpoint — `PATCH …/guardrails` (`members.manage`) for `execution_mode` and `smtp`, `PATCH …/model-tiers` and `PUT …/llm-endpoint` (`pillars.manage`) for `model_tiers` and `byo_llm`, `PATCH …/detection` (`members.manage`) for `builtin_detection`, the webhook-token mint (`pillars.manage`) for `webhook_token_hash`, `POST …/incidents/muted-rules/unmute` (`approvals.decide`, removes entries only) for `aiops_compliance_muted` — or is platform-staff only (`workspace_scope`, `sso_trusted_issuers`, `cloud_runtime`, `lab_box_of`, `aws_external_id`, `pii_salt`). Secret-shaped keys (`aws_external_id`, `smtp`, `notifications`, `test_factory`, `cloud_runtime` and anything whose name contains `salt`, `token`, `secret`, `password`, `credential`, `api_key` or `private_key`) are never serialised back to a member.

Every save is audited (`tenant.update` or `Guardrails changed`); a save that flips a JSON switch records each flipped key with its old and new value.

## Guardrails page {#guardrails-page}

Fields of `GET` / `PATCH /api/aiops/{tenant_id}/guardrails` (**Settings · Agents → Guardrails**). Permission to change any of them: `members.manage`. "Card" is the card on that page.

### Execution, cost and PII {#execution-cost-and-pii}

| Field → stored key | Shape · default | Card | What it changes |
|---|---|---|---|
| `execution_mode` → `execution_mode` | `standard` or `computer_assist` · `standard` | **Execution mode** | `computer_assist` disables every platform write; turning it on clears `aiops_auto_remediation` and `aiops_auto_remediation_direct`, and arming either under it is `409`. |
| `max_cost_usd` → `max_cost_usd` | number ≥ 0, `0` = uncapped · platform `AIOPS_MAX_COST_USD_DEFAULT` (0) | **Cost cap & PII patterns** | The per-investigation USD ceiling; `null` follows the platform default (`cost_inherited: true`). Estate x-ray has its own budget. |
| `pii_patterns` → `pii_patterns` | list of ≤ 20 regexes · `[]` | **Cost cap & PII patterns** (*Tenant PII patterns*) | Extra scrub patterns on top of the always-on floor; an invalid regex is `400 invalid regex …`. |
| `ssrf_guard` → `ssrf_guard` | `true`, `false` or `null` · platform `AIOPS_SSRF_GUARD` (on) | **Cost cap & PII patterns** | `false` lets agents and probes reach internal addresses. |
| `ssrf_allow_hosts` → `ssrf_allow_hosts` | list of ≤ 50 hostnames · `[]` | **Cost cap & PII patterns** | Internal hosts that bypass the guard while it is on. |
| — → `web_fetch_allowlist` | list of domains · `[]` | **Web egress allowlist** | Domains `web_fetch` may read without an approval; everything else is gated. Written through `PATCH /api/aiops/{tenant_id}`. |

### Budgets and the kill switch {#budgets-and-the-kill-switch}

| Field → stored key | Shape · default | Card | What it changes |
|---|---|---|---|
| `daily_cost_limit_usd` → `daily_cost_limit_usd` | number ≥ 0, `0` = off · platform `AIOPS_DAILY_COST_LIMIT_USD_DEFAULT` (0) | **AI budgets & kill switch** (*Daily limit*) | Workspace-day circuit breaker: no new run once the day's spend reaches it (`409 daily cost budget exceeded …`). |
| `monthly_cost_limit_usd` → `monthly_cost_limit_usd` | number ≥ 0, `0` = off · 0 | same (*Monthly limit*) | Workspace-month breaker; spend resumes on the 1st (UTC). |
| `incident_cost_limit_usd` → `incident_cost_limit_usd` | number ≥ 0, `0` = off · 0 | same (*Per-incident limit*) | Caps one incident across all its runs; past it the incident escalates and a person grants more (**Continue analysis (+$5)**, `POST …/incidents/{id}/budget-grant`, amount in (0, 1000]). |
| `agents_paused` → `agents_paused` | boolean · `false` | same (**Pause all agents** / **Resume all agents**) | Kill switch: in-flight runs stop within one step, nothing new starts; alerts and incidents keep recording and escalate. |
| `golden_check` → `golden_check` | `{cron, dataset_id, model?, min_pass?}`; `{}` clears · `{}` | **Daily golden check** | A scheduled benchmark run; an unreadable cron or a dataset not of this workspace is `400`. |

### Automatic fixes and change class {#automatic-fixes-and-change-class}

| Field → stored key | Shape · default | Card | What it changes |
|---|---|---|---|
| `auto_remediation` → `aiops_auto_remediation` | boolean · `false` | **Pre-approved auto-fix (L1)** (*Allow pre-approved routes to auto-fire*) | Master switch for the pre-approved L1 path: a certified `gitops_pr` route opens its pull request on its own (tier ≤ L2, once per incident). |
| `auto_remediation_direct` → `aiops_auto_remediation_direct` | `true`/`false`/`null` · platform `AIOPS_AUTO_REMEDIATION_DIRECT` (off) | same | A certified `api` runbook route with a platform-verified precedent repeats the fix without a click; needs the master switch too. See [Automatic API fixes](/help/automatic-api-fixes.md). |
| `demote_on_rollback` → `aiops_demote_on_rollback` | `true`/`false`/`null` · `AIOPS_DEMOTE_ON_ROLLBACK` (off) | same (*Demote a pre-approved route after one rollback*) | The first rollback of a pre-approved route's run takes its pre-approval away until an approver certifies it again. |
| `demote_on_deviation`, `canary_execution` | added in 0.1.157 | — | [Settings added since 0.1.157](/help/settings-reference-changes.md#automatic-fixes-and-change-class). |
| `runbook_routes_with_typed_plans` → `aiops_runbook_routes_with_typed_plans` | `true`/`false`/`null` · `AIOPS_RUNBOOK_ROUTES_WITH_TYPED_PLANS` (off) | same (*Let the analysis pick a matching route instead of typed steps*) | In a workspace with write connections the analysis may name one matching route and leave its typed steps empty. |
| `preapproved_operations` → `aiops_preapproved_operations` | list of typed operation names · `[]` | **Pre-approved operations** | Operations a matched runbook's fix may carry for its change request to class `standard` (L1); `GET` also returns `known_operations`, and an unknown name is `400 unknown operation: …`. |
| `runbook_standard_change` → `aiops_runbook_standard_change` | `true`/`false`/`null` · `AIOPS_RUNBOOK_STANDARD_CHANGE` (on) | same (*Standard change for a runbook route's operations*) | A certified `remediate-…` route covering every typed step records the request as standard even for a P1 and it is released under the standard rules; only such a route makes a change standard. Off, a P1 fix stays emergency and the skill rule decides standard. |
| `runbook_owns_operations` → `aiops_runbook_owns_operations` | `true`/`false`/`null` · `AIOPS_RUNBOOK_OWNS_OPERATIONS` (on) | same (*A certified runbook carries its own operations*) | A step runbook certified by a named approver pre-approves its own backup and change operations; only a certified runbook matching the fix exactly makes it standard; Pre-approved operations applies to single-call routes. See [Step runbooks](/help/runbooks-and-automatic-fixes.md#step-runbooks). |
| `runbook_multi_cover` → `aiops_runbook_multi_cover` | `true`/`false`/`null` · `AIOPS_RUNBOOK_MULTI_COVER` (off) | same (*Let several runbooks cover one change request*) | Consecutive certified runbooks may cover one change request in order when no single runbook matches it (a proposal still open with your change process). Off: only one runbook matching the whole fix makes it standard. |
| `change_agent_cr` → `aiops_change_agent_cr` | `true`/`false`/`null` · `AIOPS_CHANGE_AGENT_CR` (on) | **Change management agent** (*Let the change management agent draft change requests*) | After an escalated analysis mints its change request, one change-agent run (at most 8 steps) drafts the document's narrative sections. Off: the document shows the platform's records only. See [The change request document](/help/change-request-document.md). |
| `change_agent_owns_plan` → `aiops_change_agent_owns_plan` | `true`/`false`/`null` · `AIOPS_CHANGE_AGENT_OWNS_PLAN` (off) | same (*Let the change management agent draft the fix too*) | Phase 2, needs `change_agent_cr`: the incident and compliance analyses draft no plan; the change agent drafts it and the class and tier rules run on its plan. Off: the analysis drafts the plan and its prompt is unchanged. |
| `cr_template` → `cr_template` | `{organization, logo_text}` · `{}` | — | The requesting organization and one line of text above the title of the change request document (default: the workspace name). Written through `PATCH /api/aiops/{tenant_id}`. |
| `change_class_policy` → `change_class_policy` | `derived` or `standard_default` · `derived` | same | `derived`: standard only for a fix a certified runbook matches exactly (even at P1), emergency for any other P1/critical, normal otherwise; a person only raises a class. `standard_default`: every request starts standard and people raise it. |
| `rollback_first` → `aiops_rollback_first` | `true`/`false`/`null` · `AIOPS_ROLLBACK_FIRST` (on) | **Suspect change across a declared dependency** (*Recommend rolling back the pinned change first*) | Rolling back a pinned suspect change leads the recommendation. |
| `pin_across_dependency` → `aiops_pin_across_dependency` | boolean · `true` | **Suspect change across a declared dependency** | The suspect-change pin may land on a change made one hop upstream on a declared dependency; attribution only, never a fold. |
| `hold_for_approval` → `aiops_hold_for_approval` | `true`/`false`/`null` · `AIOPS_HOLD_FOR_APPROVAL` (on since 0.1.163) | **Auto-close stale escalated incidents** (*Keep an incident open while its fix waits for a decision*) | The quiet-window close leaves an escalated incident open while its change request is drafted, requested, pending approval or approved but not run, or proposed for a certified runbook's fix. |
| `auto_resolve` → `aiops_auto_resolve` | `{enabled: bool, quiet_hours: 1..168}`; `{}` restores the default · enabled, platform `AIOPS_AUTORESOLVE_QUIET_S` (21600 s = 6 h) | **Auto-close stale escalated incidents** | Closes an escalated incident once every source alert cleared and nothing re-fired for the quiet window ($0, no agent run). |

### Change safety {#change-safety}

Fields of the **Change safety** card (`change-safety`). Every switch is off by default except `reopen_raise_severity` and `pir_at_submission` (on since 0.1.133), `backup_before_change` (on since 0.1.144) and `require_before_state` (on since 0.1.163); off is the behaviour before the switch existed. `GET` returns each switch whose default the platform sets under `switch_state` as `{effective, override, default}`.

| Field → stored key | Shape | Switch label | What it changes |
|---|---|---|---|
| `require_before_state` → `aiops_require_before_state` | `true`/`false`/`null` · `AIOPS_REQUIRE_BEFORE_STATE` (on since 0.1.163) | *Refuse a change when its before-state cannot be captured* | A direct cloud write runs only when the platform first read and stored the resource's current state; otherwise the plan is manual-only. |
| `auto_rollback_cloud` → `aiops_auto_rollback_cloud` | `"L1"`, `"L2"` or `null` | *Roll back a failed cloud fix automatically* (Off / L1 / L1 and L2) | A failed post-change validation runs the compiled cloud inverse up to this tier; `null` = a person releases it. Never L3. |
| `reopen_raise_severity` → `aiops_reopen_raise_severity` | `true`/`false`/`null` · `AIOPS_REOPEN_RAISE_SEVERITY` (on) | *Reopen a failed fix one severity level higher* | |
| `pir_at_submission` → `aiops_pir_at_submission` | `true`/`false`/`null` · `AIOPS_PIR_AT_SUBMISSION` (on) | *Schedule the post-implementation review when an emergency change is submitted* | Off, the review opens when the write lands; due after `AIOPS_PIR_DUE_H` (72 h). |
| `backup_before_change` → `aiops_backup_before_change` | `true`/`false`/`null` · `AIOPS_BACKUP_BEFORE_CHANGE` (on) | *Back up a stateful target before a reboot-type change* | An AMI or OS-disk snapshot before every reboot-type step, waited for until available; no usable backup, no change. Off, no backup step. |
| `ecr_block_pulls` → `aiops_ecr_block_pulls` | `true`/`false`/`null` · `AIOPS_ECR_BLOCK_PULLS` (off) | *Allow a runbook to block pulls of a vulnerable image* | Lets a runbook's `aws.ecr.block_image_pulls` step run: one Deny on the image's ECR repository policy, L3 only, undone by Roll back ([Block pulls of this image](/help/runbooks-and-automatic-fixes.md#block-pulls)). Off, a review holding the step is refused. |
| `backup_retention_days` → `aiops_backup_retention_days` | integer 1–365, `null` = 7 | *Keep pre-change backups for (days)* | The value of each new backup's `aiops:retain-until` tag; the platform never deletes a backup. |
| `validation_metrics` → `aiops_validation_metrics` | list of rules (connection, metric, threshold, window, glob); `[]` = none | **Post-change validation** (*Rules (JSON)*) | Post-change metric rules; a rule naming a connection the workspace does not have is `400`. |
| `validation_rounds` → `aiops_validation_rounds` | integer 1–6, `null` = platform `AIOPS_VALIDATION_ROUNDS` (3) | **Post-change validation** (*Rounds*) | How many rounds a watched change is read in ([Validation after a fix](/help/thresholds-and-why.md#thresholds-validation)). |
| `validation_interval_s` → `aiops_validation_interval_s` | integer 60–1800, `null` = platform `AIOPS_VALIDATION_INTERVAL_S` (300) | **Post-change validation** (*Seconds apart*) | How long each round is, and so how far apart the reads are. |
| `audit_correlation` → `aiops_audit_correlation` | `true`/`false` | *Stamp one correlation ID on every audit row of an incident lifecycle* | |

### Validation, posture and findings {#validation-posture-and-findings}

| Field → stored key | Shape · default | Card | What it changes |
|---|---|---|---|
| `verify_typed_check` → `aiops_verify_typed_check` | `true`/`false`/`null` · `AIOPS_VERIFY_TYPED_CHECK` (on) | **Validation checks (L6)** (*Check a typed cloud fix with its own read first*) | A typed posture fix is verified by its own compiled read 30 s after the write, at no agent cost; any other fix is cleared the same way once its alerts cleared at the source after the write and its validation plan's changed resources read as expected. |
| `verify_frugal` → `aiops_verify_frugal` | `true`/`false`/`null` · `AIOPS_VERIFY_FRUGAL` (on) | same (*Skip agent checks that cannot learn anything new*) | No paid verify beside the incident's own analysis, of a target no connection reaches, or repeating an uncertain answer on unchanged evidence. |
| `validation_derived` → `aiops_validation_derived` | `true`/`false`/`null` · `AIOPS_VALIDATION_DERIVED` (on) | same (*Watch the signals of a fix that no validation rule matches*) | With no **Post-change validation** rule matching an incident's fix, the rounds read what its change request's validation plan names: the incident's open CloudWatch alarms, the changed resources' built-in signals and their target groups', and their one-hop neighbours' (which only flag a round). Nothing readable: no rounds, the verify as before. |
| `verify_alert_rung` → `aiops_verify_alert_rung` | `true`/`false`/`null` · `AIOPS_VERIFY_ALERT_RUNG` (off) | same (*Treat an alarm still firing at its source as still present*) | A CloudWatch alarm still in `ALARM` at the source, past a 30-minute grace, answers `still_present` with no agent run. Changes results. |
| `triage_checklist` → `aiops_triage_checklist` | `true`/`false`/`null` · `AIOPS_TRIAGE_CHECKLIST` (on) | **Automated triage** (*Check cloud health and known issues before each analysis*) | Before an incident's analysis, code reads the cloud provider's own health (AWS Health, else the public AWS Health Dashboard; Azure Resource Health and Service Health) through the read connections, and the known issues: incidents on services one hop away in the dependency map (open, or resolved within the [known-issue window](/help/thresholds-and-why.md#thresholds-triage)) and open tickets naming the service in a connected ServiceNow, Jira or Azure DevOps. No model spend; what it finds goes to the analysis. Off: neither runs; the fault domain and the incident's **Automated triage** card stay. |
| `known_issue_fast_path` → `aiops_known_issue_fast_path` | `true`/`false`/`null` · `AIOPS_KNOWN_ISSUE_FAST_PATH` (on) | **Automated triage** (*Shorten the analysis of a known issue*) | An alert-started analysis that code rates 100 % relevant to a known issue (see [Thresholds and why](/help/thresholds-and-why.md#thresholds-triage)) is capped at 3 steps and checks only what could make it a different incident before proposing the recorded fix again; the change request still waits for a person. Never for a P1, a Re-analyse or a run with its own step cap. Audited as **Known issue — analysis shortened**. Needs `triage_checklist`. Off: every analysis runs in full; the relevancy level is still shown. |
| `auto_investigate_p1p2` → `aiops_auto_investigate_p1p2` | `true`/`false`/`null` · platform default on (ops console *Platform defaults*) | **Automatic analysis of P1/P2 findings** | A compliance finding at P1/P2 — a Security Hub / Defender finding pulled or pushed to the compliance pillar, or a failing platform check — starts its analysis with no prepared fix; P3/P4 waits for a person, also when a prepared fix exists (the incident says the fix is ready and **Analyse** starts it). Every analysis is a paid run. |
| `compliance_enabled` → `compliance.enabled` | `true`/`false`/`null` · platform `AIOPS_COMPLIANCE_FRAMEWORKS` (off: reads as off in every workspace, the card is hidden and `true` is refused with `409`); once it is on, off for a new workspace (`null` or absent = on) | **Compliance frameworks (phase 2)** in Guardrails (*Score this workspace against SOC 2 and ISO 27001*), shown only while the platform offers the frameworks (`compliance_frameworks_available` on `GET …/guardrails`) | Off: no platform check runs on a scan or after a captured change (EST-01 to EST-06 and LOG-01, CRY-01, ACC-04, MON-01 included), the open finding of each check that stopped is resolved, and the posture scores nothing; scanner findings still arrive. |
| `compliance_platform_controls` → `compliance.platform_controls` | `true`/`false`/`null` · off | **Compliance frameworks (phase 2)** in Guardrails (*Count the checks of the platform's own settings*) | On: the 13 checks of the platform's own settings (ACC-02 to MON-02) count in the score again; they run on every scan either way. |
| `auto_investigate_catchup` → `aiops_auto_investigate_catchup` | `true`/`false`/`null` · platform default off | same | The held P1/P2 backlog is swept up, 3 runs per workspace per minute. |
| `scanner_sync_interval_s` → `aiops_scanner_sync_interval_s` | `0` (off), a whole number 300..86400, or `null` · `AIOPS_SCANNER_SYNC_INTERVAL_S` (900) | **Security posture → Posture → Scanner sync** | How often Security Hub / Defender findings are pulled through the read connections. Any other value is `400 scanner_sync_interval_s …`. |
| `risk_suppress_at_source` → `aiops_risk_suppress_at_source` | boolean · `false` | **Suppress scanner findings at the source** | A suppressed finding is also written back to the scanner (Security Hub `SUPPRESSED` / an Azure Policy exemption) through the write connection. |
| `risk_no_self_approval` → `aiops_risk_no_self_approval` | `true`/`false`/`null` · off | **Risk exceptions** (*Never let a requester approve their own exception*) | |
| `risk_expiry_sweep` → `aiops_risk_expiry_sweep` | `true`/`false`/`null` · off | **Risk exceptions** | At expiry the scheduler marks the decision expired and reopens the finding. |
| `change_principal_check` → `aiops_change_principal_check` | `true`/`false`/`null` · `AIOPS_CHANGE_PRINCIPAL_CHECK` (on) | **Unmanaged changes** (*Flag the platform's credential used without a platform receipt*) | |
| `change_recheck_mapped` → `aiops_change_recheck_mapped` | `true`/`false`/`null` · `AIOPS_CHANGE_RECHECK_MAPPED` (on) | **Unmanaged changes** (*Re-check every control an unmanaged change touches*), shown only while the platform offers SOC 2 / ISO 27001 (`compliance_frameworks_available`) | |
| `unauthorized_change_incidents` → `aiops_unauthorized_change_incidents` | `true`/`false`/`null` · `AIOPS_UNAUTHORIZED_CHANGE_INCIDENTS` (off) | **Unmanaged changes** (*Open an incident for every change with no reference*) | An applied write the audit log captured that no platform record, allowlisted principal, approved ITSM change ticket or signed change request accounts for is flagged unauthorized and opens an incident per action that always waits for a person. Fill `change_authorized_principals` first. See [Unauthorized changes and denied access](/help/changes-reference.md#unauthorized). |
| `denied_access_to_secops` → `aiops_denied_access_to_secops` | `true`/`false`/`null` · `AIOPS_DENIED_ACCESS_TO_SECOPS` (on) | **Unmanaged changes** (*Report denied access attempts to SecOps*) | While the SecOps agent is enabled, denied attempts open one SecOps incident per principal, account and hour, at P3, with no analysis. |
| — → `aiops_compliance_muted` | legacy list `[{control_id, resource, note, by, at}]`, ≤ 200 | **Legacy muted findings** (read); `GET …/incidents/muted-rules`, `POST …/incidents/muted-rules/unmute` | A muted control-resource pair is skipped before it is flagged (audited `compliance.rule_muted_hit`). Nothing adds to the list since 0.1.95 — an accepted exception replaced it; an entry stops muting after `aiops_risk_accept_days` and only the unmute endpoint removes one. |

### Models, residency and retention {#models-residency-and-retention}

| Field → stored key | Shape · default | Card | What it changes |
|---|---|---|---|
| `model_route_policy` → `model_route_policy` | `{deny_prefixes: [≤ 10, no whitespace], require_prefix, label (≤ 60)}`; `{}` clears | **Model residency** | Checked on the backing model id of every tier before a run; a denied model fails the run closed. |
| `sg_regulated` → `aiops_sg_regulated` | `true`/`false`/`null` · off | **Model residency** (*Singapore regulated*) | Implies the model route policy for MAS/PDPA; enforced where a run is built. |
| `retention_days` → `retention_days` | whole number 30..3650 or `null` · platform `AIOPS_RETENTION_DAYS_DEFAULT` (365) | **Data retention** | How long a closed record outlives its close before the daily sweep removes it; `GET` returns the window in force, its origin and the last sweep. Out of range is `422`. |
| — → `model_tiers` | `{premium, reasoning, standard, light}`, each a model string, `""` clears a tier | **Settings · Agents → AI models → Model tiers**; `PATCH /api/aiops/{tenant_id}/model-tiers`, `pillars.manage` | The models each call uses (loop and chat = standard, RCA wrap-up = reasoning, x-ray = premium); a missing tier falls to the nearest configured one, then the platform default `AIOPS_MODEL_TIERS_DEFAULT`, then `AIOPS_MODEL`. |
| — → `byo_llm` | the workspace's own OpenAI-compatible endpoint (`api_base`, key write-only) | **AI models → Bring your own LLM endpoint**; `PUT` / `DELETE /api/aiops/{tenant_id}/llm-endpoint`, `pillars.manage` | Protected from the raw settings patch; the workspace's runs and their prices then follow its own gateway. |
| `dashboard_pillars` | added in 0.1.161 | — | [Settings added since 0.1.157](/help/settings-reference-changes.md#dashboard). |

## Intake rules {#intake-rules}

Fields of `/guardrails` edited under **Settings · Data in → Alert intake & tokens → Intake rules**; `GET` returns each as `{effective, override, default}`. The same keys sent through `PATCH /api/aiops/{tenant_id}` are checked the same way.

| Field → stored key | Shape · default | Card label | What it changes |
|---|---|---|---|
| `unmapped_triage` → `aiops_unmapped_triage` | `true`/`false`/`null` · `AIOPS_UNMAPPED_TRIAGE` (on) | *Alerts that name no service* | An alert with no `service` after the maps opens its own held `unmapped=` incident in the triage queue: no run, ticket, page or auto-fix until a person selects **Analyse** (mapping the source changes only the later alerts from it). |
| `reopen_window_h` → `aiops_reopen_window_h` | whole hours 0..8760 · `AIOPS_REOPEN_WINDOW_H` (0 = off) | *Reopen window* | An alert matching an incident resolved inside the window reopens it, unless a person closed it as known noise (a new incident opens). |
| `flap_threshold` → `aiops_flap_threshold` | whole number 0..100 · `AIOPS_FLAP_THRESHOLD` (0 = off) | *Flapping* | Reopens inside the window that mark an incident flapping. |
| `repeated_other_resource` | added in 0.1.157 | — | [Settings added since 0.1.157](/help/settings-reference-changes.md#intake-rules). |
| `source_service_labels` → `source_service_labels` | `{"<source>": ["<label>", …]}`, ≤ 50 sources, ≤ 10 labels each, names ≤ 200 chars; `{}` clears · `{}` | *Mapped sources* (added from the **Unidentified service — map this source** card on an unmapped incident) | The label an alert from that source takes as its `service` when it names none. A bad shape is `400 source_service_labels …`. |
| `service_alias` → `service_alias` | `{"<alert service>": "<dependency-map service>"}`, ≤ 200 aliases, names trimmed (case kept) ≤ 200 chars, a value never also a key; `{}` or `null` clears · `{}` | **Service aliases** (its own card below Intake rules) | Renames an alert's `service` to the dependency map's name as the last step before grouping; the original stays as `service_original`. A bad shape or a chain is `400 service_alias …`. |

Alert-processing keys with no portal editor (**API only**, `PATCH /api/aiops/{tenant_id}`):

| Key | Shape · default | Checked on save | What it changes |
|---|---|---|---|
| `label_map` | `{"source_key": "canonical_key"}` | no — send a JSON object; another shape fails every delivery | Renames label keys before deduplication ([Webhook reference](/help/webhook-reference.md#canonical-labels-and-workspace-maps)). |
| `severity_map` | `{"source_value": "canonical"}` | no | Rewrites `severity` values. |
| `criticality_map` | `{"<pattern>": "low|medium|high|mission-critical"}` | yes — unknown levels are `400` | Stamps the `criticality` label from resource names and ARNs; the highest match wins. |
| `assignment_map` | `{"<service glob>": "<assignee>"}` | no | The owner a new incident opens with; first match on `service`, else `namespace`. |
| `aiops_storm_group_window_s` | whole seconds 0..3600 · `AIOPS_STORM_GROUP_WINDOW_S` (300) | yes | The first window an outage consolidates in: related incidents opened inside it are grouped under the failing service the others depend on. It is also how old a related group must be before the grouping agent reads it. |
| `aiops_grouping_wait_s` | whole seconds 0..600 · `AIOPS_GROUPING_WAIT_S` (300) | yes | The grouping wait: how long a caller's analysis waits for its dependency's alert (`0` = no wait). Separate from `aiops_storm_group_window_s`, which neither lengthens nor turns it off. |
| `aiops_storm_spread_window_s` | whole seconds 0..7200 · `AIOPS_STORM_SPREAD_WINDOW_S` (600) | yes | How much longer the outage may still spread to callers and join the same incident; group + spread = how far back grouping reaches (900 s). |
| `aiops_storm_agent` | `true`/`false` · `AIOPS_STORM_AGENT` (on) | no | The grouping agent: one turn per related group of incidents over every recorded edge; `false` keeps grouping on arrival without it. |
| `aiops_require_identity_labels` | `true`/`false`/`null` · `AIOPS_REQUIRE_IDENTITY_LABELS` (off) | yes | Holds an alert with no service-ish label and no `alertname`: the incident opens with no analysis. |
| `ticket_required_fields` | list of `service`, `severity`, `priority`, `assignee`, `notify`; `[]` = none of these · `null` = `AIOPS_TICKET_REQUIRED_FIELDS` (all five) | yes — another name is `400` | A Jira, ServiceNow or Azure DevOps ticket sent in that does not name one waits in the triage queue, and AIOps asks on the ticket once ([Tickets that come in must name five fields](/help/ticket-systems.md#required-fields)). Whatever the list, even `[]`, a ticket that names no service still waits while `aiops_unmapped_triage` is on (the default); turning that off is the only way to let every ticket through. The environment variable is a JSON list. |
| `aiops_ingest_rate_per_min` | number · `AIOPS_INGEST_RATE_PER_MIN` (1200); `≤ 0` = off | no | The webhook token bucket over alert items; `429` with `Retry-After` when dry. |
| `aiops_label_drop` | list of label names · `[]` | no | Labels removed from the alert view the agent reads. |
| `aiops_inventory_interval_s` | whole seconds 0..31536000 · `AIOPS_INVENTORY_INTERVAL_S` (86400); `0` = off | yes | The estate inventory harvest interval. |
| `aiops_topology_fold_origins` | list of `declared`, `traced`, `discovered`, `observed` · `["declared"]` | no — unknown names are ignored; a list replaces the default whole | Which dependency sources may fold alerts; stale or hub edges still show and widen blast radius. |
| `aiops_max_concurrent_investigations` | whole number ≥ 1 · `AIOPS_MAX_CONCURRENT_INVESTIGATIONS` (3) | no | Runs in flight at once for the workspace. |

## Changes and remediation {#changes-and-remediation}

**API only** unless a card is named.

| Key | Shape · default | Who | What it changes |
|---|---|---|---|
| `change_window` (legacy `aiops_change_window`) | `{"days": ["sat"], "hours": "22:00-06:00"}` UTC; overnight hours wrap midnight and belong to the day they start (Sat 22:00 to Sun 06:00); optional `"weeks": [1-5]` with days = the Nth such weekday of the month (`{"days": ["tue"], "weeks": [2]}` = the 2nd Tuesday); `{}` clears the legacy alias too | `members.manage`; **Maintenance windows** | Workspace fallback when no service glob matches. Also GET/PATCH `/guardrails`; saves validate days, weeks and hours. A route keeps its own `params.change_window`. |
| `change_windows` | `{ "payments*": {"days": ["sun"], "hours": "22:00-02:00"} }`, ≤ 50 globs, each 1–200 characters; `{}` clears | `members.manage`; **Maintenance windows** | First matching service glob wins in row order, else workspace fallback, else always open. Also GET/PATCH `/guardrails`; invalid shapes are refused with 400. |
| `change_freeze` | same shape | `members.manage` | When a change observed outside the platform is flagged unauthorized. A malformed window errs toward flagging. |
| `aiops_image_reminder_h` | whole hours 0..720 · `AIOPS_IMAGE_REMINDER_H` (24) | `members.manage` | While an image [rebuilt under a new tag](/help/runbooks-and-automatic-fixes.md#image-rebuild) is not deployed (the scanner still reports the old digest), targets taking `image_not_deployed` are reminded this often; `0` = the first message only. |
| `change_service_map` | `{"<repo glob>": "<service>"}` | `members.manage` | Names the service of a change event that carries none. |
| `change_resource_map` | `{"<resource glob>": "<service>" | ["<service>", …]}`, ≤ 200 globs, ≤ 10 services each; patterns need at least 3 literal characters (`400` otherwise) | `members.manage` | Adds linked services to captured changes; an edit re-links the last 48 h of rows. |
| `change_authorized_principals` | `["<principal glob>", …]`, ≤ 100 globs of 1–200 characters, each with at least 3 letters or digits besides glob characters and `arn:<partition>:` (`400` otherwise) · `[]` | `members.manage` | Automation principals (CI/CD roles, deploy users) whose writes need no other reference while `aiops_unauthorized_change_incidents` is on; matched case-insensitively against the AWS role or user ARN, the Azure caller and the Kubernetes user. |
| `k8s_ci_namespaces` | `["<namespace>", …]`, ≤ 20 Kubernetes namespace names; `argocd`, `kube-system`, `kube-public` and `kube-node-lease` are refused (`400`) · `null` = `AIOPS_K8S_CI_NAMESPACES` (`flux-system`, `jenkins`, `gitlab-runner`, `tekton`); `[]` = none | `members.manage` | Service accounts in these namespaces are CI/CD pipelines: their writes in the Kubernetes audit log are recorded as changes (`actor_kind: service`), one row per object per pull and at most 50 objects per cluster and account, the rest counted. Argo CD syncs come from its own webhook; the cluster's own controllers are never a change. See [CI/CD service accounts](/help/changes-reference.md#k8s-ci). |
| `itsm_change_match` | `{state_field, approved_values: [1–20], ci_field, start_field, end_field}`, field names ≤ 100 characters, every key optional (`400` for any other key) · ServiceNow `approval` / `["approved"]` / `cmdb_ci` / `start_date` / `end_date`; Jira `status` / four approved states / `customfield_service` / no window | `members.manage` | Which fields of an ITSM change ticket say it is approved, which CI it covers and its planned window — an approved ticket naming the resource inside its window is a write's reference. |
| `unauthorized_change_ticket` | `{"assignment_group": "<group>"}` (≤ 100 characters, no `^`, `=` or line breaks) · `{}` | `members.manage` | The group an unauthorized change's ticket goes to (ServiceNow: the incident's assignment group, looked up by name; every system: named on the ticket); never a person. |
| `aiops_change_harvest_interval_s` | whole seconds · `AIOPS_CHANGE_HARVEST_INTERVAL_S` (900); `≤ 0` stops the scheduled audit-log harvest | `members.manage` | How often CloudTrail / Activity-log writes are pulled; the pull when an incident opens follows it. |
| `aiops_auto_rollback` | `true`/`false` · off | `members.manage` | Lets the rollback sweep run an operator-declared rollback script for a failed L1 or L2 change; L3 and agent-drafted rollbacks always wait for a person. |
| `aiops_verify_external_changes` | `true`/`false` · `AIOPS_VERIFY_EXTERNAL_CHANGES` (off) | `members.manage` | Checks a captured external change against its dependents, advisory only. |
| `aiops_reachability_checks`, `aiops_reachability_max_per_change` | `true`/`false`/`null` · `AIOPS_REACHABILITY_CHECKS` (off); whole number 0..60 or `null` · `AIOPS_REACHABILITY_MAX_PER_CHANGE` (10) | `members.manage` | AWS Reachability Analyzer for each one-hop neighbour's EC2 network interface in a fix's validation rounds, through the change's write connection, tagged `aiops:change` and deleted after the read ([Verify recovery](/help/verify-recovery.md#recheck-and-post-close-verification)); the cap: analyses per change, about USD 0.10 each on the AWS bill. |
| `aiops_cross_cloud_plans` | `true`/`false`/`null` · off | `members.manage` | A prepared fix may act on another cloud than its incident's; the step stays reviewable with a warning the approval binds. |
| `aiops_lkg_cause_block`, `aiops_lkg_record` | `true`/`false`/`null` · `AIOPS_LKG_CAUSE_BLOCK` (on), `AIOPS_LKG_RECORD` (on) | `members.manage` | The ranked changes in the agent's prompt; the last-known-good record. |
| `aiops_lkg_lookback_h`, `aiops_lkg_suspect_h` | hours, held to 1..168 · `AIOPS_LKG_LOOKBACK_H` (72); hours, held to 1..24 · `AIOPS_LKG_SUSPECT_H` (2) | `members.manage` | How far back changes are ranked; the window a change counts as the suspect. |
| `aiops_prior_resolution` | `false` to opt out · on | `members.manage` | The cross-incident memory of prior resolutions and failed queries in the triage prompt. |
| `aiops_compliance_auto_triage` | `true`/`false` · `AIOPS_COMPLIANCE_AUTO_TRIAGE` (on) | `members.manage` | Whether a compliance finding's incident is analysed automatically (P1/P2 only, under `aiops_auto_investigate_p1p2`). |
| `aiops_patch_within_days` | whole number 0..3650 or `null` · `AIOPS_PATCH_WITHIN_DAYS` (0 = no rule) | `members.manage` | The patch report's optional rule: a node whose last patch operation (scan or install) ended more than this many days ago counts as non-compliant. No default on purpose — with `0` the report shows Patch Manager's own verdict. Anything else is `400 aiops_patch_within_days must be a whole number from 0 to 3650`. |
| `aiops_patch_findings` | `true`/`false`/`null` · `AIOPS_PATCH_FINDINGS` (on) | `members.manage` | The platform's own patch findings: each Systems Manager managed node the daily Patch Manager read (**Scores**) finds non-compliant opens a **Patching** finding (control `PATCH.COMPLIANCE`, one incident; HIGH with a missing critical patch, else MEDIUM) with the commands that patch it. A node Security Hub's SSM.2 already holds open is not raised twice; off clears every open one. |
| `aiops_backup_findings` | `true`/`false`/`null` · `AIOPS_BACKUP_FINDINGS` (on) | `members.manage` | The platform's own backup findings: each EC2 instance or RDS database the daily AWS Backup read (**Scores**) finds with no recovery point opens a **Backup** finding (control `BACKUP.PLAN`, one incident, P3) with the commands that close it — only while EST-01 does not run (SOC 2 / ISO 27001 off). A resource a Security Hub backup finding already holds open is not raised twice. |
| `aiops_backup_plan_schedule`, `aiops_backup_plan_retention_days` | an AWS Backup `cron(...)` / `rate(...)` expression · `AIOPS_BACKUP_PLAN_SCHEDULE` (`cron(0 18 * * ? *)`, 02:00 SGT daily); whole number 0..36500 or `null` · `AIOPS_BACKUP_PLAN_RETENTION_DAYS` (35; 0 = keep until deleted) | `members.manage` | The backup plan a backup finding's commands create (`aiops-daily-<days>d`). A value that is not a schedule expression reads as the default. A proposal until your backup policy names its own. |
| `aiops_change_compliance_agent` | `true`/`false` · `AIOPS_CHANGE_COMPLIANCE_AGENT` (off) | `members.manage` | The agent turn that reviews a captured change against the compliance controls. |
| `aiops_risk_accept_days`, `aiops_risk_mitigate_days` | days · 90, 180 | `members.manage` | Default expiry of an accepted and a mitigated risk decision. |
| `risk_context` | `{internet_patterns, internal_patterns, sensitive_patterns: [arn-glob], env_map: {arn-glob: env}}` | `members.manage` | Declares exposure, data sensitivity and environment for findings; only ever fills unknowns or raises exposure. |
| `aiops_xray_schedule_cron`, `aiops_xray_schedule_adopt` | UTC 5-field cron, at most daily, `""` = off; boolean · off | `members.manage`; card **Estate x-ray → Re-scan on a schedule** | A scheduled estate x-ray re-scan; adopt safe context automatically. An invalid cron is `400 aiops_xray_schedule_cron …`. |
| `aiops_min_pass` | percent · 80 | `members.manage` | The certification floor a benchmark `compare` or `certify` may only raise. |
| `aiops_prompt_cache`, `aiops_prompt_diet` | `true`/`false`/`null` · `AIOPS_PROMPT_CACHE` (on), `AIOPS_PROMPT_DIET` (off) | `members.manage` | Prompt caching with the gateway; the trimmed prompt mode. |
| `aiops_best_of_2`, `aiops_best_of_2_confidence` | boolean · off; number · 0.9 | `members.manage` | A low-confidence wrap-up reruns the whole investigation once; the confidence below which it triggers. |
| `aiops_wrapup_selfcheck` | boolean · off | `members.manage` | One extra wrap-up turn re-examining the root-cause hop. |
| `aiops_prerun_escalation` | `false` to opt out · on | `members.manage` | Severity `critical` or `env=prod` picks the reasoning tier before the run. |
| `patch_schedules` | added in 0.1.157 | — | [Settings added since 0.1.157](/help/settings-reference-changes.md#changes-and-remediation). |

## Notifications, on-call and email {#notifications-on-call-and-email}

Fields of `/guardrails` edited under **Settings · Account → Notifications & on-call** (`settings-notifications`). A chat webhook URL is the capability itself, so `GET` returns full URLs only to `members.manage`; everyone else sees the host.

| Field → stored key | Shape · default | Card | What it changes |
|---|---|---|---|
| `notifications` → `notifications` | ≤ 10 targets `{url, kind, events}`; `kind` `teams`, `slack`, `webhook`, `email` (`to` addresses, optional `oncall: true`), `pagerduty` or `opsgenie` (`key` write-only, `region` `us`/`eu`, first three events only); `events` ⊆ `open`, `escalate`, `resolve`, `golden`, `rollback`, `image_not_deployed`, `change_schedule` · `[]` | **Incident notifications** | Pushes on the chosen events ([Notifications](/help/setup-notifications.md)); `400` for a `http://` URL, an unknown kind or an empty event list. |
| `notify_min_interval_s` → `aiops_notify_min_interval_s` | whole seconds 0..3600 or `null` · `AIOPS_NOTIFY_MIN_INTERVAL_S` (120) | *Message interval (seconds)* | Chat and email bursts: one message per target per interval, the rest as a digest; `0` sends every event. Webhooks and pagers are never grouped. |
| `oncall` → `oncall` | `{rotation: [emails], period: daily|weekly, anchor: YYYY-MM-DD, overrides: [{from, to, email}]}`; `{}` clears · `{}` | **On-call schedule** | Who an email target also reaches at send time (`oncall_now` in `GET`). |
| `smtp` → `smtp` | `{host, port, username, password (write-only), from, starttls}`; `{}` clears · the platform relay | **Email delivery** | Bring-your-own SMTP; the password lives in the secret store, never in settings (`smtp_secrets_encrypted` says whether at-rest encryption is on). |

## Pillar agents {#pillar-agents}

`PATCH /api/aiops/{tenant_id}/pillars/{pillar}` (`incident`, `compliance`, `change`, `secops`, `finops`, `improvement`, `reporting`), edited on **Settings · Agents → Agents → Agent settings**; `approval_required` and `toolsets` are on **Guardrails → Extra approval gates (per pillar)**.

| Field | Shape · default | Permission | What it changes |
|---|---|---|---|
| `enabled` | boolean | `incidents.act` (platform staff toggle it on the ops tenant page too) | A disabled pillar leaves the nav, the dashboard deck and the agent-API endpoints; a signal routed to it fails its run and escalates. |
| `model` | model string · empty = the tier map | `incidents.act` | *Model override*: wins over every tier and empties the escalation ladder. |
| `schedule_cron` | cron (5 or 6 fields), blank = event-driven | `incidents.act` | *Schedule (cron)*: a scheduled run per tick. An invalid expression is `422 schedule_cron '…' is not a valid cron expression`; one that never fires is `422 schedule_cron '…' never fires`. |
| `system_prompt` | text | `incidents.act` | *System prompt (on top of the builtin persona)*. |
| `approval_required` | list of tool-name globs · `[]` | `pillars.manage` | Extra approval gates on top of the intrinsic write floor. |
| `toolsets` | object of toolset overrides | `pillars.manage` | Which connector toolsets the pillar sees. |

## Thresholds and why {#thresholds-and-why}

Every number the platform works with — its value, why it has that value, what goes wrong lower or higher, and who changes it — is on its own page: [Thresholds and why](/help/thresholds-and-why.md).

## Platform-level knobs a workspace cannot set {#platform-level-knobs}

The behaviours a workspace often asks about that only a platform administrator sets (control-plane environment variables): the correlation window `AIOPS_CORRELATION_WINDOW_S` (300 s), the approval TTL `AIOPS_APPROVAL_TTL_S` (24 h), the recheck interval `AIOPS_INCIDENT_RECHECK_INTERVAL_S` (900 s), the per-run step cap `AIOPS_MAX_STEPS` (40), PII scrubbing of alerts and tool output (`AIOPS_PII_SCRUB_ALERTS`, `AIOPS_PII_SCRUB_TOOL_OUTPUT`, `AIOPS_PII_REDACT_IP`), the licence (`AIOPS_LICENSE`, `AIOPS_LICENSE_PUBLIC_KEY`, `AIOPS_LICENSE_GRACE_DAYS` = 30), the x-ray budget `AIOPS_XRAY_MAX_COST_USD` (25), the built-in detection poll `AIOPS_BUILTIN_DETECTION_POLL_S` (300 s), how often the compliance reports (score per standard, patching, backup) are read `AIOPS_COMPLIANCE_REPORT_INTERVAL_S` (86400 s; `0` = only **Refresh reports**), whether SOC 2 / ISO 27001 are offered at all `AIOPS_COMPLIANCE_FRAMEWORKS` (off: phase 2) and the model price map. Ask your platform administrator; the ops console's *Platform defaults* page carries the ones that have an editor.


## Next steps

- Next: [Settings added since 0.1.157](/help/settings-reference-changes.md)
- [All Reference pages](/help/reference/llms.txt)
- [Documentation home](/help/)
