Docs › API reference › Maps & seasons

Maps & seasons

Parcels, zones, farmers, seasons, map layers — the farm's spatial data foundation.

34 operations · base /api/v1/gis

GET/api/v1/gis/care-schedules

Care schedule triggers (filter by parcel)

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
parcelIdGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/care-schedules" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "assignee_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "created_at": "2026-10-28T07:30:00Z",
    "delivery": "…",
    "enabled": true,
    "enabled_at": "2026-10-28T07:30:00Z",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "notify_user_ids": [
      "0190a3c2-5b1e-7c3d-8e4f-000000000001"
    ],
    "parcel_code": "…",
    "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "plot_name": "…",
    "season_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "template_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "template_name": "…"
  }
]
GET/api/v1/gis/care-schedules/preview

Preview the schedule (writes nothing)

`from` yyyy-MM-dd (defaults to today, Vietnam time), `days` 1–400 (default 30).

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
templateIdGUIDqueryno—
parcelIdGUIDqueryno—
plotIdGUIDqueryno—
seasonIdGUIDqueryno—
fromstringqueryno—
daysintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/care-schedules/preview" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "from": "…",
  "items": [
    {
      "anchor": "…",
      "due_on": "…",
      "material": "…",
      "quantity": 1.5,
      "rule_key": "…",
      "task_type": "…",
      "title": "…",
      "trees": 1,
      "unit": "…",
      "unknown_age_trees": 1
    }
  ],
  "to": "…",
  "warnings": [
    "…"
  ],
  "weeks": [
    {
      "items": [
        {
          "anchor": "…",
          "due_on": "…",
          "material": "…",
          "quantity": 1.5,
          "rule_key": "…",
          "task_type": "…",
          "title": "…",
          "trees": 1,
          "unit": "…",
          "unknown_age_trees": 1
        }
      ],
      "week_end": "…",
      "week_start": "…"
    }
  ]
}
GET/api/v1/gis/care-schedules/this-week

This week's care tasks (ENABLED triggers only)

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
parcelIdGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/care-schedules/this-week" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "plots": [
    {
      "activation_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "delivery": "…",
      "items": [
        {
          "anchor": "…",
          "due_on": "…",
          "material": "…",
          "quantity": 1.5,
          "rule_key": "…",
          "task_type": "…",
          "title": "…",
          "trees": 1,
          "unit": "…",
          "unknown_age_trees": 1
        }
      ],
      "parcel_code": "…",
      "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "plot_name": "…",
      "template_name": "…"
    }
  ],
  "week_end": "…",
  "week_start": "…"
}
GET/api/v1/gis/care-schedules/{id}/calendar

Full-year schedule of a trigger

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—
yearintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/care-schedules/<id>/calendar" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "from": "…",
  "items": [
    {
      "anchor": "…",
      "due_on": "…",
      "material": "…",
      "quantity": 1.5,
      "rule_key": "…",
      "task_type": "…",
      "title": "…",
      "trees": 1,
      "unit": "…",
      "unknown_age_trees": 1
    }
  ],
  "to": "…",
  "warnings": [
    "…"
  ],
  "weeks": [
    {
      "items": [
        {
          "anchor": "…",
          "due_on": "…",
          "material": "…",
          "quantity": 1.5,
          "rule_key": "…",
          "task_type": "…",
          "title": "…",
          "trees": 1,
          "unit": "…",
          "unknown_age_trees": 1
        }
      ],
      "week_end": "…",
      "week_start": "…"
    }
  ]
}
GET/api/v1/gis/care-schedules/{id}/generations

Generated history of a trigger

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/care-schedules/<id>/generations" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "activation_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "created_at": "2026-10-28T07:30:00Z",
    "delivery": "…",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "item_count": 1,
    "week_end": "…",
    "week_start": "…"
  }
]
GET/api/v1/gis/crop-types

List crop types

Filter by category (fruit_tree/cereal/vegetable/industrial/other), isActive. Paginated.

