> ## Documentation Index
> Fetch the complete documentation index at: https://docs.use.observer/llms.txt
> Use this file to discover all available pages before exploring further.

# Command-line interface

> Install and use the observer CLI to apply config-as-code from version control.

The `observer` CLI applies and exports your configuration through the
public API. Your configuration lives in your repository, is reviewed in
pull requests, and is applied through a workflow you control.

The CLI is open source at [github.com/useobserver/cli](https://github.com/useobserver/cli).

## Install

### Binary

The install script downloads the binary for your platform and puts it on
PATH:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/useobserver/cli/main/install.sh | sh
```

The script installs into the first of `~/.local/bin`,
`/opt/homebrew/bin`, or `/usr/local/bin` that is on your PATH and
writable, without sudo, and falls back to `~/.local/bin`. To choose
the directory, set `OBSERVER_INSTALL_DIR`:

```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/useobserver/cli/main/install.sh | OBSERVER_INSTALL_DIR="$HOME/.local/bin" sh
```

The variable goes on the `sh` side of the pipe, because the install
script reads it, not `curl`.

Binaries for Linux and macOS (x64 and arm64) and Windows (x64) are published
on the [releases page](https://github.com/useobserver/cli/releases/latest),
each with a SHA-256 checksum.

### GitHub Action

Run the CLI in CI without installing a binary:

```yaml theme={null}
- uses: useobserver/cli@v1
  with:
    file: observer.yaml
    mode: ${{ github.event_name == 'pull_request' && 'dry-run' || 'apply' }}
  env:
    OBSERVER_API_URL: ${{ vars.OBSERVER_API_URL }}
    OBSERVER_API_KEY: ${{ secrets.OBSERVER_API_KEY }}
```

This plans on pull requests (the plan is printed to the job log, and the
check fails on invalid config) and applies on merge to your default branch.
`@v1` tracks the newest 1.x release; pin a full tag such as `@cli-v1.1.0`
to freeze it.

The action takes three inputs:

| Input | Default | Notes |
| - | - | - |
| `file` | `observer.yaml` | Path to the config document. |
| `mode` | `dry-run` | `apply` or `dry-run`. Without `mode`, the action only plans. |
| `prune` | `false` | Set `true` to also delete config-managed objects removed from the document, like `--prune`. |

## Configure

```bash theme={null}
export OBSERVER_API_URL="https://use.observer"
export OBSERVER_API_KEY="obs_pub_..."   # write:config to apply, read:config to export
```

Create the key in the console under **Developers** > **API keys**. See [Authentication](https://docs.use.observer/api/getting-started/auth.md)
for the scope list.

## Commands

```bash theme={null}
# Plan only: show the diff, change nothing (run on pull requests)
observer apply -f observer.yaml --dry-run

# Apply: upsert by key (never deletes objects absent from the document)
observer apply -f observer.yaml

# Also delete config-managed objects removed from the document
observer apply -f observer.yaml --prune

# Export current state to seed your repository
observer export --format yaml -o observer.yaml
```

Exit codes: `0` success, `1` validation failure or error, `2` usage error.

Top-level keys that config does not manage, such as `incidents` or
`customers`, are ignored. From CLI 1.1.0, `apply` prints them under a
`Warnings:` heading after the plan or apply summary:

```text theme={null}
Plan: 1 created, 0 updated, 1 unchanged
  + metric checkout-latency
Warnings:
  ! incidents: incidents are not managed by config; manage incidents via the API (/api/v1/incidents) or the console
```

Warnings never fail the run.

The plan lists only what changes. The underlying
[`POST /config/apply`](https://docs.use.observer/api/config/post-config-apply) returns a diff
entry for every object the document names; pass `verbose=false` to
leave out the `unchanged` entries (the `summary` still counts them).
The [MCP server](https://docs.use.observer/docs/mcp/index.md) sends `verbose=false` by default.

## Metric fields

```yaml theme={null}
metrics:
  - key: checkout-latency
    title: Checkout latency
    agent: prod-eu
    source_type: http
    source_config:
      url: https://shop.example.com/health
    thresholds:
      healthy_operation: under
      healthy_value: 300
      unhealthy_operation: over
      unhealthy_value: 1000
    interval: 1                 # minutes
    interval_agent_push: 15     # minutes
```

| Field | Unit | Range |
| - | - | - |
| `interval` | whole minutes | 1 to 60. How often the agent samples the source. Default 1. |
| `interval_agent_push` | whole minutes | 5 to 60. How often the agent sends samples; a status change is sent at once. The plan sets a floor: 10 on Free and Starter, 5 on Pro, Business and Enterprise. Default 15. |
| `dwell_seconds_to_breach`, `dwell_seconds_to_recover` | seconds | 0 to 3600. The confirmation time: how long a new status must hold before the metric flips. Defaults 180 and 300; 0 for heartbeat and inbound alert metrics. See [Thresholds and confirmation time](https://docs.use.observer/docs/concepts/thresholds-and-dwell.md). |

Intervals are minutes, not seconds. A value such as `300` fails
validation, and the error suggests the value in minutes (`5`).

`thresholds` is required, with all four fields, when a document
creates a metric. Manual, heartbeat and inbound alert metrics are the
exception: their status does not come from thresholds you write. On an
existing metric, leaving `thresholds` out keeps the current
thresholds; include it to change them.

The unit of the value a metric reports follows from its `source_type`
(milliseconds for HTTP, TCP and DNS, days for TLS certificates, and so
on; see [Probe types](https://docs.use.observer/agent/reference/probe-types.md)). Set thresholds in
that unit. To see what a source returns before you write thresholds,
[test it once](https://docs.use.observer/agent/guides/test-a-check.md#test-through-the-api).

## Agents

Metrics other than heartbeat, manual and inbound alert metrics run on
an [Observer Agent](https://docs.use.observer/agent/overview.md). Name the agent in the metric's
`agent` field:

```yaml theme={null}
metrics:
  - key: checkout-latency
    title: Checkout latency
    agent: prod-eu
    source_type: http
    source_config:
      url: https://shop.example.com/health
```

`agent` is the agent's name as shown on the **Agents** page or by
[`GET /agents`](https://docs.use.observer/api/agents/get-agents). Apply checks it against the
agents in your organization:

* A name that matches no agent fails validation, and so does a name
  that two agents share. Nothing is applied, and the error lists the
  agent names that exist. Create the agent first (in the console or
  with [`POST /agents`](https://docs.use.observer/api/agents/post-agents)), or fix the name.
* A metric whose source runs on an agent but names none is accepted
  with a warning. It gets no data until an agent is assigned.
* A heartbeat or inbound alert metric must not name an agent.

The plan lists the agent each metric resolves to.

## Reference existing metrics

An SLO's `metric`, and the `metrics` of a page or a service, can name
a metric the document defines or a metric that already exists in your
organization. An existing metric is named by its config key or, for a
metric made in the console, by the key `observer export --all` gives
it: its title in lowercase with dashes, then the first six characters
of its id (for example `checkout-latency-3f9a1c`).

```yaml theme={null}
services:
  - key: checkout
    name: Checkout
    metrics:
      - checkout-latency          # defined in this document
      - payments-api-3f9a1c       # made in the console, referenced only
```

Referenced metrics are listed under `referenced` in the result (and
counted in the summary). Apply never creates, changes, or prunes them,
so a document can build pages, services and SLOs on metrics it does not
own. A reference that matches no metric fails validation.

## Services

A service's `metrics` sets its member metrics. When present it is the
exact set: metrics left out are removed from the service, and `[]`
removes them all. When omitted, the members stay as they are. A
service's metrics are its members plus the metrics its SLOs measure.

```yaml theme={null}
services:
  - key: checkout
    name: Checkout
    metrics:
      - checkout-latency
      - metric: checkout-errors
    slos:
      - key: checkout-availability
        metric: checkout-errors
        target_pct: 99.9
        window_days: 30
```

The plan lists the metric keys added to and removed from each service.
[`GET /services`](https://docs.use.observer/api/services/get-services) returns each service's
`metrics`, with `via: member` or `via: slo`.

## Status pages

A page's `metrics` list controls which metrics the page shows, their
group, and their order. Settings edited per metric in the console,
such as the public label and whether the uptime bar is shown, are kept
when you apply. The plan lists, for each page, the metrics added,
removed, and regrouped or reordered.

A page entry can also set the page's settings. Settings you leave out
keep their current value; they are never reset.

```yaml theme={null}
pages:
  - key: status
    subdomain: acme-status
    title: Acme status
    layout: blueprint
    timezone: Europe/London
    locale: en
    allow_subscriptions: true
    metric_group_summary: true
    theme:
      preset: midnight
    metrics:
      - metric: checkout-latency
        group: Checkout
```

| Setting | Notes |
| - | - |
| `title` | 2 to 120 characters. |
| `access_mode` | Only `public` can be set from a document; it removes any password, allow-list or sign-in setting. Restating the page's current mode changes nothing, so an exported document applies cleanly. Other modes are set in the console. |
| `layout` | `minimal`, `aurora` or `blueprint`. |
| `timezone` | IANA time zone name, for example `Europe/London`. |
| `locale` | Page language. See [Page language](https://docs.use.observer/docs/guides/page-language.md). |
| `allow_subscriptions`, `show_auto_refresh` | `true` or `false`. |
| `hide_branding` | Turning it on needs a paid plan. |
| `footer_links` | Up to 5 `{ label, href }` entries. Replaces the whole list. |
| `metric_group_order` | Group names in display order (`null` for ungrouped metrics). `null` restores the default order. |
| `metric_group_summary` | Show each metric group as one summary row. |
| `seo_description`, `seo_noindex` | See [Search engine settings](https://docs.use.observer/docs/guides/search-engine-settings.md). |
| `theme.preset` | Applies a theme preset from the console's theme gallery and replaces the page's theme. |

The plan names the settings that change on an existing page. To change
one setting without a document, call
[`PATCH /pages/{id}`](https://docs.use.observer/api/pages/patch-pages-by-id). Custom CSS, custom
domains, and access modes that carry credentials stay in the console.

To find the address of a page after applying, read `public_url` from
[`GET /pages`](https://docs.use.observer/api/pages/get-pages). It is the custom domain when one
is active, and the page's subdomain address otherwise.

## Delete one object

`--prune` deletes every config-managed object that the document no
longer names. To delete a single metric, service, SLO, or status page
instead, call its `DELETE` endpoint with the object's id or its config
key:

```bash theme={null}
curl -X DELETE "https://use.observer/api/v1/metrics/checkout-latency" \
  -H "Authorization: Bearer $OBSERVER_API_KEY"
```

The key needs `write:config`. The same rules as prune apply:

* Only config-managed objects can be deleted this way. An object made
  in the console returns `409 not_config_managed`.
* If other objects depend on it (SLOs on a metric or service, pages
  showing a metric, active subscribers of a page), the request returns
  `409 in_use` with the dependents in `referenced_by`. Repeat with
  `?cascade=true` to delete or detach them too.
* A page with a custom domain returns `409 custom_domain_attached`.
  Remove the domain in the console first.

Remove the object from your document as well, or the next apply
creates it again.

## Heartbeat metrics

A [heartbeat check](https://docs.use.observer/docs/guides/heartbeat-monitoring.md) is declared like
any other metric, with `source_type: heartbeat`:

```yaml theme={null}
metrics:
  - key: nightly-backup
    title: Nightly backup
    source_type: heartbeat
    source_config:
      period_seconds: 86400       # expected interval between successful runs
      grace_seconds: 1800         # slack after the period before the check is late
      max_runtime_seconds: 3600   # optional: a started run must finish within this
```

* Do not set `agent`. Observer evaluates heartbeats itself, and a
  heartbeat metric that names an agent fails validation.
* `thresholds` are ignored. They are derived from `period_seconds`
  and `grace_seconds`, and `observer export` omits them.
* The ping URL is never part of the document or the export. Copy it
  from the console, or read it from the
  [heartbeat API](https://docs.use.observer/api/metrics/get-metrics-by-id-heartbeat.md) with the
  same `write:config` key you apply with.
* An existing metric cannot switch into or out of `heartbeat`. Delete
  it and create it again under a new key.

> [!NOTE]
>
> **What apply changes**
>
> Apply upserts the objects named in your document and marks them
> config-managed. Objects you do not name are left alone. Pass `--prune`
> only when you want objects removed from the document deleted from Observer.
>
> Prune only deletes config-managed objects. Objects created in the
> console have no config key, so neither apply nor prune touches them
> unless your document names them (for example after
> `observer export --all`), in which case apply adopts them as
> config-managed.

`observer export` writes JSON by default; pass `--format yaml` for
YAML. It exports config-managed objects only; add `--all` to include
objects created in the console.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.