# wazuh-api-python

Companion files for the lesson [The Wazuh API with Python: Agents, Groups and Findings](https://maheshtata.com/wazuh/wazuh-api-python/).

Tested against the Wazuh 5.0 beta5 documentation (September 2026). Wazuh 5.0 is
still in beta, so treat every endpoint as subject to change at GA.

| File | What it is |
| --- | --- |
| `wazuh_client.py` | Small client for both APIs: JWT auth, paging, envelope checking, and worked calls for agents, groups, upgrades, findings and state |
| `findings-by-rule.json` | Indexer aggregation that counts findings per rule over 7 days |

## Credentials

Set them as environment variables. The script refuses to run without them and
contains no defaults.

```bash
export WAZUH_API_URL="https://MANAGER_HOST:55000"
export WAZUH_API_USER="wazuh"                 # default manager API admin user
export WAZUH_API_PASSWORD="..."               # TODO

export WAZUH_INDEXER_URL="https://INDEXER_HOST:9200"
export WAZUH_INDEXER_USER="admin"             # default indexer internal user
export WAZUH_INDEXER_PASSWORD="..."           # TODO

# Point at the CA that signed the Wazuh certificates rather than disabling
# verification. The installation assistant issues its own CA.
export WAZUH_CA_BUNDLE="/etc/wazuh-indexer/certs/root-ca.pem"
# Lab only:
# export WAZUH_INSECURE=1
```

Rotate the default `wazuh`, `wazuh-wui`, `admin` and `kibanaserver` passwords
at install time with the Wazuh passwords tool.

## Usage

```bash
pip install requests

python3 wazuh_client.py agents        # paged agent list
python3 wazuh_client.py groups
python3 wazuh_client.py outdated      # agents needing an upgrade
python3 wazuh_client.py policies      # what this token may actually do

# Idempotent onboarding. Creates the group first, because in Wazuh 5.0
# enrolment into a group that does not exist is rejected. Safe to re-run.
python3 wazuh_client.py onboard --group payments-prod --name web-01 --ip 10.0.10.11

python3 wazuh_client.py findings --days 7
python3 wazuh_client.py state --index wazuh-states-sca --agent-id 001
```

Or send the aggregation directly:

```bash
curl -sk -u "$WAZUH_INDEXER_USER:$WAZUH_INDEXER_PASSWORD" \
  -X POST "$WAZUH_INDEXER_URL/wazuh-findings-v5-*/_search" \
  -H 'Content-Type: application/json' \
  -d @findings-by-rule.json
```

Remove the `_comment` key from `findings-by-rule.json` first.

## Things the client encodes that are easy to get wrong

- **A 200 is not success.** Bulk operations return per-item errors inside
  `data.failed_items` with a 200 status, and the envelope carries its own
  top-level `error` field. `WazuhManagerAPI.check()` inspects all three.
- **The JWT expires.** Default `auth_token_exp_timeout` is 900 seconds,
  changeable with `PUT /security/config`. The client refreshes once on a 401
  and retries.
- **Listings page.** `GET /agents` has a default limit. `list_agents()` pages
  until a short result comes back.
- **429 is a design signal.** Use the endpoints' own list parameters instead
  of looping single-item calls. `upgrade_agents()` takes a `group` filter for
  exactly this reason.
- **`wazuh.rule.level` is a string**, not an integer. Values are
  informational, low, medium, high and critical. Use a `terms` query.
- **Groups first.** `onboard` calls `ensure_group()` before `add_agent()`.

## Endpoints removed in 5.0

The 5.0 specification has no paths under `/manager`, `/sca`, `/syscheck`,
`/rootcheck`, `/syscollector`, `/tasks`, `/rules`, `/decoders`, `/lists`,
`/logtest`, `/active-response`, `/vulnerability` or `/events`. Manager
operations moved under `/cluster`. Agent state moved to the indexer's
stateful indices, which `agent_state()` reads:

| Old manager endpoint | 5.0 index |
| --- | --- |
| `/sca/{agent_id}` | `wazuh-states-sca` |
| `/syscheck/{agent_id}` | `wazuh-states-fim-files` |
| `/syscollector/{agent_id}/packages` | `wazuh-states-inventory-packages` |
| `/vulnerability/{agent_id}` | `wazuh-states-vulnerabilities` |

Rule, decoder, KVDB, integration, logtest and space management moved to the
indexer's content manager API under `/_plugins/_content_manager/`.