Scope: gis.crop-types.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
categorystringqueryno—
isActiveboolqueryno—
pageintqueryno—
pageSizeintqueryno—
deletedboolqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/crop-types" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "bearing_age_years": 1,
      "category": "…",
      "code": "…",
      "effective_lifecycle": "…",
      "grow_days": 1,
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "is_active": true,
      "lifecycle": "…",
      "lifecycle_guessed": true,
      "name": "…"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/crop-types/{id}

Get a crop type by ID

Used by nextfarm-harvest to get the crop type name when building a harvest record.

Scope: gis.crop-types.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/crop-types/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "category": "…",
  "code": "…",
  "created_at": "2026-10-28T07:30:00Z",
  "grow_days": 1,
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "is_active": true,
  "name": "…",
  "updated_at": "2026-10-28T07:30:00Z"
}
GET/api/v1/gis/crop-types/{id}/perennial-profile

Perennial crop profile of a species

Life cycle, bearing age, default planting spacing, template life stages.

Scope: gis.crop-types.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/crop-types/<id>/perennial-profile" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "bearing_age_years": 1,
  "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "default_row_spacing_m": 1.5,
  "default_tree_spacing_m": 1.5,
  "effective_lifecycle": "…",
  "life_stages": [
    {
      "code": "…",
      "from_year": 1,
      "name": "…",
      "to_year": 1
    }
  ],
  "lifecycle": "…",
  "lifecycle_guessed": true
}
GET/api/v1/gis/crop-types/{id}/varieties

Varieties and rootstocks of a species

kind = variety | rootstock; empty = both.

Scope: gis.crop-types.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—
kindstringqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/crop-types/<id>/varieties" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "code": "…",
    "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "kind": "…",
    "name": "…",
    "note": "…"
  }
]
GET/api/v1/gis/facilities

List livestock facilities

Filter by facilityType (barn, pasture, feedlot, dairy, poultry_house, piggery, processing_plant, other), status (active, inactive, maintenance) and branchId. Paginated.

Scope: gis.facilities.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
facilityTypestringqueryno—
statusstringqueryno—
sourceParcelIdGUIDqueryno—
branchIdGUIDqueryno—
pageintqueryno—
pageSizeintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/facilities" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "area_ha": 1.5,
      "boundary_geo_json": "…",
      "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "capacity": 1,
      "created_at": "2026-10-28T07:30:00Z",
      "current_stock": 1,
      "facility_code": "…",
      "facility_type": "…",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "name": "…",
      "source_parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "status": "…",
      "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "zone_name": "…"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/facilities/{id}

Get a livestock facility

Returns full details including GeoJSON boundary, metadata, capacity, current_stock.

Scope: gis.facilities.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/facilities/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "area_ha": 1.5,
  "boundary_geo_json": "…",
  "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "capacity": 1,
  "created_at": "2026-10-28T07:30:00Z",
  "current_stock": 1,
  "facility_code": "…",
  "facility_type": "…",
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "metadata": "…",
  "name": "…",
  "source_parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "status": "…",
  "updated_at": "2026-10-28T07:30:00Z",
  "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "zone_name": "…"
}
GET/api/v1/gis/farmer-groups

GET /api/v1/gis/farmer-groups

Scope: gis.farmers.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
includeInactiveboolqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/farmer-groups" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "code": "…",
    "farmer_count": 1,
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "is_active": true,
    "leader_farmer_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "name": "…",
    "note": "…",
    "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001"
  }
]
GET/api/v1/gis/farmers

List farmers

Filter by zone_id. Paginated.

Scope: gis.farmers.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
zoneIdGUIDqueryno—
pageintqueryno—
pageSizeintqueryno—
deletedboolqueryno—
groupIdGUIDqueryno—
memberTypestringqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/farmers" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "address": "…",
      "cooperative_name": "…",
      "full_name": "…",
      "group_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "group_name": "…",
      "home_location_geo_json": "…",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "identity_number": "…",
      "member_type": "…",
      "phone": "…",
      "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "zone_name": "…"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/farmers/{id}

