Field Boundary Management Endpoints
About
Here we list all the available endpoints from Fields API. For easily calling them, we recommend using Leaf's Postman collection.
All HTTP methods should be prepended by this service's endpoint:
There is a REST resources section if you want to check it out.
This service has the following endpoints available:
Description | Endpoints |
---|---|
Get all fields | GET /fields |
Get a field | GET /users/{id}/fields/{id} |
Create a field | POST /users/{id}/fields |
Update a field | PATCH /users/{id}/fields/{id} |
Get all operation files of a field (deprecated) | GET /users/{leafUserId}/fields/{fieldId}/operations |
Get all operation files of a field | GET /users/{leafUserId}/fields/{fieldId}/operations/files |
Get an operation file of a field (deprecated) | GET /users/{leafUserId}/fields/{fieldId}/operations/{fileId} |
Get an operation file of a field | GET /users/{leafUserId}/fields/{fieldId}/operations/files/{fileId} |
Get fields by geometry (deprecated) | POST /fields/query/intersects |
Get fields by geometry | POST /users/{leafUserId}/fields/intersects |
Get intersection of fields | POST /users/{id}/fields/intersect |
Delete a field | DELETE /users/{id}/fields/{id} |
Get all boundaries from field | GET users/{leafUserId}/fields/{fieldId}/boundaries |
Get a boundary from field | GET users/{leafUserId}/fields/{fieldId}/boundaries/{boundaryId} |
Get active boundary from field | GET users/{leafUserId}/fields/{fieldId}/boundary |
Update active boundary from field | PUT users/{leafUserId}/fields/{fieldId}/boundary |
Get all farms | GET /farms |
Get a farm | GET /users/{id}/farms/{id} |
Create a farm | POST /users/{leafUserId}/farms |
Update a farm | PUT /users/{leafUserId}/farms/{id} |
Get all growers | GET /growers |
Get a grower | GET /users/{leafUserId}/growers/{id} |
Create a grower | POST /users/{leafUserId}/growers |
Update a grower | PUT /users/{leafUserId}/growers/{id} |
Fields
Get all fields
GET /fields
Gets a paged list of Fields. It is possible to filter the results by passing some query parameters.
type
, only matches fields with this type (string).farmId
, only matches fields from this farmId (integer).provider
, only matches fields from this provider (string).leafUserId
, only matches fields from this user (string).page
, an integer specifying the page being fetched.size
, an integer specifying the size of the page (defaults to 20).
These last two parameters are used exclusively for paging through results.
- cURL
- Python
- JavaScript
Response
A JSON array containing Fields.
Get a field
GET /users/{id}/fields/{id}
Gets a single Field by its id.
- cURL
- Python
- JavaScript
Response
A single Field as a JSON object.
Create a field
POST /users/{leafUserId}/fields
Creates a Field for the user leafUserId
. A request body must be provided
containing the entry "geometry"
object which need to have the properties "type"
and "coordinates"
.
The geometry represents the boundaries of the Field being created as a GeoJSON geometry
("type"
property must be a "MultiPolygon"
).
Consider that you can also set the id
and name
properties (both of them optional) in the request body. If no id
is provided
an UUID will be generated and this property can not be updated.
Request body example:
You can try some requests on the create fields API using the examples below.
- cURL
- Python
- JavaScript
Response
You can expect a response with a JSON Object containing the following properties.
Update a Field
PATCH /users/{leafUserId}/fields/{id}
Update the Field by "id"
for the user "leafUserId"
. The request body accepts updatable field properties like "name"
to update the field name, "farmId"
to update the related Farm of the Field and "geometry"
, which represents the boundaries of the
Field as a GeoJSON geometry (it must be a "MultiPolygon"
).
Request body example:
- cURL
- Python
- JavaScript
Response
A Field as a JSON object.
Get all operation files of a field (deprecated)
Use this endpoint instead
GET /users/{leafUserId}/fields/{fieldId}/operations
Gets a paged list of all operation files of the Field and Leaf User specified in the URL.
It is possible to filter the results by passing some query parameters. They are listed below.
Parameter (to filter by) | Type | Description |
---|---|---|
operationType | String "harvested", "planted", "applied" or "other" | retrieve operations of given type |
provider | String "CNHI", "JohnDeere", "Trimble" or "ClimateFieldView" | retrieve operations of given provider |
origin | String "provider", "automerged", "merged" or "uploaded" | retrieve operations of given origin |
crop | String name of the crop, like "corn" or "soybeans". Entire crop list available here | retrieve operations with this crop. |
startTime | ISO 8601 datetime format | retrieve operations that started after this date |
endTime | ISO 8601 datetime format | retrieve operations that ended before this date |
You can also pass some parameters used exclusively for paging through results. They are:
page
, an integer specifying the page being fetched (default is 0)size
, an integer specifying the size of the page (default is 20, max is 100)
Request
- cURL
- Python
- JavaScript
Response
Get all operation files of a field
GET /users/{leafUserId}/fields/{fieldId}/operations/files
Gets a paged list of all operation files of the Field and Leaf User specified in the URL.
It is possible to filter the results by passing some query parameters. They are listed below.
Parameter (to filter by) | Type | Description |
---|---|---|
operationType | String "harvested", "planted", "applied" or "other" | retrieve operations of given type |
provider | String "CNHI", "JohnDeere", "Trimble" or "ClimateFieldView" | retrieve operations of given provider |
origin | String "provider", "automerged", "merged" or "uploaded" | retrieve operations of given origin |
crop | String name of the crop, like "corn" or "soybeans". Entire crop list available here | retrieve operations with this crop. |
startTime | ISO 8601 datetime format | retrieve operations that started after this date |
endTime | ISO 8601 datetime format | retrieve operations that ended before this date |
You can also pass some parameters used exclusively for paging through results. They are:
page
, an integer specifying the page being fetched (default is 0)size
, an integer specifying the size of the page (default is 20, max is 100)
Request
- cURL
- Python
- JavaScript
Response
Get an operation file of a field (deprecated)
Use this endpoint instead
GET /users/{leafUserId}/fields/{fieldId}/operations/{fileId}
Gets a single Operation File of a field by its id.
Request
- cURL
- Python
- JavaScript
Response
Get an operation file of a field
GET /users/{leafUserId}/fields/{fieldId}/operations/files/{fileId}
Gets a single Operation File of a field by its id.
- cURL
- Python
- JavaScript
Response
A single Operation File.
Get Fields by geometry (deprecated)
POST /fields/query/intersects
Use this endpoint instead.
Gets a list of fields that intersect with the GeoJSON MultiPolygon sent in the request body.
- cURL
- Python
- JavaScript
Response
A JSON list of Fields.
Get Fields by geometry
POST /users/{leafUserId}/fields/intersects
Gets a list of fields that intersect with the GeoJSON MultiPolygon sent in
the request body. The minimum intersection percentage is given by
intersectionThreshold
and can range from 0.01% to 100%, its default value is 0.01
.
The intersectionThreshold
can be compared with the "intersection by field" ratio or the "intersection by geometry" ratio. Whichever is satisfied first.
Here we have a sample for a field with 100 area unit and a geometry with 10 area unit:
So, in this case, if the intersectionThreshold
were 3, then the condition would be satisfied and the field would be returned, but if the value was greater than 50, then it would not satisfy the condition, as 50% is the highest intersection value:
intersectionThreshold (%) | satisfied |
---|---|
3 | ✅ |
5 | ✅ |
37 | ✅ |
50 | ✅ |
75 | ❌ |
100 | ❌ |
- cURL
- Python
- JavaScript
Response
A JSON list of Fields.
Get intersection of fields
POST /users/{id}/fields/intersect
Gets a GeoJSON MultiPolygon corresponding to the intersection of the Fields specified by the given id's. Such Field id's goes in a list, in the request body.
- cURL
- Python
- JavaScript
Response
A JSON in the format of a GeoJSON geometry.
Delete a field
DELETE /users/{id}/fields/{id}
Deletes the field with the given id.
tip
Fields created by a provider cannot be deleted on Leaf side.
Boundaries
Get all boundaries from field
GET /users/{leafUserId}/fields/{fieldId}/boundaries
Gets a list of boundaries from a field.
- cURL
- Python
- JavaScript
Response
A list of Boundary as a JSON object.
Get a boundary from field
GET /users/{leafUserId}/fields/{fieldId}/boundaries/{boundaryId}
Gets a single Boundary from a field by its id.
- cURL
- Python
- JavaScript
Response
A single Boundary as a JSON object.
Get active boundary from field
GET /users/{leafUserId}/fields/{fieldId}/boundary
Gets the active Boundary from a field.
- cURL
- Python
- JavaScript
Response
A single Boundary as a JSON object.
Update active boundary from field
PUT /users/{leafUserId}/fields/{fieldId}/boundary
Updates the active boundary of field fieldId
. The previous active boundary is not deleted, but set as inactive.
Request body example:
- cURL
- Python
- JavaScript
Response
A Field as a JSON object.
Farms
Get all farms
GET /farms
Gets a paged list of all farms. It is possible to pass some query parameters.
growerId
, only matches Farms from this growerId (integer)provider
, only matches Farms from this provider (string)leafUserId
, only matches Farms from this Leaf User (UUID)page
, an integer specifying the page being fetchedsize
, an integer specifying the size of the page (defaults to 20)
The parameters are used exclusively for paging through results.
- cURL
- Python
- JavaScript
Response
A JSON array containing farms.
Get a farm
GET /users/{leafUserId}/farms/{id}
Gets a single farm by its id
from the user leafUserId
.
- cURL
- Python
- JavaScript
Response
A single Farm as a JSON object.
Create a farm
POST /users/{leafUserId}/farms
Creates a farm for the user leafUserId
. It's possible to pass both the farmName
and the growerId
on the body of
the request.
Request body example:
- cURL
- Python
- JavaScript
Response
A single Farm as a JSON object.
Update a farm
PUT /users/{leafUserId}/farms/{id}
Updates the farm with id id
for the user leafUserId
. It's possible to pass both the farmName
and the growerId
on the body of the request.
Request body example:
- cURL
- Python
- JavaScript
Response
A single Farm as a JSON object.
Grower
Get all growers
GET /growers
Gets a paged list of all growers. Use the following parameters for paging through results.
provider
, only matches Growers from this provider (string)leafUserId
, only matches Growers from this Leaf User (UUID)page
, an integer specifying the page being fetchedsize
, an integer specifying the size of the page (defaults to 20)
- cURL
- Python
- JavaScript
Response
A JSON array containing growers.
Get a grower
GET /users/{leafUserId}/growers/{id}
Gets a single grower by its id
from the user leafUserId
.
- cURL
- Python
- JavaScript
Response
A single Grower as a JSON object. In our system Growers are equivalent to John Deere Client. That been said, the
attribute name
comes directly from the Client's name for growers with John Deere as provider.
Create a grower
POST /users/{leafUserId}/growers
Creates a grower for the user leafUserId
. It's possible to pass name
on the body of the request.
Request body example:
- cURL
- Python
- JavaScript
Response
A single Grower as a JSON object.
Update a grower
PUT /users/{leafUserId}/growers/{id}
Updates the grower with id id
for the user leafUserId
. It's possible to pass only the name
on the body of the request.
Request body example:
- cURL
- Python
- JavaScript
Response
A single Grower as a JSON object.
REST Resources
See below the REST resources and their endpoints.
Field Resource
A field might have one or neither of the following keys:
- a "mergedFieldId" key or
- a "sources" key
A Field will only have one of the previous keys if it is either a field that has been merged with other one(s) or if it is a result of a merge. Leaf merges fields that have any sort of overlap. This makes it easier for you to query operations from a field by querying by the merged field. Because a field might exist in multiple providers, Leaf detects that and creates a single field that you can query for - and you can still query by the individual fields too.
geometry
and area
are deprecated keys that contains the geometry of the active boundary and its area, respectively.
Below are the return possibilities when passing different geometries:
Response |
---|
VALID |
REPEATED_POINT |
HOLE_OUTSIDE_SHELL |
NESTED_HOLES |
DISCONNECTED_INTERIOR |
SELF_INTERSECTION |
RING_SELF_INTERSECTION |
NESTED_SHELLS |
DUPLICATE_RINGS |
TOO_FEW_POINTS |
INVALID_COORDINATE |
RING_NOT_CLOSED |
NOT_ALLOWED_GEOMETRY_TYPE |
Description | Endpoints |
---|---|
Get all fields | GET /fields |
Get a field | GET /users/{id}/fields/{id} |
Create a field | POST /users/{id}/fields |
Get fields by geometry | POST /fields/query/intersects |
Get intersection of fields | POST /users/{id}/fields/intersect |
Delete a field | DELETE /users/{id}/fields/{id} |
Boundary Resource
Every Field at Leaf can have 0 or many boundaries. Fields created via Leaf's endpoints must have at least one boundary. Only one boundary may be active, the others are inactive boundaries. Boundaries cannot be deleted or have its geometry updated. Every update generates a new Boundary, and Leaf keeps a history of all seen Boundaries.
Each boundary has a status
and providerStatus
.
status
- Represents the current status of the boundary:ACTIVE
- If the boundary was created at Leaf, it is the active boundary. If it is from a provider, this boundary exists at the provider and is the active boundary there.INACTIVE
- If the boundary was created at Leaf, it is an inactive boundary. If it is from a provider, this boundary exists at the provider and is inactive there.OUTDATED_ON_PROVIDER
- The boundary is from a provider. The boundary once existed on the provider exactly as it is in that boundary, but it was edited (e.g. has a new geometry but the same provider boundary id).DELETED_ON_PROVIDER
- The boundary is from a provider. The boundary once existed on the provider, but it was deleted. The user won't find that boundary in the provider.
providerStatus
- Is the status of the boundary on the provider.ACTIVE
- The boundary is the active boundary in the provider.INACTIVE
- The boundary is inactive in the provider.
providerStatus
, just like the geometry, is a static attribute. In case this attribute is changed at the provider, the boundary's status
is updated and a new boundary is created with the updated providerStatus
in order to maintain history.
Description | Endpoints |
---|---|
Get all boundaries from field | GET users/{leafUserId}/fields/{fieldId}/boundaries |
Get a boundary from field | GET users/{leafUserId}/fields/{fieldId}/boundaries/{boundaryId} |
Get active boundary from field | GET users/{leafUserId}/fields/{fieldId}/boundary |
Update active boundary from field | PUT users/{leafUserId}/fields/{fieldId}/boundary |
Operation Resource
Description | Endpoints |
---|---|
Get all operations of a field (deprecated) | GET /users/{leafUserId}/fields/{fieldId}/operations |
Get all operations of a field | GET /users/{leafUserId}/fields/{fieldId}/operations/files |
Get an operation of a field (deprecated) | GET /users/{leafUserId}/fields/{fieldId}/operations/{fileId} |
Get an operation of a field | GET /users/{leafUserId}/fields/{fieldId}/operations/files/{fileId} |
Farm Resource
Description | Endpoints |
---|---|
Get all farms | GET /farms |
Get a farm | GET /users/{id}/farms/{id} |
Create a farm | POST /users/{leafUserId}/farms |
Update a farm | PUT /users/{leafUserId}/farms/{id} |
Grower Resource
If there is a name available for the grower so the name
property will be returned as well.
Description | Endpoints |
---|---|
Get all growers | GET /growers |
Get a grower | GET /growers/{id} |
Create a grower | POST /users/{leafUserId}/growers |
Update a grower | PUT /users/{leafUserId}/growers/{id} |
Troubleshooting
Currently, we get the field boundary data as available from the provider, so in some cases there may be fields without boundaries or with invalid boundaries.
Below is an example of a self-intersect geometry that would be filtered by Leaf since we do not interpret as a valid geometry.
This is what this invalid type of geometry looks like from the provider side:

To avoid these errors, with the exception of the VALID answer, do not submit the types of geometries listed in the table.