# Windsurf + Sonnet Development Guide — Zabbix Monitoring System

> **For:** AI-assisted development in Windsurf using Claude Sonnet 4.6
> **Organization:** Wulf Consulting (MSP)
> **Last updated:** 2026-03-11

---

## Table of Contents

1. [System Overview](#1-system-overview)
2. [Infrastructure & Network Topology](#2-infrastructure--network-topology)
3. [Zabbix API Reference](#3-zabbix-api-reference)
4. [Host Organization & Data Model](#4-host-organization--data-model)
5. [Notification Pipeline](#5-notification-pipeline)
6. [Trigger Naming Conventions](#6-trigger-naming-conventions)
7. [Grafana Integration](#7-grafana-integration)
8. [Pulse Integration Architecture](#8-pulse-integration-architecture)
9. [Development Patterns & Gotchas](#9-development-patterns--gotchas)
10. [API Cookbook](#10-api-cookbook)

---

## 1. System Overview

Wulf Consulting is an MSP managing ~46 client sites. The monitoring stack runs on a single
Ubuntu 24.04 server (public IP: 209.166.162.245) with all services containerized in Docker.

### Stack Components

| Service | Container | Image | Purpose |
|---------|-----------|-------|---------|
| **Zabbix Server** | `zabbix-server` | `zabbix/zabbix-server-pgsql:alpine-7.4-latest` | Core monitoring engine |
| **Zabbix Frontend** | `zabbix-frontend` | `zabbix/zabbix-web-nginx-pgsql:alpine-7.4-latest` | Web UI + API endpoint |
| **PostgreSQL** | `zabbix-postgres` | `postgres:17-alpine` | Zabbix database |
| **Grafana** | `grafana` | `grafana/grafana:latest` | Dashboards + Zabbix plugin |
| **ntfy** | `ntfy` | `binwiederhier/ntfy:latest` | Push notification server |
| **Apprise** | `apprise-api` | `caronc/apprise:latest` | Multi-channel notification router |
| **Authentik** | `authentik` | `ghcr.io/goauthentik/server:2025.8.1` | SSO/identity provider |
| **Newt** | `newt` | `fosrl/newt` | Pangolin tunnel agent |

### External Access

All services are exposed through **Pangolin** (reverse proxy/tunnel), not direct port mappings.

| Service | External URL |
|---------|-------------|
| Zabbix | `https://zabbix.wulfconsulting.cloud` |
| Grafana | *(via Pangolin — check Pangolin config for exact URL)* |

SSO is handled by Authentik with SAML integration to Zabbix.

### Software Versions

- **Zabbix:** 7.4.7
- **PostgreSQL:** 17 (Alpine)
- **Grafana:** Latest (with `alexanderzobnin-zabbix-app` 6.2.1)
- **Host OS:** Ubuntu 24.04.4 LTS

---

## 2. Infrastructure & Network Topology

### Docker Networks

```
┌─────────────────────────────────────────────────────────────┐
│                     pangolin (172.18.0.0/16)                │
│  ┌─────────────┐ ┌──────────┐ ┌──────┐ ┌───────────────┐   │
│  │zabbix-front │ │ grafana  │ │ ntfy │ │ apprise-api   │   │
│  │ 172.18.0.4  │ │172.18.0.3│ │.0.7  │ │  172.18.0.5   │   │
│  └──────┬──────┘ └────┬─────┘ └──────┘ └───────────────┘   │
│         │              │                                     │
│  ┌──────┴──────┐       │        ┌──────────┐ ┌───────────┐  │
│  │zabbix-server│       │        │authentik │ │   newt    │  │
│  │ 172.18.0.8  │       │        │172.18.0.6│ │172.18.0.2 │  │
│  └──────┬──────┘       │        └──────────┘ └───────────┘  │
│         │              │                                     │
└─────────┼──────────────┼─────────────────────────────────────┘
          │              │
┌─────────┼──────────────┼──────────────────────┐
│         │   zabbix_zabbix_internal (172.19.0.0/16)         │
│  ┌──────┴──────┐ ┌────┴─────┐ ┌──────────────┐│
│  │zabbix-server│ │ grafana  │ │zabbix-frontend││
│  │ 172.19.0.4  │ │172.19.0.3│ │ 172.19.0.5   ││
│  └─────────────┘ └──────────┘ └──────────────┘│
│                                ┌──────────────┐│
│                                │zabbix-postgres││
│                                │ 172.19.0.2   ││
│                                └──────────────┘│
└─────────────────────────────────────────────────┘
```

### Key Connectivity Facts

- **Zabbix API (internal):** `http://zabbix-frontend:8080/api_jsonrpc.php` — accessible from `zabbix-server`, `grafana`, and anything on `zabbix_zabbix_internal`
- **Zabbix API (external):** `https://zabbix.wulfconsulting.cloud/api_jsonrpc.php` — via Pangolin
- **ntfy (internal):** `http://ntfy:80` — accessible from `zabbix-server` and `zabbix-frontend` via `pangolin` network
- **Apprise API:** `http://apprise-api:8000` (also `0.0.0.0:8000` on host)
- **Zabbix agent port:** `10051` (mapped to host `0.0.0.0:10051`)
- **Grafana** has NO external port mapping — accessed only through Pangolin

### Docker Compose Location

All stack definitions: `/opt/stacks/zabbix/compose.yml`

Environment variables: `/opt/stacks/zabbix/.env`

---

## 3. Zabbix API Reference

### Authentication

Zabbix 7.4 uses Bearer token authentication:

```
Authorization: Bearer <api_token>
```

API tokens are generated in Zabbix UI: **User Settings → API tokens**

> **Important:** The `apiinfo.version` method MUST be called WITHOUT the Authorization header.
> All other methods require it.

### Base Request Format

```json
{
  "jsonrpc": "2.0",
  "method": "<method_name>",
  "params": { ... },
  "id": 1
}
```

### API Endpoint

| Context | URL |
|---------|-----|
| From `zabbix-server` or `grafana` container | `http://zabbix-frontend:8080/api_jsonrpc.php` |
| From the Docker host | `docker exec zabbix-frontend curl -s -X POST http://localhost:8080/api_jsonrpc.php ...` |
| From external / Pulse | `https://zabbix.wulfconsulting.cloud/api_jsonrpc.php` |

### Key API Methods Used

| Method | Purpose | Notes |
|--------|---------|-------|
| `host.get` | List hosts, get groups/templates | Use `selectHostGroups`, `selectParentTemplates` |
| `hostgroup.get` | List host groups | Filter by `Clients/` or `ISP/` prefix |
| `trigger.get` | Get triggers, active problems | `only_true: true` for currently-firing |
| `problem.get` | Get current problems | `recent: true` for unresolved |
| `event.get` | Get events (problems + recoveries) | Use `time_from`/`time_to`, `value: 0` for recovery |
| `mediatype.get` | Get notification media types | `selectMessageTemplates` for templates |
| `mediatype.update` | Update webhook scripts/templates | Include full `script`, `parameters`, `message_templates` |
| `action.get` | Get trigger actions | `selectOperations`, `selectRecoveryOperations` |
| `action.update` | Update actions | Add `recovery_operations` |
| `trigger.update` | Rename triggers, update descriptions | Use `description` (name) and `comments` (description text) |
| `user.get` | Get users and their media | `selectMedias` for notification channels |
| `template.get` | Get templates and their triggers | `selectTriggers` |

### Zabbix API Quirks (Zabbix 7.4)

- **Trigger `description` = trigger name** (not the description text). The description text is in `comments`.
- **Status codes:** `0` = enabled, `1` = disabled for both media types and actions.
- **`templateid` on host triggers:** If `0`, the trigger was created directly on the host (not inherited from a template). Non-zero = the parent trigger ID on the template.
- **Severity levels:** `0`=Not classified, `1`=Information, `2`=Warning, `3`=Average, `4`=High, `5`=Disaster
- **`{EVENT.VALUE}`:** `1` = problem, `0` = OK/recovery
- **`{EVENT.NSEVERITY}`:** Numeric severity (0-5)
- **Webhook scripts** run in Zabbix's built-in Duktape JavaScript engine (ES5 only — no `let`, `const`, arrow functions, template literals, `Array.find`, etc.)

---

## 4. Host Organization & Data Model

### Host Group Hierarchy

Every monitored host is assigned to multiple groups:

```
Host: "Kuhn's Quality Foods"
├── Clients/Kuhn's Quality Foods    ← client identity
├── ISP/Zito Media, L.P.           ← internet provider
└── Datto RMM Sites                ← RMM platform category
```

This triple-grouping enables:
- **Per-client dashboards** — filter by `Clients/` group
- **ISP outage detection** — if 3+ hosts on same ISP go down = likely ISP issue
- **RMM correlation** — cross-reference Zabbix with Datto RMM agent status

### Client Groups (46)

| ID | Name |
|----|------|
| 84 | Clients/3 Rivers Express |
| 90 | Clients/ABC Fire Extinguisher Inc |
| 49 | Clients/ADM Signs |
| 40 | Clients/Advanced Masonry |
| 50 | Clients/Alabek Commercial Roofing Corp. |
| 33 | Clients/All Saints Catholic Church |
| 51 | Clients/Attica Hub/Seneca Publishing |
| 42 | Clients/Bella Diamond LLC |
| 28 | Clients/Blake Dentistry |
| 85 | Clients/Bosak Eyecare & Optical |
| 55 | Clients/Bridges Health Partners Services, LLC |
| 37 | Clients/Brodaks |
| 45 | Clients/Broker's Settlement Services, Inc. |
| 43 | Clients/Brooks Diamonds |
| 57 | Clients/Buffalo Glass Block |
| 60 | Clients/CUMI America |
| 61 | Clients/Chartiers Animal Hospital Ltd |
| 58 | Clients/Cincinnati Glass Block |
| 56 | Clients/Clista Electric Inc. |
| 35 | Clients/ConnecTel, Inc. |
| 26 | Clients/Finn Chiropractic Group |
| 59 | Clients/Frew Plumbing, Heating, & Air |
| 53 | Clients/Greco Gas |
| 65 | Clients/Heart Prints Center for Early Education |
| 47 | Clients/Hergenroeder, Rega, Ewing, & Kennedy, LLC |
| 69 | Clients/Hynes Industries |
| 48 | Clients/Insurance Restoration Consultants, Inc. |
| 32 | Clients/Kuhn's Quality Foods |
| 44 | Clients/Loss Prevention Services |
| 80 | Clients/MDS Energy Development, LLC |
| 88 | Clients/Marsico Financial Group, LLC |
| 62 | Clients/Nordmann Roofing |
| 68 | Clients/North Eastern Uniforms & Equipment Inc |
| 38 | Clients/POH+W Architects |
| 75 | Clients/Penn Energy Resources |
| 86 | Clients/Pittsburgh Financial Consultants |
| 87 | Clients/Premier Automation Holdings, Inc. |
| 23 | Clients/Seubert and Associates |
| 91 | Clients/Superior Distributing Co |
| 30 | Clients/TK Plastics Company, Inc. |
| 70 | Clients/Thoroughbred Construction Group |
| 66 | Clients/Thrasher Group, Inc. |
| 71 | Clients/Universal Plastics |
| 73 | Clients/Universal Plastics Latrobe |
| 54 | Clients/V-Systems |
| 76 | Clients/Vorteq Coil Finishers |

### ISP Groups (24)

| ID | Name |
|----|------|
| 25 | ISP/AT&T Enterprises, LLC |
| 27 | ISP/Armstrong |
| 24 | ISP/Bigleaf Networks, Inc. |
| 34 | ISP/Buckeye Cablevision, Inc. |
| 52 | ISP/Charter Communications Inc |
| 74 | ISP/Citizens Telecommunication Technologies, Inc |
| 67 | ISP/CityNet |
| 31 | ISP/Comcast Cable Communications, LLC |
| 46 | ISP/DQE Communications LLC |
| 63 | ISP/Expedient |
| 77 | ISP/Fidium |
| 89 | ISP/Frontier Communications of America, Inc. |
| 82 | ISP/GeoLinks |
| 78 | ISP/JACKSON ENERGY AUTHORITY |
| 39 | ISP/Level 3 Parent, LLC |
| 92 | ISP/Metalink Technologies, Inc. |
| 72 | ISP/OneCleveland |
| 64 | ISP/Space Exploration Technologies Corporation |
| 83 | ISP/UPMC |
| 79 | ISP/Ultimate Internet Access, Inc |
| 36 | ISP/Verizon Business |
| 81 | ISP/Wave Broadband |
| 41 | ISP/Windstream Communications LLC |
| 29 | ISP/Zito Media, L.P. |

### Other Groups

| ID | Name | Purpose |
|----|------|---------|
| 19 | Applications | Application-level monitoring |
| 20 | Databases | Database servers |
| 22 | Datto RMM Sites | Hosts also managed by Datto RMM |
| 5 | Discovered hosts | Auto-discovered hosts |
| 7 | Hypervisors | Virtualization hosts |
| 2 | Linux servers | Linux-based systems |
| 93 | Studio Imagine | Internal project |
| 6 | Virtual machines | VMs |
| 4 | Zabbix servers | Zabbix infrastructure |

### Host Pattern

All client-site hosts currently use the **ICMP Ping** template (templateid: 10564) for
basic up/down monitoring. A typical host:

```
Name:      "Kuhn's Quality Foods"
Status:    enabled
Groups:    [Clients/Kuhn's Quality Foods, ISP/Zito Media L.P., Datto RMM Sites]
Templates: [ICMP Ping]
```

**77 total hosts** across the system (including Zabbix infrastructure hosts).

### Global Macros

| Macro | Value |
|-------|-------|
| `{$SNMP_COMMUNITY}` | `public` |

---

## 5. Notification Pipeline

### Current Architecture

```
Zabbix Trigger Fires
       │
       ▼
Action: "Send ntfy to NOC" (actionid: 7)
  ├── Problem  → ntfy webhook (mediatypeid: 102)
  └── Recovery → ntfy webhook (same media type, different template)
       │
       ▼
ntfy Webhook Script (Duktape JS)
  ├── Determines problem vs recovery (EVENT.VALUE)
  ├── Sets 🔴 red_circle (problem) or 🟢 green_circle (recovery)
  ├── Sets ntfy priority (high for problems, low for recovery)
  ├── Adds Click URL → Zabbix event page
  ├── For problems: queries Zabbix API for other active issues on same client
  └── POSTs to ntfy → http://ntfy/noc-alerts
       │
       ▼
ntfy Push Notification → user's phone
```

### Action Configuration

**Action:** "Send ntfy to NOC" (actionid: 7)
- **Trigger condition:** Severity >= High (conditiontype 4, operator 5, value 4)
- **Operations:** Send message to user group "Executive" (usrgrpid: 14)
- **Recovery operations:** Notify all involved (operationtype: 11)
- **Uses default messages** (default_msg: 1) — pulls from media type templates

### Users & Media

| User | Role | ntfy Media |
|------|------|------------|
| Lorentz Hinrichsen (`lorentz@wulfconsulting.com`) | Super admin (3) | `noc-alerts` topic (enabled) |
| Tom Carlin (`tom@wulfconsulting.com`) | Super admin (3) | *(removed — was duplicate to same topic)* |
| Admin | Super admin (3) | *(none)* |
| guest | Guest (4) | *(none)* |

### ntfy Webhook Script (Current)

The webhook script runs inside Zabbix Server's Duktape JS engine. It:

1. Parses parameters from Zabbix macros
2. Determines if this is a problem or recovery event
3. For problems: makes 2 internal Zabbix API calls to find related active issues for the same client
4. Formats and sends the ntfy notification with appropriate tags, priority, and click URL

**Full script and parameters** are documented in `/opt/stacks/zabbix/notification-changes.md`

### Message Templates (Current)

| Event | ntfy Tag | Subject | Body |
|-------|----------|---------|------|
| Problem | 🔴 `red_circle` | `{HOST.NAME} — {EVENT.NAME}` | Trigger description + started time + duration + related issues |
| Recovery | 🟢 `green_circle` | `Resolved: {HOST.NAME} — {EVENT.NAME}` | "Host is back online." + downtime + restored time |
| Update | *(inherits)* | `Updated: {HOST.NAME} — {EVENT.NAME}` | Who updated + action + status |

### Notification Example

**Problem:**
```
🔴 Kuhn's Quality Foods — Host Unreachable

Host failed to respond to 3 consecutive ICMP ping
requests. The site may be offline, the network path
disrupted, or the device powered off.

Started:  2026.03.11 at 14:22:10
Duration: 5m 30s

── Other active issues (Kuhn's Quality Foods) ──
 · High Packet Loss — Kuhn's SW-Core
```

**Recovery:**
```
🟢 Resolved: Kuhn's Quality Foods — Host Unreachable

Host is back online.

Downtime: 22m 15s
Restored: 2026.03.11 at 14:44:25
```

### Available but Disabled Media Types

These exist in Zabbix but are disabled. Can be enabled if needed:
- Email, Email (HTML), Gmail, Office365, SMS
- Discord, Slack, MS Teams, MS Teams Workflow, Telegram
- Jira, Jira Service Management, ServiceNow, Zendesk, PagerDuty, Opsgenie
- Many others (40+ webhook integrations available)

---

## 6. Trigger Naming Conventions

### Design Principles (Established 2026-03-11)

Triggers were renamed from Zabbix defaults to be **executive-friendly**:

| Default Zabbix Name | Current Name | Severity |
|---------------------|-------------|----------|
| `ICMP Ping: Unavailable by ICMP ping` | **Host Unreachable** | High (4) |
| `ICMP Ping: High ICMP ping loss` | **High Packet Loss** | Warning (2) |
| `ICMP Ping: High ICMP ping response time` | **Slow Response Time** | Warning (2) |

### Naming Rules

1. **No technical jargon in trigger names** — use impact-based language
2. **Trigger descriptions (comments field)** contain the technical detail: what the check does, thresholds, possible causes
3. **Keep names short** — they appear in ntfy subjects, Teams cards, dashboards
4. **No host name in trigger name** — `{HOST.NAME}` is added by the notification template

### Cisco Triggers (Not Yet Renamed)

The Cisco Catalyst SNMP templates have their own ICMP triggers that still use the old naming:
- `Cisco Catalyst 3750V2-24FS: Unavailable by ICMP ping`
- `Cisco Catalyst 3750V2-24FS: High ICMP ping loss`
- etc.

These are on separate templates (not the "ICMP Ping" template) and have `templateid: 0` on host triggers. Renaming these requires updating each Cisco template individually.

---

## 7. Grafana Integration

### Plugin

- **alexanderzobnin-zabbix-app** v6.2.1 — connects directly to Zabbix API
- Datasource URL (internal): `http://zabbix-frontend:8080/api_jsonrpc.php`

### Planned: MSP Executive Status Board

Design goal: at-a-glance dashboard for management showing:

| Panel | Type | Data Source |
|-------|------|-------------|
| Current problems by severity | Stat panels | Zabbix problems |
| Problems per client | Bar gauge | Zabbix trigger groups |
| Active problem list | Table | Zabbix problems |
| SLA/uptime per client | Zabbix SLA panel | Zabbix SLA |
| Host status grid | Status map plugin | Zabbix hosts |

Filter by `Clients/` host groups. Use Grafana variables for client selection dropdown.

---

## 8. Pulse Integration Architecture

### Overview

**Pulse** is the internal Node.js/TypeScript web app with API access to:
- Zabbix (this system)
- PSA (ticketing)
- Datto RMM
- Veeam (backups)
- Microsoft Graph (Teams, email)
- Zoom
- Apprise (notification routing)

### Planned: Morning NOC Summary

Full architecture document: `/opt/stacks/zabbix/pulse-morning-summary-architecture.md`

**Key points:**
- Scheduled job at 6:30 AM weekdays
- Aggregates data from Zabbix + PSA + Veeam in parallel
- Sends Teams adaptive card (direct via MS Graph), email/ntfy (via Apprise)
- User-configurable delivery preferences
- Killer feature: **unmatched alerts** — Zabbix problems with no PSA ticket

### Zabbix API Queries for Pulse

**Get all open problems with host/client context:**
```typescript
// 1. Get open problems
const problems = await zabbixApi('problem.get', {
  recent: true,
  sortfield: ['eventid'],
  sortorder: 'DESC'
});

// 2. Get host → client mapping (cache this)
const hosts = await zabbixApi('host.get', {
  output: ['hostid', 'host', 'name'],
  selectHostGroups: ['groupid', 'name']
});

// 3. Build client map
const clientMap = {};
for (const host of hosts) {
  const clientGroup = host.hostgroups.find(g => g.name.startsWith('Clients/'));
  if (clientGroup) {
    clientMap[host.hostid] = clientGroup.name.replace('Clients/', '');
  }
}
```

**Get overnight resolved events:**
```typescript
const resolved = await zabbixApi('event.get', {
  source: 0,
  object: 0,
  value: 0,  // recovery events only
  time_from: Math.floor(yesterday6pm.getTime() / 1000),
  time_to: Math.floor(today630am.getTime() / 1000),
  selectHosts: ['name'],
  selectRelatedObject: ['description'],
  sortfield: ['clock'],
  sortorder: 'DESC'
});
```

---

## 9. Development Patterns & Gotchas

### Zabbix Webhook Script Constraints

The webhook script runs in **Duktape** (ES5 JavaScript engine):

**DO:**
```javascript
var x = 'hello';               // var only
for (var i = 0; i < arr.length; i++) { }  // classic for loops
JSON.parse(), JSON.stringify()  // available
new HttpRequest()               // Zabbix's HTTP client
btoa()                          // Base64 encoding available
```

**DON'T:**
```javascript
let x = 'hello';               // NO let/const
const y = () => {};             // NO arrow functions
`template ${literal}`;          // NO template literals
arr.find(x => x.id === 1);     // NO Array.find/includes/map
for (const x of arr) {}        // NO for...of
```

### HttpRequest in Webhooks

```javascript
var req = new HttpRequest();
req.addHeader('Content-Type: application/json');
req.addHeader('Authorization: Bearer TOKEN');

var resp = req.post(url, body);    // POST
var resp = req.put(url, body);     // PUT
var resp = req.get(url);           // GET

var status = req.getStatus();      // HTTP status code
```

Multiple HttpRequest instances CAN be created in the same script (used for the related-problems enrichment that calls Zabbix API before calling ntfy).

### API Update Patterns

When updating a media type, you must include the FULL array for `parameters` and `message_templates` — they're **replaced entirely**, not merged.

```python
# WRONG — this deletes all other parameters
mediatype.update({ parameters: [{"name": "new_param", "value": "x"}] })

# RIGHT — include ALL parameters
mediatype.update({ parameters: [
    {"name": "endpoint", "value": "http://ntfy/noc-alerts"},
    {"name": "username", "value": "monitoring"},
    # ... all existing params ...
    {"name": "new_param", "value": "x"}
] })
```

### Trigger Description vs Comments

This is confusing in the Zabbix API:
- `description` field = **the trigger name** (what you see in the UI)
- `comments` field = **the description text** (the explanation paragraph)

```json
{
  "triggerid": "23176",
  "description": "Host Unreachable",           // ← this is the NAME
  "comments": "Host failed to respond to..."   // ← this is the DESCRIPTION
}
```

### Host Group Filtering Pattern

To get the client name for a host:

```javascript
// Get host groups
var groups = hostData.hostgroups;
var clientName = null;
for (var i = 0; i < groups.length; i++) {
    if (groups[i].name.indexOf("Clients/") === 0) {
        clientName = groups[i].name.replace("Clients/", "");
        break;
    }
}
// clientName = "Kuhn's Quality Foods"
```

### Docker Exec for API Calls

Since the Zabbix frontend doesn't have ports mapped to the host, API calls from the host must go through `docker exec`:

```bash
docker exec zabbix-frontend curl -s -X POST \
  "http://localhost:8080/api_jsonrpc.php" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{"jsonrpc":"2.0","method":"...","params":{...},"id":1}'
```

Or use Python for complex payloads (escaping JSON in bash is fragile):

```python
import json, subprocess

payload = json.dumps({...})
result = subprocess.run(
    ["docker", "exec", "zabbix-frontend", "curl", "-s", "-X", "POST",
     "http://localhost:8080/api_jsonrpc.php",
     "-H", "Content-Type: application/json",
     "-H", f"Authorization: Bearer {TOKEN}",
     "-d", payload],
    capture_output=True, text=True
)
data = json.loads(result.stdout)
```

### External API Access (from Pulse or other servers)

```typescript
const response = await fetch('https://zabbix.wulfconsulting.cloud/api_jsonrpc.php', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${ZABBIX_API_TOKEN}`
  },
  body: JSON.stringify({
    jsonrpc: '2.0',
    method: 'problem.get',
    params: { recent: true },
    id: 1
  })
});
```

---

## 10. API Cookbook

### Get All Active Problems

```json
{
  "method": "problem.get",
  "params": {
    "output": "extend",
    "recent": true,
    "sortfield": ["eventid"],
    "sortorder": "DESC"
  }
}
```

### Get Active Problems for a Specific Client

```json
{
  "method": "trigger.get",
  "params": {
    "output": ["triggerid", "description", "lastchange", "priority"],
    "groupids": ["32"],
    "only_true": true,
    "selectHosts": ["name"],
    "skipDependent": true
  }
}
```
*(groupid 32 = Clients/Kuhn's Quality Foods)*

### Get All Hosts with Client + ISP Mapping

```json
{
  "method": "host.get",
  "params": {
    "output": ["hostid", "host", "name", "status"],
    "selectHostGroups": ["groupid", "name"],
    "selectParentTemplates": ["templateid", "name"]
  }
}
```

### Get Recovery Events in a Time Window

```json
{
  "method": "event.get",
  "params": {
    "source": 0,
    "object": 0,
    "value": 0,
    "time_from": 1741647600,
    "time_to": 1741692600,
    "selectHosts": ["name"],
    "selectRelatedObject": ["description"],
    "sortfield": ["clock"],
    "sortorder": "DESC"
  }
}
```

### Update a Trigger Name + Description

```json
{
  "method": "trigger.update",
  "params": {
    "triggerid": "23176",
    "description": "Host Unreachable",
    "comments": "Host failed to respond to 3 consecutive ICMP ping requests."
  }
}
```

### Get Media Type with Full Details

```json
{
  "method": "mediatype.get",
  "params": {
    "output": "extend",
    "selectMessageTemplates": "extend",
    "mediatypeids": ["102"]
  }
}
```

### Test ntfy Notification Manually

```bash
docker exec zabbix-frontend curl -s -X PUT \
  -H "Authorization: Basic $(echo -n 'monitoring:GBigt1231#' | base64)" \
  -H "Title: Test Notification" \
  -H "Priority: 3" \
  -H "Tags: red_circle" \
  "http://ntfy/noc-alerts" \
  -d "This is a test notification from Zabbix"
```

---

## Related Documentation

- **Notification changes (before/after + restoration):** `/opt/stacks/zabbix/notification-changes.md`
- **Pulse morning summary architecture:** `/opt/stacks/zabbix/pulse-morning-summary-architecture.md`
- **Docker Compose:** `/opt/stacks/zabbix/compose.yml`
- **Zabbix SSO setup:** `/opt/stacks/zabbix/ENTRA-AUTHENTIK-ZABBIX-SSO.md`
- **Zabbix 7.4 API docs:** https://www.zabbix.com/documentation/7.4/en/manual/api
