How to monitor particular kit with Oversight: which sensor to use, where on the device to set up access, which user function and extraction rows read the response, and how to write the rules that turn it into an alarm. Search for a vendor, a product or an error message. A misspelt word still finds its article when nothing matches exactly.
A pushed reading sensor is for a system that no probe can check and no agent will run on: an old Unix box, an appliance with a shell, a batch job, anything that can make a web request. The system judges itself and sends what it found to Oversight's API. From there the reading is treated exactly as a probe's would be: its values go into slots, rules decide its state, actions tell whoever needs telling, and when the readings stop coming the sensor goes STALE.
The difference from every other sensor is who starts the conversation. A probe asks; here the system tells. So the interval is not how often anything is checked, it is how often a reading is expected, and silence is itself the alarm.
Before you start
- An API key made for pushing. Create one under Setup, API with Use set to Pushes readings. A push-only key cannot read anything, so a copy left in a script on the watched system gives nothing else away. Reads and pushes also works, if one key for everything suits you better. The key is shown once, when it is made.
- Lock the key to the sender's address if you can, under Addresses when creating it. It is optional, but a key that only works from one address is much less use to anyone who finds it.
- The sensor's UUID. Add a sensor of type Pushed reading to the device, and its panel shows the address to send to, with the UUID already in it.
Sending a reading
Every reading goes to the same address, with the key in an X-API-Key header and the sensor named by sensor=:
https://oversight.im/api/v1/push?sensor=<the sensor's UUID>
There are three ways to send the reading itself, so whatever the system has will do:
| How | What arrives |
|---|---|
| POST a JSON object | Its top-level values. Use this wherever curl is available. |
| GET with parameters | Every parameter but sensor becomes a field, so &status=OK&queue=12 arrives as {"status":"OK","queue":"12"}. For a box with wget and nothing better. |
| POST form fields | The same as a GET. |
Whichever way it comes, a reading is a flat set of named values, and it is checked strictly:
- Names are letters, digits,
_and-, up to 64 characters. A field with any other name, a dot included, is dropped. - Text keeps only letters, digits, spaces and
_ - @ ! £ $ . , ( ), and is cut to 255 characters on one line. Anything else, quotes, semicolons, slashes, colons and angle brackets among them, is removed. Sendmaintenance, notsvc:/print/server: maintenance. - Numbers stay numbers, and
trueandfalsebecome 1 and 0. - Anything nested, an object or a list inside the reading, is dropped. A JSON list on its own, or a body that is neither JSON nor form fields, is refused.
With curl:
curl -s -H "X-API-Key: ovs_..." -H "Content-Type: application/json" \
-d '{"status":"OK","queue":12}' \
"https://oversight.im/api/v1/push?sensor=..."
With wget:
wget -q -O - --header="X-API-Key: ovs_..." \ "https://oversight.im/api/v1/push?sensor=...&status=OK&queue=12"
A worked example, a Solaris print service checked from cron every five minutes:
#!/bin/sh # Report the print service's state and how many jobs are waiting. STATE=`svcs -H -o state svc:/application/print/server:default` QUEUE=`lpstat -o 2>/dev/null | wc -l | tr -d ' '` wget -q -O /dev/null --header="X-API-Key: ovs_..." \ "https://oversight.im/api/v1/push?sensor=...&status=$STATE&queue=$QUEUE"
The sensor's interval would be 300, to match the cron entry.
Reading it
A pushed reading fills no slots by itself. Say what each field means in Extraction: a row with the method JSON and the field's name as its expression. For the example above, SD01 JSON status named Service, and SV01 JSON queue named Queue. Values sent as text, as every GET parameter is, are still numbers to a numeric slot as long as they look like one. Because nothing is nested, the expression is always just the field's name.
Then write Rules on the slots as for any other sensor: SD01 not equal to online gives CRIT, SV01 greater than 50 gives WARN. What the fields are called and what counts as bad is entirely yours. If the script sends "banana":"red", extract it and write banana equal to red gives CRIT.
The reply to each push says what was read into which slot, so the easiest way to set a sender up is to run it by hand and look:
{"endpoint":"push","sensor":"...","status":"stored","ts":"2026-09-24T18:15:41.748Z",
"unreadable":0,"slots":[{"series":"SD01","name":"Service","value":"online"},
{"series":"SV01","name":"Queue","value":"12"}]}
unreadable is 1 when an extraction row found nothing to read, usually a field the sender left out or spelt differently. A reading in which no row found anything is recorded as a parse failure, and the sensor shows it cannot tell rather than calling it OK.
Awkward output
The strict rules are deliberate, and there are no exceptions to them. A reading is meant to be a verdict and a few figures, not a copy of whatever the system prints. A line like this one:
USR-4821 Zoë Müller's résumé — «café ☕ & croissants», 温泉テスト ✓ 🚀🚀 © 2026 €45.50 ß
arrives as something like USR-4821 Zo Mllers rsum caf amp croissants, x1F680 2026 45.50: accents, symbols, other scripts, emoji and punctuation all gone, and nothing a rule could sensibly test.
So shape the output on the system before it is sent, with whatever tools that system has: pick out the values that matter, give each a plain name, and reduce each to plain words and numbers, such as status=online and queue=12. How to do that on a particular system is for whoever looks after it. If you would rather GEN did it, we can, charged at our hourly service rate.
Timing and STALE
- The interval is the promise. Set it to how often the sender sends, 60 seconds at the least. A sensor that hears nothing for one and a half intervals plus a minute goes STALE, and says a reading was not pushed in time. With a five minute interval that is eight and a half minutes, enough to absorb a cron job that runs late.
- Our clock, not the sender's. A reading is timed when it arrives, so it lines up with every other reading in the estate, and a system whose clock has drifted cannot put its readings in the wrong place.
- A bare push is a heartbeat. On a sensor with no extraction rows, a push with nothing in it is heard and is OK. That alone shows a job ran: put the call at the end of a backup script and the sensor goes STALE on the night the backup does not finish.
HTTP, HTTPS and the mesh
Use https:// wherever the system can. Some older systems cannot manage current TLS at all, so plain http:// is accepted too, but with a clear warning: over HTTP everything is readable by anyone on the path, the API key included. The reading may only be the state of a print spooler, but whoever reads the key can send readings of their own to any sensor that key reaches. The measures, best first:
- GEN's private mesh. GEN can join the system to its encrypted mesh network, so the reading travels privately even over plain HTTP. This is charged separately; ask GEN.
- A key locked to the sender's address and limited to the sender's branch. A stolen key is then only good from that address, and only for the sensors under it.
- A separate key for each system. If one is exposed, revoking it stops nothing else.
What it costs
Each reading stored costs 2 credits, the same as a probe's reading, plus the usual extras for storing the raw response or using a user function. A reading refused, for a bad key or a sensor that does not exist, costs nothing. One key may push up to 300 readings a minute across all its sensors.
When a push is refused
| Reply | Meaning |
|---|---|
unauthorised | No key, or not a valid one. Check the header name is X-API-Key. |
address | The key is locked to other addresses than the one the push came from. |
purpose | The key was made for reading only. Make one that pushes. |
not_found | No sensor with that UUID that this key can reach: a typing mistake, a deleted sensor, or a key limited to another branch. |
not_push | The UUID is of a sensor that a probe or GEN runs, which cannot be pushed to. |
rate | More than 300 pushes in a minute on one key. |
A push to a sensor that a schedule has switched off is answered with "status":"off" and not recorded, so a script running out of hours does no harm.