Get a farmer

Returns full details + zone name + home location (GeoJSON Point).

Scope: gis.farmers.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/farmers/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "address": "…",
  "cooperative_name": "…",
  "created_at": "2026-10-28T07:30:00Z",
  "full_name": "…",
  "group_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "group_name": "…",
  "home_location_geo_json": "…",
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "identity_number": "…",
  "member_type": "…",
  "phone": "…",
  "public_consent_at": "2026-10-28T07:30:00Z",
  "public_consent_by": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "public_name_consent": true,
  "public_phone_consent": true,
  "updated_at": "2026-10-28T07:30:00Z",
  "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "zone_name": "…"
}
GET/api/v1/gis/layers

Danh sách map layers

Filter by layer_type (ndvi/satellite/custom/soil/weather), zone_id, status (processing/ready/failed/archived). Paginated.

Scope: gis.layers.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
layerTypestringqueryno—
zoneIdGUIDqueryno—
statusstringqueryno—
pageintqueryno—
pageSizeintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/layers" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "captured_at": "2026-10-28",
      "created_at": "2026-10-28T07:30:00Z",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "layer_type": "…",
      "name": "…",
      "source_url": "…",
      "status": "…",
      "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "zone_name": "…"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/layers/{id}

Get a map layer

Returns full details + zone name + ndvi stats + style_config jsonb.

Scope: gis.layers.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/layers/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "captured_at": "2026-10-28",
  "created_at": "2026-10-28T07:30:00Z",
  "file_path": "…",
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "layer_type": "…",
  "name": "…",
  "ndvi_avg": 1.5,
  "ndvi_max": 1.5,
  "ndvi_min": 1.5,
  "source_url": "…",
  "status": "…",
  "style_config": "…",
  "updated_at": "2026-10-28T07:30:00Z",
  "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "zone_name": "…"
}
GET/api/v1/gis/map/points

Lightweight points by bbox: farmer homes + parcel centroids

bbox=minLon,minLat,maxLon,maxLat (required, max 10°×10°). zoom 0..22 (optional): below 13 with more than limit results, returns mode=clusters (grid cells). limit defaults to 2000, cap 5000; when exceeded, results are cut and truncated=true (NO error). kind=all|farmer|parcel. Household points are included only when the caller also has gis.farmers.view and is not restricted to owned parcels.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
bboxstringqueryno—
zoomintqueryno—
limitintqueryno—
kindstringqueryno—
zoneIdGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/map/points" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "clusters": [
    {
      "count": 1,
      "latitude": 1.5,
      "longitude": 1.5
    }
  ],
  "limit": 1,
  "mode": "…",
  "points": [
    {
      "area_ha": 1.5,
      "farmer_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "kind": "…",
      "latitude": 1.5,
      "longitude": 1.5,
      "name": "…",
      "parcel_code": "…",
      "parcel_count": 1,
      "status": "…"
    }
  ],
  "total": 1,
  "truncated": true,
  "zoom": 1
}
GET/api/v1/gis/parcels

List parcels

Filter by bbox (minLon,minLat,maxLon,maxLat), zoneId, ownerId, cropTypeId, status, branchId. Paginated. Bbox at most 2°×2°, up to 1000 results.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
bboxstringqueryno—
zoneIdGUIDqueryno—
ownerIdGUIDqueryno—
cropTypeIdGUIDqueryno—
statusstringqueryno—
branchIdGUIDqueryno—
pageintqueryno—
pageSizeintqueryno—
deletedboolqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/parcels" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "active_season_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "active_season_name": "…",
      "active_seasons": [
        {
          "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
          "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
          "kind": "…",
          "name": "…",
          "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001"
        }
      ],
      "area_ha": 1.5,
      "boundary_geo_json": "…",
      "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "created_at": "2026-10-28T07:30:00Z",
      "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "crop_type_name": "…",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "latitude": 1.5,
      "longitude": 1.5,
      "owner_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "owner_name": "…",
      "parcel_code": "…",
      "status": "…",
      "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "zone_name": "…"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/parcels/summary

