MT2Data Platform

MT2 Data offers customized solutions to apply machine learning in the agricultural sector. Our platform connects your operations to leading agronomic data providers through a unified, secure API.

Products

Colmeia

Colmeia is the integration hub that connects your application to agricultural data providers. It exposes a unified REST API that normalises data from multiple providers behind a single authentication model.

Supported integrations:

  • John Deere — equipment, fields, and operations data
  • Syngenta Cropwise — properties, crops, and monitoring
  • Solinftec — data integration

All endpoints return a standardised JSON envelope:

{ "success": true,  "data": { /* ... */ } }
{ "success": false, "error": "<snake_case_code>" }

Base URL: https://colmeia.mt2data.cloud/api

Datalake Gateway

The MT2Data Datalake Gateway provides access to the platform's agronomic data lake via OAuth 2.0 Client Credentials. When using Colmeia, token management is handled automatically on your behalf.

Base URL: https://datalake.mt2data.cloud

Getting started

Access credentials and onboarding details are provided during the integration handoff with the MT2Data team. Once you have your credentials, refer to the product guides in this documentation to start making API calls.

Colmeia Platform

Colmeia is the API middleware layer for the MT2Data agricultural data platform. It provides a unified, tenant-scoped interface for accessing farm management data from multiple integrated providers.

Public base URL: https://colmeia.mt2data.cloud/api

What Colmeia provides

  • Provider integrations — John Deere Operations Center, Syngenta Cropwise, Solinftec SCDI, and MT2Data Datalake
  • Transparent proxying — all provider authentication is handled server-side; you only need a tenant bearer secret
  • Mobile access — WhatsApp/bot interface for field-level data queries

Authentication

Your tenant ID and bearer secret are provisioned by MT2Data. Include the bearer secret in every request:

Authorization: Bearer <tenant-secret>

MT2Data Datalake via Colmeia

Overview

Access MT2Data Datalake files through the Colmeia middleware. Your Datalake credentials are connected to your tenant by MT2Data — you only need your tenant bearer secret to make requests.

Base URL: https://colmeia.mt2data.cloud/api/dl

Authentication

All data routes require your tenant bearer secret:

Authorization: Bearer <tenant-secret>

The middleware handles Datalake OAuth token management automatically.


Catalog

GET /api/dl/data/:tenantId/catalog

Returns all Symbols your Datalake subscription is authorized to access, with descriptions and file paths. Fast (~1s).

curl https://colmeia.mt2data.cloud/api/dl/data/YOUR_TENANT_ID/catalog \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"
{
  "clientId": "your-account",
  "files": [
    {
      "Symbol": "DatasetSymbol",
      "Description": "Human-readable description",
      "FileName": "Data/MT2/DatasetSymbol.parquet"
    }
  ],
  "count": 1
}

File Listing

GET /api/dl/data/:tenantId/list

Lists files that appear in your catalog and exist in storage — with size and upload timestamp. Uses batched verification (~2s for 500 files).

ParameterRequiredDefaultDescription
limitNo1000Max files to return
curl "https://colmeia.mt2data.cloud/api/dl/data/YOUR_TENANT_ID/list?limit=100" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

GET /api/dl/data/:tenantId/count

Returns the total count of accessible files (~2s).

curl https://colmeia.mt2data.cloud/api/dl/data/YOUR_TENANT_ID/count \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

File Access

GET /api/dl/data/:tenantId/info/:symbol

Returns metadata for a file by Symbol (~0.5s).

curl https://colmeia.mt2data.cloud/api/dl/data/YOUR_TENANT_ID/info/DatasetSymbol \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

GET /api/dl/data/:tenantId/download/:symbol

Downloads a file by Symbol. Returns binary content.

curl https://colmeia.mt2data.cloud/api/dl/data/YOUR_TENANT_ID/download/DatasetSymbol \
  -H "Authorization: Bearer YOUR_TENANT_SECRET" \
  -o dataset.parquet

Performance Reference

EndpointSpeedUse Case
/catalog~1sBrowse available Symbols
/info/:symbol~0.5sFile metadata
/download/:symbol~0.5s + transferDownload a file
/list~2sVerify file existence with metadata
/count~2sCount accessible files

Recommended workflow:

  1. GET /catalog — find the Symbol
  2. GET /download/:symbol — download directly

Code Examples

Python

import requests

BASE = "https://colmeia.mt2data.cloud/api/dl/data"
TENANT = "YOUR_TENANT_ID"
HEADERS = {"Authorization": "Bearer YOUR_TENANT_SECRET"}

# Browse catalog
catalog = requests.get(f"{BASE}/{TENANT}/catalog", headers=HEADERS).json()
print(f"Symbols available: {catalog['count']}")

