{
  "info": {
    "name": "Agrovisio Data API",
    "description": "Generated from /v1/openapi.json by scripts/publicapi_docs_build.py.\n\nSet `apiKey` to your sandbox key (`sk_test_...`) and `parcelId` to one of the sandbox parcels from `GET /v1/parcels`. Every POST gets a fresh `Idempotency-Key` from a pre-request script.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.agrovisio.ai/v1"
    },
    {
      "key": "apiKey",
      "value": "sk_test_replace_me"
    },
    {
      "key": "parcelId",
      "value": "prc_replace_me"
    },
    {
      "key": "assessmentId",
      "value": "dmg_replace_me"
    },
    {
      "key": "jobId",
      "value": "job_replace_me"
    },
    {
      "key": "definitionId",
      "value": "idx_replace_me"
    },
    {
      "key": "webhookId",
      "value": "00000000-0000-4000-8000-000000000000"
    },
    {
      "key": "idempotencyKey",
      "value": ""
    }
  ],
  "item": [
    {
      "name": "Account",
      "description": "Plan, quota and billing period status.",
      "item": [
        {
          "name": "Account, plan and quota status",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/account",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "account"
              ]
            },
            "description": "Returns the plan, the products it includes, rate limits and the current billing period usage.\n\nThis endpoint stays available while the account is suspended for an overdue payment - every other endpoint returns `402 payment_required`."
          },
          "response": []
        },
        {
          "name": "Service status",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "status"
              ]
            },
            "description": "Public service status - no API key required.\n\n`status` is `ok`, `degraded` (API answers, but limits, jobs or a data\nqueue are impaired) or `down` (database unreachable). Poll it from your\nmonitoring at most once per 30 s; it is not rate limited but it is not\nfree for us either. The HTTP code is always 200 so that a monitoring\nclient can distinguish \"the status endpoint failed\" from \"the service\nreports a problem\"."
          },
          "response": []
        }
      ]
    },
    {
      "name": "CAP",
      "description": "Subsidies, eco-schemes and rotation compliance.",
      "item": [
        {
          "name": "Optimal eco-scheme combination",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/cap/eco-schemes/optimize",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cap",
                "eco-schemes",
                "optimize"
              ]
            },
            "description": "Picks the highest value combination of eco-schemes each parcel is eligible for, respecting the incompatibility rules of the national catalogue, and reports what to add and what to drop against the schemes already claimed.\n\n`conservative` (the default) leaves out organic farming, which requires a certificate the holding may not have; `aggressive` includes the whole catalogue.\n\nSchemes that depend on where the parcel is rather than what grows on it (biodiversity strips, water buffers) are **not** recommended automatically. They are returned under `candidates`, because claiming one without a site check is how a holding fails an inspection.\n\n`Idempotency-Key` is required, as on every POST. Repeating the same key with the same body replays the stored response with `Idempotent-Replay: true`; repeating it with a different body returns `409 idempotency_conflict`. The calculation is a read: both the first call and a replay answer `200`.\n\nCrops may be sent as canonical codes (`winter_wheat`) or as free text in the national language. Recognition goes through the canonical crop registry, never through substring matching, so 'Vasariniai kvieciai' and 'spring wheat' resolve to the same code. A v",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"country\": \"LT\",\n  \"mode\": \"conservative\",\n  \"parcels\": [\n    {\n      \"area_ha\": 24.5,\n      \"crop\": \"winter_wheat\",\n      \"ref\": \"field-12\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "Crop rotation compliance",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/cap/rotation/check?country=LT&history=2023:winter_wheat,2024:winter_wheat,2025:winter_wheat",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cap",
                "rotation",
                "check"
              ],
              "query": [
                {
                  "key": "country",
                  "value": "LT",
                  "description": "ISO 3166-1 alpha-2, for example 'LT'",
                  "disabled": false
                },
                {
                  "key": "history",
                  "value": "2023:winter_wheat,2024:winter_wheat,2025:winter_wheat",
                  "description": "Comma separated 'YYYY:crop' items, oldest or newest first",
                  "disabled": false
                }
              ]
            },
            "description": "Checks a crop history against the national monoculture limit (in Lithuania: the same crop may be grown for at most three consecutive seasons, so a fourth season in a row is a violation - the same reading the declaration validator applies).\n\nHistory is passed as `YYYY:crop` items separated by commas, for example `2022:winter_wheat,2023:winter_wheat,2024:winter_wheat,2025:winter_wheat`. Years must be four digits, not earlier than 1990 and not later than next year (a planned sowing may be included); anything else is a `validation_error`. Only **adjacent** years form a run: 2021, 2022 and 2024 is not a three season streak.\n\n`diversity` is informational and does not affect `compliant` - the EU rule limits monoculture, not low crop diversity. A crop that could not be resolved is listed in `unrecognised_crops` and breaks the run rather than being assumed to be the same crop as its neighbour.\n\nCrops may be sent as canonical codes (`winter_wheat`) or as free text in the national language. Recognition goes through the canonical crop registry, never through substring matching, so 'Vasariniai kvieciai' and 'spring wheat' resolve to the same code. A value that cannot be resolved is reported in "
          },
          "response": []
        },
        {
          "name": "Direct payments for a holding",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/cap/subsidy/calculate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cap",
                "subsidy",
                "calculate"
              ]
            },
            "description": "Calculates basic income support (BISS), the complementary redistributive payment (CRIS) and coupled support for every parcel, using the published national rates.\n\n**Send the whole holding in one call.** CRIS applies to the first N hectares *of the holding*, so splitting the parcels across two requests would award the full redistributive band twice and overstate the result.\n\n**Eco-schemes are voluntary and are excluded from `total_eur`.** Their value is reported separately as `eco_potential_eur`, because a farm that has not enrolled will not receive it, and a lender must not book it as income.\n\n**A parcel below the national minimum field area (Lithuania: 0.1 ha, ministerial order 3D-92 point 5.6) receives no direct payment.** It is returned with the warning `area_below_biss_minimum`, an empty `lines` list and `total_eur: 0`, and it does not consume the CRIS band for the other parcels - the same reading the declaration validator applies. `ref` is your own reference and does not have to be unique (the same field split into two crops is fine): results are returned in the order the parcels were sent, and every row keeps its own `eco_potential_eur`.\n\n`Idempotency-Key` is required, as on every POST. Repeating the same key with t",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"country\": \"LT\",\n  \"parcels\": [\n    {\n      \"area_ha\": 24.5,\n      \"crop\": \"winter_wheat\",\n      \"ref\": \"field-12\"\n    },\n    {\n      \"area_ha\": 140.0,\n      \"crop\": \"field_peas\",\n      \"ref\": \"field-13\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Damage",
      "description": "Before/after damage assessment and certificates.",
      "item": [
        {
          "name": "Order a damage assessment",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/damage/assess",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "damage",
                "assess"
              ]
            },
            "description": "Orders a before/after damage assessment for one parcel and one insured event. The assessment compares Sentinel-1 backscatter in the 14 days before the event with the 14 days after it, in the **same relative orbit**, and removes the expected seasonal change using the parcel baseline.\n\nThe work is asynchronous: this endpoint returns `202` with a `job_id`. Poll `GET /v1/jobs/{job_id}` or subscribe to the `damage.completed` webhook. A finished job carries the full result, and `GET /v1/damage/{assessment_id}` returns it again at any time.\n\n**Billing.** This is a metered unit (EUR 35 per assessment). You are charged only when the job finishes with `status: completed` **and** `confidence` of at least 0.40. An assessment that ends as `insufficient_data`, or whose confidence stays below that, is recorded for audit with `billable: false` and costs nothing. `Idempotency-Key` is required: retrying with the same key returns the same job with its **current** status and is never charged twice.\n\n**Event date.** The 14 day window after the event must have closed: an `event_date` less than 14 days ago returns `400 date_range_invalid` with `detail.reason: post_window_open` and `detail.available_from`",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"claim_ref\": \"CLM-2026-8812\",\n  \"event_date\": \"2026-07-15\",\n  \"parcel_id\": \"prc_9F3AMNPQRSTVWXYZ0123\",\n  \"peril\": \"hail\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "Read a damage assessment",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/damage/:assessment_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "damage",
                ":assessment_id"
              ],
              "variable": [
                {
                  "key": "assessment_id",
                  "value": "{{assessmentId}}"
                }
              ]
            },
            "description": "Returns a finished assessment with everything needed to defend the number in a claim file: the damaged zones, the bootstrap confidence interval, the scenes used, the seasonal correction and the method version with its configuration hash.\n\n`status: insufficient_data` is a normal outcome, not an error: the job ran, but the satellite record did not support a result. `reason` says what was missing and `billable` is `false`."
          },
          "response": []
        },
        {
          "name": "Download the damage certificate",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/damage/:assessment_id/certificate.pdf",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "damage",
                ":assessment_id",
                "certificate.pdf"
              ],
              "variable": [
                {
                  "key": "assessment_id",
                  "value": "{{assessmentId}}"
                }
              ]
            },
            "description": "Returns the PDF certificate for an assessment. The document is generated deterministically from the stored result, so the `sha256` you received with the assessment stays valid for every later download - that is what makes the certificate usable as evidence.\n\nA certificate is issued only when `confidence` is at least 0.50. Below that the assessment is still available as JSON, but a signed document would give it a weight the data does not carry, so this endpoint returns `422 insufficient_data`."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Indices",
      "description": "NDVI, NDWI and EVI time series with baselines.",
      "item": [
        {
          "name": "Index time series for up to 500 parcels",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/indices/batch",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "indices",
                "batch"
              ]
            },
            "description": "Returns the same series as `GET /parcels/{id}/indices` for a list of parcels in one call. Parcels are processed independently: an unknown or deleted id is reported as `status: not_found` in its own result row instead of failing the whole request.\n\n`Idempotency-Key` is required. Repeating the same key with the same body replays the stored response with `Idempotent-Replay: true`; repeating it with a different body returns `409 idempotency_conflict`. The batch is a read: both the first call and a replay answer `200`.\n\nThe batch uses parcel baselines only - it never falls back to the district baseline, because that would mean up to 50 neighbour baselines per parcel in a single request. Parcels whose own history is too short come back with `baseline.reason` set; request them individually to get the district norm.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"baseline\": true,\n  \"from\": \"2026-05-01\",\n  \"index\": \"NDVI\",\n  \"parcel_ids\": [\n    \"prc_9F3AKMNPQRSTVWXYZ012\",\n    \"prc_7B2CKMNPQRSTVWXYZ345\"\n  ],\n  \"to\": \"2026-09-01\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "NDVI / NDWI / EVI time series with baseline",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/indices?index=NDVI&from=2026-04-01&to=2026-09-01&baseline=true",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "indices"
              ],
              "query": [
                {
                  "key": "index",
                  "value": "NDVI",
                  "description": "NDVI | NDWI | EVI",
                  "disabled": false
                },
                {
                  "key": "from",
                  "value": "2026-04-01",
                  "description": "Inclusive start date (default: 180 days ago)",
                  "disabled": false
                },
                {
                  "key": "to",
                  "value": "2026-09-01",
                  "description": "Inclusive end date (default: today)",
                  "disabled": false
                },
                {
                  "key": "baseline",
                  "value": "true",
                  "description": "Include the baseline block and per observation deviations",
                  "disabled": false
                },
                {
                  "key": "quality",
                  "value": "",
                  "description": "Return only observations of this quality class: good | partial | cloud_masked",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Sentinel-2 L2A time series aggregated over the parcel with a 10 m inner buffer and the SCL mask applied (method `indices-1.0`).\n\nEvery observation carries its quality class: `good` (>= 80% valid pixels), `partial` (50-79%) or `cloud_masked` (< 50%). A `cloud_masked` observation is returned with `value: null` and `reason: cloud_masked` - the series is never interpolated.\n\n`quality` keeps only observations of one class (`good`, `partial`, `cloud_masked`); `observations` counts what is returned.\n\nThe `baseline` block is the five season norm for the same day of year (method `baseline-1.0`). Every observation is compared with the norm of its own season - the five complete seasons before it - so a window that spans several years uses one norm per season, and `anomaly.baseline_season` says which. The `baseline` block describes the norm for the season of `to`. While the historical backfill is still running the endpoint returns `422 baseline_not_ready`; pass `baseline=false` to receive the raw series in the meantime.\n\nThe date range must not exceed 5 years."
          },
          "response": []
        },
        {
          "name": "Most recent usable index observation",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/indices/latest?index=NDVI",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "indices",
                "latest"
              ],
              "query": [
                {
                  "key": "index",
                  "value": "NDVI",
                  "description": "NDVI | NDWI | EVI",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Returns the most recent Sentinel-2 observation for the parcel within the lookback window (30 days). Cloud covered scenes are skipped for this endpoint - use `GET /parcels/{id}/indices` to see them with `value: null` and their reason.\n\nWhen the baseline backfill is still running the observation is returned without the deviation block and `baseline` is null with a reason - the measurement itself is still valid.\n\nWhen the lookback window holds no usable observation (no overpass, or every scene cloud masked) the endpoint returns `422 insufficient_data` with `detail.reason`, like every other synchronous product - never a `200` that looks like a measurement."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Jobs",
      "description": "Asynchronous job polling.",
      "item": [
        {
          "name": "Poll an asynchronous job",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/jobs/:job_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "jobs",
                ":job_id"
              ],
              "variable": [
                {
                  "key": "job_id",
                  "value": "{{jobId}}"
                }
              ]
            },
            "description": "Returns the status of a job created by an asynchronous endpoint such as `POST /v1/damage/assess` or `POST /v1/parcels/bulk`.\n\nA job that belongs to another client returns `404 not_found` - not `403` - so that job identifiers cannot be probed across accounts."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Parametric Index",
      "description": "Index definitions, evaluation and backtest.",
      "item": [
        {
          "name": "Create a parametric index definition",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions"
              ]
            },
            "description": "Creates a **draft** index definition. Drafts are free and can be evaluated and backtested as many times as you like; only `POST /v1/index-definitions/{id}/publish` starts the annual fee.\n\nThe target is exactly one of `parcel_id`, `geometry` or `district_code`. A `geometry` target is normalised and its geometry hash is **frozen at creation time**, so a future change in our geometry handling cannot silently move the satellite data a signed policy is based on.\n\nThe whole method configuration (quality classes, baseline rules, district aggregation) is frozen into the definition as well. Editing `index-1.0.yaml` later therefore affects new definitions only.\n\nTriggers come in two forms and exactly one must be present: `{\"op\": \"<\", \"pct_of_baseline\": 65}` compares the window value against the parcel's own history, `{\"op\": \"<\", \"absolute\": 0.40}` compares it against a fixed number and needs no baseline at all.\n\nA published definition is immutable. Editing it returns `409 definition_published`; to change the methodology create a new definition with `supersedes` pointing at the old one. The old definition keeps working and keeps every evaluation it has already produced, because policies writt",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"aggregation\": \"mean\",\n  \"baseline_years\": 5,\n  \"index\": \"NDVI\",\n  \"min_observations\": 3,\n  \"name\": \"Winter wheat June drought\",\n  \"target\": {\n    \"parcel_id\": \"prc_9F3AMNPQRSTVWXYZ0123\"\n  },\n  \"trigger\": {\n    \"op\": \"<\",\n    \"pct_of_baseline\": 65\n  },\n  \"window\": {\n    \"end\": \"06-30\",\n    \"start\": \"06-01\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "List index definitions",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions?limit=50&status=published",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50",
                  "description": "Page size, default 100, maximum 500",
                  "disabled": false
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "Opaque cursor",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "published",
                  "description": "draft | published",
                  "disabled": false
                },
                {
                  "key": "index",
                  "value": "",
                  "description": "Filter by index name",
                  "disabled": true
                }
              ]
            },
            "description": "Cursor paginated list, newest first. Offsets are not supported: they are unstable while definitions are being added. Pass the returned `next_cursor` back as `?cursor=` until `has_more` is `false`.\n\n`?status=published` returns the definitions currently in force, `?status=draft` the ones still being prepared. `?index=NDVI` keeps only definitions of that index (case-insensitive). Filters are applied before pagination, so `has_more` and `next_cursor` stay correct when they are combined with `limit`."
          },
          "response": []
        },
        {
          "name": "Read one index definition",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions/:definition_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions",
                ":definition_id"
              ],
              "variable": [
                {
                  "key": "definition_id",
                  "value": "{{definitionId}}"
                }
              ]
            },
            "description": "Returns the definition exactly as it will be evaluated, including the frozen `config_hash` and `method_version`. Quote both in the policy document: together with `scene_ids` from each evaluation they are the full reproducibility key."
          },
          "response": []
        },
        {
          "name": "Edit a draft index definition",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions/:definition_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions",
                ":definition_id"
              ],
              "variable": [
                {
                  "key": "definition_id",
                  "value": "{{definitionId}}"
                }
              ]
            },
            "description": "Edits a **draft**. A published definition is immutable. Editing it returns `409 definition_published`; to change the methodology create a new definition with `supersedes` pointing at the old one. The old definition keeps working and keeps every evaluation it has already produced, because policies written against it are still in force.\n\nThe method configuration is re-frozen on every edit, so a draft always carries the configuration that was current when it was last touched, and a published definition carries the one that was current when it was published.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"min_observations\": 6,\n  \"name\": \"Winter wheat June drought v2\",\n  \"trigger\": {\n    \"op\": \"<\",\n    \"pct_of_baseline\": 72\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Backtest an index over past seasons",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions/:definition_id/backtest?years=10",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions",
                ":definition_id",
                "backtest"
              ],
              "query": [
                {
                  "key": "years",
                  "value": "10",
                  "description": "How many past seasons to test; default 10",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "definition_id",
                  "value": "{{definitionId}}"
                }
              ]
            },
            "description": "Applies exactly the same logic to each past season and returns the per year outcome plus `trigger_rate` - the number you price the policy with.\n\n**Only closed windows are counted.** The current, unfinished season is skipped: including it would systematically lower the trigger rate and therefore the premium.\n\n**`trigger_rate` divides by evaluated years, not by requested years.** A season with no usable scenes is not a season that 'did not trigger'; counting it as one would understate the risk. Check `years_insufficient` before trusting the rate.\n\nBacktests are included in the annual index fee and are not charged separately, but they run on the expensive rate limit pool: ten years with a five year baseline reads fifteen seasons of history."
          },
          "response": []
        },
        {
          "name": "Evaluate an index for one season",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions/:definition_id/evaluate?year=2026",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions",
                ":definition_id",
                "evaluate"
              ],
              "query": [
                {
                  "key": "year",
                  "value": "2026",
                  "description": "Season to evaluate; defaults to the current year",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "definition_id",
                  "value": "{{definitionId}}"
                }
              ]
            },
            "description": "Evaluates the definition for one year and returns whether it triggered, together with every scene used.\n\n**`triggered` is `null`, never `false`, when the data was not sufficient.** A window needs at least `min_observations` good quality observations; below that the answer is `status: insufficient_data` with a `reason`. A cloudy scene is never turned into a zero - a zero NDVI would look like a triggered drought index and pay out a claim that did not happen.\n\nOnce the definition is published **and** the window has closed, the result is stored and every later call returns that stored result (`final: true`). An open window returns `final: false` and will keep changing as scenes arrive.\n\nThe answer is self-contained evidence: `threshold_pct` and `trigger` repeat the threshold that was applied, so a claim file does not need to quote the definition separately, and `evidence_url` is the permanent address of the stored record - present only when the result is `final`.\n\nThis endpoint never waits for Copernicus: it reads the shared satellite cache. A parcel whose history is still being backfilled reports `insufficient_data` rather than blocking for minutes."
          },
          "response": []
        },
        {
          "name": "Publish an index definition",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/index-definitions/:definition_id/publish",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "index-definitions",
                ":definition_id",
                "publish"
              ],
              "variable": [
                {
                  "key": "definition_id",
                  "value": "{{definitionId}}"
                }
              ]
            },
            "description": "Freezes the definition and starts the annual fee of EUR 600 per index, charged once at publication (backtests are included).\n\nFrom this moment the definition is immutable and every evaluation of a **closed** window is stored and returned unchanged forever: a later reprocessing of the satellite archive must not move a payout decision that has already been made.\n\nPublishing twice is a no-op and is **not** charged twice - the second call returns the same document with `Idempotent-Replay: true`."
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Parcels",
      "description": "Parcel registry - the billing unit.",
      "item": [
        {
          "name": "Look up a parcel geometry by cadastral number",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/cadastre/lookup?no=000467-3875&country=LT",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cadastre",
                "lookup"
              ],
              "query": [
                {
                  "key": "no",
                  "value": "000467-3875",
                  "description": "Cadastral or LPIS block number",
                  "disabled": false
                },
                {
                  "key": "country",
                  "value": "LT",
                  "description": "ISO 3166-1 alpha-2, for example LT",
                  "disabled": false
                }
              ]
            },
            "description": "Resolves a national cadastral or LPIS block number to a geometry without creating a parcel. Use it to show the farmer's field on a map before you commit to monitoring it.\n\nLithuania is served from the national LPIS layer. A number that does not exist returns `404 cadastre_not_found` with `detail.source`. A parcel that exists but is not registered as farmland returns `200` with `land_use: \"non_agricultural\"` and a warning - satellite products on a parking lot are meaningless, but that is your decision, not ours. Registries that are not connected yet return `502 upstream_unavailable` naming the registry."
          },
          "response": []
        },
        {
          "name": "Create a parcel",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels"
              ]
            },
            "description": "Registers one parcel from a GeoJSON geometry **or** a national cadastral number. The parcel is the billing unit: you pay for the peak number of monitored parcels per month, never per request.\n\nReturns `201` for a new parcel and `200` with the existing parcel when the same geometry is already registered for your account (identical `geom_hash`).\n\n`data_status` tells you how much history the parcel already has. When another account already monitors the same geometry the history is available immediately; otherwise a backfill is queued and `baseline_ready` stays `false` until it finishes. Anomaly values are not returned before that - a deviation computed from three observations looks exactly like a real one.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"country\": \"LT\",\n  \"crop_hint\": \"winter_wheat\",\n  \"external_ref\": \"POLICY-2026-00412\",\n  \"geometry\": {\n    \"coordinates\": [\n      [\n        [\n          24.0,\n          55.0\n        ],\n        [\n          24.005,\n          55.0\n        ],\n        [\n          24.005,\n          55.003\n        ],\n        [\n          24.0,\n          55.003\n        ],\n        [\n          24.0,\n          55.0\n        ]\n      ]\n    ],\n    \"type\": \"Polygon\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "List parcels",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels?limit=100&country=LT",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "100",
                  "description": "Page size, default 100, maximum 500",
                  "disabled": false
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "Opaque cursor",
                  "disabled": true
                },
                {
                  "key": "country",
                  "value": "LT",
                  "description": "ISO 3166-1 alpha-2, for example LT",
                  "disabled": false
                },
                {
                  "key": "land_use",
                  "value": "",
                  "description": "One of: arable, grassland, orchard, unknown, non_agricultural",
                  "disabled": true
                },
                {
                  "key": "external_ref",
                  "value": "",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "cadastral_no",
                  "value": "",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "created_after",
                  "value": "",
                  "description": "Only parcels created on or after this date",
                  "disabled": true
                },
                {
                  "key": "bbox",
                  "value": "",
                  "description": "minLon,minLat,maxLon,maxLat; at most 10 000 km2",
                  "disabled": true
                },
                {
                  "key": "monitoring",
                  "value": "",
                  "description": "Filter by monitoring state",
                  "disabled": true
                }
              ]
            },
            "description": "Cursor paginated list, newest first. Offsets are not supported: they are unstable while parcels are being added. Pass the returned `next_cursor` back as `?cursor=` until `has_more` is `false`.\n\nFilters are validated the same way as the create body: an unknown `land_use` value, a `country` that is not an ISO 3166-1 alpha-2 code or a query parameter that does not exist (`offset`, a misspelt filter) returns `400 validation_error` with the supported values in `detail`, never a silently unfiltered or empty list.\n\nGeometries are not included in the list - fetch `GET /v1/parcels/{parcel_id}` for one parcel's geometry."
          },
          "response": []
        },
        {
          "name": "Bulk import parcels",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/bulk",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                "bulk"
              ]
            },
            "description": "Imports many parcels in one asynchronous job and returns `202` with a `job_id`. Poll `GET /v1/jobs/{job_id}` or subscribe to the `job.completed` webhook.\n\nA single bad row never fails the import: the job result lists every rejected row with its zero based `index`, so you can find it in your own file. Rows whose geometry already exists for your account are counted as `duplicates`, not as errors.\n\nThe result also carries `rows`, one entry per submitted row in submission order: `{index, external_ref, status, parcel_id, error}` with `status` one of `created`, `duplicate` or `failed`. A `duplicate` row carries the `parcel_id` of the existing parcel it repeats, so every line of your file can be mapped to a parcel without a second lookup; `parcel_ids` lists only the created ones.\n\nThe parcel quota is checked before the job is created, but only against the rows that can actually create a parcel: rows that repeat a geometry already registered for your account (or another row of the same file) do not count, so re-importing a file you already loaded is accepted even at the quota limit. `429 parcel_quota_exceeded` is returned immediately when the new rows alone do not fit the plan.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"parcels\": [\n    {\n      \"external_ref\": \"POLICY-1\",\n      \"geometry\": {\n        \"coordinates\": [\n          [\n            [\n              24.0,\n              55.0\n            ],\n            [\n              24.005,\n              55.0\n            ],\n            [\n              24.005,\n              55.003\n            ],\n            [\n              24.0,\n              55.003\n            ],\n            [\n              24.0,\n              55.0\n            ]\n          ]\n        ],\n        \"type\": \"Polygon\"\n      }\n    },\n    {\n      \"cadastral_no\": \"000467-3875\",\n      \"country\": \"LT\",\n      \"external_ref\": \"POLICY-2\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "Get one parcel",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id"
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Returns the parcel with its canonical geometry and `data_status`.\n\nA parcel that belongs to another account returns `404`, never `403`: a `403` would confirm that the identifier exists."
          },
          "response": []
        },
        {
          "name": "Remove a parcel from monitoring",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id"
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Stops monitoring the parcel. The row is soft deleted: the data stays available to support and audits for 90 days and the satellite cache is kept, because it is shared by geometry hash.\n\nBilling: the parcel leaves your quota from the start of the next period. The current period is billed on its peak parcel count, so deleting on the last day of the month does not reduce that month's invoice.\n\nThe call is idempotent: deleting a parcel you already deleted returns `204` again, so a retry after a network failure is safe. `404` means the identifier was never yours. The original `deleted_at` is kept."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Risk",
      "description": "Parcel risk scores and portfolio aggregates.",
      "item": [
        {
          "name": "Explainable parcel risk score",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/risk-score?as_of=2026-09-01",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "risk-score"
              ],
              "query": [
                {
                  "key": "as_of",
                  "value": "2026-09-01",
                  "description": "Defaults to today. Cannot be in the future.",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Returns a 0-100 risk score (higher is riskier), its class A-E and the six components it was built from.\n\nEvery component is a raw indicator normalised to 0-100 by its **percentile rank in a reference population**: parcels in the same country and the same land use class. A score therefore answers 'how does this parcel compare with its peers this season', not 'how does it compare with an absolute curve'. In a drought year the whole population moves, and a parcel that merely followed the country does not silently become high risk.\n\nWeights: vegetation_trend 0.25, sar_events 0.20, water_heat_stress 0.20, yield_variability 0.15, soil_topography 0.10, land_use_compliance 0.10. A component that cannot be computed is dropped and the remaining weights are renormalised to 1.0; it still appears in `components` with `available: false` and a `reason`. When three or more components are missing the score is not computed at all and `status` becomes `insufficient_data`.\n\nThe score is produced by arithmetic only. No language model takes part in deriving it.\n\nScores for a past `as_of` are immutable: the stored result is returned unchanged, so a score quoted in a credit file a year ago can still be re"
          },
          "response": []
        },
        {
          "name": "Ranked parcel list for a portfolio",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portfolios/:portfolio_id/parcels?class=D&min_score=60&limit=100",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portfolios",
                ":portfolio_id",
                "parcels"
              ],
              "query": [
                {
                  "key": "as_of",
                  "value": "",
                  "description": "Defaults to today. Cannot be in the future.",
                  "disabled": true
                },
                {
                  "key": "class",
                  "value": "D",
                  "description": "Only parcels in this class, A-E",
                  "disabled": false
                },
                {
                  "key": "min_score",
                  "value": "60",
                  "description": "Only parcels scoring at least this much, 0-100",
                  "disabled": false
                },
                {
                  "key": "district",
                  "value": "",
                  "description": "Only parcels with this `district_code`",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "100",
                  "description": "Page size, default 100, maximum 500",
                  "disabled": false
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "Opaque cursor",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "portfolio_id",
                  "value": "default"
                }
              ]
            },
            "description": "Every parcel in the portfolio with its latest score on or before `as_of`, highest score first, as a cursor paginated list. `summary` stops at the 20 riskiest parcels; this is the endpoint for 'every class D and E parcel in the Kaunas district' or 'everything above 60'.\n\nFilters: `class` (A-E), `min_score` (0-100) and `district` (a `district_code`). Parcels without a usable score are listed **after** every scored parcel with `score: null` and a `status` that says why (`insufficient_data`, or `not_scored` when nothing has been computed yet); `class` and `min_score` exclude them, because they cannot match. A parcel's class is the one its rounded score falls into, exactly as `risk-score` reports it.\n\nOffsets are not supported. Pass the returned `next_cursor` back as `?cursor=` until `has_more` is `false`; an unknown query parameter is `400 validation_error`, never a silently unfiltered list. A portfolio with no parcels at all is a 404, a filter that matches nothing is an empty page.\n\n`portfolio_id` selects parcels you already registered: `default` is your whole book, `district:LT-KAU` one district, `land_use:arable` one land use class and `country:LT` one country. An unknown selector i"
          },
          "response": []
        },
        {
          "name": "Recompute every score in a portfolio",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portfolios/:portfolio_id/risk-score/refresh",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portfolios",
                ":portfolio_id",
                "risk-score",
                "refresh"
              ],
              "variable": [
                {
                  "key": "portfolio_id",
                  "value": "default"
                }
              ]
            },
            "description": "Queues a batch recomputation and returns `202` with a `job_id`. Poll `/v1/jobs/{job_id}`; `risk.score_changed` webhooks are emitted for parcels whose class actually moved, and `job.completed` when the batch ends.\n\nThis is an `expensive` pool call: a 10 000 parcel book is hours of work, not a request you retry in a loop. Repeating the same `Idempotency-Key` returns the existing job instead of starting a second one.\n\n`portfolio_id` selects parcels you already registered: `default` is your whole book, `district:LT-KAU` one district, `land_use:arable` one land use class and `country:LT` one country. An unknown selector is a 404 rather than a silent widening to every parcel."
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "Aggregated portfolio view",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/portfolios/:portfolio_id/summary",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "portfolios",
                ":portfolio_id",
                "summary"
              ],
              "query": [
                {
                  "key": "as_of",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "portfolio_id",
                  "value": "default"
                }
              ]
            },
            "description": "Area weighted portfolio score, the A-E distribution, the change against the score 90 days ago and the 20 highest scoring parcels with your own `external_ref`.\n\nParcels that have no usable score are **not** averaged in. They are reported separately as `parcels_without_score`, because for a lender the difference between 'this book is safe' and 'a quarter of this book is unknown' is the whole point.\n\n`portfolio_id` selects parcels you already registered: `default` is your whole book, `district:LT-KAU` one district, `land_use:arable` one land use class and `country:LT` one country. An unknown selector is a 404 rather than a silent widening to every parcel."
          },
          "response": []
        }
      ]
    },
    {
      "name": "SAR",
      "description": "Sentinel-1 events: lodging, mowing, harvest, tillage.",
      "item": [
        {
          "name": "Sentinel-1 VV/VH backscatter time series",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/sar?from=2026-06-01&to=2026-08-01",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "sar"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "2026-06-01",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "to",
                  "value": "2026-08-01",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "orbit_dir",
                  "value": "",
                  "description": "Restrict to one orbit direction: A (ascending) or D (descending)",
                  "disabled": true
                },
                {
                  "key": "rel_orbit",
                  "value": "",
                  "description": "Restrict to one Sentinel-1 relative orbit number (1-175)",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Calibrated sigma0 backscatter in dB for the parcel, one row per Sentinel-1 scene.\n\nEvery comparison is made **within one relative orbit and one orbit direction**. Scenes from different orbits see the parcel from a different angle, so mixing them produces events that never happened. Scenes without orbit metadata are excluded rather than guessed. That is why observations are grouped into `tracks`: each track is one relative orbit and one direction, and only scenes inside a track may be compared with each other.\n\nValues are means inside a 10 m negative buffer, so field edges, ditches and roads do not drag the signal. A value is either measured or `null` with a `reason` - never interpolated.\n\nParcels below 1 ha return `422 insufficient_data`: at that size the buffered parcel no longer contains enough Sentinel-1 pixels to mean anything.\n\n`orbit_dir` and `rel_orbit` (1-175) restrict the answer to one track. When scenes exist but none is on the requested orbit, the response is `422 insufficient_data` with `detail.reason: no_scenes_for_filter` and `detail.available_tracks` listing the orbits that do cover the parcel."
          },
          "response": []
        },
        {
          "name": "Detected SAR events (lodging, harvest, mowing, tillage, regrowth)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/sar/events?from=2026-06-01&to=2026-08-01&type=lodging",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "sar",
                "events"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "2026-06-01",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "to",
                  "value": "2026-08-01",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "type",
                  "value": "lodging",
                  "description": "Filter by event type: lodging, harvest, mowing, tillage, regrowth",
                  "disabled": false
                },
                {
                  "key": "min_confidence",
                  "value": "",
                  "description": "Only events at or above this confidence",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Runs the `sar-events-2.0` detector over the Sentinel-1 scenes of the parcel and returns the events it is confident about.\n\nEvery comparison is made **within one relative orbit and one orbit direction**. Scenes from different orbits see the parcel from a different angle, so mixing them produces events that never happened. Scenes without orbit metadata are excluded rather than guessed.\n\n**Confidence.** Every event carries `confidence` (`0.40*signal + 0.15*scenes + 0.25*phenology + 0.20*ndvi`) and a label: `high` from 0.75, `medium` from 0.50, `low` from 0.40. Detections below 0.40 are stored for calibration but never returned - `candidates_suppressed` tells you how many there were.\n\n**Phenology.** A lodging signal in March is not lodging. Each rule is checked against a crop and country specific day-of-year window; signals outside it lose confidence instead of being emitted silently.\n\n**Not enough data** (parcel below 1 ha, or fewer than three scenes in one orbit within 30 days) returns `422 insufficient_data` with `detail.reason`, never an empty list that looks like 'no events'.\n\nA change is measured between two neighbouring scenes, so the detector reads 30 days of scenes before `fro"
          },
          "response": []
        },
        {
          "name": "Relative soil moisture from Sentinel-1",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/sar/soil-moisture?from=2026-07-01&to=2026-07-31",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "sar",
                "soil-moisture"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "2026-07-01",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "to",
                  "value": "2026-07-31",
                  "description": "",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Relative soil moisture derived from VV backscatter (`sar-moisture-1.0`).\n\n**This is a relative index, not a volumetric measurement.** `unit` is always `relative_index`. 0 means as dry as this parcel has been in the last 24 months on this orbit, 1 means as wet. The dry and wet ends come from the 5th and 95th VV percentile of the same relative orbit - comparing across orbits would compare different viewing geometries. Only scenes with a known `NDVI < 0.45` enter the percentiles: a scene under canopy is biomass, not moisture, and would pull the wet end towards vegetation. `reference.samples` is the number of such scenes, `reference.scenes_in_lookback` the number of all scenes on that orbit in the 24 months.\n\n**Vegetation masks the soil.** The index is only computed when `NDVI < 0.45`; above that the radar sees the canopy, not the ground. Those dates come back with `value: null` and `status: vegetation_masked`, and a window where every date is masked returns `422 vegetation_masked`."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Thermal",
      "description": "Land surface temperature and water stress.",
      "item": [
        {
          "name": "Land surface temperature and anomaly versus the 10 year norm",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/thermal?from=2026-07-01&to=2026-07-31",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "thermal"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "2026-07-01",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "to",
                  "value": "2026-07-31",
                  "description": "",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Sentinel-3 SLSTR land surface temperature for the parcel, with the anomaly against a ten year norm for the same day of year (+/- 7 days), built from the Sentinel-3 archive and NASA POWER `T2M_MAX` as specified by `thermal-wsi-1.0`.\n\n**Resolution is the limit of this product.** Sentinel-3 SLSTR has a 1 km native pixel, resampled onto a 300 m grid. Parcels below 5 ha are refused with `422 insufficient_resolution`; between 5 and 20 ha the response carries `resolution_warning: true`, a plain language `note` and a confidence already multiplied by 0.6. The caveat travels with the number, not only in this documentation.\n\nA temperature is either measured or `null` with a `reason` (`cloud_masked`, `no_valid_pixels`). The anomaly is `null` with `z_reason` whenever the norm rests on too few samples or too little spread - a `0.0` anomaly would read as 'a normal day'."
          },
          "response": []
        },
        {
          "name": "Water stress index, class and consecutive stress days",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/water-stress?season=2026",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "water-stress"
              ],
              "query": [
                {
                  "key": "season",
                  "value": "2026",
                  "description": "Calendar year; defaults to the last 30 days",
                  "disabled": false
                },
                {
                  "key": "from",
                  "value": "",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Water stress index (`thermal-wsi-1.0`) combining the land surface temperature anomaly with the NDWI anomaly:\n\n```\nraw = 0.6 * z_lst - 0.4 * z_ndwi\nWSI = 1 / (1 + exp(-1.2 * (raw - 1.0)))\n```\n\n`stress_class` is `low` below 0.30, `moderate` below 0.60 and `high` from 0.60. `consecutive_stress_days` is the longest uninterrupted run with `WSI >= 0.60` - over the whole season when `season` is given, otherwise in the last 30 days before `to` - the number that separates a hot week from a drought.\n\nWith `season=YYYY` the headline fields (`water_stress_index`, `stress_class`, `lst_celsius`, `anomaly_vs_baseline`, `confidence`) describe the season's **peak**, the observation with the highest index, and `headline_basis` is `season_peak`: a drought in June must not be hidden by a mild December. Without `season` they describe the latest measured observation (`headline_basis: latest`). `peak` is returned in both cases and `latest_date` is always the date of the latest measured observation.\n\nWhen NDWI is missing (clouds, or no baseline yet) the index is computed from the temperature term alone and `confidence` is multiplied by 0.7. When the temperature baseline itself is missing, the index is `nu"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Weather",
      "description": "Forecast, history and agro-climate indicators.",
      "item": [
        {
          "name": "7 day forecast for the parcel",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/weather/forecast?days=7",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "weather",
                "forecast"
              ],
              "query": [
                {
                  "key": "days",
                  "value": "7",
                  "description": "1-7 days, default 7",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Daily forecast at the parcel centroid: maximum and minimum temperature, precipitation and its probability, maximum wind and FAO-56 reference evapotranspiration.\n\nFields the provider did not return are `null` - they are never filled in from a neighbouring day."
          },
          "response": []
        },
        {
          "name": "Daily weather history from 1981",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/weather/history?from=2025-04-01&to=2025-09-01",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "weather",
                "history"
              ],
              "query": [
                {
                  "key": "from",
                  "value": "2025-04-01",
                  "description": "Inclusive start date",
                  "disabled": false
                },
                {
                  "key": "to",
                  "value": "2025-09-01",
                  "description": "Inclusive end date",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Daily temperature, precipitation and humidity for the parcel centroid, plus the NASA POWER season aggregate.\n\nHistory starts in 1981 and ends today: an earlier `from` or a `to` in the future returns `400 date_range_invalid` (`detail.reason: to_in_future`; future days are the forecast endpoint's job). One request may cover at most 10 years. The archive lags a few days behind real time, so the newest days may be missing from `daily`; `days` says how many were returned.\n\nThe `summary` block comes from a second provider (NASA POWER). If that provider does not answer, `summary` is `null` with `summary_reason` and the daily series is still returned."
          },
          "response": []
        },
        {
          "name": "Agroclimate indicators for a season",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/weather/indicators?season=2026",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "weather",
                "indicators"
              ],
              "query": [
                {
                  "key": "season",
                  "value": "2026",
                  "description": "Calendar year; default: current",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Growing degree days (base 5 C and 10 C, capped at 30 C), heat days above 30 C, frost days, dry days and the longest dry spell for the growing season (day of year 90-290, truncated at today).\n\nThresholds live in the versioned method configuration `weather-1.0`; the response repeats them so a number computed a year ago can be reproduced.\n\nIf less than 60% of the days in the window have data, the endpoint returns `422 insufficient_data` with the coverage in `detail` (`coverage_pct`, `days_counted`, `days_expected`), like every other synchronous product - a half season of growing degree days looks exactly like a cold season, so no number is returned at all."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Webhooks",
      "description": "Event subscriptions and signed delivery.",
      "item": [
        {
          "name": "List webhook endpoints",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": ""
          },
          "response": []
        },
        {
          "name": "Register a webhook endpoint",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{idempotencyKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": "Registers an HTTPS endpoint for the given event types and returns the signing secret **once** (an idempotent retry of the same request returns the same response).\n\nThe URL must be `https://`, at most 2048 characters, without credentials, and must point to a public host: loopback, private, link-local, cloud-metadata and internal names are rejected with `validation_error` (`detail.fields[].field == \"url\"`).\n\nEvery delivery is signed: `X-Agrovisio-Signature: v1=hex(hmac_sha256(secret, timestamp + '.' + body))`. Reject the delivery when `|now - X-Agrovisio-Timestamp| > 300`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"events\": [\n    \"damage.completed\",\n    \"parcel.data_ready\",\n    \"job.failed\"\n  ],\n  \"url\": \"https://hooks.example.com/agrovisio\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [],
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Kiekvienas POST turi turėti savo Idempotency-Key.",
                  "pm.variables.set('idempotencyKey',",
                  "    (typeof crypto !== 'undefined' && crypto.randomUUID)",
                  "        ? crypto.randomUUID()",
                  "        : String(Date.now()) + '-' + Math.random().toString(16).slice(2));"
                ]
              }
            }
          ]
        },
        {
          "name": "Webhook event catalog",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhooks/events",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhooks",
                "events"
              ]
            },
            "description": "Lists every event type that can be subscribed to."
          },
          "response": []
        },
        {
          "name": "Delete a webhook endpoint",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/webhooks/:endpoint_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "webhooks",
                ":endpoint_id"
              ],
              "variable": [
                {
                  "key": "endpoint_id",
                  "value": "{{webhookId}}"
                }
              ]
            },
            "description": "Deletes the endpoint together with its delivery history; pending retries are dropped."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Yield",
      "description": "Yield forecasts with confidence intervals.",
      "item": [
        {
          "name": "Yield forecast (alias of /yield-forecast)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/yield?crop=winter_wheat&season=2026",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "yield"
              ],
              "query": [
                {
                  "key": "crop",
                  "value": "winter_wheat",
                  "description": "Crop code or name",
                  "disabled": false
                },
                {
                  "key": "season",
                  "value": "2026",
                  "description": "Season year",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Identical to `GET /v1/parcels/{parcel_id}/yield-forecast`. Both paths are supported and return the same document.\n\nReturns a yield forecast for one parcel with a mandatory confidence interval.\n\n`yield_t_ha = base_yield(crop) * f_ndvi * f_weather * f_disease`, where `f_ndvi` is the season NDVI integral divided by the same integral of the parcel baseline (clipped to 0.4-1.4), and `f_weather` charges 0.6% per heat day above 30 C after the first five and 0.4% per dry day after the first ten. Every factor is returned with the reason it took its value - a single number without them cannot be argued with.\n\nThe width of the interval follows the growth phase: +/-40% before sowing, +/-20% during vegetative growth, +/-10% at flowering, +/-5% at ripening and +/-3% just before harvest. Quote the interval, not the point estimate.\n\nWithout a crop the forecast is not produced: this endpoint returns `400 crop_required` and puts `ndvi_integral` and `pct_of_baseline` in `error.detail`, so you still get the vegetation picture. Pass `?crop=` or store `crop_hint` on the parcel."
          },
          "response": []
        },
        {
          "name": "Yield forecast with a confidence interval",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/parcels/:parcel_id/yield-forecast?crop=winter_wheat&season=2026",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "parcels",
                ":parcel_id",
                "yield-forecast"
              ],
              "query": [
                {
                  "key": "crop",
                  "value": "winter_wheat",
                  "description": "Crop code or name. Falls back to the parcel crop_hint.",
                  "disabled": false
                },
                {
                  "key": "season",
                  "value": "2026",
                  "description": "Season year; defaults to the current one",
                  "disabled": false
                }
              ],
              "variable": [
                {
                  "key": "parcel_id",
                  "value": "{{parcelId}}"
                }
              ]
            },
            "description": "Returns a yield forecast for one parcel with a mandatory confidence interval.\n\n`yield_t_ha = base_yield(crop) * f_ndvi * f_weather * f_disease`, where `f_ndvi` is the season NDVI integral divided by the same integral of the parcel baseline (clipped to 0.4-1.4), and `f_weather` charges 0.6% per heat day above 30 C after the first five and 0.4% per dry day after the first ten. Every factor is returned with the reason it took its value - a single number without them cannot be argued with.\n\nThe width of the interval follows the growth phase: +/-40% before sowing, +/-20% during vegetative growth, +/-10% at flowering, +/-5% at ripening and +/-3% just before harvest. Quote the interval, not the point estimate.\n\nWithout a crop the forecast is not produced: this endpoint returns `400 crop_required` and puts `ndvi_integral` and `pct_of_baseline` in `error.detail`, so you still get the vegetation picture. Pass `?crop=` or store `crop_hint` on the parcel."
          },
          "response": []
        }
      ]
    }
  ]
}