Parcel area summary by household / zone / crop

group_by = owner (by household) | zone (by zone) | crop (by crop type). Each row: household count, parcel count, total area (excluding parcels with invalid boundaries), number of parcels WITHOUT a boundary. Additional filters: zoneId, cropTypeId, status. Paginated (max 200 rows/page), sorted by total area descending. Same visibility scope as the parcel list.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
group_bystringqueryno—
zoneIdGUIDqueryno—
cropTypeIdGUIDqueryno—
statusstringqueryno—
pageintqueryno—
pageSizeintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/parcels/summary" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "estimated_area_ha": 1.5,
      "excluded_area_ha": 1.5,
      "farmer_count": 1,
      "implausible_count": 1,
      "key": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "name": "…",
      "parcel_count": 1,
      "parcels_without_boundary_count": 1,
      "total_area_ha": 1.5
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/parcels/{id}

Get a parcel

Returns full details including GeoJSON boundary + nested zone/owner/crop_type + ndvi_history.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/parcels/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "area_ha": 1.5,
  "boundary_geo_json": "…",
  "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "created_at": "2026-10-28T07:30:00Z",
  "crop_type": {
    "category": "…",
    "code": "…",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "name": "…"
  },
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "latitude": 1.5,
  "longitude": 1.5,
  "ndvi_history": [
    {
      "ndvi_value": 1.5,
      "recorded_at": "2026-10-28"
    }
  ],
  "owner": {
    "full_name": "…",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "phone": "…",
    "public_name_consent": true,
    "public_phone_consent": true
  },
  "parcel_code": "…",
  "status": "…",
  "updated_at": "2026-10-28T07:30:00Z",
  "zone": {
    "code": "…",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "name": "…",
    "zone_type": "…"
  }
}
GET/api/v1/gis/parcels/{id}/contains-point

Check whether a GPS point is inside a parcel boundary

Returns 200 with `{ parcel_id, inside }`. BEING OUTSIDE THE BOUNDARY IS A VALID ANSWER (`inside=false`), NOT an error — this endpoint reports the geometric truth; warning or blocking is the CALLER's job. A point lying EXACTLY ON AN EDGE counts as INSIDE (Covers, not Contains): a farmer standing on the field's edge must not be wrongly flagged. 422 only when the coordinates are outside the valid range. Do not call without GPS. This is the INTERIM STEP — the goal is a LOCAL check on the device via the offline bundle `GET /gis/sync/parcels` (already available), because losing signal is exactly when people are in the middle of the field.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—
latintqueryno—
lngintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/parcels/<id>/contains-point" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "inside": true,
  "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001"
}
GET/api/v1/gis/parcels/{id}/history

Parcel change history

Audit trail of every change (created/boundary-updated/owner-changed/crop-type-changed/status-changed/deleted). Sorted newest first.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—
pageintqueryno—
pageSizeintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/parcels/<id>/history" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "change_type": "…",
      "changed_at": "2026-10-28T07:30:00Z",
      "changed_by": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "new_boundary_geo_json": "…",
      "notes": "…",
      "old_boundary_geo_json": "…",
      "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/parcels/{parcelId}/photos

List photos of a parcel

Returns the photo list with presigned GET URLs (TTL 1 hour).

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
parcelIdGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/parcels/<parcelId>/photos" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "content_type": "…",
    "created_at": "2026-10-28T07:30:00Z",
    "file_name": "…",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "size_bytes": 1,
    "url": "…"
  }
]
GET/api/v1/gis/plots

List plots of a parcel