# Download a file
with requests.get(f"{BASE}/{TENANT}/download/DatasetSymbol", headers=HEADERS, stream=True) as r:
    r.raise_for_status()
    with open("dataset.parquet", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

JavaScript / Node.js

const BASE = 'https://colmeia.mt2data.cloud/api/dl/data/YOUR_TENANT_ID';
const HEADERS = { Authorization: 'Bearer YOUR_TENANT_SECRET' };

const catalog = await fetch(`${BASE}/catalog`, { headers: HEADERS }).then(r => r.json());
console.log(`Symbols: ${catalog.count}`);

const fileData = await fetch(`${BASE}/download/DatasetSymbol`, { headers: HEADERS })
  .then(r => r.arrayBuffer());
require('fs').writeFileSync('dataset.parquet', Buffer.from(fileData));

Error Codes

HTTPCodeMeaning
401no_token_foundYour Datalake account is not connected — contact MT2Data
403upstream Access deniedSymbol not in your subscription
404upstream Not foundFile does not exist

John Deere Integration

Overview

Access agricultural data from John Deere Operations Center through the Colmeia middleware. Your John Deere account is connected to your tenant by MT2Data — you only need your tenant bearer secret to make requests.

Base URL: https://colmeia.mt2data.cloud/api/jd

Authentication

All data routes require your tenant bearer secret:

Authorization: Bearer <tenant-secret>

Pagination

John Deere uses HATEOAS pagination. Responses include a links array where a nextPage entry signals additional pages. The middleware rewrites all uri values in links to point through itself, so you can follow pagination links without a JD token:

{
  "total": 153,
  "links": [
    { "rel": "self",     "uri": "https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/flag-categories" },
    { "rel": "nextPage", "uri": "https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/flag-categories?pageOffset=10&itemLimit=10" }
  ],
  "values": [...]
}

Follow the nextPage URI directly — no additional auth headers required.


Organizations & Farms

Get Organizations

Endpoint: GET /jd/data/:tenantId/organizations

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Get Single Organization

Endpoint: GET /jd/data/:tenantId/organizations/:orgId

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Get Organization Settings

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/settings

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/settings \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Get Farms for Organization

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/farms

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/farms \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Fields & Boundaries

Get Fields for Organization

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/fields

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/fields \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

List Boundaries for Organization

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/boundaries

List Boundaries for Field

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/fields/:fieldId/boundaries

Get Boundary by ID

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/fields/:fieldId/boundaries/:boundaryId

Get Field Operation Boundary

Endpoint: GET /jd/data/:tenantId/field-operations/:operationId/boundary


Equipment

Note: The deprecated /machines and /implements endpoints were removed by John Deere on March 15, 2025. Use the Equipment API routes below. Scope eq1 is required.

Get Equipment for Organization

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/equipment

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/equipment \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Response:

{
  "total": 1,
  "values": [
    {
      "@type": "Machine",
      "id": "equipment-id-123",
      "name": "Tractor Model 2022",
      "modelYear": 2022,
      "links": [...]
    }
  ]
}

Get Equipment by ID

Endpoint: GET /jd/data/:tenantId/equipment/:equipmentId

Get Field Operations for Organization

Endpoint: GET /jd/data/:tenantId/organizations/:orgId/field-operations


Machine Telemetry

These endpoints require the machine to be a connected JD telematics device (active modem). Machines without an active modem return 404 or 410 — this is expected JD behavior, not a middleware error. Check the telematicsCapable field on equipment records as an indicator.

Device State Reports

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/machines/MACHINE_ID/device-state-reports \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Machine Alerts

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/machines/MACHINE_ID/alerts \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Location History

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/machines/MACHINE_ID/location-history \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"
curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/machines/MACHINE_ID/breadcrumbs \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Hours of Operation

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/machines/MACHINE_ID/hours-of-operation \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Engine Hours

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/machines/MACHINE_ID/engine-hours \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Assets

List Assets for Organization

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/assets \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Get Asset by ID

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/assets/ASSET_ID \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Get Asset Locations

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/assets/ASSET_ID/locations \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Asset Catalog

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/asset-catalog \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Flags & Preferences

List Flags for Organization

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/flags \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

List Flag Categories

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/flag-categories \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

List Flags for Field

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/fields/FIELD_ID/flags \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Products & Agronomic Catalog

Chemicals

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/chemicals \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Fertilizers

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/fertilizers \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Varieties

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/varieties \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Active Ingredients

Served by the JD Equipment API (scope eq1 required).

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/active-ingredients \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Crop Types

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/crop-types \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Files & Map Layers

Files

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/files \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Map Layer Summaries

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/fields/FIELD_ID/map-layer-summaries \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Map Layers

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/map-layers/LAYER_ID \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Guidance Lines

curl https://colmeia.mt2data.cloud/api/jd/data/YOUR_TENANT_ID/organizations/YOUR_ORG_ID/fields/FIELD_ID/guidance-lines \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Code Example

const TENANT_ID = 'YOUR_TENANT_ID';
const BASE = 'https://colmeia.mt2data.cloud/api';
const headers = { Authorization: 'Bearer YOUR_TENANT_SECRET' };

// List organizations
const orgs = await fetch(`${BASE}/jd/data/${TENANT_ID}/organizations`, { headers })
  .then(r => r.json());

// Get fields for an org
const orgId = orgs.values[0].id;
const fields = await fetch(`${BASE}/jd/data/${TENANT_ID}/organizations/${orgId}/fields`, { headers })
  .then(r => r.json());

console.log(`Found ${fields.values?.length} fields`);

Scope Reference

ScopeDescription
org1View staff, operators, and partners
eq1View equipment data, machine locations
ag1View locations, farms, fields
ag2View + analyze production data, field operations
offline_accessLong-term token access

Error Codes

CodeMeaning
no_token_foundYour John Deere account is not connected — contact MT2Data
org_disabledThis organization has been disabled for your tenant

Middleware API

Overview

The Colmeia middleware provides a unified API for accessing agricultural data from connected providers. Each tenant has a bearer secret that authenticates all data requests.

Base URL: https://colmeia.mt2data.cloud/api

Authentication

All data routes require a tenant bearer secret in the Authorization header:

Authorization: Bearer <tenant-secret>

Tenant secrets are provisioned by MT2Data and can be rotated through the dashboard. Contact support if you need a new secret.


Connection Status

GET /api/data/:tenantId/connections

Returns the connection state for all providers configured for your tenant. Use this as a pre-flight check before making data requests.

curl https://colmeia.mt2data.cloud/api/data/YOUR_TENANT_ID/connections \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Response:

{
  "success": true,
  "data": {
    "tenant_id": "your-tenant-id",
    "connections": [
      { "provider": "john_deere", "connected": true, "expired": false, "expires_at": 1714086400, "auto_renew": true },
      { "provider": "syngenta",   "connected": true, "expired": false },
      { "provider": "solinftec",  "connected": true, "expired": false, "expires_at": 2092433279, "auto_renew": true },
      { "provider": "datalake",   "connected": true, "expired": false, "expires_at": 1714090000, "auto_renew": true }
    ]
  }
}

A connected: false provider means that integration has not been configured for your tenant yet. Contact MT2Data to enable it.


Route Discovery

GET /api/

Returns the full list of available routes. No auth required.

curl https://colmeia.mt2data.cloud/api/

Response Envelope

All endpoints return a consistent JSON envelope:

{ "success": true,  "data": { ... } }
{ "success": false, "error": "snake_case_error_code" }

Client-facing error codes

CodeHTTPMeaning
unauthorized401Missing or invalid tenant bearer secret
unsupported_provider400Unknown provider ID
phone_not_linked404Phone number not provisioned to any tenant

Solinftec Integration

Overview

Access field operations, refueling, and meteorological data from the Solinftec SCDI platform through the Colmeia middleware. Your Solinftec account is connected to your tenant by MT2Data — you only need your tenant bearer secret to make requests.

Base URL: https://colmeia.mt2data.cloud/api/sl

Authentication

All data routes require your tenant bearer secret:

Authorization: Bearer <tenant-secret>

The middleware handles Solinftec's rotating short-lived tokens automatically. No manual token management is needed on your side.

Pagination

All three endpoints support page and size query parameters. Each response includes:

FieldDescription
pageCurrent page number
page_sizeRecords per page
total_pagesTotal pages available
dataArray of records

Increment page until page >= total_pages to retrieve all records.


Operation Details

Endpoint: GET /api/sl/data/:tenantId/operations

Returns field operation history with text descriptions alongside numeric codes. Recommended to query one day at a time.

Query Parameters:

ParameterRequiredFormatDescription
dateiniYesDD/MM/YYYY HH:MM:SSStart datetime
datefimYesDD/MM/YYYY HH:MM:SSEnd datetime
pageNointegerPage number (default: 1)
sizeNointegerRecords per page (default: 1000)
equipamentoNostringFilter by equipment code
operacaoNostringFilter by operation code
operadorNostringFilter by operator code
unidadeNostringFilter by unit code
talhaoNostringFilter by field (talhão) code
ordemservicoNostringFilter by work order
curl "https://colmeia.mt2data.cloud/api/sl/data/YOUR_TENANT_ID/operations?dateini=10/09/2024%2000:00:00&datefim=10/09/2024%2023:59:59" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Response:

{
  "page": 1,
  "page_size": 1000,
  "total_pages": 1,
  "total_rows": 42,
  "data": [
    {
      "cd_id": 12345,
      "dt_movimentacao": "10/09/2024 00:00:00",
      "cd_equipamento": "T001",
      "desc_equipamento": "Equipment description",
      "cd_operacao": "101",
      "desc_operacao": "Operation description",
      "cd_operador": "OP01",
      "desc_operador": "Operator name",
      "vl_consumo": 45.2
    }
  ]
}

Train Refueling

Endpoint: GET /api/sl/data/:tenantId/refueling

Returns fuel volume dispensed and hourmeter readings. Returns all active trains — no per-train filtering.

Query Parameters:

ParameterRequiredFormatDescription
dateYesDD/MM/YYYYDate to query
pageNointegerPage number (default: 1)
sizeNointegerRecords per page (default: 1000, max: 10000)
curl "https://colmeia.mt2data.cloud/api/sl/data/YOUR_TENANT_ID/refueling?date=10/02/2024" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Meteorological Data

Endpoint: GET /api/sl/data/:tenantId/weather

Returns hourly climate metrics (rainfall, wind speed, temperature, humidity).

Query Parameters:

ParameterRequiredFormatDescription
dateYesDD/MM/YYYYDate to query
pageNointegerPage number (default: 1)
sizeNointegerRecords per page (default: 1000)
curl "https://colmeia.mt2data.cloud/api/sl/data/YOUR_TENANT_ID/weather?date=02/11/2024" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Pagination Example

PAGE=1
TOTAL=999

while [ $PAGE -le $TOTAL ]; do
  RESP=$(curl -s "https://colmeia.mt2data.cloud/api/sl/data/YOUR_TENANT_ID/operations?dateini=10/09/2024%2000:00:00&datefim=10/09/2024%2023:59:59&page=$PAGE" \
    -H "Authorization: Bearer YOUR_TENANT_SECRET")
  TOTAL=$(echo $RESP | jq '.total_pages')
  # process $RESP data...
  PAGE=$((PAGE + 1))
done

Error Codes

HTTPCodeMeaning
400missing_date_rangedateini or datefim not provided (operations)
400missing_datedate not provided (refueling / weather)
401no_token_foundYour Solinftec account is not connected — contact MT2Data

Syngenta Cropwise Integration

Overview

Access farm properties, fields, and monitoring data from Syngenta Cropwise through the Colmeia middleware. Your Cropwise account is connected to your tenant by MT2Data — you only need your tenant bearer secret to make requests.

Base URL: https://colmeia.mt2data.cloud/api/cw

Authentication

All data routes require your tenant bearer secret:

Authorization: Bearer <tenant-secret>

Farm Management

List Properties (Farms)

Endpoint: GET /cw/data/:tenantId/properties

curl https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/properties \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Response:

{
  "total_elements": 3,
  "content": [
    {
      "id": "property-uuid",
      "name": "Property Name",
      "field_count": 27,
      "total_area": 3864.4776,
      "state": "MT",
      "city": "City Name",
      "reference_point": {
        "type": "Point",
        "coordinates": [-52.78, -11.31]
      }
    }
  ]
}

List Fields

Endpoint: GET /cw/data/:tenantId/properties/:propertyId/fields

curl https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/properties/PROPERTY_ID/fields \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Response:

{
  "total_elements": 70,
  "content": [
    {
      "id": "field-id",
      "name": "Field Name",
      "area": 154.32,
      "crop_type": "Soybean",
      "boundary": { "type": "Polygon", "coordinates": [[]] }
    }
  ]
}

Monitoring Data

Get Field Indicators

Endpoint: GET /cw/data/:tenantId/monitoring/indicators

Get monitoring indicators (pest pressure, disease risk, etc.) for fields.

curl "https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/monitoring/indicators?propertyId=PROPERTY_ID&start=2024-01-01&end=2024-12-31" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Query Parameters:

ParameterRequiredDescription
propertyIdYesProperty ID
startNoStart date (YYYY-MM-DD)
endNoEnd date (YYYY-MM-DD)

Get Geo-Referenced Monitoring Points

Endpoint: GET /cw/data/:tenantId/monitoring/points

curl "https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/monitoring/points?propertyId=PROPERTY_ID" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Get Monitoring Changes (Incremental Sync)

Endpoint: GET /cw/data/:tenantId/properties/:propertyId/monitoring/changes

Fetch only observations that changed within a date range — efficient for incremental sync.

curl "https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/properties/PROPERTY_ID/monitoring/changes?start=2026-01-25&end=2026-01-31" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Query Parameters:

ParameterRequiredDescription
startYesStart date (YYYY-MM-DD)
endYesEnd date (YYYY-MM-DD) — max 7 days from start
date_referenceNo"sync_date" or "observation_date" (default: "sync_date")

Agronomic Context

Get Current Season Crops

Endpoint: GET /cw/data/:tenantId/properties/:propertyId/crops

curl "https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/properties/PROPERTY_ID/crops" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Query Parameters: date (YYYY-MM-DD, defaults to today)

Get Crops Catalog

Endpoint: GET /cw/data/:tenantId/catalog/crops

curl "https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/catalog/crops?country=BR" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Field Notes & Observations

Get Field Notes

Endpoint: GET /cw/data/:tenantId/properties/:propertyId/notes

curl "https://colmeia.mt2data.cloud/api/cw/data/YOUR_TENANT_ID/properties/PROPERTY_ID/notes" \
  -H "Authorization: Bearer YOUR_TENANT_SECRET"

Code Example

const TENANT_ID = 'YOUR_TENANT_ID';
const BASE = 'https://colmeia.mt2data.cloud/api';
const headers = { Authorization: 'Bearer YOUR_TENANT_SECRET' };

// Get all properties
const properties = await fetch(`${BASE}/cw/data/${TENANT_ID}/properties`, { headers })
  .then(r => r.json());

// Get fields for first property
const propertyId = properties.content[0].id;
const fields = await fetch(
  `${BASE}/cw/data/${TENANT_ID}/properties/${propertyId}/fields`, { headers }
).then(r => r.json());

console.log(`${properties.total_elements} properties, ${fields.total_elements} fields`);

Error Codes

CodeMeaning
no_token_foundYour Cropwise account is not connected — contact MT2Data
authentication_failedStored credentials need to be refreshed — contact MT2Data

Troubleshooting

Common Errors

401 — Missing or invalid tenant secret

Cause: The Authorization: Bearer header is missing, empty, or contains an incorrect secret.

Fix: Verify your tenant secret is correct and included on every request:

Authorization: Bearer <your-tenant-secret>

401 — no_token_found

Cause: Your tenant does not have a connection configured for the provider you are trying to access.

Fix: Contact MT2Data to confirm that the integration (John Deere, Syngenta, Solinftec, or Datalake) has been enabled for your tenant.


403 — forbidden

Cause: Your tenant secret is valid but does not have access to the requested resource.

Fix: Confirm you are using the correct tenant ID in the URL path and that the secret belongs to that tenant.


502 / upstream errors

Cause: The upstream provider (John Deere, Syngenta, Solinftec, or MT2Data Datalake) returned an error or is temporarily unavailable.

Fix: Retry after a short wait. If the issue persists, use GET /api/data/:tenantId/connections to check which providers are currently connected and whether any have expired tokens.


no_token_found on a previously working provider

Cause: The stored provider token may have expired and automatic renewal failed.

Fix: Contact MT2Data support to re-connect the integration for your tenant.


Getting Help

If you encounter an issue not covered here, reach out through any of the following:

When contacting support, include:

  • Your tenant ID
  • The endpoint you were calling
  • The error code and HTTP status returned
  • Approximate time of the request

About Colmeia

Colmeia is the MT2Data middleware platform. It provides a unified API for accessing agricultural data from John Deere Operations Center, Syngenta Cropwise, Solinftec SCDI, and the MT2Data Datalake — all through a single tenant bearer secret, with all provider authentication handled server-side.

For more information about the MT2Data platform, visit mt2data.cloud.

MT2Data Datalake Gateway

Secure, index-based access to the MT2Data agronomic data lake. The gateway provides authenticated access to parquet data files for authorized enterprise clients.

Base URL: https://datalake.mt2data.cloud

What the Datalake Gateway provides

  • OAuth 2.0 Client Credentials — enterprise M2M authentication with 1-hour JWT tokens
  • Symbol-based file access — files are identified by human-readable Symbols, not raw paths
  • Subscription-scoped access — your account defines exactly which files you can retrieve
  • Fast catalog browsing — browse all authorized Symbols and descriptions in ~1s

If you already use the Colmeia platform, you do not need to integrate directly against this gateway. Connect your Datalake credentials once through the Colmeia integration dashboard and access files via:

GET https://colmeia.mt2data.cloud/api/dl/data/:tenantId/download/:symbol
Authorization: Bearer <tenant-secret>

Colmeia manages token refresh automatically.

API Reference

Base URL: https://datalake.mt2data.cloud

All data endpoints require authentication. See Authentication for how to obtain an access token.


POST /oauth/token

Obtain an OAuth 2.0 access token using client credentials.

Request:

POST /oauth/token HTTP/1.1
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "your-client-id",
  "client_secret": "your-client-secret"
}

