Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Extract a legal description from a recorded document

POST
/v1/extract
curl --request POST \
--url https://example.com/v1/extract \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/pdf' \
--header 'X-Document-Name: forsyth_3210_350.pdf' \
--header 'X-Upload-Id: upl_9dK2mQxLp7ZvR4tYbN6w' \
--data binary
X-Document-Name
Any of:
string

Filename to record in the usage ledger and echo back. Optional; multipart uploads take it from the file part instead.

X-Upload-Id
Any of:
string

A slot from POST /v1/uploads, for a document too big to send in one request. Send an empty body with it. The staged object is deleted as it is read.

The recorded document: PDF, TIFF (including multi-page), PNG, or JPEG. Images are converted to PDF on arrival. Maximum 4.5 MB, which is what the hosting platform will carry in one request; stage anything larger through POST /v1/uploads.

Raw PDF bytes. Pair with an X-Document-Name header.

string format: binary

Successful Response

Media typeapplication/json
ExtractResponse
object
extraction
required
Extraction

The extracted legal description.

object
document_kind
required
string
Allowed values: deed easement plat as_built other
legal_description_found
required
boolean
basis_of_bearings
required
Any of:
string
tracts
required
Array<object>
object
name
required
string
pob_description
required
string
calls
required
Array<object>
object
call_type
required
string
Allowed values: line curve
verbatim_text
required
string
monument
required
Any of:
string
confidence
required
string
Allowed values: high medium low
bearing
required
Any of:
object
ns
required
string
Allowed values: N S
degrees
required
integer
minutes
required
integer
seconds
required
number
ew
required
string
Allowed values: E W
distance
required
Any of:
number
chord_bearing
required
Any of:
object
ns
required
string
Allowed values: N S
degrees
required
integer
minutes
required
integer
seconds
required
number
ew
required
string
Allowed values: E W
chord_length
required
Any of:
number
radius
required
Any of:
number
curve_direction
required
Any of:
string
Allowed values: left right
arc_length
required
Any of:
number
adjoiner
required
string
distance_unit
required
string
Allowed values: feet inches yards miles rods poles perches chains links meters kilometers varas arpents
is_exception
required
boolean
tie_calls
required
Array<string>
description_type
required
string
Allowed values: metes_and_bounds aliquot centerline blanket exhibit_referenced facility_relative
stated_area
required
string
easement
required
string
aliquot
required
string
notes
required
Any of:
string
metadata
required
object
grantors
required
Array<string>
grantees
required
Array<string>
recording
required
string
prior_references
required
Array<string>
transfer_date
required
string
transfer_date_verbatim
required
string
county
required
string
state
required
string
lot_block
required
Array<string>
sheet
required
string
groundtruth_verdicts
required
Groundtruth Verdicts
Array<object>
GroundTruthVerdict

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
tract
required
Tract
string
status
required
Status

PASS: traverse closes and corroborates stated acreage. REVIEW: something disagrees — a human decides. FAIL: the traverse could not be computed.

string
Allowed values: PASS REVIEW FAIL
reasons
Reasons
Array<string>
closure_ratio
Any of:
string
computed_acres
Any of:
number
computed_sqft
Any of:
number
computed_sqm
Any of:
number
computed_hectares
Any of:
number
distance_unit
Distance Unit

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.

string
default: feet
geometry
Any of:
TractGeometry

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
vertices
required
Vertices

One point per call endpoint plus the start: N calls give N+1 vertices.

Array<array>
misclosure
required
Misclosure

Distance from the last computed point back to the start.

number
perimeter_ft
required
Perimeter Ft
number
closure_denominator
Any of:
number
acres
required
Acres
number
georef
Any of:
GeorefGeometry

Aliquot geometry located on the earth: lon/lat rings from the BLM CadNSDI PLSS fabric, quartered by the deed’s aliquot chain.

object
crs
required
Crs

Always “EPSG:4326” today.

string
rings
required
Rings

