Registering fields
Every satellite request refers to a field your application registered. Registration takes the field boundary and your own identifier for it, and starts preparing the field’s history.
Register a field
Section titled “Register a field”POST /v1/fields takes three members:
| Member | Type | Notes |
|---|---|---|
name |
string, up to 200 characters | A label for people. |
external_id |
string, up to 128 characters | Your identifier for the field. Unique within your organization. |
geometry |
GeoJSON Polygon or MultiPolygon |
WGS84, coordinates in longitude, latitude order. |
curl https://api.agrosat.uz/v1/fields \ -H "X-API-Key: $AGROSAT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "North parcel 3", "external_id": "f-1001", "geometry": { "type": "Polygon", "coordinates": [[[68.552, 40.3084], [68.560, 40.3084], [68.560, 40.3139], [68.552, 40.3139], [68.552, 40.3084]]] } }'def register(session, name, external_id, geometry): r = session.post( "https://api.agrosat.uz/v1/fields", json={"name": name, "external_id": external_id, "geometry": geometry}, timeout=30, ) body = r.json() if not body["success"]: raise RuntimeError(f'{r.status_code} {body["error"]["code"]}: {body["error"]["message"]}') return body["data"]async function register(name, externalId, geometry) { const res = await fetch("https://api.agrosat.uz/v1/fields", { method: "POST", headers: { "X-API-Key": process.env.AGROSAT_API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ name, external_id: externalId, geometry }), }); const body = await res.json(); if (!body.success) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`); return body.data;}A new field comes back with 201:
{ "success": true, "data": { "id": "cmgz7k2qv0004l8p3a9d1x6rt", "external_id": "f-1001", "name": "North parcel 3", "area_ha": 41.53, "status": "pending", "created_at": "2026-04-28T09:14:03.512Z" }}Keep the id: every other endpoint takes it in the path. area_ha is the area AgroSat computed from the geometry, rounded to two decimals. status stays pending until the history is prepared; see Field readiness.
Repeat requests safely
Section titled “Repeat requests safely”Registration is idempotent on external_id:
| You send | You get |
|---|---|
A new external_id |
201 and a new field |
An existing external_id with the same geometry |
200 and the existing field; nothing new is created or billed |
An existing external_id with a different geometry |
409 external_id_conflict, with the existing field’s id in error.existing_field_id |
So after a timeout or a network error you can simply send the same request again.
The API has no call that changes a boundary. To replace one, delete the field and register the new boundary; once the old field is deleted its external_id is free again. The new field gets a new id, its area is consumed in full, and the deleted field’s area stays consumed.
What registration checks
Section titled “What registration checks”| Check | Error |
|---|---|
Geometry is a valid WGS84 Polygon or MultiPolygon, not self-intersecting |
400 invalid_geometry |
| Field area is within the per-field limit of your contract (5,000 ha unless agreed otherwise) | 400 field_too_large |
| Body has all members with the right types | 400 invalid_request |
| The field fits in the hectares left in your contract period | 403 quota_exceeded |
| The field does not overlap one you already registered by more than 80 % of its area | 409 duplicate_geometry, with error.existing_field_id |
Read and delete fields
Section titled “Read and delete fields”GET /v1/fields/{id}returns one field with its currentstatus.GET /v1/fields?external_id=f-1001finds a field by your identifier.GET /v1/fieldslists all fields page by page; see Listing fields.DELETE /v1/fields/{id}removes the field and answers204with no body. Afterwards every request for it returns404 field_not_found.
Field calls cost no units.