Also accepts application/x-www-form-urlencoded.

Response (200):

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Response (401):

{
  "error": "invalid_client",
  "error_description": "Invalid client credentials"
}

GET /catalog

Get metadata for all files you are authorized to access. Returns Symbol identifiers, human-readable descriptions, and R2 file paths.

Response (200):

{
  "clientId": "your-account",
  "files": [
    {
      "Symbol": "DatasetSymbol",
      "Description": "Human-readable description of the dataset",
      "FileName": "path/to/DatasetSymbol.parquet"
    }
  ],
  "count": 1
}

Performance: ~1s — use this as your primary way to discover available files.


GET /list

List files that appear in your catalog and physically exist in R2, with size and upload timestamp. Uses batched HEAD verification.

Parameters:

ParameterRequiredDefaultDescription
limitNo1000Max files to return

Response (200):

{
  "files": [
    {
      "symbol": "DatasetSymbol",
      "description": "Human-readable description of the dataset",
      "size": 1048576,
      "uploaded": "2026-01-15T10:30:00Z"
    }
  ],
  "count": 1,
  "limit": 1000,
  "truncated": false
}

Performance: ~2s for a full catalog (~500 files).


GET /count

Returns the total number of accessible files that physically exist in R2.

Response (200):

{ "count": 506 }

