Version 1.0
Last updated 5 October 2026
Webhooks let Water Rangers notify your service when data changes, instead of your service asking us repeatedly whether anything has changed. If you are currently polling an endpoint on a timer, this will reduce both your request volume and the delay before you see new data.
For context: one integration currently issues around 24,000 requests a day against our API to discover roughly 16 new E. coli results. The same coverage as a webhook is a few dozen deliveries.
Before you start
- An API key, requested through the data sharing documentation. The key must be associated with your user account.
- An HTTPS endpoint that accepts
POSTand responds with a 2xx status. - The endpoint must be reachable from the public internet. Private and loopback address ranges are rejected when the webhook is saved.
Creating a webhook
Webhooks are managed at /webhooks. Create one, choose the events you want, narrow it with filters, and send a test delivery before you rely on it.
Events
A webhook fires on one or more of the following. Choose only what you need, since every event you subscribe to is a delivery your endpoint has to handle.
observation.createdandobservation.updatedlocation.createdandlocation.updateddataset.createdanddataset.updated
Filters
Filters are optional and combine with one another. A webhook with no filters receives every matching event on the platform, which is rarely what you want.
- Parameters. Only records that include one of the parameters you list, for example
e_coli. - Datasets. Only records belonging to the datasets you select.
- Organisation. Only records belonging to a single organisation you are a member of.
- Countries. Only records whose location falls in the countries you list.
- Area. A latitude, longitude and radius in kilometres. All three are required together.
- Form template. Only records collected using the source templates you select.
What we send
Every delivery is a POST with a JSON body containing the event name and the record that triggered it.
POST https://your-endpoint.example.org/water-rangers
Content-Type: application/json
{
"event": "observation.created",
"payload": {
"id": "obs_456",
"observed_at": "2026-10-05T09:14:00.000Z",
"location_id": "loc_123",
"dataset_id": "ds_789",
"tested_parameters": ["E. coli", "Temperature"],
"readings": [
{ "parameter": "E. coli", "unit": "CFU/100ml", "value": "42" }
]
}
}
Treat the payload as the notification, not as the authoritative record. If you need the full object, or fields that are not present, request it from the matching API endpoint using the id.
Verifying the request came from us
Each webhook is issued a signing secret. We sign every delivery with it, so your endpoint can confirm the request is genuine and has not been replayed.
X-Waterrangers-Signature: sha256=<hex digest>
X-Waterrangers-Timestamp: 1759665240
The digest is an HMAC-SHA256 over the string "{timestamp}.{raw body}" using your signing secret. Compute the same value and compare the two using a constant-time comparison. Reject anything where the timestamp is older than a few minutes, which is what stops a captured request being replayed later.
# Ruby
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{raw_body}")
valid = ActiveSupport::SecurityUtils.secure_compare("sha256=#{expected}", signature)
Delivery, retries and failure
We allow 5 seconds to open the connection and 10 seconds for your endpoint to respond. Anything slower is treated as a failure, so acknowledge quickly and do the work afterwards rather than processing inline.
A failed delivery is retried 3 times with increasing gaps. If deliveries keep failing, the webhook disables itself after 5 consecutive failures and we email the owner. Nothing is queued while a webhook is disabled, so events during that period are not replayed when you switch it back on.
Each attempt is recorded with its response code, duration and any error, so you can see what your endpoint returned without adding logging on your side.
Testing
Use the test send on the webhook page. It posts a webhook.test event to your endpoint using the same signing and timeouts as a real delivery, and shows you the response. A test send does not count towards the failure limit.
Choosing between webhooks and polling
Webhooks suit anything event-driven: alerting, dashboards that should feel current, or syncing into another system. Polling remains the better fit when you want a periodic bulk snapshot, when you are backfilling history, or when your service cannot accept inbound requests.
If you are polling today, the straightforward migration is to keep your existing import for history, add a webhook for anything new, and reduce the polling interval sharply rather than removing it immediately.
Getting help
If deliveries are failing and the recorded responses do not explain why, get in touch and we can look at what we sent. Please include the webhook URL and roughly when you expected a delivery.