Returns the plots (Plot) belonging to parcel_id, with crop name, crop group (for the FE to decide dot rendering), plant count + estimated density, and boundary GeoJSON.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
parcel_idGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/plots" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "area_m2": 1.5,
    "boundary_geo_json": "…",
    "created_at": "2026-10-28T07:30:00Z",
    "crop_category": "…",
    "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "crop_type_name": "…",
    "crops": [
      {
        "crop_category": "…",
        "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
        "crop_type_name": "…",
        "estimated_tree_count": 1,
        "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
        "note": "…",
        "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
        "role": "…",
        "row_spacing_m": 1.5,
        "tree_spacing_m": 1.5
      }
    ],
    "custom_trees_geo_json": "…",
    "density_per1000_m2": 1.5,
    "estimated_tree_count": 1,
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "name": "…",
    "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "row_spacing_m": 1.5,
    "tree_spacing_m": 1.5
  }
]
GET/api/v1/gis/plots/{id}/crops

Species planted in the plot (intercropping)

The main species comes first (role = main, matching the plot's crop_type_id). Roles: main · intercrop · shade · border · temporary.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/plots/<id>/crops" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "crop_category": "…",
    "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "crop_type_name": "…",
    "estimated_tree_count": 1,
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "note": "…",
    "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "role": "…",
    "row_spacing_m": 1.5,
    "tree_spacing_m": 1.5
  }
]
GET/api/v1/gis/season-templates

List process templates

With `cropTypeId` ⇒ templates for that crop + shared templates (not tied to a crop).

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
cropTypeIdGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/season-templates" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "description": "…",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "name": "…",
    "rules": [
      {
        "age_from": 1,
        "age_to": 1,
        "anchor": "…",
        "day": 1,
        "day_offset": 1,
        "doses": [
          {
            "age_from": null,
            "age_to": null,
            "quantity_per_tree": null
          }
        ],
        "event": "…",
        "every_days": 1,
        "from_day": 1,
        "from_month": 1,
        "key": "…",
        "material": "…",
        "month": 1,
        "note": "…",
        "offset_days": 1,
        "task_type": "…",
        "title": "…",
        "to_day": 1,
        "to_month": 1,
        "unit": "…"
      }
    ],
    "stages": [
      {
        "day_offset": 1,
        "name": "…"
      }
    ],
    "updated_at": "2026-10-28T07:30:00Z"
  }
]
GET/api/v1/gis/season-templates/{id}

Get a process template

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/season-templates/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "description": "…",
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "name": "…",
  "rules": [
    {
      "age_from": 1,
      "age_to": 1,
      "anchor": "…",
      "day": 1,
      "day_offset": 1,
      "doses": [
        {
          "age_from": 1,
          "age_to": 1,
          "quantity_per_tree": 1.5
        }
      ],
      "event": "…",
      "every_days": 1,
      "from_day": 1,
      "from_month": 1,
      "key": "…",
      "material": "…",
      "month": 1,
      "note": "…",
      "offset_days": 1,
      "task_type": "…",
      "title": "…",
      "to_day": 1,
      "to_month": 1,
      "unit": "…"
    }
  ],
  "stages": [
    {
      "day_offset": 1,
      "name": "…"
    }
  ],
  "updated_at": "2026-10-28T07:30:00Z"
}
GET/api/v1/gis/seasons

List seasons

At least one of the two is required: `parcelId` (cultivation history of one parcel) or `year` (12-month bar). With `year`, a season is included if it **TOUCHES** that year — a season planted 12/2025 and harvested 03/2026 appears in both years; otherwise January–February 2026 would look as if there were no season while crops are actually in the field.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
parcelIdGUIDqueryno—
yearintqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/seasons" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "closed_on": "2026-10-28",
    "crop_name": "…",
    "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "expected_harvest_on": "2026-10-28",
    "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "kind": "…",
    "name": "…",
    "notes": "…",
    "parcel_code": "…",
    "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "planted_on": "2026-10-28",
    "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "stages": [
      {
        "day_offset": 1,
        "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
        "name": "…",
        "sort_order": 1
      }
    ],
    "status": "…",
    "year": 1,
    "yield_kg": 1.5
  }
]
GET/api/v1/gis/seasons/groups