Performance: ~2s.


GET /info/

Get metadata for a specific file by its Symbol identifier without downloading it.

GET /info/YOUR_SYMBOL HTTP/1.1
Authorization: Bearer <access_token>

Response (200):

{
  "symbol": "YOUR_SYMBOL",
  "size": 1048576,
  "uploaded": "2026-01-15T10:30:00Z",
  "httpMetadata": {},
  "customMetadata": {}
}

Performance: ~0.5s.


GET /download/

Download a file by its Symbol identifier. Returns binary content.

GET /download/YOUR_SYMBOL HTTP/1.1
Authorization: Bearer <access_token>

Response (200): File content as binary stream. Content-Type and Content-Disposition headers are forwarded from R2.

Response (403):

{
  "error": "Access denied",
  "message": "Symbol not found in your subscription"
}

Response (404):

{
  "error": "Not found",
  "message": "File does not exist"
}

Performance: ~0.5s + file transfer time.


GET /

Direct file download by file path (alternative to Symbol-based access).

curl -H "Authorization: Bearer $TOKEN" \
  "https://datalake.mt2data.cloud/path/to/your-file.parquet" -o your-file.parquet

Error Codes

HTTPMeaning
400Bad request (OAuth: invalid_request, unsupported_grant_type)
401Missing or expired credentials (OAuth: invalid_client)
403Access denied — file not in client's index, or invalid API key
404File not found in R2, or catalog not found
500Internal server error

