Skip to main content

For integrators

Every number carries the band it was measured over.

These endpoints turn a photograph into millimetres, a pin into a screen, and a frame into a redacted frame. What makes them usable in a compliance record is not the numbers — it is that each one states how wrong it can be, and refuses outright when the photograph cannot answer. This page is generated from the running service, so it cannot drift from what the API actually does.

basehttps://geo.qa/api
indexGET /api/partner
boardGET /api/probes
keyGET /api/verify/key
in-regionGET /api/partner/dist

§ 1

Endpoints

agents integrating geo.qa perception into their own pipeline — 10 endpoints, as the service lists them now.

POST
/api/produce/measure — measure produce in a photograph against a marketing standard
needsimage_base64 (or image_url); mm_per_px + scale_basis for metric
returnslength along the convex face, grade at the mid-point, colour stage, blemish fraction, slenderness, and the accuracy of each
refusesmillimetres without a scale anchor, and a scale without a stated origin
standardCommission Delegated Reg. (EU) 2023/2429 Annex I Part B Part 11
measured onwith the card in frame the photograph is RECTIFIED onto the card's plane before anything is measured, so one pixel is one distance in every direction. A local scale alone fixes magnitude and not anisotropy: measured, the image-plane length ran +2.6% at 5 degrees of camera tilt and +23% at 25, against 0.2%-1.4% on the plane
receiptevery reply carries an ed25519 receipt binding the image bytes, the numbers returned and that model hash, plus emem's signed log head as a time bound. `verify` in the same reply is the five-step offline recipe; none of steps 1-4 asks this host whether the answer is yes
POST
/api/redact/people — mosaic every person in a photograph before it is stored
needsimage_base64; optional floor (default 0.05)
returnsthe redacted image, what was redacted, what sat near the floor, and whether the floor was actually reached
refusesto call the result safe. It is a COCO person detector, not an anonymiser: it does not see a face in a mirror, a name on a shirt, or a hand holding an identity document
POST
/api/stack/count — count the crate faces in a stack photograph
needsimage_base64
returnsthe count per axis, the pitch it measured, and when it abstains, which of the three reasons
refusesto count a load with no lattice, a stack with a row part out of frame, or a photograph taken at an angle -- the last one comes back as retake: square_on
POST
/api/ppe/spraying — read a spray photograph for respirator, gloves and coverall, keeping no face -- ppe_spraying@1, eudr#172
needsimage_base64
returnseach of the three as present / absent / not_visible, with the reason whenever it is not_visible; a signed receipt over the image bytes and the verdicts
POST
/api/prescreen — rank candidate farms from their pins, before anyone drives out
needspins[] with lat and lng; optional weights
returnsthe pins worth visiting in order, the ones disqualified by a post-cut-off loss year removed from the ranking rather than scored low, and what could not be read at each
refusesa readiness window -- that needs a season of NDVI the shared memory does not hold at an unseeded cell -- and a value range, which is your schedule against your market
POST
/api/water/distance — find the nearest recurring surface water, for a spray buffer
needslat, lng; optional radii_m and bearings
returnsa distance BRACKET and the bearing, plus every ring it read
refusesa single metre. The rings are sampled, so water lies between two radii and saying 412 m would be precision invented out of a sampling grid
POST
/api/crop/agree — check whether a photograph could be of the field it claims
needsimage_base64, lat, lng; taken_at strongly recommended
returnsconsistent / disagrees / cannot_say, with the reason, and needs_a_person on a disagreement
refusesto compare the colour stage against a vegetation curve. That needs a season of NDVI the shared memory does not hold at an unseeded cell, and a curve fitted to one point would be the failure this check exists to catch
POST
/api/decide — rank candidates on typed features, renormalising over what is known
needscandidates[].features{value,known}, weights{}
returnsranking, per-feature contributions, margin, decisive, and what was unmeasured
refusespricing a feature nobody measured
GET
/api/produce/card.png — the printable scale card — print at 100%, lay it flat beside the fruit, and send the photo with no mm_per_px
needsnothing. `?dpi=` to match a printer
returnsa PNG. The marker spacing is printed ON the card so it can be checked against a ruler before a season of measurements is taken with it
GET
/api/produce/selftest — score the measurement against geometry whose answer is exact, live, and return every case
needsnothing
returnsper-sweep measured vs true, the band those errors span, the published band they are checked against, and the recipe to rebuild the fixture yourself

§ 2

The board you can gate on

holds19
fails0
undetermined4
stalefalse
age0s

Three states, not two. undetermined is not a failure — it means we could not find out, because an upstream archive was unreachable or somebody else’s box was slow. Gate on fails, not on the absence of green, or an outage you do not own will hold your release.

Not holding right now

?
witness-is-a-vouched-operator — geo.qa's co-signature counts as an independent OPERATOR on emem.dev.
?
receipts-carry-a-time-bound — a receipt we sign now is bounded by somebody else's clock, not only ours.
?
prescreen-reads-real-pins — /prescreen reads real coordinates and reports what it could not read.
?
crop-agreement-separates-three-answers — /crop/agree keeps `disagrees` and `cannot_say` apart on the live route.

fail on counts.fails > 0. Do NOT fail on counts.undetermined: that means the check did not run, which is a different fact from the claim being broken, and a board that reds on both is a board people stop reading. DO fail on `stale` -- a cached green whose refresh has died is the one answer worse than a red

§ 3

Run it in your region

Calling these endpoints sends the photograph across a border. For a photo of a person that is the thing the requirement is trying to avoid, so the same code ships as a wheel you install where the photograph already is.

GET
/api/partner/dist. The wheel, the detector weights, and a sha256 for each. Pin both — a redaction is only as trustworthy as the weights, and those do not change when the version does.

model_identity() returns two hashes: the module alone, identical on any machine, and one that also pins OpenCV and NumPy. Both are computed, never declared — an OpenCV upgrade can move a distance transform while the source is untouched, and a version string somebody remembers to bump would not show it.

Full reference in the docs, § 6.