openobserve-cli 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/PKG-INFO +19 -1
  2. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/README.md +18 -0
  3. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/pyproject.toml +9 -1
  4. openobserve_cli-0.2.0/skills/openobserve/SKILL.md +145 -0
  5. openobserve_cli-0.2.0/skills/openobserve/references/admin.md +126 -0
  6. openobserve_cli-0.2.0/skills/openobserve/references/alerts.md +184 -0
  7. openobserve_cli-0.2.0/skills/openobserve/references/cli.md +148 -0
  8. openobserve_cli-0.2.0/skills/openobserve/references/dashboards.md +155 -0
  9. openobserve_cli-0.2.0/skills/openobserve/references/endpoints.md +693 -0
  10. openobserve_cli-0.2.0/skills/openobserve/references/metrics.md +72 -0
  11. openobserve_cli-0.2.0/skills/openobserve/references/pipelines.md +119 -0
  12. openobserve_cli-0.2.0/skills/openobserve/references/search.md +162 -0
  13. openobserve_cli-0.2.0/skills/openobserve/references/streams.md +137 -0
  14. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/__init__.py +1 -1
  15. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/cli.py +36 -0
  16. openobserve_cli-0.2.0/src/oo_cli/skill.py +54 -0
  17. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/spec.py +16 -8
  18. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/tests/test_cli.py +11 -1
  19. openobserve_cli-0.2.0/tests/test_skill.py +41 -0
  20. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/tests/test_spec.py +8 -0
  21. openobserve_cli-0.1.0/.github/workflows/publish.yaml +0 -36
  22. openobserve_cli-0.1.0/.github/workflows/test.yaml +0 -45
  23. openobserve_cli-0.1.0/.pre-commit-config.yaml +0 -39
  24. openobserve_cli-0.1.0/uv.lock +0 -453
  25. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/.gitignore +0 -0
  26. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/LICENSE +0 -0
  27. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/__main__.py +0 -0
  28. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/client.py +0 -0
  29. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/config.py +0 -0
  30. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/py.typed +0 -0
  31. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/src/oo_cli/timeutil.py +0 -0
  32. {openobserve_cli-0.1.0 → openobserve_cli-0.2.0}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openobserve-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: CLI for the OpenObserve HTTP API
5
5
  Project-URL: Homepage, https://github.com/paveldedik/oo-cli
6
6
  Project-URL: Repository, https://github.com/paveldedik/oo-cli
@@ -111,6 +111,24 @@ oo spec refresh # refetch after an OpenObserve upgrade
111
111
  Without a reachable spec the CLI falls back to v1, except for `alerts`, `folders` and
112
112
  `reports`, the three resources that have a v2 in OpenObserve 0.91.
113
113
 
114
+ ## Skill for agents
115
+
116
+ The CLI carries a skill for coding agents: how the commands map onto the API, what the
117
+ bodies look like, and a reference for every endpoint, written from an instance's own
118
+ OpenAPI document. It installs itself.
119
+
120
+ ```bash
121
+ oo skill install # -> ~/.claude/skills/openobserve
122
+ oo skill update # after upgrading the CLI
123
+ ```
124
+
125
+ `--dir` puts it somewhere else (`oo skill install --dir .claude/skills` for one project),
126
+ `--force` overwrites an existing copy, and `oo skill path` prints the bundled original.
127
+ Without installing anything: `uvx openobserve-cli skill install`.
128
+
129
+ The files are also plain Markdown in `skills/openobserve`, so any agent that reads
130
+ Markdown can be pointed straight at `SKILL.md`.
131
+
114
132
  ## Development
115
133
 
