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:
| Header | Description |
|---|---|
Gardin-Signature | HMAC-SHA256 signature of the payload for verification |
User-Agent | Identifies 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:
Validate the User-Agent header: Check that the
User-Agentheader starts with the stringGardin-Webhook/. Reject requests that do not match this pattern.Obtain your signing key: Retrieve your unique secret key from the Gardin Management Console. This key is used to verify the HMAC-SHA256 signature.
Compute the expected signature: Using your secret key, compute the HMAC-SHA256 signature of the raw request body.
Compare signatures: Compare your computed signature with the value in the
Gardin-Signatureheader. Only process the notification if they match exactly.Parse the notification: Once verified, parse the JSON payload. Check the
notificationTypefield to determine the notification type, then handle based on the specific fields available to that type.Return HTTP 200: Optionally return a
200 OKstatus code to acknowledge receipt.
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.