Convert an extraction into a GeoPackage
const url = 'https://example.com/v1/geopackage';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"document":"forsyth_3210_350.pdf","engine_version":"0.4.0","extraction":{"basis_of_bearings":"Deed Book 1150, Page 235","document_kind":"deed","legal_description_found":true,"tracts":[{"calls":[{"bearing":{"degrees":87,"ew":"E","minutes":30,"ns":"N","seconds":0},"call_type":"line","confidence":"high","distance":150,"monument":"iron stake","verbatim_text":"North 87 deg. 30\' East 150.0 feet"}],"distance_unit":"feet","is_exception":false,"name":"TRACT 1","pob_description":"BEGINNING at an iron stake in the eastern margin of Hinshaw Avenue","stated_acreage":0.17}]},"groundtruth_verdicts":[{"closure_ratio":"1:96,419","computed_acres":0.17,"geometry":{"acres":0.1702,"closure_denominator":96418.6,"misclosure":0.0062,"perimeter_ft":600,"vertices":[[0,0],[149.86,6.54],[0,0]]},"reasons":[],"status":"PASS","tract":"TRACT 1"}],"model":"claude-opus-5","usage":{"input_tokens":7373,"output_tokens":1140,"seconds":16.4}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/geopackage \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "document": "forsyth_3210_350.pdf", "engine_version": "0.4.0", "extraction": { "basis_of_bearings": "Deed Book 1150, Page 235", "document_kind": "deed", "legal_description_found": true, "tracts": [ { "calls": [ { "bearing": { "degrees": 87, "ew": "E", "minutes": 30, "ns": "N", "seconds": 0 }, "call_type": "line", "confidence": "high", "distance": 150, "monument": "iron stake", "verbatim_text": "North 87 deg. 30'\'' East 150.0 feet" } ], "distance_unit": "feet", "is_exception": false, "name": "TRACT 1", "pob_description": "BEGINNING at an iron stake in the eastern margin of Hinshaw Avenue", "stated_acreage": 0.17 } ] }, "groundtruth_verdicts": [ { "closure_ratio": "1:96,419", "computed_acres": 0.17, "geometry": { "acres": 0.1702, "closure_denominator": 96418.6, "misclosure": 0.0062, "perimeter_ft": 600, "vertices": [ [ 0, 0 ], [ 149.86, 6.54 ], [ 0, 0 ] ] }, "reasons": [], "status": "PASS", "tract": "TRACT 1" } ], "model": "claude-opus-5", "usage": { "input_tokens": 7373, "output_tokens": 1140, "seconds": 16.4 } }'Turn a /v1/extract response into a GeoPackage.
The GIS twin of /v1/dxf, and it holds the same bargain: stateless
(the customer posts back the JSON they already hold), free, and
authenticated only — the extraction was already paid for, and a
customer at their cap can still collect work they bought.
Local-plane tracts land under GeoPackage’s undefined Cartesian SRS (POB at the origin, feet); aliquot tracts the PLSS fabric located additionally land in a WGS 84 layer that opens placed.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
The extracted legal description.
object
object
object
object
The verification layer’s judgement on one tract.
Index-aligned with extraction.tracts: entry i judges tract i, and
the arrays are always the same length. Clients depend on that pairing.
This is the authoritative verdict. A client is not expected to recompute it — the rules and tolerances behind it are server-side.
object
PASS: traverse closes and corroborates stated acreage. REVIEW: something disagrees — a human decides. FAIL: the traverse could not be computed.
The unit the document measured in: “feet”, “meters”, “varas” or “chains”. Distances in extraction are as recorded, in this unit; the computed geometry is always feet, converted once.
The computed traverse, in its own local plane: POB at the origin, feet.
Everything a client does with a placed tract — previewing it under the cursor, writing it to a layer — is an affine transform of these vertices, so returning them is what lets a client be fully interactive without carrying the traverse math itself.
object
One point per call endpoint plus the start: N calls give N+1 vertices.
Distance from the last computed point back to the start.
Aliquot geometry located on the earth: lon/lat rings from the BLM CadNSDI PLSS fabric, quartered by the deed’s aliquot chain.
object
Always “EPSG:4326” today.
Exterior ring(s), lon/lat pairs.
CadNSDI PLSSID of the resolved township/range.
Provenance of the fabric, e.g. “BLM CadNSDI”.
Where the server thinks a metes-and-bounds tract sits.
A suggestion, never a result. status stays REVIEW even when every check
agreed, because the operator confirms a position with the same click they
already make; the product does not place a parcel on anybody’s behalf.
Filled only by POST /v1/locate. An /v1/extract response always carries
null here.
object
Always “EPSG:4326” today.
Which method produced this: “plss_corner” today.
The monument this was worked from, in words.
CadNSDI PLSSID, when one was resolved.
An operator supplied a better reading of the document and this verdict was recomputed over it. The status means exactly what it always means; corrections on the response says what changed, from what, and by whom.
This tract’s geometry was fitted after the fact by a compass rule. The status above did not move: it judges the recorded description, which still does not close. The adjusted figure is a drawing and export aid, and adjustments says how much misclosure was distributed.
The verification layer’s judgement on a document that has no tracts.
A recorded plat and a utility as-built get identified rather than traced:
the boundary a plat carries is drawn linework on a scan, and an as-built
states no coordinate basis at all. Both leave groundtruth_verdicts empty,
so this block is where their verdict lives.
Never PASS. These classes cannot corroborate anything, and the reasons say which gap applies rather than leaving “REVIEW” to be guessed at.
object
What the document is, e.g. “Plat” or “As-built”.
REVIEW: identified, with reasons naming what could not be verified. FAIL: nothing in the sheet identifies it.
One line naming the sheet, for a list or a header.
object
Wall-clock seconds spent in the extraction call.
Echo of the document name, if one was sent.
True when an east_test_ key produced this: a fixed sample extraction, no model call, no usage recorded, nothing billed. Declared here rather than dropped, because the attestation signs the whole body and a field the model discards would break every certificate.
Operator corrections applied to this extraction, in the order they were made. Empty on every /v1/extract response; filled only by /v1/corrections. Inside the attestation, so the log cannot be stripped from a certified payload without detection.
One field an operator changed, and what it read before.
The extraction is never overwritten. A correction rides beside it as an attributed log, the certificate prints the diff, and the attestation covers both — so a reader downstream can always see what a person changed and what the document as extracted actually said.
before is load-bearing twice over: it is what makes the certificate diff
reconstructible without a second copy of the extraction, and it is the
staleness guard. A client holding an old copy of a re-extracted document
names a before that no longer matches and is refused rather than
silently overwriting a value nobody saw.
object
Index into extraction.tracts.
Index into that tract’s calls (or tie_calls).
What the field read before. Must match the current value or the correction is refused as stale.
What it should read. Same shape as before.
Which list call indexes.
Who made the change. Recorded, never verified.
When, ISO 8601 UTC. Server-stamped if omitted.
Why — e.g. “source text reads 149.5”.
Compass-rule adjustments applied to this extraction’s geometry. The verdicts above judge the recorded description and were NOT recomputed over these.
A disclosed best fit over courses that do not close.
This never moves a verdict. The recorded description still miscloses, and the verdict on the tract still says so; the adjusted geometry is a drawing and export aid. An adjustment whose size nobody downstream can see is the silent auto-close this product exists to refuse, which is why the misclosure it distributed is carried here and printed on the certificate.
object
Index into extraction.tracts.
The Bowditch rule: misclosure apportioned by course length.
Feet of misclosure that were distributed.
Feet of perimeter it was spread over.
What lets this response be certified later without being trusted.
POST /v1/certificate is stateless: you post back the response you already
hold. That endpoint recomputes this signature over the body it received and
refuses anything that does not match, so a certificate cannot be produced
by editing a verdict in a text editor. Keep the response as the API
returned it; re-serializing is fine, editing is not.
object
SHA-256 of the document as extracted, after any image-to-PDF normalization. Empty on a batch document submitted before this field existed.
UTC timestamp of the extraction, RFC 3339.
HMAC-SHA256 over the response body with attestation excluded, serialized with sorted keys and compact separators. Empty if the server had no signing key configured.
Responses
Section titled “Responses”A GeoPackage with tract and call layers for every tract with geometry, plus a WGS 84 layer for tracts the PLSS fabric located.
Missing, unknown, or revoked API key
object
object
Example
{ "error": { "documents_used": 200, "message": "Monthly document quota reached: 200 of 200 documents used this month. The cap resets on 2026-08-01. Email sales@easting.ai to raise it.", "monthly_cap": 200, "resets_on": "2026-08-01", "type": "quota_exceeded" }}Nothing in the extraction has geometry to draw
object
object
Example
{ "error": { "documents_used": 200, "message": "Monthly document quota reached: 200 of 200 documents used this month. The cap resets on 2026-08-01. Email sales@easting.ai to raise it.", "monthly_cap": 200, "resets_on": "2026-08-01", "type": "quota_exceeded" }}