Meta Fields
curl --request GET \
--url https://api.example.com/v1/meta/fieldsimport requests
url = "https://api.example.com/v1/meta/fields"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v1/meta/fields', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/meta/fields",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/meta/fields"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/v1/meta/fields")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/meta/fields")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyCatalog
GET /v1/meta/fields
Self-discovery: field + preset catalog with ETag-based caching.
GET
/
v1
/
meta
/
fields
Meta Fields
curl --request GET \
--url https://api.example.com/v1/meta/fieldsimport requests
url = "https://api.example.com/v1/meta/fields"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v1/meta/fields', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/meta/fields",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/meta/fields"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/v1/meta/fields")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/meta/fields")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyWhen to use this
Hit/v1/meta/fields once at startup to discover the catalog. The
response sets ETag and Cache-Control: public, max-age=3600, so a
long-lived agent or service fetches it once an hour at most. The catalog
drives:
- Client-side validation — reject unknown field names before sending
a
/v1/fetchrequest. - Tool descriptions — agents that build their own UI from the catalog populate field pickers and preset selectors from this endpoint.
- LLM planners — third-party agents that route to Mireye via the MCP
server can render the catalog into their own planner system prompt,
same as Mireye’s
/v1/askdoes internally.
Response shape
{
"billing": {
"currency": "USD",
"price_usd": "0.00",
"unit": "location"
},
"fields": [
{
"billing": {
"currency": "USD",
"price_usd": "0.00",
"unit": "location"
},
"derivation": null,
"description": "Ground elevation above the NAVD88 vertical datum at the queried point. Sourced from the USGS 3DEP 1/3 arc-second seamless DEM (~10m), with the USGS EPQS dynamic service as a coverage fallback.",
"interpretation_hints": "Below 10m within 5km of MARINE coast \u2192 storm-surge exposure relevant, but coast_distance_m does NOT establish that the nearby shoreline is marine \u2014 it also measures tidal rivers and estuaries, so it reads a few metres at points tens of km inland (Philadelphia ~13 m, the Anacostia in DC ~215 m). Confirm the shoreline is genuinely marine before drawing the surge conclusion from this pair. Above 3000m \u2192 alpine permitting, snow loads. Combine with coast_distance_m and within_floodplain_polygon for flood-zone reasoning.",
"layer": "terrain",
"lifecycle": "stable",
"name": "elevation",
"null_meaning": null,
"nullable": false,
"presets": [
"terrain",
"flood_risk",
"wildfire_underwrite",
"site_selection",
"wind_siting"
],
"source": "USGS_3DEP",
"source_url": "https://www.usgs.gov/3d-elevation-program",
"ttl_seconds": 31536000,
"type": "float",
"unit": "meters"
}
/* ...314 more fields, ordered by layer */
],
"presets": {
"terrain": ["elevation", "slope_degrees", "aspect_cardinal", "coast_distance_m", "soil_drainage_class", "bedrock_depth_cm"],
"flood_risk": ["elevation", "coast_distance_m", "within_floodplain_polygon", "intersects_nhd_area", "intersects_wetland", "wetland_type", "wetland_subtype", "wetland_acres", "nearest_wetland_distance_m", "wetlands_within_100m_count", "wetlands_within_500m_count", "surface_water_permanence_pct", "nearest_waterbody_name"]
/* ...13 more presets */
},
"us_envelope": {
"lat_max": 72.0,
"lat_min": 18.0,
"lng_max": -65.0,
"lng_min": -180.0
},
"version": "0.17.0"
}
Self-discovery flow
The recommended client pattern:import httpx, time
BASE = "https://api.mireye.com"
class CatalogClient:
def __init__(self):
self._etag: str | None = None
self._catalog: dict | None = None
self._fetched_at: float = 0.0
async def get(self) -> dict:
# Refresh at most once an hour, even if our cache lies.
if self._catalog and time.time() - self._fetched_at < 3600:
return self._catalog
headers = {}
if self._etag:
headers["If-None-Match"] = self._etag
async with httpx.AsyncClient() as c:
r = await c.get(f"{BASE}/v1/meta/fields", headers=headers)
if r.status_code == 304:
self._fetched_at = time.time()
return self._catalog # unchanged
r.raise_for_status()
self._etag = r.headers["ETag"]
self._catalog = r.json()
self._fetched_at = time.time()
return self._catalog
If-None-Match returns 304 Not Modified with no body when the catalog
hasn’t changed. The ETag covers the whole payload, so any catalog
change — new fields, edited hints, preset changes — produces a fresh
ETag alongside the version bump (see the versioning
policy).
Each field object also carries honesty metadata: nullable marks fields
that can legitimately return null at valid coordinates, and
null_meaning says what such a null means (e.g. “no wetland within the
search radius”) so clients don’t misread semantic absence as a fetch
failure.
How the catalog drives the planner
Mireye’s own/v1/ask planner renders the catalog into its system
prompt:
You are a geospatial data planner. Given a US coordinate and a natural-
language question, pick the catalog fields needed to answer it.
Available fields:
elevation (terrain, float, meters) — Ground elevation above NAVD88...
Hint: Use this when the caller asks about elevation...
Presets: terrain, flood_risk, site_selection, wildfire_underwrite
slope_degrees (terrain, float, degrees) — Slope at the coordinate...
Hint: Spread potential for wildfire; higher = faster spread...
/* ... */
Presets (use one of these instead of listing fields if it fits):
flood_risk: elevation, coast_distance_m, within_floodplain_polygon, ...
/* ... */
Versioning
Theversion field is the wire-envelope protocol version from
catalog/taxonomy.yaml. Additive field, source, or preset growth does not
bump it; clients discover those additions through the catalog payload and its
ETag. The version changes only when the response envelope or another
wire-visible protocol contract changes.
Read the version from the JSON response body (there is no version response
header), cache the payload by ETag, and feature-detect field names instead of
assuming that one protocol version freezes catalog membership.
us_envelope remains the backwards-compatible coarse primary envelope. The
runtime validator also accepts the positive-longitude Western Aleutian
rectangle: lat ∈ [51, 54], lng ∈ [172, 180). Client-side validation
must accept either region.Response
200 - application/json
Successful Response