Estate Ops
Estate Ops API
Read your estate's data from your own systems. Read-only, over plain web requests, in JSON, CSV or GeoJSON.
What it's for
The feed lets the systems you already use pull your estate's records straight from Estate Ops — an accounts package, a Power BI or Excel report, a map in QGIS — instead of someone exporting and re-typing them. It only ever reads. Nothing sent to the feed can change a record.
It comes with the API & data feed add-on, £350 a month per estate — see pricing.
Getting a key
An estate admin opens Admin → API access in the portal, switches the add-on on and chooses Create a key. Only estate admins can see that page.
- Give each system its own key, named after it, so one can be revoked without the others.
- The key is shown once, when it is created. Copy it then — it can't be shown again. Lose it, and you revoke it and make another.
- A key can be given an end date, and revoked at any time. Either stops it straight away.
- Switching the add-on off stops every key at once.
That page also shows your feed address — the start of every request below, written here as https://ourbcrppvxmdukfnemgc.supabase.co/functions/v1/data-feed/v1.
Signing requests
Send the key in an Authorization header on every request:
Authorization: Bearer YOUR_KEY
Treat a key like a password: keep it out of shared spreadsheets, emails and code that others can read. If one might have been seen, revoke it.
Listing datasets
curl -H "Authorization: Bearer YOUR_KEY" https://ourbcrppvxmdukfnemgc.supabase.co/functions/v1/data-feed/v1/datasets
Returns the datasets your key can read, by name. The list is live — it follows your key's scopes and grows as Estate Ops does — so it, not this page, is the reference for what is available.
Reading a dataset
GET https://ourbcrppvxmdukfnemgc.supabase.co/functions/v1/data-feed/v1/datasets/{name}?format=json&limit=1000&cursor=…&changed_since=…| Option | What it does |
|---|---|
format | json (the default), csv for spreadsheets, or geojson for maps. |
limit | How many rows to return, up to 1,000. |
cursor | Where to carry on from — the next_cursor from the page before. |
changed_since | Only rows changed after this time. Use the watermark from your last run (below). |
A JSON response looks like this:
{
"data": [ { "id": "…", … }, … ],
"next_cursor": "…",
"has_more": true,
"watermark": "2026-09-27T10:15:00Z"
}Paging. While has_more is true, ask again with cursor set to next_cursor.
CSV, for spreadsheets. Add format=csv. The file is the rows only; the next cursor comes back in the X-Next-Cursor response header. In Excel or Power BI, use Data → From Web, paste the address and add the Authorization header.
GeoJSON, for maps. Add format=geojson to a dataset that has a map position — pins, for example — and load it into QGIS or any GIS as a layer from a web address, with the Authorization header set.
Keeping a copy up to date
The feed serves rows once they have been unchanged for about two minutes, so a record being edited right now arrives on your next run rather than half-finished. Every response carries a watermark: the time it is safe to carry on from next time.
- First run: read the dataset with no
changed_since, page by page, and keep thewatermarkfrom the first page. - Save each row in your system by its id — add it if new, replace it if you have it.
- Next run: read with
changed_sinceset to the watermark you kept, and keep the new one. - Then ask for deletions since the same watermark (below) and remove those rows from your copy.
A row can occasionally come back twice across runs; saving by id makes that harmless.
Deleted records
GET https://ourbcrppvxmdukfnemgc.supabase.co/functions/v1/data-feed/v1/deletions?since=2026-09-27T10:15:00Z
Lists records deleted since that time — whether moved to Recently deleted or removed for good — so your copy can drop them. Deletions are kept for 90 days. Ask for anything older and you get a 410: read the dataset again from the start instead.
Records the law or your accounts rely on — game and cartridge records, sales, inspections and the like — are never deleted: they are voided with a reason. A voided record is listed here like any other deletion, with a void_reason saying why.
What a key can read
Each key is given scopes when it is created.
| Scope | What it covers |
|---|---|
core | Everyday estate records — everything that isn't staff, certificate or firearms data. Every key has it. |
staff | Staff hours, leave and personal records. |
certificates | The certificates register. Every read is recorded in the estate's audit trail. |
firearms | Armoury data. Needs a valid firearms certificate and the Armoury add-on. Every read is recorded in the estate's audit trail. |
Where people are is never included.The location of any person — a lone worker's position, say — is never served, whatever scopes a key has.
Limits
- Up to 1,000 rows a page.
- 120 requests a minute for each key. Past that you get a 429 — wait a minute and carry on.
- Rows appear about two minutes after their last change.
- Deletions are kept for 90 days.
Errors
| Code | What it means | What to do |
|---|---|---|
| 401 | The key is wrong, revoked or past its end date. | Check the header, or create a new key. |
| 403 | The API & data feed add-on is switched off. | An estate admin switches it on in API access. |
| 404 | No such dataset, or not one this key's scopes cover. | Check /datasets for the names your key can read. |
| 410 | The since time for deletions is more than 90 days ago. | Read the dataset again from the start. |
| 429 | Too many requests from this key. | Wait a minute, then carry on. |