Skip to content

Enterprise API

ChillCheck provides a read-only REST API for Enterprise plan customers. Use it to pull temperature data, alerts, and audit logs into your own BI tools, food safety management systems, or spreadsheet integrations.

Enterprise plan required

API access is only available on the Enterprise plan. Contact hello@chillcheck.online to upgrade.


Authentication

All API requests must include an API key in the Authorization header:

Authorization: Bearer cc_live_<your_key>

Generate and manage API keys in Settings → API. Keys are organisation-scoped — a single key provides read access to all sites and cabinets within your organisation.

Keep your key secret

API keys grant read access to all your monitoring data. Treat them like passwords. The full key is only shown once at generation — store it somewhere secure (a password manager or secrets manager). If a key is compromised, revoke it immediately and generate a replacement.


Base URL

https://app.chillcheck.online/api/v1

Response format

All endpoints return a JSON object with data and meta:

{
  "data": [ ... ],
  "meta": {
    "count": 4820,
    "limit": 1000,
    "page": 1,
    "has_more": true
  }
}
Field Description
data Array of resource objects
meta.count Total matching rows (across all pages)
meta.limit Page size used
meta.page Current page number
meta.has_more true if more pages exist

Pagination

Use ?limit=N&page=N to paginate:

GET /api/v1/readings?cabinet_id=abc&limit=500&page=2
  • Default limit: 1,000 rows
  • Maximum limit: 10,000 rows per request
  • Pages are 1-indexed

Error responses

Status Meaning
401 Missing, invalid, or revoked API key
403 Organisation is not on an Enterprise plan
500 Internal error — contact support if persistent

Endpoints

GET /api/v1/sites

List all sites in your organisation.

Query parameters: none

Example response:

{
  "data": [
    {
      "id": "uuid",
      "name": "Riverside Kitchen",
      "address": "12 Broad Street, Bristol",
      "postcode": "BS1 2EP",
      "timezone": "Europe/London",
      "created_at": "2026-01-15T09:00:00Z",
      "updated_at": "2026-01-15T09:00:00Z"
    }
  ],
  "meta": { "count": 3 }
}

GET /api/v1/cabinets

List active cabinets, optionally filtered by site.

Query parameters:

Parameter Type Description
site_id UUID Filter to a specific site

Example response:

{
  "data": [
    {
      "id": "uuid",
      "site_id": "uuid",
      "name": "Walk-in Fridge",
      "type": "fridge",
      "location": "Main kitchen",
      "target_temp": 4,
      "warn_low": 1,
      "warn_high": 6,
      "crit_low": -1,
      "crit_high": 8,
      "notes": "Serves prep and cold storage",
      "active": true,
      "alerts_muted_until": null,
      "created_at": "2026-01-15T10:00:00Z",
      "updated_at": "2026-04-20T14:00:00Z"
    }
  ],
  "meta": { "count": 5 }
}

GET /api/v1/readings

Temperature readings, newest first. Default 1,000 rows per page.

Query parameters:

Parameter Type Description
cabinet_id UUID Filter to a specific cabinet
site_id UUID Filter to a specific site
from ISO 8601 Start of time range (inclusive), e.g. 2026-05-01T00:00:00Z
to ISO 8601 End of time range (inclusive)
source sensor | manual Filter by reading source
limit integer Rows per page (max 10,000)
page integer Page number (1-indexed)

Example:

GET /api/v1/readings?cabinet_id=abc123&from=2026-05-01T00:00:00Z&to=2026-05-31T23:59:59Z

Example response:

{
  "data": [
    {
      "id": "uuid",
      "cabinet_id": "uuid",
      "site_id": "uuid",
      "sensor_id": "uuid",
      "temperature": 4.2,
      "recorded_at": "2026-05-31T14:32:00Z",
      "synced_at": "2026-05-31T14:32:05Z",
      "source": "sensor",
      "method": null,
      "notes": null
    }
  ],
  "meta": {
    "count": 44640,
    "limit": 1000,
    "page": 1,
    "has_more": true
  }
}

Fetching all readings for a time range

For large exports, loop through pages until has_more is false:

page = 1
all_readings = []
while True:
    r = requests.get(f"{BASE}/readings?cabinet_id={cabinet_id}&from={from_ts}&limit=10000&page={page}", headers=headers)
    body = r.json()
    all_readings.extend(body["data"])
    if not body["meta"]["has_more"]:
        break
    page += 1

GET /api/v1/alerts

Alerts (temperature breaches, sensor/device offline), newest first.

Query parameters:

