Fields

A Field is a geospatial land parcel within a Farm — its boundary, soil type, and the imagery/pricing actions that key off that boundary. This page covers what's specific to Fields. For the base URL, authentication, the response envelope, pagination, sharing, the foreign-key _id write convention, and soft delete, see Farm Management Overview.

Endpoints

MethodPathPurpose
GET/field/List fields (cached)
POST/field/Create a field (single object or bulk array)
GET/field/FIELD_ID/Retrieve a single field (cached)
PUT/field/FIELD_ID/Replace a field
PATCH/field/FIELD_ID/Partially update a field
DELETE/field/FIELD_ID/Soft delete a field

GET /field/FIELD_ID/ also has a side effect: if the field's crop history hasn't been populated yet, the retrieve request triggers an asynchronous job to fetch it. The response you get back won't include that data yet — poll crop-history (below) or retrieve the field again afterward.

Auth

Requires a Bearer token — see Farm Management Overview for how to send one.

Each action also requires a dynamic permission on top of the token:

ActionRequired permission
List / Retrievefield.view
Createfield.create
Update / Partial updatefield.edit
Deletefield.delete

A request with a valid token but missing the required permission gets an HTTP 403 with the structured body described in Farm Management Overview.

Create fields

FieldTypeRequiredDefaultNotes
namestring, max 255Yes
farm_idinteger (FK)YesMust reference an existing farm — see the _id write convention in Farm Management Overview
boundaryGeoJSON objectYesA full GeoJSON FeatureCollection; the server reads geometry from features[0]
soil_typeintegerNo1

Response-only fields

Beyond the fields above, a Field response also includes:

FieldNotes
areaComputed server-side from boundary — don't send it on create/update
field_uuidServer-generated UUID, set on creation
latest_map_request_timeSet once a satellite map has ever been ordered for this field — see the update lock below
cultivationsNested list of this field's cultivations; nulled to [] when viewed via ?share=true unless the sharing grant covers sub-resources
total_map_requestNulled to null under the same ?share=true condition, unless the grant covers maps
is_registeredSubscription-tier flag
farmNested, read-only — see Farm Management Overview on the _id-for-write convention
created_byNested, read-only — no created_by_id to write

Updates lock once a map exists

PUT/PATCH /field/FIELD_ID/ returns 409 Conflict if the field's latest_map_request_time is already set — i.e. once a satellite map has ever been ordered for it, its core data is locked:

{
  "status": "error",
  "message": "Modification not allowed: A map with this field already exists. Please create a new field.",
  "details": {}
}

There's no way to unlock an existing field once a map has been ordered against it — the workaround is to create a new field.

Bulk create

POST /field/ also accepts a JSON array of field objects instead of a single object. See Farm Management Overview for how Fields' bulk-error status code (402 Payment Required for a bulk array with any invalid item) differs from Farms', Cultivations', and Fertilization's (400 for both single and bulk-array validation failures). A single invalid object sent to POST /field/ still returns a plain 400, regardless of the bulk quirk.

Status codes

StatusTrigger
200List, retrieve, or update succeeded.
201Create succeeded (single object or bulk array).
204Delete succeeded (soft delete; no response body).
400Validation failure on a single-object create.
402Validation failure on a bulk-array create where any item is invalid — see Farm Management Overview.
403Missing field.view / field.create / field.edit / field.delete permission.
404Field not found (wrong FIELD_ID, wrong company, or already deleted).
409Update blocked because a map already exists for this field — see above.

Field-specific actions

Beyond core CRUD, the Field resource exposes several actions oriented toward satellite imagery and pricing rather than basic field management. These are real, live endpoints on the same /field/FIELD_ID/... resource:

MethodPathPurposePermission slug
GET/field/FIELD_ID/crop-history/?crop-name=Crop history for this fieldfield.crop_history
GET/field/FIELD_ID/price/Pricing info for map ordering on this fieldfield.price
GET/field/FIELD_ID/maps/Maps ordered for this fieldfield.maps
GET/field/FIELD_ID/download/?format-type=geojson|shapefile&is-base64=Download the field boundaryfield.download_shapefile
GET/field/FIELD_ID/available-image/Available satellite image datesfield.available_image_date
GET/field/FIELD_ID/available-fused-image/Available fused-image datesfield.available_fused_image_date
GET/field/FIELD_ID/season/SEASON_ID/profile/Field profile for a given seasonfield.field_profile

download and both available-image/available-fused-image endpoints catch broad, unfiltered exceptions internally, so their status codes are approximate rather than precise on these specific actions: download returns a bare 404 for any internal failure, not just "boundary not found," and available-image/available-fused-image each return 503 Service Unavailable for any internal failure, not just a genuine upstream outage. Don't treat those two status codes as a precise signal on these three actions specifically — inspect the response body (where present) rather than branching purely on status code.

Create request

curl -X POST https://backend.spacenus.de/api/v1.2/field/ \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "North Block",
    "farm_id": 101,
    "boundary": {
      "type": "FeatureCollection",
      "features": [
        {
          "type": "Feature",
          "properties": {},
          "geometry": {
            "type": "Polygon",
            "coordinates": [[[13.4050, 52.5200], [13.4080, 52.5200], [13.4080, 52.5230], [13.4050, 52.5230], [13.4050, 52.5200]]]
          }
        }
      ]
    },
    "soil_type": 2
  }'

Response:

{
  "status": "success",
  "message": "Resource created successfully.",
  "data": {
    "id": 501,
    "field_uuid": "3f9a2c1e-6b4d-4a2f-8e7a-1d5c9b0f4a22",
    "name": "North Block",
    "soil_type": 2,
    "area": null,
    "latest_map_request_time": null,
    "total_map_request": 0,
    "is_registered": false,
    "boundary": {
      "type": "FeatureCollection",
      "features": [ ]
    },
    "farm": {
      "id": 101,
      "name": "North Ridge Farm"
    },
    "cultivations": [ ],
    "created_by": {
      "id": 42,
      "name": "Jane Doe"
    },
    "created_at": "2026-08-19T10:20:00Z",
    "updated_at": "2026-08-19T10:20:00Z"
  }
}

Retrieve response

{
  "status": "success",
  "message": "Operation successful.",
  "data": {
    "id": 501,
    "field_uuid": "3f9a2c1e-6b4d-4a2f-8e7a-1d5c9b0f4a22",
    "name": "North Block",
    "soil_type": 2,
    "area": "4.820",
    "latest_map_request_time": "2026-08-20T09:00:00Z",
    "total_map_request": 3,
    "is_registered": true,
    "boundary": {
      "type": "FeatureCollection",
      "features": [ ]
    },
    "farm": {
      "id": 101,
      "name": "North Ridge Farm"
    },
    "cultivations": [
      {
        "id": 900,
        "season": { "id": 12, "name": "2026 Summer" },
        "crop": { "id": 3, "name": "Wheat" }
      }
    ],
    "created_by": {
      "id": 42,
      "name": "Jane Doe"
    },
    "created_at": "2026-08-19T10:20:00Z",
    "updated_at": "2026-08-20T09:00:00Z"
  }
}