Authentication

The MT2Data Datalake Gateway uses OAuth 2.0 Client Credentials flow for authentication. You receive a client_id and client_secret from MT2Data, exchange them for a short-lived access token, and include that token in all API requests.

Base URL: https://datalake.mt2data.cloud

Your Credentials

You will receive credentials from MT2Data:

FieldDescription
Client IDYour service account identifier
Client SecretSecret key — keep secure, treat like a password
Token Endpointhttps://datalake.mt2data.cloud/oauth/token

Step 1 — Get an Access Token

curl -X POST https://datalake.mt2data.cloud/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "your-client-id",
    "client_secret": "your-client-secret"
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Tokens are valid for 1 hour (3600 seconds).

Step 2 — Use the Token

Include the access token in the Authorization header on all API requests:

curl https://datalake.mt2data.cloud/catalog \
  -H "Authorization: Bearer <your-access-token>"

Token Expiry & Refresh

When a token expires you will receive a 401. Request a new token via Step 1.

Best practice: track expires_in in your application and refresh the token ~60 seconds before expiry to avoid interruption. See Code Examples for complete implementations with automatic token refresh.

Code Examples

Complete client implementations in Python, JavaScript/Node.js, and C# with automatic token refresh.

Python

import requests
from datetime import datetime, timedelta