Parameter Type Description
cabinet_id UUID Filter to a specific cabinet
site_id UUID Filter to a specific site
status open | resolved | all Default all
severity warning | critical Filter by severity
from ISO 8601 Filter by triggered_at ≥ this value
to ISO 8601 Filter by triggered_at ≤ this value
limit integer Rows per page (max 10,000)
page integer Page number (1-indexed)

Example response:

{
  "data": [
    {
      "id": "uuid",
      "cabinet_id": "uuid",
      "site_id": "uuid",
      "sensor_id": "uuid",
      "type": "high_temp",
      "severity": "critical",
      "temperature": 9.4,
      "message": "Walk-in Fridge reached 9.4°C (critical high: 8°C)",
      "triggered_at": "2026-05-30T22:14:00Z",
      "resolved_at": "2026-05-30T22:52:00Z",
      "acknowledged_at": "2026-05-30T22:18:00Z",
      "acknowledged_by": "Jane Smith",
      "root_cause": "door_left_open",
      "acknowledgment_note": "Found door ajar after delivery",
      "escalation_level": 1
    }
  ],
  "meta": { "count": 14, "limit": 1000, "page": 1, "has_more": false }
}

Alert types:

Type Description
high_temp Temperature above critical or warning high threshold
low_temp Temperature below critical or warning low threshold
sensor_offline Sensor not reporting
device_offline Hub not reporting
low_battery Sensor battery below threshold
low_signal Sensor signal below threshold
predictive_drift Temperature trending towards a breach (predictive, Pi-side)

GET /api/v1/audit

Immutable audit log of all user and system events, newest first. Useful for compliance reporting.

Query parameters:

Parameter Type Description
action string Filter by action prefix, e.g. alert. or cabinet.
from ISO 8601 Start of time range
to ISO 8601 End of time range
limit integer Rows per page (max 5,000)
page integer Page number (1-indexed)

Example:

GET /api/v1/audit?action=alert.&from=2026-05-01T00:00:00Z

Example response:

{
  "data": [
    {
      "id": "uuid",
      "profile_id": "uuid",
      "action": "alert.acknowledged",
      "metadata": {
        "alert_id": "uuid",
        "cabinet_name": "Walk-in Fridge",
        "root_cause": "door_left_open"
      },
      "created_at": "2026-05-30T22:18:00Z"
    }
  ],
  "meta": { "count": 312, "limit": 500, "page": 1, "has_more": true }
}

Common audit actions include alert.raised, alert.resolved, alert.acknowledged, cabinet.created, cabinet.updated, sensor.assigned, user.invited, api_key.created, api_key.revoked, and more.


Code examples

Python (requests)

import requests

BASE = "https://app.chillcheck.online/api/v1"
HEADERS = {"Authorization": "Bearer cc_live_your_key_here"}

# List sites
sites = requests.get(f"{BASE}/sites", headers=HEADERS).json()

# Fetch readings for a cabinet, last 7 days
from datetime import datetime, timedelta, timezone
from_ts = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()

readings = requests.get(
    f"{BASE}/readings",
    params={"cabinet_id": "your-cabinet-uuid", "from": from_ts, "limit": 10000},
    headers=HEADERS,
).json()
print(f"Fetched {readings['meta']['count']} readings")

curl

API_KEY="cc_live_your_key_here"
BASE="https://app.chillcheck.online/api/v1"

# List sites
curl -H "Authorization: Bearer $API_KEY" "$BASE/sites"

# Open alerts for a site
curl -H "Authorization: Bearer $API_KEY" \
  "$BASE/alerts?site_id=your-site-uuid&status=open"

# Readings for a date range
curl -H "Authorization: Bearer $API_KEY" \
  "$BASE/readings?cabinet_id=your-cabinet-uuid&from=2026-05-01T00:00:00Z&to=2026-05-31T23:59:59Z"

Google Sheets (importdata workaround)

For simple use cases, you can proxy the API via a small script or use Apps Script to import data directly into a Google Sheet using the UrlFetchApp service.


Managing API keys

API keys are managed in Settings → API (Enterprise plan accounts only).

  • Generate a new key — Enter a descriptive name (e.g. "BI dashboard", "Zapier"). The full key is shown once — copy it immediately.
  • Revoke a key — Click Revoke next to any key. Revocation is immediate; any integration using the key will receive 401 responses.
  • Keys do not expire automatically. Create a rotation policy appropriate for your security requirements.

Only organisation owners can generate and revoke keys. Admins and members can view the key list (prefixes only — not the full key).