Agrology · OpenAPI Overlay 1.0.0

API Evangelist enhancements for the Agrology Public API v2

16 actions 16 updates documentation extends openapi/agrology-public-api-openapi.yml
Generated by API Evangelist Written by API Evangelist tooling for Agrology's API. It is a proposal applied on top of the contract, not a document Agrology publishes.
View Overlay File View on GitHub Overlay Specification

What the actions change

descriptionoperationIdsummarycontactlicensetermsOfServiceurlapiKeyAuth

Targets 16

$.info
$.externalDocs
$.servers[0]
$.components.securitySchemes.publicApiAuthorizer
$.components.securitySchemes
$.paths['/access'].get
$.paths['/geojson/site/{siteID}'].get
$.paths['/historical/ground-truth/{siteID}/{timeRange}'].get
$.paths['/historical/ground-truth/{siteID}/{timeRange}'].get.parameters
$.paths['/predictions/microclimate/{siteID}'].get
$.paths['/alerts/customer/{customerID}'].get
$.paths['/fileserver/customer/{customerID}'].get
$.paths['/dashboards/{owner}/{id}'].post
$.paths['/feedback'].post
$.paths['/experiments/data/{customerID}/e/{experimentID}/regenerate'].post
$.paths['/access/manage/{customerID}/{accessUser}/{accessLevel}'].post

OpenAPI Overlay

Raw ↑
overlay: 1.0.0
info:
  title: API Evangelist enhancements for the Agrology Public API v2
  version: 1.0.0
