Use JettyScript rules so sensors trigger AI agents — not the other way around.
jettyd includes a built-in rules engine called JettyScript. Rules evaluate every incoming telemetry value and fire actions when conditions are met — sending webhooks, alerts, device commands, or pushing data to an AI agent.
The standard pattern is: device publishes telemetry → rule fires → webhook hits your server → your server calls the Claude API. This inverts the usual polling loop: the sensor wakes the agent instead of the agent polling for changes.
Rules follow a when … then … syntax:
when <metric> <operator> <value> [cooldown <duration>] then <action>
temperature > 40 — thresholdstatus == "error" — equalitytemperature between 18 and 25 — rangesoil_moisture changes by 50 within 5m — rate-of-change detectiondevice goes offline — connectivity events
Add cooldown 10m (or 30s, 2h) to suppress repeated
firings. Without a cooldown, the rule fires on every matching telemetry message.
send alert "message" — create an alert eventsend webhook to https://… — POST to an external URLsend command <cmd> to self — issue a device commandled blink 500ms / relay on channel 1 for 30s — on-device actions (firmware required)Webhooks are created in the dashboard under Webhooks or via the API. Each webhook has a signing secret used to verify delivery authenticity.
curl -X POST https://api.jettyd.com/v1/webhooks \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Claude trigger",
"url": "https://your-server.example.com/jettyd-hook",
"events": ["alert.*", "telemetry.*"]
}'
The response contains a one-time secret field. Store it; you'll use it to
verify incoming requests.
Use these patterns in the events array:
* — all eventstelemetry.* — all telemetryalert.* — all alerts (info, warning, critical)device.offline — exact matchcommand.* — command lifecycle events
You can also filter by device ID, device group, or device type using the
filter_devices, filter_groups, and filter_device_types
fields.
Every delivery includes an X-Jettyd-Signature header with a hex-encoded
HMAC-SHA256 of the raw request body, keyed with your webhook secret.
import hmac, hashlib
def verify_jettyd_signature(payload: bytes, header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
This example fires a JettyScript rule when a temperature sensor exceeds 40 °C, posts to your server, and your server calls the Claude API to generate an alert summary and decide on an action.
when temperature > 40 cooldown 10m then send alert "High temperature"
In the dashboard go to Rules → Create Rule, paste the rule text, and optionally scope it to a specific device.
curl -X POST https://api.jettyd.com/v1/webhooks \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Temperature alert to Claude",
"url": "https://your-server.example.com/hook",
"events": ["alert.*"]
}'
Save the returned secret.
jettyd sends a POST with a JSON body like:
{
"event_id": "evt_01jx…",
"event_type": "alert.warning",
"tenant_id": "…",
"device": {
"id": "…",
"key": "dk_abc123",
"name": "Greenhouse sensor 1",
"device_type": "env-sensor"
},
"data": {
"level": "warning",
"message": "High temperature",
"metric": "temperature",
"value": 42.1
},
"occurred_at": "2026-06-12T10:34:22Z"
}
Your server verifies the signature, then calls the Anthropic API to let Claude interpret the alert and decide on a next action (e.g. send a command, page someone, or log the event).
import anthropic, json
def handle_jettyd_webhook(payload: dict) -> str:
client = anthropic.Anthropic()
device_name = payload["device"]["name"]
alert_msg = payload["data"]["message"]
metric = payload["data"].get("metric", "unknown")
value = payload["data"].get("value")
message = client.messages.create(
model="claude-opus-4-7",
max_tokens=256,
system=(
"You are an IoT monitoring assistant. "
"When a sensor fires an alert, provide a one-sentence diagnosis "
"and the single most important action to take."
),
messages=[{
"role": "user",
"content": (
f"Device '{device_name}' fired alert: {alert_msg}. "
f"Current {metric} reading: {value}. "
"What should I do?"
)
}]
)
return message.content[0].text
cache_control: {"type": "ephemeral"} to reduce latency and cost.
Parse Claude's response and call the jettyd command API to act on the device, or send the summary to a Slack channel or incident tracker.
import requests
def send_device_command(device_id: str, command: str, api_key: str):
requests.post(
f"https://api.jettyd.com/v1/devices/{device_id}/commands",
headers={"Authorization": f"Bearer {api_key}"},
json={"command_type": command, "payload": {}}
)
jettyd retries failed deliveries up to 3 times with exponential back-off. After 3 attempts
the delivery is marked dead_letter. You can inspect all delivery attempts in the
dashboard under Webhooks → Log, or via the API:
curl https://api.jettyd.com/v1/webhooks/{id}/deliveries \
-H "Authorization: Bearer $API_KEY"
In the device detail page open the Rules tab to see which rules apply and a time-ordered log of recent firings, including the action taken and result. You can also query the API:
curl https://api.jettyd.com/v1/devices/{id}/rules/logs \
-H "Authorization: Bearer $API_KEY"