class MT2DataClient:
    def __init__(self, client_id: str, client_secret: str):
        self.base_url = "https://datalake.mt2data.cloud"
        self.client_id = client_id
        self.client_secret = client_secret
        self.access_token = None
        self.token_expires_at = None

    def _get_token(self) -> str:
        """Get or refresh access token."""
        if self.access_token and self.token_expires_at > datetime.now():
            return self.access_token

        response = requests.post(
            f"{self.base_url}/oauth/token",
            json={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret
            }
        )
        response.raise_for_status()

        data = response.json()
        self.access_token = data["access_token"]
        # Refresh 60s before actual expiry
        self.token_expires_at = datetime.now() + timedelta(seconds=data["expires_in"] - 60)
        return self.access_token

    def _headers(self) -> dict:
        return {"Authorization": f"Bearer {self._get_token()}"}

    def get_catalog(self) -> dict:
        """Get all authorized Symbols and descriptions."""
        return requests.get(f"{self.base_url}/catalog", headers=self._headers()).json()

    def list_files(self, limit: int = 1000) -> dict:
        """List accessible files with existence verification (~2s)."""
        return requests.get(
            f"{self.base_url}/list",
            headers=self._headers(),
            params={"limit": limit}
        ).json()

    def count_files(self) -> int:
        """Count total accessible files."""
        return requests.get(f"{self.base_url}/count", headers=self._headers()).json()["count"]

    def get_file_info(self, symbol: str) -> dict:
        """Get metadata for a file by Symbol."""
        return requests.get(f"{self.base_url}/info/{symbol}", headers=self._headers()).json()

    def download_file(self, symbol: str, local_path: str) -> None:
        """Download a file by Symbol to disk."""
        with requests.get(
            f"{self.base_url}/download/{symbol}",
            headers=self._headers(),
            stream=True
        ) as r:
            r.raise_for_status()
            with open(local_path, "wb") as f:
                for chunk in r.iter_content(chunk_size=8192):
                    f.write(chunk)