Season groups of a year (same crop, same season name)

Used for the per-season target screen: "Rice · Winter–Spring 2026–27" across many parcels is ONE group. Group key = <crop_type_id>|<season name>|<year>. `branchId` filters seasons on parcels of one farm.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
yearintqueryno—
branchIdGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/seasons/groups" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
[
  {
    "branch_ids": [
      "0190a3c2-5b1e-7c3d-8e4f-000000000001"
    ],
    "crop_name": "…",
    "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
    "end_date": "2026-10-28",
    "key": "…",
    "missing_end_count": 1,
    "name": "…",
    "parcel_count": 1,
    "season_count": 1,
    "start_date": "2026-10-28",
    "year": 1
  }
]
GET/api/v1/gis/seasons/{id}

Get a season by ID

Used by `nextfarm-harvest`: when declaring a harvest it needs to know WHAT CROP the season grows and WHETHER IT IS CLOSED; when building the traceability page it needs the season name + planting date + expected harvest date to print on the label.

Scope: gis.parcels.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/seasons/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "closed_on": "2026-10-28",
  "crop_name": "…",
  "crop_type_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "expected_harvest_on": "2026-10-28",
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "kind": "…",
  "name": "…",
  "notes": "…",
  "parcel_code": "…",
  "parcel_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "planted_on": "2026-10-28",
  "plot_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "stages": [
    {
      "day_offset": 1,
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "name": "…",
      "sort_order": 1
    }
  ],
  "status": "…",
  "year": 1,
  "yield_kg": 1.5
}
GET/api/v1/gis/zones

Danh sách zones

Filter by zone_type (province/district/commune/material_zone). Paginated.

Scope: gis.zones.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
zone_typestringqueryno—
pageintqueryno—
page_sizeintqueryno—
deletedboolqueryno—
include_statsboolqueryno—
branch_idGUIDqueryno—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/zones?page_size=20" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "items": [
    {
      "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "code": "…",
      "contact_person": "…",
      "contact_phone": "…",
      "description": "…",
      "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "implausible_parcel_count": 1,
      "main_crop": "…",
      "name": "…",
      "parcel_area_ha": 1.5,
      "parcel_count": 1,
      "parent_zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
      "total_area_ha": 1.5,
      "zone_type": "…"
    }
  ],
  "page": 1,
  "page_size": 1,
  "page_size_capped": true,
  "total": 1
}
GET/api/v1/gis/zones/{id}

Get a zone

Returns full details + parent zone name + boundary GeoJSON.

Scope: gis.zones.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/zones/<id>" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "boundary_geo_json": "…",
  "branch_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "code": "…",
  "contact_person": "…",
  "contact_phone": "…",
  "created_at": "2026-10-28T07:30:00Z",
  "description": "…",
  "id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "main_crop": "…",
  "name": "…",
  "parent_zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "parent_zone_name": "…",
  "total_area_ha": 1.5,
  "updated_at": "2026-10-28T07:30:00Z",
  "zone_type": "…"
}
GET/api/v1/gis/zones/{id}/stats

Zone statistics

Number of parcels + total area belonging to the current workspace in this zone.

Scope: gis.zones.viewTry in API Explorer ▶

Parameter

ParameterTypeInRequiredDescription
idGUIDpathyes—

Example

curl -X GET "https://api-prod.nextfarm.vn/api/v1/gis/zones/<id>/stats" \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

Sample response

200 OK
{
  "active_parcel_count": 1,
  "disputed_parcel_count": 1,
  "excluded_area_ha": 1.5,
  "implausible_parcel_count": 1,
  "inactive_parcel_count": 1,
  "parcel_count": 1,
  "total_area_ha": 1.5,
  "zone_id": "0190a3c2-5b1e-7c3d-8e4f-000000000001",
  "zone_name": "…"
}