Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.nox.energy/llms.txt

Use this file to discover all available pages before exploring further.

Registering your webhook URL

Webhook endpoints are not registered through our API. To subscribe, contact us through support@nox.energy with your HTTPS callback URL and which events you wish to subscribe to. We will configure delivery on our side. You will use the same NOX API key as for API requests when verifying signatures. Keep it secret; see the Authentication guide.

Using the webhook

Your webhook endpoint receives HTTP POST requests with JSON payloads.
  • Return 2xx to acknowledge receipt.
  • Return 4xx for permanent failures (we will not retry).
  • Return 5xx or time out to trigger a retry.

Request format

HeaderDescription
Content-Typeapplication/json
X-Webhook-IdIdempotency key: {event_id}:{sub_event_type}. Use to deduplicate deliveries.
X-Webhook-TimestampUnix timestamp (seconds) when the webhook was signed. Required for signature verification.
X-Webhook-SignatureHMAC-SHA256 signature. Format: sha256= followed by the lowercase hex digest. The signature is created with your NOX API key.
Body (JSON):
{
  "type": "device.steerable_status.being_steered.updated",
  "timestamp": "2026-03-17T16:30:41Z",
  "id": "evt_b56ebdfe5ece4e16930c63b5",
  "data": {
    "value": true,
    "previous_value": false
  },
  "metadata": {
    "device_id": "749f2d79-7ba3-4c2d-8339-eee4d8ba0bef",
    "user_id": "45bc7bf6-576f-43cf-a0bc-8106ae93b59b",
    "external_user_id": "49db88ac-08b7-424b-9392-41cba24f3d1c",
    "brand": "Fictivia Appliances",
    "energy_supplier": "Acme Power Company"
  }
}
FieldDescription
idEvent ID
typeEvent type (see Subscribable event types)
timestampISO 8601 timestamp
data.valueNew value for the changed attribute
data.previous_valuePrevious value
metadata.external_user_idYour user ID for this user
metadata.user_idNOX user ID
metadata.device_idThe manufacturer ID of the device (can be null for user-scoped settings)
metadata.brandThe device manufacturer name
metadata.energy_supplierThe energy supplier name

Signature verification

Your NOX API key is the webhook signing secret. Never log it, expose it in client-side code, or commit it to a repository.
Verify each request as follows:
  1. Replay protection: Reject if X-Webhook-Timestamp is older than 5 minutes compared to your server clock.
  2. Signed payload: Reconstruct {timestamp}.{raw_body} where raw_body is the exact bytes received (the raw HTTP body).
  3. HMAC: Compute HMAC-SHA256(signed_payload, secret) using your NOX API key as secret. Compare to X-Webhook-Signature (strip the sha256= prefix).
The body is canonical JSON (sort_keys=True, no extra whitespace). Use the raw request body as received; do not re-serialize before verifying. Python example:
import hmac
import hashlib
import time

def verify_webhook(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False
    signed = f"{timestamp}.{raw_body.decode()}"
    expected = "sha256=" + hmac.new(secret.encode(), signed.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Subscribable event types

Event typeDescriptiondata.value type
device.steerable_status.learning_period_ended.updatedLearning period ended changedboolean
device.steerable_status.being_steered.updatedBeing steered status changedboolean
device.steerable_status.needs_reauthentication.updatedRe-authentication needed changedboolean
device.steerable_status.steerable.updatedSteerable status changedboolean
device.steerable_status.general_reason.updatedGeneral reason changedstring or null
device.steerable_status.dhw_reason.updatedDHW (domestic hot water) reason changedstring or null
device.steerable_status.room_reason.updatedRoom heating reason changedstring or null
device.settings.optimization.pv_self_consumption.updatedPV self-consumption (optimization) changedboolean or null
device.settings.optimization.dynamic_tariff.updatedDynamic tariff (optimization) changedboolean or null
device.settings.optimization.flex_trading.updatedFlex trading (optimization) changedboolean or null
device.settings.optimization.weather_predictive_control.updatedWeather predictive control (optimization) changedboolean or null
device.settings.general.silence_mode.updatedSilence mode changedboolean or null
device.settings.dhw.temperature_upper_bound.updatedDHW upper temperature bound changednumber or null
device.settings.dhw.temperature_lower_bound.updatedDHW lower temperature bound changednumber or null
device.settings.room.preferred_temperature.updatedRoom preferred temperature changednumber or null
device.settings.room.temperature_upper_bound.updatedRoom upper temperature bound changednumber or null
device.settings.room.temperature_lower_bound.updatedRoom lower temperature bound changednumber or null
device.settings.room.temperature_control_mode.updatedRoom temperature control mode changedstring or null
device.settings.room.schedule_enabled.updatedRoom schedule enabled changedboolean or null
user.settings.pv_self_consumption.updatedLegacy alias, emitted alongside device.settings.optimization.pv_self_consumption.updated. Kept for backward compatibility.boolean or null
user.settings.dynamic_tariff.updatedLegacy alias, emitted alongside device.settings.optimization.dynamic_tariff.updated. Kept for backward compatibility.boolean or null
user.settings.flex_trading.updatedLegacy alias, emitted alongside device.settings.optimization.flex_trading.updated. Kept for backward compatibility.boolean or null