---
title: 'Configuration (YAML)'
description: 'Complete reference for yorker.config.yaml — every field, type, default, and constraint.'
section: 'Reference'
canonical_url: 'https://yorkermonitoring.com/docs/reference/configuration'
---

# Configuration (YAML)

Yorker uses a declarative YAML file to define monitors, alerts, SLOs, and notification channels as code. The CLI validates every field through the same Zod schemas that the API uses, so defaults and constraints are applied identically.

To get started, create a `yorker.config.yaml` in your project root.

---

## Root Fields

| Field | Type | Required | Description |
|---|---|---|---|
| `project` | `string` | Yes | Project name. Displayed in deploy plan output. |
| `alertChannels` | `object` | No | Named notification channel definitions (key-value map). |
| `defaults` | `object` | No | Default values inherited by all monitors. |
| `groups` | `array` | No | Groups of monitors that share frequency, locations, and alerts. |
| `monitors` | `array` | No | Top-level monitor definitions (outside any group). |
| `slos` | `array` | No | Service Level Objective definitions. |
| `maintenanceWindows` | `array` | No | Scheduled silences / pauses. See [`maintenanceWindows`](#maintenancewindows). |

```yaml
project: "my-app"

alertChannels:
  # ...

defaults:
  # ...

groups:
  # ...

monitors:
  # ...

slos:
  # ...
```

---

## `alertChannels`

To configure notification channels, define them as named entries under `alertChannels`. Alerts and SLOs reference channels by name using `@channel-name` syntax.

Each channel must have a `type` field. The remaining fields depend on the type.

### Slack

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"slack"` | Yes | Channel type. |
| `webhookUrl` | `string` (URL) | Yes | Slack incoming webhook URL. |

```yaml
alertChannels:
  ops-slack:
    type: slack
    webhookUrl: "https://hooks.slack.com/services/T00/B00/xxxx"
```

### Email

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"email"` | Yes | Channel type. |
| `addresses` | `string[]` | Yes | At least one valid email address. |

```yaml
alertChannels:
  team-email:
    type: email
    addresses:
      - oncall@example.com
      - devops@example.com
```

### Webhook

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `type` | `"webhook"` | Yes | -- | Channel type. |
| `url` | `string` (URL) | Yes | -- | Webhook endpoint URL. |
| `method` | `"POST"` \| `"PUT"` | No | `"POST"` | HTTP method. |
| `headers` | `object` | No | -- | Custom headers (key-value string pairs). |

```yaml
alertChannels:
  generic-webhook:
    type: webhook
    url: "{{secrets.OPSGENIE_WEBHOOK_URL}}"
    method: POST
    headers:
      Authorization: "GenieKey {{secrets.OPSGENIE_KEY}}"
```

Use the dedicated `pagerduty` and `servicenow` channel types below for those targets; the generic webhook type is for Opsgenie, custom integrations, or anything not covered by a first-class channel type.

### PagerDuty

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `type` | `"pagerduty"` | Yes | -- | Channel type. |
| `routingKey` | `string` (1-64 chars) | Yes | -- | PagerDuty Events API v2 integration key. |
| `serviceRegion` | `"us"` \| `"eu"` | No | `"us"` | `us` for events.pagerduty.com, `eu` for events.eu.pagerduty.com. |

```yaml
alertChannels:
  pagerduty-oncall:
    type: pagerduty
    routingKey: "{{secrets.PAGERDUTY_ROUTING_KEY}}"
    serviceRegion: us
```

### ServiceNow

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `type` | `"servicenow"` | Yes | -- | Channel type. |
| `instanceUrl` | `string` (URL) | Yes | -- | Full ServiceNow instance URL (e.g. `https://acme.service-now.com`). |
| `username` | `string` (1-128 chars) | Yes | -- | ServiceNow account with permission to create incidents. |
| `password` | `string` (1-512 chars) | Yes | -- | ServiceNow account password or OAuth token. Stored as a secret at rest. |
| `assignmentGroup` | `string` (max 128) | No | -- | Optional group name (maps to `sys_user_group.name`) for new incidents. |

```yaml
alertChannels:
  servicenow-prod:
    type: servicenow
    instanceUrl: "https://acme.service-now.com"
    username: "{{secrets.SNOW_USER}}"
    password: "{{secrets.SNOW_PASSWORD}}"
    assignmentGroup: "Platform Oncall"
```

---

## `defaults`

To set values that apply to all monitors unless overridden, use the `defaults` block. Monitors and groups can override any default.

| Field | Type | Default | Description |
|---|---|---|---|
| `frequency` | `string` | `"5m"` (300s) | Check interval. Format: `Ns`, `Nm`, or `Nh`. Range: 10s-86400s. |
| `locations` | `string[]` | `["loc_us_east", "loc_eu_central"]` | Location IDs to run from. Must be non-empty. |
| `http` | `object` | -- | Default HTTP check configuration. |
| `browser` | `object` | -- | Default browser check configuration. |
| `alerts` | `array` | -- | Default alert rules applied to all monitors. |

### Frequency Format

The frequency value uses a duration string with a numeric value and a unit suffix.

| Unit | Suffix | Example | Seconds |
|---|---|---|---|
| Seconds | `s` | `30s` | 30 |
| Minutes | `m` | `5m` | 300 |
| Hours | `h` | `1h` | 3600 |

The resolved value in seconds must be between **10** and **86400** (24 hours).

```yaml
defaults:
  frequency: "5m"
  locations:
    - loc_us_east
    - loc_eu_central
    - loc_ap_northeast
```

### `defaults.http`

To configure default values for all HTTP monitors, use the `defaults.http` block.

| Field | Type | Default | Description |
|---|---|---|---|
| `timeoutMs` | `number` | `30000` | Request timeout in milliseconds. |
| `followRedirects` | `boolean` | `true` | Whether to follow HTTP redirects. |
| `maxRedirects` | `number` | `5` | Maximum number of redirects to follow. |
| `assertions` | `array` | `[]` | Default assertion rules. See [Assertions](/docs/reference/assertions). |

```yaml
defaults:
  http:
    timeoutMs: 15000
    followRedirects: true
    maxRedirects: 3
    assertions:
      - type: status_code
        value: 200
      - type: response_time
        max: 5000
```

### `defaults.browser`

To configure default values for all browser monitors, use the `defaults.browser` block.

| Field | Type | Default | Constraints | Description |
|---|---|---|---|---|
| `timeoutMs` | `number` | `30000` | 5000-120000 | Script execution timeout in milliseconds. |
| `viewport` | `object` | `{ width: 1280, height: 720 }` | -- | Browser viewport dimensions. |
| `viewport.width` | `number` | `1280` | -- | Viewport width in pixels. |
| `viewport.height` | `number` | `720` | -- | Viewport height in pixels. |
| `screenshotMode` | `string` | `"every_step"` | `every_step` \| `failure_only` \| `disabled` | When to capture screenshots. |
| `videoEnabled` | `boolean` | `false` | -- | Whether to record video. |
| `device` | `string` | -- | -- | Playwright device name for emulation (e.g., `"iPhone 14"`). |

```yaml
defaults:
  browser:
    timeoutMs: 60000
    viewport:
      width: 1920
      height: 1080
    screenshotMode: every_step
    videoEnabled: false
```

### `defaults.alerts`

To set alert rules that apply to all monitors by default, define them under `defaults.alerts`. Each alert requires at least one condition and at least one channel reference.

```yaml
defaults:
  alerts:
    - name: "default-failure-alert"
      conditions:
        - type: consecutive_failures
          count: 3
      channels:
        - "@ops-slack"
```

See [Groups and Monitors](#groups) for how alert inheritance works.

---

## `groups`

To organize monitors that share configuration, use groups. Groups can override `defaults` for frequency, locations, and alerts.

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | `string` | Yes | Group name (for display in deploy plan). |
| `frequency` | `string` | No | Overrides `defaults.frequency` for all monitors in this group. |
| `locations` | `string[]` | No | Overrides `defaults.locations` for all monitors in this group. |
| `alerts` | `array` | No | Overrides `defaults.alerts` for all monitors in this group. |
| `monitors` | `array` | Yes | Monitor definitions within this group. |

```yaml
groups:
  - name: "US API endpoints"
    frequency: "1m"
    locations:
      - loc_us_east
      - loc_us_west
    alerts:
      - name: "api-down"
        conditions:
          - type: consecutive_failures
            count: 2
        channels:
          - "@ops-slack"
          - "@team-email"
    monitors:
      - name: "Users API"
        type: http
        url: "https://api.example.com/v1/users"
      - name: "Orders API"
        type: http
        url: "https://api.example.com/v1/orders"
```

---

## `monitors`

### HTTP Monitors

To define an HTTP monitor, set `type: http` and provide a `url`.

| Field | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
| `name` | `string` | Yes | — | 1-255 characters | Unique monitor name. |
| `type` | `"http"` | Yes | — | — | Monitor type. |
| `url` | `string` | Yes | — | Valid URL | Target URL to check. |
| `method` | `string` | No | `"GET"` | `GET` \| `POST` \| `PUT` \| `DELETE` \| `PATCH` \| `HEAD` | HTTP method. |
| `headers` | `object` | No | — | — | Custom request headers (key-value string pairs). |
| `body` | `string` | No | — | — | Request body (ignored for GET and HEAD). |
| `auth` | `object` | No | — | — | Authentication configuration. See [Auth](#auth). |
| `followRedirects` | `boolean` | No | `true` | — | Whether to follow redirects. Overrides `defaults.http`. |
| `maxRedirects` | `number` | No | `5` | — | Maximum redirects. Overrides `defaults.http`. |
| `timeoutMs` | `number` | No | `30000` | — | Request timeout in ms. Overrides `defaults.http`. |
| `assertions` | `array` | No | `[]` | — | Assertion rules. **Replaces** defaults (not merged). See [Assertions](/docs/reference/assertions). |
| `frequency` | `string` | No | From defaults/group | 10s-86400s | Check interval. |
| `locations` | `string[]` | No | From defaults/group | Non-empty | Location IDs. |
| `alerts` | `array` | No | From defaults/group | — | Alert rules. |
| `labels` | `string[]` | No | — | See [Labels](#labels) | Labels attached to this check. Emitted as OTel resource attributes. |
| `enabled` | `boolean` | No | `true` | — | Whether this monitor is active. |

```yaml
monitors:
  - name: "Homepage"
    type: http
    url: "https://www.example.com"
    method: GET
    timeoutMs: 10000
    assertions:
      - type: status_code
        value: 200
      - type: response_time
        max: 3000
      - type: body_contains
        value: "Welcome"

  - name: "Create Order API"
    type: http
    url: "https://api.example.com/v1/orders"
    method: POST
    headers:
      Content-Type: "application/json"
    body: '{"item": "test", "quantity": 1}'
    auth:
      type: bearer
      token: "{{secrets.API_TOKEN}}"
    assertions:
      - type: status_code
        value: 201
```

### Browser Monitors

Browser monitors defined in `yorker.config.yaml` are always scripted — point `script` at a Playwright TypeScript file and Yorker runs it on each check. URL-mode browser monitors (which navigate a single URL without a script) are currently created through the Web UI or the REST API only; they cannot be deployed via YAML yet.

| Field | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
| `name` | `string` | Yes | — | 1-255 characters | Unique monitor name. |
| `type` | `"browser"` | Yes | — | — | Monitor type. |
| `script` | `string` | Yes | — | — | Path to Playwright script file (relative to config file). |
| `steps` | `array` | No | — | — | Optional named steps. Each entry has `name` (must match a `// @step: Name` marker in the script), optional `timeoutMs`, and optional `assertions`. |
| `viewport` | `object` | No | `{ width: 1280, height: 720 }` | — | Browser viewport dimensions. Overrides `defaults.browser`. |
| `device` | `string` | No | — | — | Playwright device name for emulation. Overrides `defaults.browser`. |
| `screenshotMode` | `string` | No | `"every_step"` | `every_step` \| `failure_only` \| `disabled` | Screenshot capture mode. Overrides `defaults.browser`. |
| `videoEnabled` | `boolean` | No | `false` | — | Whether to record video. Overrides `defaults.browser`. |
| `timeoutMs` | `number` | No | `30000` | 5000-120000 | Script timeout in ms. Overrides `defaults.browser`. |
| `frequency` | `string` | No | From defaults/group | 10s-86400s | Check interval. |
| `locations` | `string[]` | No | From defaults/group | Non-empty | Location IDs. |
| `alerts` | `array` | No | From defaults/group | — | Alert rules. |
| `labels` | `string[]` | No | — | See [Labels](#labels) | Labels attached to this check. |
| `enabled` | `boolean` | No | `true` | — | Whether this monitor is active. |

```yaml
monitors:
  - name: "Login Flow"
    type: browser
    script: "./monitors/login.ts"
    viewport:
      width: 1920
      height: 1080
    screenshotMode: every_step
    timeoutMs: 60000
    frequency: "10m"
    locations:
      - loc_us_east
      - loc_eu_west
```

### MCP Monitors

To define an MCP monitor (for checking Model Context Protocol servers over Streamable HTTP), set `type: mcp` and provide an `endpoint`.

| Field | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
| `name` | `string` | Yes | — | 1-255 characters | Unique monitor name. |
| `type` | `"mcp"` | Yes | — | — | Monitor type. |
| `endpoint` | `string` | Yes | — | Valid URL | Streamable HTTP endpoint of the MCP server. |
| `timeoutMs` | `number` | No | `30000` | 5000-120000 | Request timeout in ms. |
| `auth` | `object` | No | — | — | Same shape as HTTP [`auth`](#auth). |
| `expectedTools` | `string[]` | No | — | — | Tool names that must be present. Missing tools fail the check. |
| `testCalls` | `array` | No | — | — | Tool invocations to exercise. See below. |
| `detectSchemaDrift` | `boolean` | No | `true` | — | Emit events when the tool list or tool signatures change. |
| `frequency` | `string` | No | From defaults/group | 10s-86400s | Check interval. |
| `locations` | `string[]` | No | From defaults/group | Non-empty | Location IDs. |
| `alerts` | `array` | No | From defaults/group | — | Alert rules. |
| `labels` | `string[]` | No | — | See [Labels](#labels) | Labels attached to this check. |
| `enabled` | `boolean` | No | `true` | — | Whether this monitor is active. |

`testCalls` entry fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `toolName` | `string` | Yes | Name of the tool to invoke. |
| `arguments` | `object` | No | Plain JSON key-value map of arguments passed to the tool. |
| `expectedOutputContains` | `string` | No | Substring that must appear in the tool's result. |

```yaml
monitors:
  - name: "Docs MCP"
    type: mcp
    endpoint: "https://mcp.example.com/sse"
    frequency: "5m"
    auth:
      type: bearer
      token: "{{secrets.MCP_TOKEN}}"
    expectedTools:
      - search_docs
      - fetch_page
    testCalls:
      - toolName: search_docs
        arguments:
          query: "pricing"
        expectedOutputContains: "Plans"
    detectSchemaDrift: true
    locations:
      - loc_us_east
```

---

## `auth`

To authenticate HTTP requests, add an `auth` block to an HTTP monitor. Three authentication types are supported.

### Basic Auth

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"basic"` | Yes | Auth type. |
| `username` | `string` | Yes | Username. |
| `password` | `string` | Yes | Password. |

```yaml
auth:
  type: basic
  username: "{{secrets.BASIC_USER}}"
  password: "{{secrets.BASIC_PASS}}"
```

### Bearer Token

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | `"bearer"` | Yes | Auth type. |
| `token` | `string` | Yes | Bearer token value. |

```yaml
auth:
  type: bearer
  token: "{{secrets.API_TOKEN}}"
```

### API Key

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `type` | `"api-key"` | Yes | -- | Auth type. |
| `header` | `string` | No | `"X-API-Key"` | Header name to send the key in. |
| `value` | `string` | Yes | -- | API key value. |

```yaml
auth:
  type: api-key
  header: "X-Custom-Key"
  value: "{{secrets.CUSTOM_API_KEY}}"
```

---

## `slos`

To define Service Level Objectives, add entries to the `slos` array. Each SLO is linked to a monitor by name.

| Field | Type | Required | Default | Constraints | Description |
|---|---|---|---|---|---|
| `name` | `string` | Yes | -- | 1-255 characters | Unique SLO name. |
| `monitor` | `string` | Yes | -- | Must match a monitor `name` | Reference to the monitor this SLO tracks. |
| `target` | `string` \| `number` | Yes | -- | Resolves to 1-9999 basis points | Availability target. `"99.9%"` or `99.9`. |
| `window` | `string` \| `number` | Yes | -- | `"7d"` \| `"14d"` \| `"30d"` or `7` \| `14` \| `30` | Rolling window. |
| `burnRateAlerts` | `boolean` | No | `true` | -- | Enable burn rate alerting. |
| `channels` | `array` | No | `[]` | -- | Channel references for burn rate alerts (e.g., `["@ops-slack"]`). |
| `enabled` | `boolean` | No | `true` | -- | Whether this SLO is active. |

### Target Format

The target can be specified as a percentage string or a number:

- `"99.9%"` -- parsed as 9990 basis points
- `99.9` -- treated as a percentage, parsed as 9990 basis points
- Valid range: 1-9999 basis points (0.01% to 99.99%)

### Window Format

The window can be a duration string or a number:

- `"7d"`, `"14d"`, `"30d"` -- string format
- `7`, `14`, `30` -- numeric format (days)
- Only these three values are allowed.

```yaml
slos:
  - name: "Homepage Availability"
    monitor: "Homepage"
    target: "99.9%"
    window: "30d"
    burnRateAlerts: true
    channels:
      - "@ops-slack"

  - name: "API Uptime"
    monitor: "Users API"
    target: 99.95
    window: 7d
    channels:
      - "@ops-slack"
      - "@team-email"
```

---

## `maintenanceWindows`

To silence alerts during scheduled work, add entries to `maintenanceWindows`. A window can pause checks entirely or let them continue running while suppressing notifications.

> **Single-file format only.** `maintenanceWindows` is supported when you deploy from a single `yorker.config.yaml`. The directory format (`yorker init --format directory`) does not currently support a per-resource maintenance-window YAML file. If you need maintenance windows in code, stay on the single-file format. Also note that `yorker pull` does not export maintenance windows and overwrites the YAML file; see the [CLI reference for `yorker pull`](/docs/reference/cli#yorker-pull) for the round-trip caveat.

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `name` | `string` | Yes | — | Unique window name. |
| `mode` | `string` | No | `pause` | `pause` (stop running checks) or `continue` (run but silence alerts). |
| `checks` | `"all"` \| `string[]` | Yes | — | `"all"` or a list of monitor names covered by the window. |
| `startsAt` | `string` | Yes | — | ISO-8601 start timestamp. |
| `endsAt` | `string` | Yes | — | ISO-8601 end timestamp. Must be after `startsAt`. |
| `recurring` | `boolean` | No | `false` | Enable recurrence. |
| `recurrenceRule` | `string` | No | — | RRULE string (e.g., `FREQ=WEEKLY;BYDAY=SU`). Required when `recurring: true`. |

```yaml
maintenanceWindows:
  - name: "Weekly DB maintenance"
    mode: pause
    checks: all
    startsAt: "2026-04-12T02:00:00Z"
    endsAt:   "2026-04-12T03:00:00Z"
    recurring: true
    recurrenceRule: "FREQ=WEEKLY;BYDAY=SU"

  - name: "Black Friday cut-over"
    mode: continue
    checks:
      - "Homepage"
      - "Checkout Flow"
    startsAt: "2026-11-27T05:00:00Z"
    endsAt:   "2026-11-27T06:00:00Z"
```

---

## `alerts` (alert rule definitions)

Alert rules live on monitors, in `groups`, or in `defaults`. Each rule has a list of `conditions` (ANDed together) and `channels` (channel references).

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `name` | `string` | No | — | Optional rule name. |
| `enabled` | `boolean` | No | `true` | Whether the rule is active. |
| `conditions` | `array` | Yes | — | At least one condition. |
| `channels` | `string[]` | Yes | — | Channel references using `@channel-name` syntax. |

### Condition types

| Type | Fields | Description |
|---|---|---|
| `consecutive_failures` | `count` (default `2`, min `1`) | Trigger after N failures in a row. |
| `response_time_threshold` | `maxMs` (required) | Trigger when response time exceeds threshold. |
| `multi_location_failure` | `minLocations` (default `2`, min `2`), `windowSeconds` (default `300`) | Trigger when failures correlate across multiple locations. |
| `ssl_expiry` | `daysBeforeExpiry` (default `14`, min `1`), `severity` (optional) | Trigger when SSL cert approaches expiration. |
| `ssl_certificate_changed` | `severity` (optional) | Trigger when the leaf certificate fingerprint changes between runs. |
| `ssl_self_signed` | `severity` (optional) | Trigger when a self-signed or untrusted certificate is detected. |
| `ssl_protocol_deprecated` | `minProtocol` (default `TLSv1.2`; allowed `TLSv1.2`, `TLSv1.3`), `severity` (optional) | Trigger when the handshake negotiates a protocol older than `minProtocol`. |
| `burn_rate` | `sloId`, `burnRateThreshold`, `longWindowMinutes` (min `60`), `shortWindowMinutes` (min `5`, must be less than long) | SLO burn-rate alert. Most users let SLOs generate burn-rate alerts automatically via `burnRateAlerts: true`. |
| `baseline_anomaly` | `metric` (required; `response_time`, `dns_lookup`, `tls_handshake`, `ttfb`, `content_transfer`, `lcp`, `fcp`, or `cls`), `sigmaThreshold` (default `3`, 2–10), `consecutiveCount` (default `3`, integer 2–20), `direction` (default `above`; allowed `above`, `below`, `both`), `severity` (default `warning`) | Trigger when the last `consecutiveCount` runs are all successes and each deviates by more than `sigmaThreshold`·σ from its own (hour × day-of-week × location) baseline in the configured direction. Any non-success run inside the window breaks the chain. |

All SSL conditions (including `ssl_expiry`), `mcp_schema_drift`, and `baseline_anomaly` support an optional `severity` field: `critical`, `warning`, or `info`. `mcp_schema_drift` and `baseline_anomaly` default to `warning` from the schema; SSL conditions fall back to `critical` via the evaluator when unset.

See [Set Up Alerts](/docs/guides/set-up-alerts) for worked examples.

---

## Labels

Labels attach metadata to checks. They serve two purposes:

1. **Filtering and grouping** in the dashboard.
2. **OTel resource attributes** — Yorker emits every label as a `yorker.label.*` resource attribute on metrics and traces, so you can slice telemetry by label in your observability backend.

Labels follow this format: `[a-zA-Z0-9][a-zA-Z0-9_.:-]*`, max 128 characters.

| Label form | OTel attribute |
|---|---|
| `env:production` | `yorker.label.env="production"` |
| `service:payments` | `yorker.label.service="payments"` |
| `critical` *(no colon)* | `yorker.label.critical="true"` |

```yaml
monitors:
  - name: "Payments API"
    type: http
    url: "https://api.example.com/payments"
    labels:
      - env:production
      - service:payments
      - critical
```

| Behavior | Meaning |
|---|---|
| `labels` omitted | Labels are **unmanaged** by config — the CLI preserves whatever labels exist on the remote. |
| `labels: []` | Explicitly clears all labels on the check. |
| `labels: [...]` | Sets the full list of labels on the check. |

---

## Secret Interpolation

To inject secrets and environment variables into your config, use placeholder syntax. Placeholders are resolved at deploy time from environment variables.

### `{{secrets.NAME}}`

Reads `YORKER_SECRET_NAME` first, then falls back to `NAME` from the environment.

```yaml
auth:
  type: bearer
  token: "{{secrets.API_TOKEN}}"
# Resolves: $YORKER_SECRET_API_TOKEN ?? $API_TOKEN
```

### `{{env.NAME}}`

Reads the environment variable `NAME` directly.

```yaml
url: "{{env.BASE_URL}}/health"
# Resolves: $BASE_URL
```

### `${NAME}` (Legacy)

Reads the environment variable `NAME` directly. This is the legacy syntax, supported only in YAML config values. It is **not** applied inside browser script files because `${...}` conflicts with JavaScript template literals.

```yaml
url: "${BASE_URL}/health"
# Resolves: $BASE_URL
```

### Script Interpolation

Browser script files (`.ts` files referenced by `script:`) support `{{secrets.NAME}}` and `{{env.NAME}}` interpolation. The legacy `${NAME}` syntax is intentionally excluded from scripts to avoid conflicts with JavaScript template literals.

### Unresolved Placeholders

If a placeholder cannot be resolved, a warning is printed and the raw placeholder text is preserved. After interpolation, the CLI checks for any remaining unresolved placeholders and fails with an error listing each one and its location in the config.

---

## Inheritance Rules

Configuration values cascade through three levels: **defaults**, **group**, and **monitor**. The most specific value wins.

```
monitor > group > defaults
```

| Setting | Behavior |
|---|---|
| `frequency` | Monitor overrides group, group overrides defaults. Falls back to 300s (5m). |
| `locations` | Monitor overrides group, group overrides defaults. Falls back to `["loc_us_east", "loc_eu_central"]`. |
| `alerts` | Monitor overrides group, group overrides defaults. **Explicit presence (even an empty array) is an intentional override.** An empty `alerts: []` on a monitor disables all alerts for that monitor. |
| `assertions` | Monitor-level assertions **replace** defaults entirely. They are **not merged** with `defaults.http.assertions`. This matches Terraform/Checkly semantics. |
| `labels` | Monitor-only (no cascade). Omitting leaves labels unmanaged. `labels: []` clears all labels. |
| HTTP config (`timeoutMs`, `followRedirects`, `maxRedirects`) | Monitor overrides defaults. |
| Browser config (`timeoutMs`, `viewport`, `device`, `screenshotMode`, `videoEnabled`) | Monitor overrides defaults. |

---

## Available Locations

To see all available locations and their IDs, call `GET /api/locations`.

| Location ID | Display Name | Fly Region |
|---|---|---|
| `loc_us_east` | US East (Ashburn) | `iad` |
| `loc_us_south` | US South (Dallas) | `dfw` |
| `loc_us_west` | US West (Los Angeles) | `lax` |
| `loc_na_north` | Canada (Toronto) | `yyz` |
| `loc_sa_east` | South America (São Paulo) | `gru` |
| `loc_eu_west` | Europe West (London) | `lhr` |
| `loc_eu_west_2` | Europe West (Paris) | `cdg` |
| `loc_eu_central` | Europe Central (Frankfurt) | `fra` |
| `loc_eu_north` | Europe North (Stockholm) | `arn` |
| `loc_ap_southeast` | Asia Pacific (Singapore) | `sin` |
| `loc_ap_northeast` | Asia Pacific (Tokyo) | `nrt` |
| `loc_ap_south` | Asia Pacific (Mumbai) | `bom` |
| `loc_ap_oceania` | Oceania (Sydney) | `syd` |
| `loc_af_south` | Africa (Johannesburg) | `jnb` |

---

## Full Example

```yaml
project: "acme-production"

alertChannels:
  ops-slack:
    type: slack
    webhookUrl: "{{secrets.SLACK_WEBHOOK_URL}}"
  oncall-email:
    type: email
    addresses:
      - oncall@acme.com
  pagerduty-oncall:
    type: pagerduty
    routingKey: "{{secrets.PAGERDUTY_ROUTING_KEY}}"

defaults:
  frequency: "5m"
  locations:
    - loc_us_east
    - loc_eu_central
  http:
    timeoutMs: 15000
    followRedirects: true
    assertions:
      - type: status_code
        value: 200
  browser:
    timeoutMs: 60000
    screenshotMode: every_step
  alerts:
    - name: "default-alert"
      conditions:
        - type: consecutive_failures
          count: 3
      channels:
        - "@ops-slack"

groups:
  - name: "Critical APIs"
    frequency: "1m"
    locations:
      - loc_us_east
      - loc_us_west
      - loc_eu_west
    alerts:
      - name: "critical-api-alert"
        conditions:
          - type: consecutive_failures
            count: 2
          - type: multi_location_failure
            minLocations: 2
            windowSeconds: 300
        channels:
          - "@ops-slack"
          - "@pagerduty-oncall"
    monitors:
      - name: "Auth API"
        type: http
        url: "https://api.acme.com/v1/auth/health"
      - name: "Payments API"
        type: http
        url: "https://api.acme.com/v1/payments/health"
        assertions:
          - type: status_code
            value: 200
          - type: response_time
            max: 1000

monitors:
  - name: "Homepage"
    type: http
    url: "https://www.acme.com"

  - name: "Checkout Flow"
    type: browser
    script: "./monitors/checkout.ts"
    frequency: "10m"
    locations:
      - loc_us_east
    alerts:
      - name: "checkout-alert"
        conditions:
          - type: consecutive_failures
            count: 1
        channels:
          - "@ops-slack"
          - "@oncall-email"

slos:
  - name: "Auth API SLO"
    monitor: "Auth API"
    target: "99.95%"
    window: "30d"
    channels:
      - "@ops-slack"

  - name: "Homepage SLO"
    monitor: "Homepage"
    target: "99.9%"
    window: "7d"
    burnRateAlerts: true
    channels:
      - "@ops-slack"
```
