Skip to main content

curl quickstart

A minimal walkthrough for anyone who wants to start using the API in a few minutes.

Prerequisites

  • Access to a RuleForge instance (URL + organization).
  • A role that can create API keys (org_admin or equivalent).
  • curl installed.

We define environment variables for the example:

export RF_BASE="http://localhost:8000/api/v1"
export RF_KEY="rfk_YOUR_KEY_HERE"

1. Create an API key

Via UI: Settings → API keys → New key. Pick at least the analysis:run and projects:read scopes. The full value is shown once — copy it and save into $RF_KEY.

Via API (if you already have admin auth):

curl -X POST "$RF_BASE/platform/organizations/$ORG_ID/api-keys" \
-H "X-API-Key: $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline",
"scopes": ["analysis:run", "projects:read"],
"expires_at": "2027-04-24T00:00:00Z"
}'

The response includes api_key with the secret. Use it as $RF_KEY.

2. Confirm the key works

curl -s "$RF_BASE/health" -H "X-API-Key: $RF_KEY"
# => {"status":"ok"}

A 401 means the key is wrong or expired. A 200 means you're good — proceed.

3. Validate a ruleset

The most common endpoint: POST /analysis/validate. It compiles a set of rules/decoders (including disk baselines if you want) and returns diagnostics.

curl -X POST "$RF_BASE/analysis/validate" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d '{
"include_disk_defaults": true,
"include_disk_custom": false,
"files": [
{
"filename": "local_rules.xml",
"kind": "rules",
"scope": "adhoc",
"content": "<group name=\"local\"><rule id=\"100010\" level=\"3\"><description>Manual test</description><group>local_test</group></rule></group>"
}
]
}'

Summary response:

{
"ok": true,
"summary": {
"total_rule_files": 1,
"total_decoder_files": 0,
"total_rules": 8621,
"total_decoders": 623,
"compiled_hash": "abc123…",
"loaded_from_disk": true
},
"diagnostics": [],
"baseline_issue_count": 0,
"performance": {
"source_build_ms": 45.2,
"compile_ms": 120.5,
"total_ms": 165.7,
"cache_hit": false
}
}
  • ok: true → valid ruleset.
  • diagnostics[] lists errors and warnings from your content (baseline is hidden by default; baseline_issue_count shows how many were filtered out).

If ok: false, read diagnostics[].message and diagnostics[].line to locate the problem.

4. Test an event against the ruleset

POST /analysis/logtest-native runs the full pipeline (predecoder → decoder → rule) over a single event.

curl -X POST "$RF_BASE/analysis/logtest-native" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d '{
"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 port 44218 ssh2",
"log_format": "syslog"
}
}'

Response (summarized):

{
"ok": true,
"predecoded": {
"timestamp": "Jan 10 12:00:01",
"hostname": "web01",
"program_name": "sshd",
"raw_message": "Failed password for root from 10.0.0.5 port 44218 ssh2",
"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"],
"matched": true
},
"extracted_fields": { "dstuser": "root", "srcip": "10.0.0.5" },
"trace": [
{ "phase": "predecode", "name": "predecoder", "matched": true },
{ "phase": "decode", "name": "sshd", "matched": true },
{ "phase": "rule", "name": "5716", "matched": true }
]
}

Read:

  • rule → the rule that matched (may be null when nothing matches).
  • matched_rules → full list when multiple rules match.
  • extracted_fields → values the decoder extracted.
  • trace → steps executed; useful to debug a decoder that doesn't match.
  • insights → natural-language tips generated by the backend.

5. Create a project

If you're going to automate the content lifecycle, start by creating a project:

curl -X POST "$RF_BASE/platform/projects" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Edge rules",
"description": "Custom rules for the edge team",
"ruleset_version": "baseline-4.10"
}'

Save the returned id — you'll use it on every subsequent call.

6. Create a test case

Cases are events + expectations that run in regression:

curl -X POST "$RF_BASE/platform/projects/$PROJECT_ID/cases" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "SSH brute force",
"event": "Jan 10 12:00:01 web01 sshd[1234]: Failed password for root from 10.0.0.5",
"log_format": "syslog",
"expected_rule_id": "5716",
"expected_level": 5
}'

7. Run every case at once

curl -X POST "$RF_BASE/platform/projects/$PROJECT_ID/cases/run" \
-H "X-API-Key: $RF_KEY"

The response includes passed, failed and the detailed list of each case.

Full sample script

#!/usr/bin/env bash
set -euo pipefail

: "${RF_BASE:?}"
: "${RF_KEY:?}"

# 1. validate
curl -fsS -X POST "$RF_BASE/analysis/validate" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d @payload.json | jq '{ok, diagnostic_count: (.diagnostics | length)}'

# 2. logtest
curl -fsS -X POST "$RF_BASE/analysis/logtest-native" \
-H "X-API-Key: $RF_KEY" \
-H "Content-Type: application/json" \
-d @logtest.json | jq '{rule: .rule.id, matched: .rule.matched}'

Next steps

Tips

  • Send x-locale: en-US if you want messages in English.
  • The response's X-Request-Id header helps support.
  • include_disk_defaults=true uses the Wazuh ruleset shipped with the server — you don't need to send baselines along.
  • In CI pipelines, validate before running logtest: if validate failed → stop there.