# 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 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": "", "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 " \ -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