# Usage
if __name__ == "__main__":
    client = MT2DataClient(
        client_id="your-client-id@company.com",
        client_secret="your-secret-key-here"
    )

    # Browse available files
    catalog = client.get_catalog()
    print(f"Available Symbols: {catalog['count']}")
    for f in catalog["files"][:5]:
        print(f"  {f['Symbol']}: {f['Description']}")

    # Download a file by Symbol
    client.download_file("YOUR_SYMBOL", "dataset.parquet")
    print("Downloaded dataset.parquet")

JavaScript / Node.js

const axios = require('axios');
const fs = require('fs');

class MT2DataClient {
  constructor(clientId, clientSecret) {
    this.baseUrl = 'https://datalake.mt2data.cloud';
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.accessToken = null;
    this.tokenExpiresAt = null;
  }

  async _getToken() {
    if (this.accessToken && this.tokenExpiresAt > Date.now()) {
      return this.accessToken;
    }
    const response = await axios.post(`${this.baseUrl}/oauth/token`, {
      grant_type: 'client_credentials',
      client_id: this.clientId,
      client_secret: this.clientSecret
    });
    this.accessToken = response.data.access_token;
    // Refresh 60s before expiry
    this.tokenExpiresAt = Date.now() + (response.data.expires_in - 60) * 1000;
    return this.accessToken;
  }

  async _headers() {
    return { Authorization: `Bearer ${await this._getToken()}` };
  }

  async getCatalog() {
    return (await axios.get(`${this.baseUrl}/catalog`, { headers: await this._headers() })).data;
  }

  async listFiles(limit = 1000) {
    return (await axios.get(`${this.baseUrl}/list`, {
      headers: await this._headers(),
      params: { limit }
    })).data;
  }

  async getFileInfo(symbol) {
    return (await axios.get(`${this.baseUrl}/info/${symbol}`, { headers: await this._headers() })).data;
  }

  async downloadFile(symbol, localPath) {
    const response = await axios.get(`${this.baseUrl}/download/${symbol}`, {
      headers: await this._headers(),
      responseType: 'stream'
    });
    await new Promise((resolve, reject) => {
      response.data.pipe(fs.createWriteStream(localPath))
        .on('finish', resolve)
        .on('error', reject);
    });
  }
}

// Usage
async function main() {
  const client = new MT2DataClient(
    'your-client-id@company.com',
    'your-secret-key-here'
  );

  const catalog = await client.getCatalog();
  console.log(`Available Symbols: ${catalog.count}`);

  await client.downloadFile('YOUR_SYMBOL', 'dataset.parquet');
  console.log('Downloaded dataset.parquet');
}

main().catch(console.error);

C# / .NET

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class MT2DataClient
{
    private readonly string _baseUrl = "https://datalake.mt2data.cloud";
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly HttpClient _httpClient = new();
    private string _accessToken;
    private DateTime _tokenExpiresAt;

    public MT2DataClient(string clientId, string clientSecret)
    {
        _clientId = clientId;
        _clientSecret = clientSecret;
    }

    private async Task<string> GetTokenAsync()
    {
        if (_accessToken != null && _tokenExpiresAt > DateTime.UtcNow)
            return _accessToken;

        var content = new StringContent(
            JsonSerializer.Serialize(new {
                grant_type = "client_credentials",
                client_id = _clientId,
                client_secret = _clientSecret
            }),
            Encoding.UTF8, "application/json"
        );

        var response = await _httpClient.PostAsync($"{_baseUrl}/oauth/token", content);
        response.EnsureSuccessStatusCode();

        using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
        _accessToken = doc.RootElement.GetProperty("access_token").GetString();
        var expiresIn = doc.RootElement.GetProperty("expires_in").GetInt32();
        _tokenExpiresAt = DateTime.UtcNow.AddSeconds(expiresIn - 60);
        return _accessToken;
    }

    public async Task<string> GetCatalogAsync()
    {
        var token = await GetTokenAsync();
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", token);
        var response = await _httpClient.GetAsync($"{_baseUrl}/catalog");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }

    public async Task DownloadFileAsync(string symbol, string localPath)
    {
        var token = await GetTokenAsync();
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", token);
        var bytes = await _httpClient.GetByteArrayAsync($"{_baseUrl}/download/{symbol}");
        await System.IO.File.WriteAllBytesAsync(localPath, bytes);
    }
}