extends: openapi/agrology-public-api-openapi.yml
x-generated: '2026-09-13'
x-method: generated
x-source: >-
  Generated from the provider's own published documentation
  (https://github.com/agrology/public-api-docs/blob/main/README.md) and from live probes
  of https://api.agrology.ag/v2. Every action below adds description, example or
  contact/licence metadata that Agrology states in prose but omits from its machine-readable
  contract. NOTHING here changes a path, a parameter, a schema type or a security
  requirement, and the original spec in openapi/ is never mutated.
actions:

- target: $.info
  description: >-
    Add the contact, licence, terms and description metadata the contract omits. All four
    values are published by Agrology — contact from the GitHub organization's public
    email, licence from the LICENSE file alongside the spec in the same repository.
  update:
    description: |
      The Agrology Public API v2 exposes site and node configuration, field geometry as
      GeoJSON, historical ground-truth sensor telemetry, historical and forecast weather
      service data, ML-synthesized microclimate metrics, microclimate predictions,
      threshold alerts, report definitions and runs, library charts, dashboards, customer
      inputs and field experiments.

      Authentication: attach either `Authorization: Bearer $ACCESS_TOKEN` (a Cognito JWT
      collected from the Grower's Portal footer, valid one hour) or `x-api-key: $API_KEY`
      (issued by Agrology staff). Either satisfies every operation.

      Start with `GET /access`. Authorization is an entity access list rather than OAuth
      scopes: that response names every customer, site and node the credential may reach,
      and every other path is addressed with ids drawn from it.

      Note the provider's own caveat: this specification is under development, may include
      undocumented endpoints, and its response schemas currently lack data typing.
    contact:
      name: Agrology Support
      email: support@agrology.ag
      url: https://agrology.ag/contact
    license:
      name: Apache-2.0
      url: https://github.com/agrology/public-api-docs/blob/main/LICENSE
    termsOfService: https://agrology.ag/terms

- target: $.externalDocs
  description: Point at the provider's own README, which carries the time-range grammar,
    the filtering rules and every response example the spec lacks.
  update:
    description: Agrology Public API documentation
    url: https://github.com/agrology/public-api-docs/blob/main/README.md

- target: $.servers[0]
  description: Name the single production server.
  update:
    description: Production

- target: $.components.securitySchemes.publicApiAuthorizer
  description: >-
    Document how the bearer token is obtained and that it expires after one hour — stated
    in the README, absent from the scheme.
  update:
    description: |
      Bearer token authentication. Obtain a token by logging in to the Grower's Portal at
      https://grower.agrology.ag/ and clicking your username in the application footer.

      Tokens expire ONE HOUR after issue and there is no refresh flow — you must return to
      the portal for a new one. For unattended machine clients use the x-api-key scheme
      instead.

      Tokens are RS256 JWTs issued by the Amazon Cognito user pool us-east-1_qBmvSfyNK,
      whose OIDC discovery document is published at
      https://cognito-idp.us-east-1.amazonaws.com/us-east-1_qBmvSfyNK/.well-known/openid-configuration

- target: $.components.securitySchemes
  description: >-
    Add the x-api-key scheme. The README states that clients must attach "either a Bearer
    Token or an API key" to ALL requests, but the contract declares only the bearer
    scheme, so a generated client has no way to send the credential most integrations
    actually use.
  update:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: |
        Static API key authentication. Contact Agrology staff to be issued a key. Self-service
        key rotation from the Grower's Portal is stated by the provider as a roadmap item and
        is not yet available.

        This is the credential to use for unattended machine clients, because it does not
        expire after an hour the way a bearer token does.

        Documented at
        https://github.com/agrology/public-api-docs/blob/main/README.md#api-key

- target: $.paths['/access'].get
  description: Describe the access-list endpoint and give it the operationId the spec omits.
  update:
    operationId: getAccessList
    summary: List the customers, sites and nodes this credential can reach
    description: |
      Returns the caller's access-control list: `siteAccess[]`, one entry per customer,
      each carrying a `displayName` and a `sites` map keyed by site id, each site carrying
      the caller's `role` and its `nodes[]`.

      CALL THIS FIRST. Authorization in this API is entity-scoped, not scope-scoped —
      there is no way to know which customers, sites or nodes a credential may address
      without this response.

      Identifier shapes are not uniform: older customers and sites use slug-style ids
      while newer ones use UUIDs, and all nodes are UUIDs. Do not validate these as UUIDs.

- target: $.paths['/geojson/site/{siteID}'].get
  description: Document the GeoJSON contract and its templated telemetry links.
  update:
    operationId: getSiteGeoJSON
    summary: Get site and node geometry as a GeoJSON FeatureCollection
    description: |
      Returns an RFC 7946 FeatureCollection. Features carry `properties.elementType` of
      `site` or `node`; node features carry Point geometry in longitude, latitude order
      (WGS 84 decimal degrees) along with elevation, crops and the attached device
      inventory.

      The response also carries foreign members alongside the standard GeoJSON ones:
      `historicalURL`, `predictionsURL` and `syntheticsURL` are TEMPLATED links ({site},
      {dataset}) and are this API's only link-following affordance — they take a consumer
      from mapped geometry directly to the matching telemetry endpoints.

      Note the provider's field spelling `siteClassificaiton` (observed value "lab");
      match it as written.

- target: $.paths['/historical/ground-truth/{siteID}/{timeRange}'].get
  description: >-
    Document the time-range grammar, the filter parameters and the response envelope. The
    spec types this response as a bare string; the README describes the real shape.
  update:
    operationId: getGroundTruthHistory
    summary: Retrieve measured in-field sensor telemetry for a site over a time range
    description: |
      Returns measured ground-truth telemetry captured by Agrology's own in-field sensors.

      TIME RANGE ({timeRange} path segment) is `{startTime}-{endTime}`. The end time may be
      omitted, defaulting to now, BUT THE HYPHEN IS STILL REQUIRED. Start and end may use
      different formats:

        - relative: `<integer><unit>`, unit one of s, m, h, d, w — `6d`, `20m`, `36h`.
          Compound forms such as `2h5m` are NOT supported.
        - epoch seconds: exactly 10 digits — `1618203722`
        - epoch milliseconds: exactly 13 digits — `1618203722000`
        - `yyyyMMddhhmm`: exactly 12 digits, beginning `20`, UTC — `202108010000`
        - `yyyyMMddhhmmss`: exactly 14 digits, beginning `20`, UTC — `20210801052530`

      Digit count is how the format is disambiguated: zero-pad and never trim. ISO-8601 is
      not accepted as input.

      FILTERING: optional `deviceType` and `metrics` query parameters each take a
      comma-separated list. Values must match `id`s from
      `GET /historical/ground-truth/device-types` and
      `GET /historical/ground-truth/metrics`. With no filter, every applicable device and
      metric is returned.

      RESPONSE: samples are grouped by node under `nodes{}` and ordered chronologically
      within each node. Node ordering is explicitly NOT guaranteed between requests — key
      on node id, never on array position. Each sample's `d` object is an open map of
      metric id to value whose keys vary by device type, so resolve ids and units from the
      metrics endpoint rather than hard-coding them.

- target: $.paths['/historical/ground-truth/{siteID}/{timeRange}'].get.parameters
  description: >-
    Add the two documented filter parameters. The README defines them precisely; the
    contract omits them entirely, so no generated client can filter.
  update:
  - name: deviceType
    in: query
    required: false
    description: >-
      Comma-separated list of device types to include. Values must match `id` values from
      GET /historical/ground-truth/device-types. Example: `vwc,gV1`.
    schema:
      type: string
    example: vwc,gV1
  - name: metrics
    in: query
    required: false
    description: >-
      Comma-separated list of metric names to include. Values must match `id` values from
      GET /historical/ground-truth/metrics. Example: `airTemp,humidity`.
    schema:
      type: string
    example: airTemp,humidity

- target: $.paths['/predictions/microclimate/{siteID}'].get
  description: Record the forecast horizon and the omitted-time-range behaviour.
  update:
    operationId: getMicroclimateForecast
    summary: Retrieve Agrology microclimate predictions for a site
    description: |
      Microclimate predictions are generated by Agrology using a statistical model with
      inputs from weather-service forecasts and Agrology historical ground-truth data for
      that microclimate.

      Predictions are available up to FOUR DAYS ahead. With the time range omitted (this
      operation), the start is assumed to be now and all future predictions are returned.

      Response uses the standard time-series envelope.

- target: $.paths['/alerts/customer/{customerID}'].get
  description: Document the alert envelope, including its push-notification block and its
    inconsistent timestamp format.
  update:
    operationId: listCustomerAlerts
    summary: List threshold alerts for a customer
    description: |
      Returns alerts raised when configured thresholds are met — for example a forecast
      extreme-cold or extreme-heat warning. Alerts are aggregated per customer and may
      span multiple sites.

      The envelope carries an `aps` block (an Apple Push Notification payload) alongside
      `alerts[]`, because this endpoint doubles as the mobile push feed.

      NOTE a real inconsistency: alert `startTime` and `updateTime` are ISO-8601 strings
      ("2023-03-07T13:01:41Z"), whereas every other timestamp in this API — including
      sample `ts` and every {timeRange} input — is an epoch integer.

      There is no webhook or event-stream equivalent; alerts must be polled.

- target: $.paths['/fileserver/customer/{customerID}'].get
  description: Document the two-step pre-signed download flow.
  update:
    operationId: getCustomerFileDownloadURL
    summary: Exchange an s3:// file location for a pre-signed download URL
    description: |
      Some response bodies carry internet file locations such as
      `s3://bucket-name.domain.com/path/to/file.txt` rather than fetchable URLs. To read
      one, call this endpoint with the file location path in the `file` query parameter —
      PROPERLY URLENCODED — and it returns a temporary pre-signed URL.

      Example: `/fileserver/customer/{customerID}?file=/path/to/file.txt`

      The returned URL is short-lived. Follow it immediately; do not cache or persist it.
      Re-call this endpoint to mint a new one.

- target: $.paths['/dashboards/{owner}/{id}'].post
  description: >-
    Make the reversibility of dashboard deletion explicit. This is the ONLY reversible
    delete in the entire API and the contract states it only in a four-word summary.
  update:
    operationId: undeleteDashboard
    summary: Undelete a specific Dashboard
    description: |
      Restores a dashboard previously removed with DELETE /dashboards/{owner}/{id}.

      This is the only documented reversal path in the Agrology Public API. Deletes of
      experiments, experiment groups, experiment members, experiment metrics, report
      definitions, library charts and customer inputs have NO undelete and should be
      treated as permanent.

      Agrology does not publish a retention window for this restore, so the period during
      which it will succeed is unknown.

- target: $.paths['/feedback'].post
  description: Give the feedback endpoint an operationId and note its test flag.
  update:
    operationId: submitFeedback
    description: |
      Submit a feature request, bug report or other feedback about the Agrology platform.

      Set `isTestMessage: true` for any submission that is not genuine so the
      customer-success team can ignore it. Set `wantsFollowUp: true` to ask to be
      contacted.

      This write is not idempotent and has no reversal — a retry files a second record.

- target: $.paths['/experiments/data/{customerID}/e/{experimentID}/regenerate'].post
  description: Warn that regeneration is destructive to computed data.
  update:
    operationId: regenerateExperimentData
    description: |
      Requests that the experiment's computed time-series data be fully regenerated from
      source telemetry. The existing computed series is discarded and rebuilt.

      Returns 204. This is not a reversal — there is no way to restore the previous
      computation. Prefer POST .../run for an incremental re-run.

- target: $.paths['/access/manage/{customerID}/{accessUser}/{accessLevel}'].post
  description: Flag the consequence of granting access.
  update:
    operationId: grantCustomerAccess
    description: |
      Grants {accessUser} access to {customerID} at {accessLevel}.

      HIGH CONSEQUENCE: this changes who can read a grower's agronomic data. The
      accessLevel vocabulary is not published; the only value appearing in Agrology's own
      documentation is `admin`.

      Reversed with DELETE /access/manage/{customerID}/{accessUser}.