116
134
  ```bash
@@ -88,6 +88,24 @@ oo spec refresh # refetch after an OpenObserve upgrade
88
88
  Without a reachable spec the CLI falls back to v1, except for `alerts`, `folders` and
89
89
  `reports`, the three resources that have a v2 in OpenObserve 0.91.
90
90
 
91
+ ## Skill for agents
92
+
93
+ The CLI carries a skill for coding agents: how the commands map onto the API, what the
94
+ bodies look like, and a reference for every endpoint, written from an instance's own
95
+ OpenAPI document. It installs itself.
96
+
97
+ ```bash
98
+ oo skill install # -> ~/.claude/skills/openobserve
99
+ oo skill update # after upgrading the CLI
100
+ ```
101
+
102
+ `--dir` puts it somewhere else (`oo skill install --dir .claude/skills` for one project),
103
+ `--force` overwrites an existing copy, and `oo skill path` prints the bundled original.
104
+ Without installing anything: `uvx openobserve-cli skill install`.
105
+
106
+ The files are also plain Markdown in `skills/openobserve`, so any agent that reads
107
+ Markdown can be pointed straight at `SKILL.md`.
108
+
91
109
  ## Development
92
110
 
93
111
  ```bash
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  # "oo", "oo-cli" and "openobserve" are all taken on PyPI.
3
3
  name = "openobserve-cli"
4
- version = "0.1.0"
4
+ version = "0.2.0"
5
5
  description = "CLI for the OpenObserve HTTP API"
6
6
  readme = "README.md"
7
7
  keywords = ["openobserve", "observability", "logs", "metrics", "traces", "cli"]
@@ -42,6 +42,14 @@ dev = ["pytest>=8", "mypy>=1.18", "ruff>=0.14"]
42
42
  [tool.hatch.build.targets.wheel]
43
43
  packages = ["src/oo_cli"]
44
44
 
45
+ # The agent skill lives at the top of the repository, where agents browsing GitHub find
46
+ # it, and rides along in the package so `oo skill install` needs no checkout.
47
+ [tool.hatch.build.targets.wheel.force-include]
48
+ "skills/openobserve" = "oo_cli/_skill"
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = ["src", "skills", "tests", "README.md", "LICENSE", "pyproject.toml"]
52
+
45
53
  [tool.ruff]
46
54
  line-length = 100