// Usage
var client = new MT2DataClient("your-client-id@company.com", "your-secret-key-here");
Console.WriteLine(await client.GetCatalogAsync());
await client.DownloadFileAsync("YOUR_SYMBOL", "dataset.parquet");

Performance Guide

Endpoint Speed Reference

EndpointSpeedUse Case
GET /catalog~1sBrowse all authorized Symbols and descriptions
GET /info/{symbol}~0.5sGet metadata for a specific file
GET /download/{symbol}~0.5s + transferDownload a specific file
GET /list~2sVerify file existence with size and timestamp
GET /count~2sCount total accessible files

Downloading a known file

If you already know the Symbol:

# Direct download — fastest path
GET /download/IbgePnadAgricultureStk

Discovering and downloading files

# Step 1: Browse catalog (~1s) — find the Symbol you need
GET /catalog
# Returns: [{ "Symbol": "IbgePnadAgricultureStk", "Description": "..." }, ...]

# Step 2: Download by Symbol (~0.5s + transfer)
GET /download/IbgePnadAgricultureStk

Verifying which files exist

# Use /list when you need to confirm files exist in R2 with metadata
GET /list?limit=1000
# ~2s with batched HEAD calls — returns Symbol, size, upload date

Quick file count

GET /count
# ~2s, returns total number of accessible files

Token Caching

Tokens are valid for 1 hour. Do not request a new token on every API call — cache it and refresh when it is ~60 seconds from expiry.

# Good — reuse token until near-expiry
if not self.access_token or self.token_expires_at < datetime.now():
    self.access_token = fetch_new_token()

# Bad — fetches a new token on every request
token = fetch_new_token()
call_api(token)

Parallelism

/list and /count internally make batched parallel HEAD requests against R2 — they are already optimized. For downloading multiple files, parallelize client-side:

from concurrent.futures import ThreadPoolExecutor

symbols = ["SymbolA", "SymbolB", "SymbolC"]

with ThreadPoolExecutor(max_workers=5) as executor:
    futures = [executor.submit(client.download_file, s, f"{s}.parquet") for s in symbols]
    for f in futures:
        f.result()

Troubleshooting

Common Errors

401 — "Invalid client credentials"

Cause: Client ID or secret is incorrect, or the OAuth client does not exist.

Fix:

  • Verify credentials are exactly correct (no trailing spaces or newlines)
  • Contact MT2Data to confirm your account is active

401 — "Token expired"

Cause: Access token has expired (lifetime is 1 hour).

Fix: Request a new token via POST /oauth/token. Implement token caching with expiry tracking in your application — see the Performance Guide.


403 — "Access denied" / "Symbol not found in your subscription"

Cause: You are trying to access a file that is not in your index.

Fix:

  • Use GET /catalog to see which Symbols you are authorized to access
  • Contact MT2Data if you need access to additional files

403 — "Invalid API key"

Cause: API key does not exist or has the wrong prefix.

Fix: Verify the key exists and is prefixed with mt2_admin_ or mt2_client_.


404 — File not found

Cause: The file exists in your catalog but has not yet been synced to R2, or the path/Symbol is incorrect.

Fix:

  • Use GET /catalog to verify the Symbol is in your subscription
  • Use GET /list to confirm the file actually exists in R2
  • Check that the Symbol spelling is exact (case-sensitive)

404 — "Catalog not found"

Cause: Your client does not have an index file configured in R2.

Fix: Contact MT2Data to ensure your index file has been uploaded.


400 — "unsupported_grant_type"

Cause: grant_type is missing or not set to client_credentials.

Fix:

  • Set grant_type=client_credentials in the request body
  • Ensure Content-Type is application/json or application/x-www-form-urlencoded

Changes not applying immediately

KV-stored credentials (new keys, updated index files) can take up to 60 seconds to propagate globally. Wait and retry.


Getting Help

If you encounter an issue not covered here, reach out through any of the following:

When contacting support, include:

  • Your Client ID (never the secret)
  • The exact error message and HTTP status code
  • The endpoint you were calling
  • Approximate timestamp of the request

Security Best Practices

  1. Never share your client secret — treat it like a password
  2. Use environment variables or a secrets manager — do not hardcode secrets in source code
  3. Cache tokens — request a new token only when the current one is near expiry, not on every call
  4. Use HTTPS only — all endpoints enforce HTTPS
  5. Rotate compromised secrets immediately — contact MT2Data if you suspect a secret has been exposed