Exterior ring(s), lon/lat pairs.

Array<Array<array>>
acres
required
Acres
number
plss_id
required
Plss Id

CadNSDI PLSSID of the resolved township/range.

string
source
required
Source

Provenance of the fabric, e.g. “BLM CadNSDI”.

string
location
Any of:
SuggestedLocation

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
crs
Crs

Always “EPSG:4326” today.

string
default: EPSG:4326
pob
Any of:
array
>= 2 items <= 2 items
rotation_deg
Any of:
number
source
Source

Which method produced this: “plss_corner” today.

string
""
basis
Basis

The monument this was worked from, in words.

string
""
plss_id
Plss Id

CadNSDI PLSSID, when one was resolved.

string
""
status
Status
string
default: REVIEW
reasons
Reasons
Array<string>
tie_offset
Any of:
Array<number>
anchor
Any of:
string
corrected
Corrected

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.

boolean
adjusted
Adjusted

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.

boolean
document_verdict
Any of:
DocumentVerdict

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
document
required
Document

What the document is, e.g. “Plat” or “As-built”.

string
status
required
Status

REVIEW: identified, with reasons naming what could not be verified. FAIL: nothing in the sheet identifies it.

string
Allowed values: REVIEW FAIL
reasons
Reasons
Array<string>
summary
Summary

One line naming the sheet, for a list or a header.

string
""
usage
required
TokenUsage
object
input_tokens
required
Input Tokens
integer
output_tokens
required
Output Tokens
integer
seconds
required
Seconds

Wall-clock seconds spent in the extraction call.

number
model
required
Model
string
engine_version
required
Engine Version
string
document
Document

Echo of the document name, if one was sent.

string
""
test_mode
Test Mode

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.

boolean
corrections
Corrections

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.

Array<object>
Correction

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
tract
required
Tract

Index into extraction.tracts.

integer
call
required
Call

Index into that tract’s calls (or tie_calls).

integer
field
required
Field
string
Allowed values: distance chord_length radius arc_length bearing chord_bearing curve_direction
before
required
Before

What the field read before. Must match the current value or the correction is refused as stale.

after
required
After

What it should read. Same shape as before.

kind
Kind

Which list call indexes.

string
default: boundary
Allowed values: boundary tie
by
By

Who made the change. Recorded, never verified.

string
""
at
At

When, ISO 8601 UTC. Server-stamped if omitted.

string
""
note
Note

Why — e.g. “source text reads 149.5”.

string
""
adjustments
Adjustments

Compass-rule adjustments applied to this extraction’s geometry. The verdicts above judge the recorded description and were NOT recomputed over these.

Array<object>
Adjustment

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
tract
required
Tract

Index into extraction.tracts.

integer
method
Method

The Bowditch rule: misclosure apportioned by course length.

string
default: compass_rule
Allowed value: compass_rule
misclosure_before
Misclosure Before

Feet of misclosure that were distributed.

number
0
perimeter
Perimeter

Feet of perimeter it was spread over.

number
0
by
By
string
""
at
At
string
""
attestation
Any of:
Attestation

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
document_sha256
required
Document Sha256

SHA-256 of the document as extracted, after any image-to-PDF normalization. Empty on a batch document submitted before this field existed.

string
extracted_at
required
Extracted At

UTC timestamp of the extraction, RFC 3339.

string
signature
required
Signature

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.

string
Example
{
"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
}
}

The request was understood and refused

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Missing, unknown, or revoked API key

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Monthly document cap or overage ceiling reached

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

No such resource under this caller

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

The request conflicts with current state

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Document over the size limit

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Body was empty, or was not a PDF, TIFF, PNG, or JPEG

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Rate limited; read Retry-After

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Extraction failed upstream

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}

Extraction temporarily unavailable

Media typeapplication/json
ErrorResponse
object
error
required
ErrorDetail
object
type
required
Type
string
message
required
Message
string
key
additional properties
any
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"
}
}