Analysis endpoints
/analysis/* group + /rulesets/compile — ruleset validation, native logtest (predecoder → decoder → rule) and consultation of the XML schema supported by the engine.
All endpoints require the analysis:run scope (or a session with equivalent permission).
Endpoint table
| Method | Path | Scope | Description |
|---|---|---|---|
| POST | /rulesets/compile | analysis:run | Historical alias for /analysis/validate. |
| POST | /analysis/validate | analysis:run | Compile and validate a ruleset, returns diagnostics. |
| GET | /analysis/schema | analysis:run | Returns the accepted XML schema (tags, attributes, values). |
| POST | /analysis/logtest-native | analysis:run | Runs an event against the ruleset and returns the full trace. |
| POST | /analysis/full | analysis:run | Identical to logtest-native + extra static analysis. |
/rulesets/compileand/analysis/validateaccept the same request/response. Use/analysis/validate— the other exists for backward compatibility.
POST /analysis/validate
Compiles a set of rules/decoders and returns diagnostics. Used to validate before publishing, or in CI as a quality gate.
Auth: analysis:run scope.
Query parameters:
| Name | Type | Default | Description |
|---|---|---|---|
include_baseline | bool | false | When true, diagnostics[] also contains items originating from the Wazuh baseline (source_scope="default"). By default the API returns only findings in your content — baseline_issue_count still reflects the full set. Use true to inspect baseline warnings (e.g., local debugging, comparing with the UI). |
Request body (ValidationRequest):
{
"include_disk_defaults": true,
"include_disk_custom": true,
"files": [
{
"filename": "local_rules.xml",
"kind": "rules",
"scope": "adhoc",
"content": "<group name=\"local\"><rule id=\"100010\" level=\"3\">...</rule></group>"
},
{
"filename": "local_decoders.xml",
"kind": "decoders",
"scope": "adhoc",
"content": "<decoder name=\"custom\">...</decoder>"
}
]
}
include_disk_defaults(bool, defaulttrue) — include the Wazuh rules/decoders shipped with the server.include_disk_custom(bool, defaulttrue) — include rules/decoders from the instance'sdata/customfolder.files[]— extra content sent inline. Each file:filename(string, 1–255 chars) — logical name; used in error messages.kind—"rules"or"decoders".scope—"default","custom"or"adhoc"(the normal for client-sent content).content— raw XML.
Response 200 (ValidationResponse):
{
"ok": true,
"summary": {
"total_rule_files": 12,
"total_decoder_files": 8,
"total_rules": 8621,
"total_decoders": 623,
"compiled_hash": "abc123…",
"loaded_from_disk": true
},
"diagnostics": [
{
"severity": "warning",
"source": "rules",
"code": "rule_overlapping_level",
"message": "Rule 100010 shadows rule 5716",
"filename": "local_rules.xml",
"line": 3,
"column": 5,
"hint": "Consider using an ID outside the baseline range",
"evidence": "<rule id=\"100010\">",
"source_scope": "adhoc"
}
],
"performance": {
"source_build_ms": 45.2,
"compile_ms": 120.5,
"total_ms": 165.7,
"cache_hit": false
},
"baseline_issue_count": 0
}
ok—trueif no diagnostic withseverity="error"was found in your content (baseline errors don't block the gate, even wheninclude_baseline=true).diagnostics[]— by default, only diagnostics from client content (files withscope="custom"or"adhoc"). Problems in the Wazuh baseline are hidden and counted inbaseline_issue_count. To include baseline items, pass?include_baseline=true.performance.cache_hit—truewhen thecompiled_hashwas already cached.
Diagnostic
Each item in diagnostics[]:
| Field | Possible values |
|---|---|
severity | error, warning, info, success |
source | xml, rules, decoders, engine, session, filesystem |
code | Error-type identifier (e.g., decoder_missing_parent, regex_invalid, rule_duplicate_id). |
message | Human-readable message. |
filename | Source file (if applicable). |
line, column | Position in the file (1-indexed). |
hint | Suggested action. |
evidence | XML snippet that triggered the diagnostic. |
source_scope | default, custom, adhoc or null. |
Side effects
- Emits webhook
validation.completedwith{ok, diagnostic_count, total_rules, total_decoders}. - Counts against the organization plan (
analysis_runsmetric — although/validateitself currently doesn't consume a run; consumption happens inlogtest-nativeandfull).
curl example (clean response, only your own findings):
# RF_BASE without trailing slash — avoids //analysis/validate, which some proxies don't normalize.
RF_BASE="https://www.ruleforge.cloud/api/v1"
curl -X POST "$RF_BASE/analysis/validate" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d @payload.json
To inspect Wazuh baseline warnings as well (e.g., local debugging, comparing with the UI):
curl -X POST "$RF_BASE/analysis/validate?include_baseline=true" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d @payload.json
GET /analysis/schema
Returns the declarative schema of accepted XML tags — tags, attributes, allowed values. Used by the frontend editor for autocomplete/validation; can be useful for content generators.
Auth: analysis:run scope.
Response 200 (ValidationSchemaResponse):
{
"version": "4.10",
"references": ["https://documentation.wazuh.com/…"],
"decoders": {
"top_level_tags": ["decoder"],
"wrapper_tags": [],
"elements": [
{
"name": "decoder",
"context": "root",
"description": "Root decoder element",
"runtime_supported": true,
"attributes": [
{
"name": "name",
"required": true,
"allowed_values": [],
"description": "Unique decoder identifier"
}
],
"child_tags": ["program_name", "prematch", "regex", "order"],
"allowed_values": []
}
],
"root_attributes": [],
"group_attributes": [],
"mitre_elements": []
},
"rules": { "...": "..." }
}
POST /analysis/logtest-native
Runs the full pipeline over a single event: predecoder → decoder → rule → insights. Returns the execution trace for debugging.
Auth: analysis:run scope. Consumes 1 unit of the plan (analysis_runs).
Request body (LogtestRequest):
{
"include_disk_defaults": true,
"include_disk_custom": false,
"files": [],
"input": {
"event": "Jan 10 12:00:01 web01 sshd[1234]: Failed password for root from 10.0.0.5",
"log_format": "syslog",
"location": "/var/log/auth.log",
"session_id": "correlation-123"
}
}
- Everything in
ValidationRequestis accepted (ruleset). input.event— event string (1 byte to 8 MB).input.log_format—auto,syslog,json,eventchannel,plain.input.location— optional, 0–512 chars; used by decoders that depend on path.input.session_id— optional, 0–128 chars; groups events for correlation/frequency rules.
Response 200 (LogtestResponse):
{
"ok": true,
"summary": { "total_rules": 8621, "...": "..." },
"diagnostics": [],
"performance": {
"source_build_ms": 0,
"compile_ms": 0,
"predecode_ms": 0.8,
"decode_ms": 2.1,
"rule_ms": 6.4,
"total_ms": 10.1,
"cache_hit": true
},
"predecoded": {
"timestamp": "Jan 10 12:00:01",
"hostname": "web01",
"program_name": "sshd",
"location": "/var/log/auth.log",
"raw_message": "Failed password for root from 10.0.0.5",
"detected_format": "syslog"
},
"decoder": {
"name": "sshd",
"parent": null,
"order": ["srcip", "dstuser"]
},
"rule": {
"id": "5716",
"level": 5,
"description": "sshd: authentication failed.",
"groups": ["syslog", "sshd", "authentication_failed"],
"mitre_ids": ["T1110"],
"matched": true
},
"matched_rules": [
{ "id": "5716", "matched": true, "...": "..." }
],
"extracted_fields": {
"dstuser": "root",
"srcip": "10.0.0.5"
},
"trace": [
{ "phase": "predecode", "name": "predecoder", "matched": true, "details": {} },
{ "phase": "decode", "name": "sshd", "matched": true, "line": 42, "filename": "0100-pam_decoders.xml", "details": {} },
{ "phase": "rule", "name": "5716", "matched": true, "line": 105, "filename": "0025-sshd_rules.xml", "details": {} }
],
"insights": [
"Rule 5716 matched based on decoded field dstuser='root'.",
"Consider correlating with rule 5720 to detect brute-force."
]
}
rule— the highest-severity rule that matched (may benull).matched_rules— every rule that matched, in evaluation order.trace— each step executed;phasesays which layer. Useful to understand why a rule didn't match (e.g., decoder didn't match first).insights— natural-language tips.
Side effects
- Emits webhook
logtest.completedwith{ok, matched_rule_id, matched_rules}. - Consumes 1 unit of
analysis_runsfrom the organization.
POST /analysis/full
Identical to logtest-native in input and output — it is a distinct endpoint reserving room to grow with extra static analysis (decoder complexity, duplicates, etc.). Today returns the same LogtestResponse.
Recommendation: use /analysis/logtest-native. This endpoint exists for when additional features are enabled.
POST /rulesets/compile
Historical alias for /analysis/validate with the same request/response. Kept for backward compatibility.
Common errors
| Code | code | Cause |
|---|---|---|
400 | bad_request | files[].content isn't valid XML. |
403 | forbidden | Insufficient scope. |
413 | payload_too_large | Ruleset > 8 MB — split files. |
422 | validation_failed | Invalid body schema. |
429 | rate_limited | > 120 req/min or plan quota exhausted. |