Lift JSON Sensor Connection (beta)
Use a Lift JSON connection when your own script, gateway or system already has the temperatures and you want to send them to OpenBoxes Lift. Each request is signed with the connection's secret, so Lift knows it came from you.
Lift shows a copy of your logger's record. Use your logger's own record for decisions about your vaccines and medicines, and for inspections. Lift is not a validated system.
Sensor connections are in beta on every plan, at no extra charge during the beta. See Temperature Loggers for how readings, alert rules and alerts work.
1. Add the connection in Lift#
- In the customer portal, open Devices & sensors > Sensors > Connections. Only the account owner or an Admin can add a connection.
- Choose Add a connection, pick Lift JSON (signed) and give it a name.
- Lift shows the connection's web address and its secret. Copy the secret now: Lift shows it only once.
The web address looks like https://api.openboxes.cloud/api/v1/sensors/ingest/<connection id>. Keep the secret out of source code and logs.
2. Build the body#
Send a JSON object with a readings list:
{
"readings": [
{ "device": "fridge-1", "at": "2026-10-08T12:00:00Z", "temp": 4.5 },
{ "device": "fridge-1", "at": "2026-10-08T14:15:00+02:00", "temp": 40.1, "unit": "F" }
]
}
device: your name for the sensor, 1 to 64 characters, no spaces.at: when the reading was taken, in ISO 8601 with a time zone (Zor an offset such as+02:00). A time without one is not stored.temp: the temperature, as a number.unit:"C"or"F". Leave it out for °C.
Send at most 500 readings in one request, and keep the body under 256 KB. Other fields are ignored. Readings more than 35 days old, or more than 10 minutes in the future, are not stored. If the same device sends the same time twice, the first value is kept.
3. Sign the request#
Put this header on every request:
X-Lift-Signature: t=<unix time in seconds>,v1=<signature>
The signature is the HMAC-SHA256 of <t>.<body> (the same Unix time, a full stop, then the exact body you send), keyed with the connection's secret and written in hex. Lift accepts a request only within 5 minutes of the time in t, and accepts each signed request only once, so sign each request when you send it and keep your clock right.
An example with curl and openssl (replace the placeholders with your own values):
SECRET='<your connection secret>'
URL='https://api.openboxes.cloud/api/v1/sensors/ingest/<connection id>'
BODY='{"readings":[{"device":"fridge-1","at":"2026-10-08T12:00:00Z","temp":4.5}]}'
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -X POST "$URL" \
-H 'Content-Type: application/json' \
-H "X-Lift-Signature: t=$T,v1=$SIG" \
--data-binary "$BODY"
Sign the exact bytes you send. If your code re-formats the JSON after signing, the signature no longer matches.
Ready-made recipes#
These recipes sign and send requests exactly as above, so you do not write the signing yourself. Each is a recipe you copy and adapt: Lift does not run Node-RED or OpenFn for you. Send a test reading first.
- Node-RED: one function node in front of an http request node.
- OpenFn: one job step on the http adaptor.
- A Node.js script: sends the readings in a JSON file, with nothing to install.
4. Accept the device#
Lift does not store readings from a device until it is accepted. After your first request, open the connection's Devices in the portal: the device waits there. Choose Accept readings, and give the logger a name if you like. Readings sent before you accept it are not added. Then place the logger at its OpenBoxes location on Locations and give it an alert rule.
What Lift answers#
200 with a count for each outcome. Every reading in the body lands in exactly one of them, so they add up to
received. The answer never echoes a device, a value or anything else from the body:{ "received": 3, "stored": 3, "duplicate": 0, "conflict": 0, "unknownDevice": 0, "inactiveDevice": 0, "tooOld": 0, "future": 0, "invalid": 0, "overDailyLimit": 0, "unknownUnit": 0, "ignored": 0 }stored: stored now.duplicate: already stored with the same value (for example, a retry).conflict: already stored with a different value. The first value is kept.unknownDevice: from a device not yet accepted on this connection. It waits under Devices and is not stored.inactiveDevice: from an accepted device whose logger is turned off.tooOld: more than 35 days old, or older than Lift keeps.future: more than 10 minutes in the future.invalid: a missing or unusable field (no device, no time with a time zone, or no plausible temperature).overDailyLimit: over your account's limit of 200,000 readings a day (a UTC day).unknownUnit: in a unit other thanCorF.ignored: not a temperature reading. A Lift JSON reading is never counted here.
400 when the body is not valid JSON in this shape, or has more than 500 readings.
401 when the signature, the connection or its secret is not valid, the time in
tis more than 5 minutes away, or the request was already received.413 when the body is larger than 256 KB.
429 when Lift is busy. Wait the number of seconds in the
Retry-Afterheader and send the same readings again.
Good to know#
- An account can have up to 10 connections, each with up to 200 accepted devices.
- Make a new secret keeps the old secret working for 24 hours while you update your system. If a secret may have leaked, use Turn off instead: it stops the connection at once. Then add a new one. A connection can't be deleted: once turned off, it stays in the list, below the active ones.
- Temperature alerts are a second channel, not a replacement for your device's own alarm.
Related#
- Cold chain overview: the beta on one page, with every way to bring readings in and how temperature alerts work.
- Temperature Loggers: uploads, alert rules, excursions and temperature alerts.