47
55
  target-version = "py311"
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: openobserve
3
+ description: Query and manage OpenObserve from a terminal with the `oo` CLI - search logs, traces and metrics with SQL or PromQL, and read or change streams, dashboards, alerts, pipelines, functions and reports. Use for any task that mentions OpenObserve, an `oo` command, or an OpenObserve API path.
4
+ license: MIT
5
+ ---
6
+
7
+ # OpenObserve from the command line
8
+
9
+ `oo` is a transcription of the OpenObserve HTTP API, not a model of it. One rule covers
10
+ almost everything:
11
+
12
+ ```
13
+ oo <method> <resource> -> <METHOD> /api/{org}/<resource>
14
+ ```
15
+
16
+ So `oo get dashboards` is `GET /api/default/dashboards`, and `oo delete alerts/7` is
17
+ `DELETE /api/v2/default/alerts/7`. Where an endpoint exists in two versions the CLI picks
18
+ v2; there is no version flag. Anything the rule does not reach is still one command away
19
+ with `oo api <METHOD> <path>`.
20
+
21
+ Install with `uv tool install openobserve-cli`, or run it without installing:
22
+ `uvx openobserve-cli get streams`. This skill came from that package - `oo skill update`
23
+ refreshes it after the CLI is upgraded.
24
+
25
+ ## Start here
26
+
27
+ Check what you are pointed at before anything else - it is the difference between an empty
28
+ result and an empty instance:
29
+
30
+ ```bash
31
+ oo api GET /healthz # {"status":"ok"} - confirms it is an OpenObserve
32
+ oo get organizations # confirms the credentials and lists the orgs you can use
33
+ oo get streams # what data this org actually holds
34
+ ```
35
+
36
+ Configuration is environment only:
37
+
38
+ | Variable | Required | Default |
39
+ |---------------------------|-----------------------------------|-------------------------|
40
+ | `OO_ENDPOINT` | optional | `http://localhost:5080` |
41
+ | `OO_ORG` | optional | `default` |
42
+ | `OO_TOKEN` | to reach anything but `/healthz` | - |
43
+ | `OO_USER` / `OO_PASSWORD` | instead of `OO_TOKEN` | - |
44
+ | `OO_COOKIE` | when a gateway guards the host | - |
45
+ | `OO_TIMEOUT` | optional | `60` (seconds) |
46
+
47
+ `--endpoint`, `--org` and `--timeout` override the variables per command. Never print a
48
+ token or cookie value back to the user; keep it in the environment.
49
+
50
+ ## The grammar
51
+
52
+ ```bash
53
+ oo get alerts --folder default # unknown --flags become query parameters
54
+ oo get streams/app_logs/schema # slashes are path segments
55
+ oo post alerts -f alert.json # -f file, -d '<literal>', -d @file, -d @-
56
+ oo put alerts/7 -d @- < alert.json # body on stdin
57
+ oo --org other get streams # global flags go before the verb
58
+ oo search --sql "select * from k8s_logs limit 10" --from -15m
59
+ oo api GET /api/default/prometheus/api/v1/query --query "up"
60
+ oo spec paths alerts # what this instance actually serves
61
+ ```
62
+
63
+ - Output is pretty-printed JSON on stdout; errors on stderr. Pipe into `jq`.
64
+ - Exit code 1 on HTTP >= 400 (the response body is printed), 2 on a bad command.
65
+ - `--raw` prints the body unformatted, for CSV or a large export.
66
+ - Values starting with `-` need `--flag=value`; `--from` and `--to` are handled for you.
67
+
68
+ ## Time
69
+
70
+ Every timestamp the API takes or returns is **epoch microseconds**. `oo search --from/--to`
71
+ and nothing else accepts friendly forms: `now`, an offset (`-15m`, `-2h`, `-7d`), an epoch
72
+ in seconds/milliseconds/microseconds, or ISO 8601 (local time when it carries no zone).
73
+ Elsewhere - `start_time` in a body, `_values`, alert history - pass microseconds:
74
+
75
+ ```bash
76
+ python3 -c 'import time; print(int(time.time()*1_000_000))'
77
+ date -v-1H +%s000000 # macOS (GNU: date -d '1 hour ago' +%s000000)
78
+ ```
79
+
80
+ ## Searching
81
+
82
+ `oo search` is the one command that is not a straight transcription: it wraps
83
+ `POST /api/{org}/_search` so the body does not have to be written by hand.
84
+
85
+ ```bash
86
+ oo search --sql "select * from k8s_logs where level = 'error'" --from -1h --size 50
87
+ oo search --sql "select k8s_namespace_name, count(*) as n from k8s_logs
88
+ group by k8s_namespace_name order by n desc" --from -6h
89
+ oo search --type metrics --sql "select * from up" --from -5m
90
+ ```
91
+
92
+ `--from` defaults to `-1h`, `--to` to `now`, `--size` to 100, `--type` to `logs`. The SQL is
93
+ DataFusion SQL: the stream is the table, `_timestamp` is microseconds, and full text lives in
94
+ `match_all('needle')`. Read `references/search.md` before writing anything more involved -
95
+ histograms, `_values`, `_around`, async search jobs and the raw `_search` body are all there.
96
+
97
+ Two habits that keep a search cheap on a busy instance: always bound the time range, and
98
+ aggregate in SQL rather than pulling rows and counting them locally. A wide range over a raw
99
+ stream can cost the instance far more memory than it costs you to type.
100
+
101
+ ## Common tasks
102
+
103
+ | Question | Command |
104
+ |----------|---------|
105
+ | what is in this instance | `oo get streams`, then `oo get streams/<name>/schema` |
106
+ | errors in the last hour | `oo search --sql "select * from <stream> where level = 'error'" --from -1h` |
107
+ | how many, by service | `oo search --sql "select service, count(*) as n from <stream> group by service order by n desc" --from -6h` |
108
+ | which values does this field take | `oo get <stream>/_values --fields <field> --size 20 --start_time <us> --end_time <us>` |
109
+ | current value of a metric | `oo api GET /api/{org}/prometheus/api/v1/query --query '<promql>'` |
110
+ | which alerts exist, and are they on | `oo get alerts \| jq -r '.list[] \| "\(.enabled) \(.name)"'` |
111
+ | why an alert did not fire | `oo get alerts/history --alert_id <id> --start_time <us> --end_time <us>` |
112
+ | back up a dashboard | `oo get dashboards/<id> --folder <folder> > dashboard.json` |
113
+ | change one field of an object | read it, edit the JSON, `oo put <resource>/<id> -f patched.json` |
114
+ | does this instance have X | `oo spec paths <needle>` |
115
+
116
+ ## Rules worth keeping
117
+
118
+ 1. **Read before you write.** `PUT` replaces the whole object. Fetch it, edit that JSON,
119
+ send it back - never hand-write a replacement from the docs.
120
+ 2. **Dashboards need their `hash`.** `PUT dashboards/<id>` takes `?folder=<id>&hash=<hash>`
121
+ from the object you just read; without it a concurrent edit is silently overwritten.
122
+ 3. **Folders are ids, not names.** `--folder default` is the id of the default folder;
123
+ any other folder is a ksuid you get from `oo get folders/<dashboards|alerts|reports>`.
124
+ 4. **Deletes are permanent** and `oo delete streams/<name> --delete_all=true` takes the
125
+ alerts and dashboards with it. Confirm with the user before any delete.
126
+ 5. **Ask the spec, not your memory.** `oo spec paths <needle>` lists what the connected
127
+ instance serves; `oo spec refresh` refetches it after an upgrade. Endpoint availability
128
+ differs between OSS and enterprise builds - several alert and cipher endpoints answer
129
+ "not supported" on OSS.
130
+ 6. When a command says a path *is not in the spec*, the resource name is probably wrong -
131
+ check with `oo spec paths` instead of retrying variations.
132
+
133
+ ## References
134
+
135
+ | File | What is in it |
136
+ |------|---------------|
137
+ | `references/cli.md` | Full command grammar, v1/v2 resolution, the spec cache, gateways, error messages |
138
+ | `references/search.md` | `_search` body, SQL dialect, histograms, `_values`, `_around`, search jobs, patterns |
139
+ | `references/metrics.md` | PromQL instant/range queries, labels, series, metadata, metric ingestion |
140
+ | `references/alerts.md` | Alert model (v2), trigger and query conditions, destinations, templates, history, incidents |
141
+ | `references/dashboards.md` | Dashboards, panels, annotations, folders, scheduled reports |
142
+ | `references/streams.md` | Streams, schemas, settings, retention, ingestion endpoints, enrichment tables |
143
+ | `references/pipelines.md` | Pipelines (nodes and edges), VRL functions, actions |
144
+ | `references/admin.md` | Organizations, settings, users, roles, groups, service accounts, tokens, KV |
145
+ | `references/endpoints.md` | Every endpoint the API exposes, as the `oo` command that reaches it |
@@ -0,0 +1,126 @@
1
+ # Organizations, users and the rest
2
+
3
+ ## Organizations
4
+
5
+ ```bash
6
+ oo get organizations # /api/organizations - no org in the path
7
+ oo post organizations -d '{"name":"team-b"}'
8
+ oo put rename -d '{"new_name":"Platform"}'
9
+ oo get summary # ingestion, storage, stream and function counts
10
+ ```
11
+
12
+ `oo get organizations` is the first call to make against an unfamiliar instance: it proves
13
+ the credential works and lists the org identifiers `--org` accepts. A root user sees every
14
+ organization. `oo get clusters` lists clusters in a distributed deployment.
15
+
16
+ ## Ingestion tokens
17
+
18
+ ```bash
19
+ oo get passcode # the current ingestion token for this user and org
20
+ oo put passcode # rotate it - every ingester using the old one breaks
21
+ oo get rumtoken
22
+ oo post rumtoken # create the first RUM token
23
+ oo put rumtoken # rotate it
24
+ ```
25
+
26
+ These return credentials. Never print one into a chat, a commit or a log; redirect to a file
27
+ or into an environment variable.
28
+
29
+ ## Settings
30
+
31
+ ```bash
32
+ oo get settings
33
+ oo post settings -d '{"scrape_interval": 15}'
34
+ ```
35
+
36
+ Org settings hold `scrape_interval`, `trace_id_field_name` / `span_id_field_name`,
37
+ `max_series_per_query`, `min_auto_refresh_interval`, `toggle_ingestion_logs`,
38
+ `usage_stream_enabled`, `enable_streaming_search`, `streaming_aggregation_enabled`, the two
39
+ theme colours and `cross_links`.
40
+
41
+ A newer key-value system sits alongside it, resolving system -> org -> user:
42
+
43
+ ```bash
44
+ oo get settings/v2 # everything, resolved
45
+ oo get settings/v2 --category ui
46
+ oo get settings/v2/<key> --user_id <id> # the value this user actually sees
47
+ oo post settings/v2 -d '{"setting_key":"...","setting_value":...,"setting_category":"ui"}'
48
+ oo post settings/v2/user/<user_id> -d '{"setting_key":"...","setting_value":...}'
49
+ oo delete settings/v2/<key> # falls back to the system default
50
+ ```
51
+
52
+ ## Users, roles, groups, service accounts
53
+
54
+ ```bash
55
+ oo get users
56
+ oo post users -d '{"email":"a@example.com","password":"...","role":"admin","first_name":"A","last_name":"B"}'
57
+ oo put users/<email> -d '{"first_name":"A"}'
58
+ oo post users/<email> -d '{"role":"member"}' # add an existing user to this org
59
+ oo delete users/<email> # removes from the org
60
+
61
+ oo get roles # enterprise RBAC
62
+ oo get roles/<role_id>/permissions/<resource>
63
+ oo get roles/<role_id>/users
64
+ oo post roles -d '{"role":"readonly"}'
65
+ oo put roles/<role_id> -f permissions.json
66
+
67
+ oo get groups
68
+ oo post groups -d '{"name":"platform"}'
69
+ oo put groups/<group_name> -d '{"add_users":["a@example.com"],"add_roles":["readonly"]}'
70
+ ```
71
+
72
+ A service account is a user without a password, used for automation; its token comes from
73
+ `passcode` once the account exists.
74
+
75
+ ```bash
76
+ oo get service_accounts
77
+ oo post service_accounts -d '{"email":"ci@example.com","first_name":"CI","last_name":"bot"}'
78
+ oo delete service_accounts/<email>
79
+ ```
80
+
81
+ Roles and groups are enterprise features and answer with an error on OSS builds. Creating or
82
+ deleting users changes who can reach the data - always confirm first.
83
+
84
+ ## Key-value store
85
+
86
+ A small per-org store the UI uses and you can too:
87
+
88
+ ```bash
89
+ oo get kv # every key
90
+ oo get kv --prefix dashboard_
91
+ oo get kv/<key> # the raw value, not JSON
92
+ oo post kv/<key> -d 'some text'
93
+ oo delete kv/<key>
94
+ ```
95
+
96
+ ## Short URLs
97
+
98
+ ```bash
99
+ oo post short -d '{"original_url":"https://openobserve.example.com/web/logs?..."}'
100
+ oo api GET /short/default/short/<short_id> --type ui # resolve without redirecting
101
+ ```
102
+
103
+ ## Cipher keys and rate limits
104
+
105
+ `cipher_keys` (encryption key management) and `ratelimit` (per-module and per-role request
106
+ limits) are enterprise-only:
107
+
108
+ ```bash
109
+ oo get cipher_keys
110
+ oo get ratelimit/module_list --org_id default
111
+ oo get ratelimit/role_list --org_id default --user_role admin
112
+ ```
113
+
114
+ ## Health and the spec
115
+
116
+ ```bash
117
+ oo api GET /healthz # {"status":"ok"}, the one endpoint needing no credential
118
+ oo spec paths # every path this build serves
119
+ oo spec refresh # after an upgrade
120
+ ```
121
+
122
+ ## MCP
123
+
124
+ Recent builds expose `POST /api/{org}/mcp`, an MCP server over the same API. The endpoint
125
+ carries per-operation hints (which operations are exposed, which need confirmation) that this
126
+ skill's guidance is drawn from. Check `oo spec paths mcp` before assuming it is there.
@@ -0,0 +1,184 @@
1
+ # Alerts, destinations and templates
2
+
3
+ Three objects, in the order you need them:
4
+
5
+ 1. a **template** formats the message,
6
+ 2. a **destination** says where it goes and which template it uses,
7
+ 3. an **alert** says what to watch and names its destinations.
8
+
9
+ Alerts live in folders and use the v2 API, so `oo get alerts` is
10
+ `GET /api/v2/{org}/alerts`. Destinations and templates are v1.
11
+
12
+ ## Listing
13
+
14
+ ```bash
15
+ oo get alerts # every alert in the org
16
+ oo get alerts --folder default --enabled true
17
+ oo get alerts --alert_name_substring celery --alert_type scheduled
18
+ oo get alerts --stream_type logs --stream_name k8s_logs
19
+ oo get alerts --page_size 50 --page_idx 1
20
+ ```
21
+
22
+ Filters: `folder` (a folder **id**), `stream_type`, `stream_name` (only with
23
+ `stream_type`), `alert_name_substring` (case-insensitive), `owner`, `enabled`,
24
+ `alert_type` (`all`, `scheduled`, `realtime`, `anomaly_detection`), `page_size`, `page_idx`.
25
+
26
+ Each item carries `alert_id`, `name`, `enabled`, `folder_id`, `folder_name`,
27
+ `is_real_time`, `last_triggered_at` and `last_satisfied_at`.
28
+
29
+ ```bash
30
+ oo get alerts | jq -r '.list[] | select(.enabled) | "\(.folder_name)/\(.name)"'
31
+ ```
32
+
33
+ ## One alert
34
+
35
+ ```bash
36
+ oo get alerts/<alert_id> # add --folder <id> for a non-default folder
37
+ oo post alerts/<alert_id>/export # the same thing, shaped for re-import
38
+ oo patch alerts/<alert_id>/enable --value=false
39
+ oo patch alerts/<alert_id>/trigger # fire it now, to test the destination
40
+ oo post alerts/<alert_id>/clone -d '{"name": "copy_of_x", "folder_id": "<id>"}'
41
+ oo patch alerts/move -d '{"alert_ids": ["<id>"], "dst_folder_id": "<id>"}'
42
+ oo delete alerts/<alert_id> # permanent
43
+ ```
44
+
45
+ ## The alert object
46
+
47
+ Names must be snake_case: no spaces and none of `: # ? & % /` or quotes. `destinations` is
48
+ required and every name in it must already exist.
49
+
50
+ ```json
51
+ {
52
+ "name": "api_error_rate",
53
+ "stream_type": "logs",
54
+ "stream_name": "k8s_logs",
55
+ "is_real_time": false,
56
+ "enabled": true,
57
+ "description": "5xx from the API gateway",
58
+ "destinations": ["slack_alerts"],
59
+ "query_condition": {
60
+ "type": "sql",
61
+ "sql": "select count(*) as n from k8s_logs where status >= 500"
62
+ },
63
+ "trigger_condition": {
64
+ "period": 10, "frequency": 600, "operator": ">=", "threshold": 5, "silence": 30
65
+ },
66
+ "context_attributes": {"team": "platform"},
67
+ "row_template": "{alert_name} fired: {n}"
68
+ }
69
+ ```
70
+
71
+ ### `trigger_condition` - when it runs and what counts as firing
72
+
73
+ | Field | Unit | Meaning |
74
+ |-------|------|---------|
75
+ | `period` | minutes | how far back each evaluation looks |
76
+ | `frequency` | seconds | how often it evaluates (`frequency_type: "minutes"`) |
77
+ | `frequency_type` | - | `minutes` or `cron` |
78
+ | `cron` | - | schedule when `frequency_type` is `cron` |
79
+ | `timezone` | - | timezone for the cron expression |
80
+ | `operator` | - | `=`, `!=`, `>`, `>=`, `<`, `<=`, `contains`, `not_contains` |
81
+ | `threshold` | - | compared against the row count or the aggregate |
82
+ | `silence` | minutes | mute after firing, so one incident is one notification |
83
+ | `align_time` | - | snap evaluations to the clock |
84
+ | `tolerance_in_secs` | seconds | slack for late-arriving data |
85
+
86
+ `period` shorter than the data's ingestion delay is the usual reason an alert never fires.
87
+
88
+ ### `query_condition` - what it asks
89
+
90
+ `type` is one of three, and the other fields follow from it:
91
+
92
+ - **`custom`** - `conditions` (a condition group), optional `aggregation`, `vrl_function`,
93
+ `multi_time_range`. The UI builds this one.
94
+ - **`sql`** - `sql` plus optional `vrl_function`. The query must return rows only when
95
+ something is wrong, or return a number that `trigger_condition` compares.
96
+ - **`promql`** - `promql` plus `promql_condition` (`{"column", "operator", "value"}`), for
97
+ alerts on metrics.
98
+
99
+ `aggregation` narrows a custom alert: `function` is one of `avg`, `min`, `max`, `sum`,
100
+ `count`, `median`, `p50`, `p75`, `p90`, `p95`, `p99`, with `group_by` and a `having` clause
101
+ shaped `{"column", "operator", "value", "ignore_case"}`.
102
+
103
+ `POST /api/v2/{org}/alerts/generate_sql` turns a custom condition into the SQL it would run -
104
+ useful for checking an alert before saving it.
105
+
106
+ The `conditions` group is the same nested format pipelines use (see `pipelines.md`):
107
+ `{"filterType": "group", "logicalOperator": "AND", "conditions": [...]}` with leaves
108
+ `{"filterType": "condition", "column", "operator", "value", "logicalOperator"}`. When in
109
+ doubt, build one alert in the UI, `oo get alerts/<id>`, and copy the shape.
110
+
111
+ ### Editing an existing alert
112
+
113
+ ```bash
114
+ oo get alerts/<id> > alert.json
115
+ jq '.trigger_condition.threshold = 10' alert.json > patched.json
116
+ oo put alerts/<id> -f patched.json
117
+ ```
118
+
119
+ `PUT` replaces the object, so always start from the current one.
120
+
121
+ ## Destinations
122
+
123
+ ```bash
124
+ oo get alerts/destinations
125
+ oo get alerts/destinations --module pipeline # pipeline destinations instead
126
+ oo get alerts/destinations/<name>
127
+ oo post alerts/destinations -d '{"name":"slack_alerts","type":"http","url":"https://hooks.example.com/x","method":"post","template":"Default"}'
128
+ oo post alerts/destinations -d '{"name":"oncall_mail","type":"email","emails":["oncall@example.com"],"template":"Default"}'
129
+ oo delete alerts/destinations/<name>
130
+ ```
131
+
132
+ `template` is **required** for an alert destination - without it the destination is created
133
+ as a pipeline destination and no alert can use it. `type` is `http`, `email` or `sns`
134
+ (`sns_topic_arn` + `aws_region`). `headers` adds HTTP headers, `skip_tls_verify` disables
135
+ certificate checks. A destination in use cannot be deleted.
136
+
137
+ ## Templates
138
+
139
+ ```bash
140
+ oo get alerts/templates
141
+ oo get alerts/templates/system/prebuilt # read-only, Slack/Teams/PagerDuty/...
142
+ oo get alerts/templates/<name>
143
+ oo post alerts/templates -d '{"name":"short","type":"http","body":"{\"text\":\"{alert_name} fired\"}"}'
144
+ ```
145
+
146
+ `body` is the message with `{variable}` placeholders; `title` is used for email. Variables
147
+ come from the alert (`alert_name`, `stream_name`, `org_name`, `alert_start_time`, ...),
148
+ its `context_attributes`, and the matching rows via `row_template`. Templates named
149
+ `system_*` are prebuilt and read-only.
150
+
151
+ ## History and incidents
152
+
153
+ ```bash
154
+ oo get alerts/history --alert_id <id> --start_time <us> --end_time <us> --size 100
155
+ oo get alerts/history --sort_by timestamp --sort_order desc
156
+ ```
157
+
158
+ History comes from the org's own triggers stream: when an alert ran, whether it was
159
+ satisfied, whether it was silenced, how long the evaluation took, and any error. It is the
160
+ first place to look when someone says an alert did not fire.
161
+
162
+ Incidents group notifications for alerts with `creates_incident: true`:
163
+
164
+ ```bash
165
+ oo get alerts/incidents --status open --limit 20 # open, acknowledged or resolved
166
+ oo get alerts/incidents/stats
167
+ oo get alerts/incidents/<incident_id>
168
+ oo patch alerts/incidents/<incident_id>/update -d '{"status":"resolved"}' # one field per call
169
+ ```
170
+
171
+ Deduplication (`alerts/deduplication/...`) and RCA are enterprise features; on OSS they
172
+ answer "not supported".
173
+
174
+ ## Folders
175
+
176
+ ```bash
177
+ oo get folders/alerts # folder_type is dashboards, alerts or reports
178
+ oo get folders/alerts/name/<folder_name>
179
+ oo post folders/alerts -d '{"name":"platform","description":"team alerts"}'
180
+ oo delete folders/alerts/<folder_id>
181
+ ```
182
+
183
+ `--folder` everywhere takes the **folderId**, not the name. The default folder's id is
184
+ literally `default`.