Skip to main content

Notification API

The Notification API provides real-time notifications for plant health events, enabling immediate reaction to critical alerts, stress alerts, light alerts, and recovery alerts.

Notifications can be delivered via two methods:

  • Webhook: HTTP POST requests to your configured endpoint
  • WebSocket: Real-time streaming connection

Both delivery methods use the same notification payload format, and provide identical functionality.

Webhook delivery​

Setup​

Configure your webhook endpoint via the Gardin Management Console. Once configured, notifications will be sent as HTTP POST requests to your endpoint.

Request headers​

All webhook POST requests include the following headers:

HeaderDescription
Gardin-SignatureHMAC-SHA256 signature of the payload for verification
User-AgentIdentifies the request source in the format Gardin-Webhook/<request-id>

Verifying and processing notifications​

To ensure the authenticity of webhook requests and correctly process notifications, follow these steps:

  1. Validate the User-Agent header: Check that the User-Agent header starts with the string Gardin-Webhook/. Reject requests that do not match this pattern.

  2. Obtain your signing key: Retrieve your unique secret key from the Gardin Management Console. This key is used to verify the HMAC-SHA256 signature.

  3. Compute the expected signature: Using your secret key, compute the HMAC-SHA256 signature of the raw request body.

  4. Compare signatures: Compare your computed signature with the value in the Gardin-Signature header. Only process the notification if they match exactly.

  5. Parse the notification: Once verified, parse the JSON payload. Check the notificationType field to determine the notification type, then handle based on the specific fields available to that type.

  6. Return HTTP 200: Optionally return a 200 OK status code to acknowledge receipt.

Failed publishes

We do not make repeat attempts at delivery should publishing fail due to an error on the receivers' side.

Example: Receiving a plant critical alert​

The following Python example demonstrates how to process Notification API webhook events as part of a simple API built using FastAPI. The signing key is retrieved from a secure data store (in this case, environment variables):

import hmac
import hashlib
import os
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

def get_signing_key() -> str:
"""Retrieve signing key from secure storage."""
# In production, retrieve from a secrets manager (e.g. AWS Secrets Manager, HashiCorp Vault etc.)
key = os.environ.get("GARDIN_WEBHOOK_SIGNING_KEY")
if not key:
raise ValueError("Signing key not configured")
return key

@app.post("/webhook")
async def handle_webhook(request: Request):
# Step 1: Validate User-Agent header
user_agent = request.headers.get("User-Agent", "")
if not user_agent.startswith("Gardin-Webhook/"):
raise HTTPException(status_code=403, detail="Invalid User-Agent")

# Step 2: Get the signature from header
received_signature = request.headers.get("Gardin-Signature")
if not received_signature:
raise HTTPException(status_code=403, detail="Missing signature")

# Step 3: Compute expected signature
payload = await request.body()
signing_key = get_signing_key()
computed_signature = hmac.new(
signing_key.encode("utf-8"),
msg=payload,
digestmod=hashlib.sha256
).hexdigest()

# Step 4: Compare signatures
if not hmac.compare_digest(computed_signature, received_signature):
raise HTTPException(status_code=403, detail="Invalid signature")

# Step 5: Parse and process the notification
notification = await request.json()

# Check notification type first
notification_type = notification.get("notificationType")
if notification_type != "alert":
# Handle other notification types as required.
return {"status": "ok"}

alert_category = notification.get("alertCategory")
event = notification.get("event")
display_name = notification.get("displayName")
location = notification.get("displayPath")

print(f"Received {event}: {display_name} at {location}")

# Handle based on alert category
if alert_category == "PLANT_CRITICAL_ALERT":
# Trigger immediate action - plants need urgent attention
print(f"CRITICAL: Immediate intervention required at {location}")
elif alert_category == "PLANT_STRESS_ALERT":
# Early warning - investigate conditions
print(f"WARNING: Early stress detected at {location}")
elif alert_category == "PLANT_LIGHT_ALERT":
# Check lighting conditions
print(f"LIGHT: Insufficient light at {location}")
elif alert_category == "PLANT_RECOVERY_ALERT":
# Plants recovering - good news
print(f"RECOVERY: Plants recovering at {location}")

# Step 6: Return success
return {"status": "ok"}

WebSocket delivery​

Authentication​

Before connecting to the WebSocket endpoint, you need to obtain an access token via the Login API. Ensure your client credentials include either the notifications:subscribe scope (for all notifications) or notifications.alerts:subscribe scope (for alerts only).

Connection​

Connect to the WebSocket endpoint at:

wss://notifications.ws.api.gardin.ag?token=<ACCESS_TOKEN>

Replace <ACCESS_TOKEN> with the access token obtained from the Login API.

Example: Connecting and receiving plant alerts​

The following Python example demonstrates the complete process of obtaining an access token, connecting to the WebSocket, and processing notifications:

import asyncio
import os
import httpx
import websockets
import json

async def get_access_token() -> str:
"""Obtain access token from the Login API."""
client_id = os.environ.get("GARDIN_CLIENT_ID")
client_secret = os.environ.get("GARDIN_CLIENT_SECRET")

if not client_id or not client_secret:
raise ValueError("Client credentials not configured")

async with httpx.AsyncClient() as client:
response = await client.post(
"https://auth.gardin.ag/oauth2/token",
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "notifications.alerts:subscribe"
},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
response.raise_for_status()
return response.json()["access_token"]

async def listen_for_alerts():
# Step 1: Obtain access token from the Login API
token = await get_access_token()
uri = f"wss://notifications.ws.api.gardin.ag?token={token}"

# Step 2: Connect to the WebSocket
async with websockets.connect(uri) as websocket:
print("Connected to Notification API")

# Step 3: Listen for notifications
async for message in websocket:
# Step 4: Parse the notification
notification = json.loads(message)

# Check notification type first
notification_type = notification.get("notificationType")
if notification_type != "alert":
# Handle other notification types as required
continue

alert_category = notification.get("alertCategory")
event = notification.get("event")
display_name = notification.get("displayName")
location = notification.get("displayPath")

print(f"Received {event}: {display_name} at {location}")

# Step 5: Handle based on alert category
if alert_category == "PLANT_CRITICAL_ALERT":
# Trigger immediate action
print(f"CRITICAL: Immediate intervention required at {location}")
elif alert_category == "PLANT_STRESS_ALERT":
# Early warning
print(f"WARNING: Early stress detected at {location}")
elif alert_category == "PLANT_LIGHT_ALERT":
# Check lighting
print(f"LIGHT: Insufficient light at {location}")
elif alert_category == "PLANT_RECOVERY_ALERT":
# Good news
print(f"RECOVERY: Plants recovering at {location}")

# Run the listener
asyncio.run(listen_for_alerts())

Notification types (payloads)​

See the API Reference for the complete notification payload schema and examples.

Need help?​

For assistance with the Notification API, contact the Gardin Platform team via integrationsupport@gardin.ag.