{"components":{"schemas":{"AskReq":{"properties":{"cell":{"type":"string"},"include_image":{"default":false,"type":"boolean"},"lat":{"type":"number"},"lng":{"type":"number"},"place":{"type":"string"},"q":{"type":"string"},"verbose":{"default":false,"description":"When false (default), trim per-algorithm formulas + per-fact band_metadata + long _explanation prose so the response fits MCP's 25 KB cap. The signed receipt stays intact in either mode.","type":"boolean"}},"required":["q"],"type":"object"},"AskResp":{"description":"Response of /v1/ask. Single envelope combining (a) place resolution, (b) topic-router classification, (c) recalled facts under those topics, (d) applicable algorithm recipes that compose those bands into named scores, (e) optional Sentinel-2 RGB thumbnail URL, and (f) caveats. All facts are signed and content-addressed.","properties":{"algorithms_for_question":{"items":{"properties":{"formula":{"type":"string"},"key":{"type":"string"},"topic":{"type":"string"}},"type":"object"},"type":"array"},"answer":{"description":"Short natural-language summary of what the responder found, synthesised deterministically from the structured fields (every cited value traces to a fact_cid in the receipt). On a cold cell whose bands are not yet materialized it states that plainly and points at `next_steps`; never an LLM call.","type":"string"},"answer_md":{"description":"Markdown variant of `answer`.","type":"string"},"caveats":{"items":{"type":"string"},"type":"array"},"facts":{"properties":{"bands_already_attested_at_cell":{"items":{"type":"string"},"type":"array"},"facts":{"items":{"$ref":"#/components/schemas/Fact"},"type":"array"}},"type":"object"},"foundation_embeddings":{"description":"Per-encoder neighbour lists and consensus voting. Populated when the intent matches `find places like` / `what changed`.","type":"object"},"materialize_notes":{"items":{"$ref":"#/components/schemas/MaterializeNote"},"type":"array"},"next_steps":{"description":"Present when the routed algorithms could not evaluate because their input bands are not materialized at this cell. Each item is a literal follow-up call (e.g. POST /v1/recall with the exact missing bands) the agent can issue, then re-ask.","items":{"properties":{"action":{"type":"string"},"body":{"type":"object"},"method":{"type":"string"},"path":{"type":"string"},"url":{"type":"string"},"why":{"type":"string"}},"type":"object"},"type":"array"},"place_resolved":{"$ref":"#/components/schemas/LocateResp"},"receipt":{"$ref":"#/components/schemas/Receipt"},"topic_routing":{"properties":{"matched_keywords":{"items":{"type":"object"},"type":"array"},"matched_topics":{"items":{"type":"string"},"type":"array"},"out_of_scope":{"type":"boolean"},"routing":{"type":"object"}},"type":"object"}},"required":["topic_routing","facts","receipt"],"type":"object"},"BackfillReq":{"properties":{"band":{"description":"band key to backfill, e.g. 'open_meteo.t2m'","type":"string"},"cell":{"description":"cell64 string (or place name; resolved through the same geocoder as /v1/locate)","type":"string"},"end_unix":{"description":"Unix epoch seconds (UTC) for window end. Default: now.","type":"integer"},"max_facts":{"default":16,"description":"Cap on facts materialized in one call. Default 16 — fits inside a 60s tool-call window for any LLM host. Raise for explicit wide backfills (cap 1024).","maximum":1024,"minimum":1,"type":"integer"},"start_unix":{"description":"Unix epoch seconds (UTC) for window start. Default: 30 days ago for fast bands, 365 days ago for slow.","type":"integer"}},"required":["cell","band"],"type":"object"},"BoringPostReq":{"description":"Body for POST /v1/{ndvi,air,lst,soil,water,forest,weather,at,elevation}. Supply `place` (free-text geocoded via /v1/locate) OR `lat`+`lng` (or `lon`); /v1/at and /v1/elevation also accept optional `band`/`bands`/`tslot` and `cell64` respectively. When `place` resolves to an OSM feature with extent (airport, park, lake, region) the response includes a `polygon` block + per-band `stats` (mean/median/min/max/std for numeric bands, mode + class distribution for categorical bands like esa_worldcover.lc_2021); pass `n_cells: 1` to force point behaviour at the centroid instead. Single-band endpoints default to 16 sample cells when polygon detected; /v1/at defaults to 1 (multi-band × multi-cell explodes upstream fetch count). Use POST /v1/recall_polygon for raw per-cell facts.","properties":{"band":{"description":"Single band key (used by /v1/at).","type":"string"},"bands":{"description":"CSV of band keys (used by /v1/at).","type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"lon":{"description":"Alias for `lng`.","type":"number"},"n_cells":{"description":"Polygon-aggregation knob. When `place` resolves to a feature with extent and `n_cells` is unset, single-band endpoints fan out to 16 sample cells; /v1/at defaults to 1. `n_cells: 1` forces point behaviour at the centroid; values in 2..=64 are honoured. Anything else returns 400 — heavy queries belong on POST /v1/recall_polygon.","maximum":64,"minimum":1,"type":"integer"},"name":{"description":"Alias for `place`.","type":"string"},"place":{"description":"Free-text place name; resolved via embedded gazetteer → cache → Photon → Nominatim.","type":"string"},"q":{"description":"Alias for `place`.","type":"string"},"query":{"description":"Alias for `place`.","type":"string"},"tslot":{"type":"integer"}},"type":"object"},"Cell64":{"description":"cell64 wire form: four base-65,536 bigrams separated by dots, e.g. `defi.zb4d9.pefa.zf619`. Encoded resolution is ~9.55 m at the equator. Each bigram is either a CVCV quad — consonant `[bcdfghjklmnpqrstvwxyz]` followed by vowel `[aeiouAEIOU]` repeated twice — OR a synthetic 5-char `z[0-9a-f]{4}` slot used for the unused pad cells in the 65,536-entry alphabet. The regex pin matches `pattern` below byte-for-byte and is also surfaced under `Cell64Pattern` so agents can validate before sending.","example":"defi.zb4d9.pefa.zf619","maxLength":23,"minLength":19,"pattern":"^(?:(?:[bcdfghjklmnpqrstvwxyz][aeiouAEIOU]){2}|z[0-9a-f]{4})(?:\\.(?:(?:[bcdfghjklmnpqrstvwxyz][aeiouAEIOU]){2}|z[0-9a-f]{4})){3}$","type":"string"},"Cell64Pattern":{"description":"Reference copy of the cell64 regex for tooling that wants the pattern without round-tripping through Cell64. Same string as Cell64.pattern.","example":"^(?:(?:[bcdfghjklmnpqrstvwxyz][aeiouAEIOU]){2}|z[0-9a-f]{4})(?:\\.(?:(?:[bcdfghjklmnpqrstvwxyz][aeiouAEIOU]){2}|z[0-9a-f]{4})){3}$","type":"string"},"Claim":{"properties":{"agg":{"description":"Aggregation over `window`","enum":["any","all","mean","min","max"],"type":"string"},"band":{"description":"Band key (e.g. `indices.ndvi`, `copdem30m.elevation_mean`)","type":"string"},"op":{"description":"Comparison or membership operator","enum":["eq","ne","lt","le","gt","ge","in","ni","exists","absent"],"type":"string"},"tslot":{"description":"Specific tslot; one of `tslot` or `window` MUST be set","type":"integer"},"value":{"description":"Right-hand value, band-typed (number for scalar bands, array for vector bands, set for in/ni). Required even for exists/absent where it is ignored."},"window":{"description":"Inclusive [start, end] u64 Unix-epoch range","items":{"type":"integer"},"maxItems":2,"minItems":2,"type":"array"}},"required":["band","op","value"],"type":"object"},"CompareBandsReq":{"description":"Body for POST /v1/compare_bands. Compares two bands at the same cell. Returns scalar delta + percent change for scalar pairs, cosine + L2 for vector pairs. The vector arm requires `a == b` with different tslots (one band across two vintages), because no two bands share a vector space: `clay_v1` and `prithvi_eo2` are both 384-D but encode different chips at different receptive fields, so a cosine between them is meaningless and is refused with a 400 rather than served. Optional `predicate` folds a consistency check (e.g. \"DEM and GMRT agree within 200 m\") into a signed verdict.","properties":{"a":{"description":"Band A key, e.g. 'copdem30m.elevation_mean'. For a vector cosine this must equal `b` (same band, two tslots).","type":"string"},"b":{"description":"Band B key, e.g. 'gmrt.elevation_mean'. For a vector cosine this must equal `a`.","type":"string"},"cell":{"description":"cell64 string. `cell64` accepted as alias.","type":"string"},"predicate":{"description":"Optional consistency predicate. Tagged enum: {kind: 'abs_diff_le'|'abs_diff_lt'|'cosine_ge'|'cosine_gt'|'l2_distance_le', threshold: number}.","properties":{"kind":{"enum":["abs_diff_le","abs_diff_lt","cosine_ge","cosine_gt","l2_distance_le"],"type":"string"},"threshold":{"type":"number"}},"required":["kind","threshold"],"type":"object"},"tslot_a":{"description":"Optional tslot pin for band A. When omitted, the responder picks the latest tslot with an attested fact for band A at this cell.","type":"integer"},"tslot_b":{"description":"Optional tslot pin for band B. Same semantics as `tslot_a`.","type":"integer"}},"required":["cell","a","b"],"type":"object"},"CompareBandsResp":{"description":"Response of /v1/compare_bands. Numeric delta + percent change for scalar pairs; cosine + L2 for vector pairs. `predicate_verdict` is present when a consistency predicate was passed.","properties":{"cosine":{"type":"number"},"delta":{"type":"number"},"l2":{"type":"number"},"percent_change":{"type":"number"},"predicate_verdict":{"properties":{"holds":{"type":"boolean"},"kind":{"type":"string"},"threshold":{"type":"number"}},"type":"object"},"receipt":{"$ref":"#/components/schemas/Receipt"}},"required":["delta","receipt"],"type":"object"},"CompareReq":{"properties":{"a":{"type":"string"},"b":{"type":"string"},"family":{"type":"string"}},"required":["a","b"],"type":"object"},"CompareResp":{"description":"Response of /v1/compare. Single cosine similarity score (vector bands) or scalar delta (scalar bands) plus per-band breakdown.","properties":{"deltas":{"additionalProperties":{"type":"number"},"description":"Per-band scalar delta for scalar comparisons; absent for vector cosine.","type":"object"},"receipt":{"$ref":"#/components/schemas/Receipt"},"score":{"type":"number"}},"required":["score","receipt"],"type":"object"},"Cost":{"description":"Self-declared cost block on every receipt. Honest accounting: latencies are observed, freshness is the upstream-provider's `last_modified`, `was_cached` is true when the hot cache served the read.","properties":{"credits":{"description":"Conceptual cost units; 0 for L0/L1 read endpoints on the hosted responder.","type":"number"},"latency_p50_ms":{"type":"number"},"latency_p99_ms":{"type":"number"},"source_freshness_s":{"description":"Seconds since the upstream provider's last-modified timestamp.","type":"integer"},"was_cached":{"type":"boolean"}},"type":"object"},"DiffReq":{"properties":{"band":{"type":"string"},"cell":{"type":"string"},"tslot_a":{"type":"integer"},"tslot_b":{"type":"integer"}},"required":["cell","band","tslot_a","tslot_b"],"type":"object"},"Error":{"properties":{"code":{"example":"invalid_argument","type":"string"},"details":{"type":"object"},"message":{"type":"string"}},"required":["code","message"],"type":"object"},"ErrorEnvelope":{"description":"The `emem.error.v1` failure envelope returned by every endpoint on a 4xx/5xx. Branch on the stable `code` (not the human `message`). See GET /v1/errors for the full code catalog.","properties":{"code":{"description":"Stable machine-readable error code. One of the codes in GET /v1/errors.","example":"invalid_argument","type":"string"},"details":{"description":"Optional structured recovery hints; present on errors that ship machine-readable next-steps.","type":"object"},"message":{"description":"Human-readable detail. For invalid_argument this names the offending field (e.g. \"missing field `q`\").","type":"string"},"path":{"description":"Request path that produced the error.","example":"/v1/ask","type":"string"},"schema":{"const":"emem.error.v1","type":"string"}},"required":["code","message","schema"],"type":"object"},"EudrDdsReq":{"description":"POST /v1/eudr_dds body — produces a signed Annex II-shaped Due Diligence Statement per Regulation (EU) 2023/1115. Pair every plot with its operator-supplied geometry (GeoJSON Polygon for >4 ha, Point for ≤4 ha non-cattle per Article 2(28)), country of production (ISO3), Combined Nomenclature code (HS-6+), and quantity in kg. The endpoint runs eudr_compliance@1 per cell (JRC GFC2020 V3 legal baseline + Hansen GFC v1.12 post-cut-off loss-year consensus; JRC GFC2020 is read as 10° COG tiles for fast cold reads across all geographies). JRC TMF v2025, WRI-Sims driver attribution and RADD SAR are deferred off the hot path for latency (TMF) / pending fast public endpoints (WRI, RADD). Applies the Article 2(4) 0.5 ha MMU floor at plot aggregation, validates `commodity_hs` against Annex I, and emits the structured envelope. The response carries an explicit `legality_disclaimer` because Article 9(1)(b) legality verification (land tenure, FPIC, country-of-origin law compliance) is structurally out of Earth-observation scope. Response includes an ed25519-signed `receipt` over the union of every per-cell fact_cid; verifiable offline at `/verify` (or `/v1/verify_receipt`). Pass an optional `scope` block (`{user_id, agent_id, run_id, org_id}`) to bind the receipt to a tenant.","properties":{"cut_off_date":{"default":"2020-12-31","description":"EUDR cut-off date (ISO 8601). The regulation's value is 2020-12-31.","type":"string"},"forest_baseline_override":{"description":"Optional baseline override: 'jrc_gfc2020_v3' (default), 'hansen_only', or 'both' (consensus).","type":"string"},"legality_module":{"description":"Operator-chosen legality provider. Default 'none' surfaces the explicit Article 9(1)(b) out-of-scope disclaimer.","type":"string"},"max_cells_per_plot":{"default":"auto-derived from polygon area (~110 cells/ha, clamped to 51,200)","description":"Sample budget per POLYGON plot. POINT plots evaluate at 1 cell regardless of this value.","maximum":51200,"minimum":1,"type":"integer"},"operator":{"description":"Operator identification per Annex II §1.","properties":{"address":{"type":"string"},"eori":{"type":"string"},"name":{"type":"string"}},"type":"object"},"plots":{"description":"One or more plots to evaluate.","items":{"properties":{"commodity_hs":{"description":"Combined Nomenclature code (HS-6+). First 4 digits detect cattle (0102/0201/0202) for the Article 2(28) cattle exemption.","type":"string"},"commodity_name":{"description":"Optional plain-English commodity name.","type":"string"},"country_of_production":{"description":"ISO 3166-1 alpha-3 (e.g. BRA, IDN, CIV).","type":"string"},"geometry_geojson":{"description":"GeoJSON Polygon (preferred) OR Point (for ≤4 ha non-cattle) OR a bare {bbox:[minlng,minlat,maxlng,maxlat]}.","oneOf":[{"properties":{"coordinates":{},"type":{"enum":["Polygon","Point"],"type":"string"}},"required":["type","coordinates"],"type":"object"},{"properties":{"bbox":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"}},"required":["bbox"],"type":"object"}]},"plot_id":{"description":"Operator-supplied identifier; preserved verbatim in the response.","type":"string"},"quantity_kg":{"description":"Net mass in kilograms (Annex II §3).","type":"number"},"request_visual_evidence":{"default":false,"description":"Opt-in: build a per-year visual deforestation-evidence block for this plot. Adds a `visual_evidence` field to the per-plot result containing a Sentinel-2 NDVI annual timeline from 2020..current_year + Sentinel-1 RTC VV-backscatter cloud-independent confirmation + per-cell scene.png URLs the agent can render as a 6-up year-by-year grid. Adds upstream Sentinel fan-out per plot; the EUDR budget auto-bumps to 60s + 110s × n_visual_plots (capped at 480s, deliberately under the ~300s client/edge cut for 1–2 plots) when this flag is set on any plot. Visual-evidence concurrency is bounded (EMEM_EUDR_VISUAL_CONCURRENCY) and the route is capped (EMEM_EUDR_MAX_INFLIGHT) so a visual-evidence burst can't starve the server. The block carries its own `verdict` (`no_visual_deforestation` / `visual_deforestation_suspected` / `indeterminate_no_baseline`) computed from NDVI drop ≥ EMEM_VISUAL_NDVI_DROP_THRESHOLD (default 0.15 vs 2020 — Pelletier 2024) and S1 VV drop ≥ EMEM_VISUAL_S1_DROP_DB_THRESHOLD (default 3 dB — Reiche 2018). All underlying facts are signed Primary records under the responder's identity; auditors cite ndvi_fact_cids + s1_fact_cids.","type":"boolean"},"supplier":{"description":"Optional supplier identifier.","type":"string"}},"required":["plot_id","geometry_geojson","country_of_production","commodity_hs","quantity_kg"],"type":"object"},"minItems":1,"type":"array"}},"required":["plots"],"type":"object"},"Fact":{"description":"A primary attestation at (cell, band, tslot). `value` is the band's typed reading (number, array of numbers for vector bands, or a categorical class id). `unit` is the band's declared unit (e.g. `m_msl`, `degC`, `mm`).","properties":{"absence_reason":{"description":"Present only when kind=`absence`.","enum":["unavailable_capability","outside_coverage","archetype_seed_unavailable","gpu_unavailable","upstream_error","upstream_timeout"],"type":"string"},"band":{"type":"string"},"cell":{"$ref":"#/components/schemas/Cell64"},"fact_cid":{"$ref":"#/components/schemas/FactCid"},"kind":{"description":"`primary` = signed measurement; `absence` = signed \"we don't have this here\" with a typed reason.","enum":["primary","absence"],"type":"string"},"provenance":{"description":"Upstream source key (e.g. `copdem30m`, `s2_l2a`, `cams_eu`).","type":"string"},"receipt":{"$ref":"#/components/schemas/Receipt"},"tslot":{"$ref":"#/components/schemas/Tslot"},"unit":{"type":"string"},"value":{"description":"Number, array of numbers, or class id depending on band type."}},"required":["kind","cell","band","tslot","value","fact_cid","receipt"],"type":"object"},"FactCid":{"description":"Content id of a fact: base32-nopad-lowercase encoding of `blake3(canonical_cbor(fact))[..16]`. 26 characters, alphabet `[a-z2-7]`.","example":"bafy2bzaceaqxfn4obg3orodvm6","type":"string"},"FetchReq":{"description":"Body for POST /v1/fetch. Either `cid` (resolve a fact by content-address) OR `cell`+`band` (materialize / read-through that band at that cell, optionally pinned to `tslot`). `cell` may be a cell64 string or a free-text place name resolved through /v1/locate.","properties":{"band":{"description":"Band key (required when `cell` is given).","type":"string"},"cell":{"description":"cell64 or place name.","type":"string"},"cid":{"description":"emem fact CID (blake3 base32-nopad lowercase).","type":"string"},"tslot":{"description":"Optional tslot pin; defaults to canonical.","type":"integer"}},"type":"object"},"FieldBoundariesResp":{"description":"Response of /v1/field_boundaries. `fields` is an array of per-field GeoJSON-Polygon features from Fields of The World (CC-BY-4.0). `attribution` and `license` must be surfaced with any rendered map.","properties":{"attribution":{"example":"Fields of The World / Taylor Geospatial Institute","type":"string"},"fields":{"items":{"properties":{"area_ha":{"type":"number"},"confidence":{"type":"number"},"country":{"type":"string"},"geometry":{"description":"GeoJSON Polygon.","type":"object"}},"type":"object"},"type":"array"},"license":{"example":"CC-BY-4.0","type":"string"},"receipt":{"$ref":"#/components/schemas/Receipt"}},"required":["fields","license","attribution"],"type":"object"},"FindSimilarReq":{"properties":{"band":{"default":"geotessera","description":"Vector band to scan. Default geotessera (128-D, int8+scale upstream → decoded f32 over the wire). Pass `geotessera.bin128` (or any band's `.bin128` sibling, plus `mode:\"hamming\"`) for the binary fast path.","type":"string"},"k":{"default":10,"maximum":1000,"minimum":1,"type":"integer"},"key":{"description":"cell64 (look up that cell's vector) or 'inline:[x,y,...]' literal vector","type":"string"},"mode":{"default":"cosine","description":"Scoring mode. `cosine` (default) is fp32 over the full vector. `hamming` is sign-bit popcount over the binary sibling band — ~1000× faster scan, ~65% recall@10 alone. `hamming_then_rerank` triages with Hamming then re-ranks the top 4·k by cosine — matches cosine precision at ~16× less work.","enum":["cosine","hamming","hamming_then_rerank"],"type":"string"}},"required":["key"],"type":"object"},"FindSimilarResp":{"description":"Response of /v1/find_similar. `neighbors` is the top-k list ordered by similarity (descending). `mode` echoes the scoring choice (`cosine` / `hamming` / `hamming_then_rerank`).","properties":{"band":{"type":"string"},"mode":{"enum":["cosine","hamming","hamming_then_rerank"],"type":"string"},"neighbors":{"items":{"description":"Stable neighbor schema: cell/score/lat/lng/place_label_cached are always present. lat/lng are explicit null for inline-vector queries or undecodable cells (no honest centroid) — never absent, never fabricated.","properties":{"cell":{"$ref":"#/components/schemas/Cell64"},"fact_cid":{"$ref":"#/components/schemas/FactCid"},"label":{"description":"Reader-friendly place label, if the cell is named in the gazetteer.","type":"string"},"lat":{"description":"Centroid latitude decoded from `cell`; null when the cell has no honest centroid (inline vector / undecodable).","type":["number","null"]},"lng":{"description":"Centroid longitude decoded from `cell`; null when unknown (see `lat`).","type":["number","null"]},"place_label_cached":{"description":"Best-effort gazetteer label (~25 km gate); null when the cell isn't near a known anchor.","type":["string","null"]},"score":{"description":"Cosine similarity in [-1, 1] for `cosine` / `hamming_then_rerank`; normalised Hamming agreement in [0, 1] for `hamming`.","type":"number"}},"required":["cell","score","lat","lng"],"type":"object"},"type":"array"},"receipt":{"$ref":"#/components/schemas/Receipt"}},"required":["neighbors","receipt"],"type":"object"},"HeatSolveReq":{"properties":{"cell":{"description":"cell64 string. The solver evaluates LST evolution at this cell's centre.","type":"string"},"diffusivity_m2_per_s":{"default":1e-6,"description":"Thermal diffusivity α (m²/s). Default 1e-6 matches urban surfaces (Oke 2017 §2.3 Table 2.4); use ~5e-7 for vegetation, ~1.4e-7 for water.","type":"number"},"hours_ahead":{"default":6,"description":"Forecast horizon in hours. Capped at 168 (one week).","type":"number"}},"required":["cell"],"type":"object"},"HuntReq":{"description":"Hunter-mode body. Either `region` (geocoded) or `polygon_bbox` (explicit). The responder samples up to 32 cells (8 for slow primary bands such as MODIS LST), recalls the algorithm's primary scalar input plus any configured gate band, optionally re-ranks the top-K via Tessera embedding coherence, and returns the top 8 hotspots.","properties":{"event":{"description":"Event keyword. Maps to one registered detection algorithm. Aliases accepted (case-insensitive): bloom/algae_bloom/chlorophyll_bloom → algal_bloom; forest_loss/tree_loss → deforestation; flood/inundation/flooded_fields → flood_extent; fire/bushfire/burn_severity → wildfire; uhi/heat_island/heat → urban_heat_island; methane/ghg_leak/super_emitter → methane_plume; mudslide/debris_flow/slope_failure → landslide; dry_spell/rainfall_deficit → drought; salinity → soil_salinity; crop_damage/stressed_crops → crop_stress; turbidity/sediment_plume → water_turbidity; oil_spill → oil_slick. The classifier in /v1/ask accepts the same set on free-text input.","enum":["algal_bloom","deforestation","flood_extent","wildfire","urban_heat_island","methane_plume","landslide","drought","soil_salinity","crop_stress","water_turbidity","oil_slick"],"type":"string"},"polygon_bbox":{"description":"Explicit polygon bbox; alternative to `region`.","properties":{"max_lat":{"type":"number"},"max_lng":{"type":"number"},"min_lat":{"type":"number"},"min_lng":{"type":"number"}},"type":"object"},"region":{"description":"Free-text region. Resolved through the same geocoder as /v1/locate. REQUIRED unless `polygon_bbox` is provided.","type":"string"}},"required":["event"],"type":"object"},"JepaPredictReq":{"properties":{"band":{"default":"indices.ndvi","description":"Band to forecast. v1 supports 'indices.ndvi' only.","type":"string"},"cell":{"description":"cell64 to forecast at.","type":"string"},"forecast_horizon_months":{"default":1,"description":"Horizon in months ahead. v1 supports 1; multi-step rollout lands in @2.","maximum":1,"minimum":1,"type":"integer"},"lookback_months":{"default":6,"description":"How many past months of history to read.","maximum":24,"minimum":1,"type":"integer"}},"required":["cell"],"type":"object"},"LocateReq":{"description":"Body for POST /v1/locate. Provide `place` (free-text, geocoded via embedded gazetteer → cache → Photon → Nominatim) OR `lat`+`lng`. Mutually exclusive: at least one of (`place`/`q`/`query`/`name`) or (`lat`+`lng`) must be present.","oneOf":[{"required":["place"]},{"required":["q"]},{"required":["query"]},{"required":["name"]},{"required":["lat","lng"]}],"properties":{"lat":{"description":"WGS-84 latitude. REQUIRED with `lng` unless `place` is provided.","type":"number"},"lng":{"description":"WGS-84 longitude. REQUIRED with `lat` unless `place` is provided.","type":"number"},"name":{"description":"Alias for `place`.","type":"string"},"place":{"description":"Free-text place name. Resolved via embedded gazetteer → cache → Photon → Nominatim.","type":"string"},"q":{"description":"Alias for `place`.","type":"string"},"query":{"description":"Alias for `place`.","type":"string"}},"type":"object"},"LocateResp":{"description":"Response of /v1/locate. `cell64` is the canonical handle for the resolved place; `polygon_bbox` is present when the geocoder found an extent (city / park / lake / country / region), absent for point features. `via` declares which layer of the seven-tier embedded cascade answered, falling back to network (Photon → Nominatim) only when no embedded layer matched.","properties":{"cell64":{"$ref":"#/components/schemas/Cell64"},"data_at_this_cell":{"description":"Topic-grouped inventory of recallable bands and applicable algorithms at this cell. Lets the caller chain into /v1/recall without a second introspection round-trip.","type":"object"},"label":{"description":"Reader-friendly place label.","type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"localized_names":{"additionalProperties":{"type":"string"},"description":"Map of ISO 639 language tag (`en`, `bn`, `zh-Hans`, `ar`, …) to localized name, when the resolved entity is in Overture and carries `names.common`. Lets an agent surface the user's-language label without a second geocoder call.","type":"object"},"neighborhood_cells":{"description":"Eight neighbouring cell64s of the resolved centre cell.","items":{"$ref":"#/components/schemas/Cell64"},"type":"array"},"overture_division":{"description":"Overture-divisions provenance, present when the cascade pulled an authoritative admin polygon. `division_id` is the GERS ID (globally stable, citable in receipts). `subtype` declares the admin level (country/region/county/locality/etc). `country` is the ISO 3166-1 alpha-2 owner.","properties":{"country":{"description":"ISO 3166-1 alpha-2 (e.g. `BD`, `US`).","type":"string"},"division_id":{"type":"string"},"schema_url":{"type":"string"},"subtype":{"enum":["country","region","county","localadmin","locality","borough","macrohood","neighborhood","microhood","dependency"],"type":"string"}},"type":"object"},"polygon_bbox":{"description":"Present when the place has spatial extent.","properties":{"max_lat":{"type":"number"},"max_lng":{"type":"number"},"min_lat":{"type":"number"},"min_lng":{"type":"number"},"source":{"description":"`overture_division_area` is authoritative (conflated OSM+Esri+Meta+TomTom polygon), preferred whenever Overture has a row for the entity. `country_table` / `admin1_table` / `admin2_table` / `admin3_table` are cities1000-aggregated approximations used when Overture is unreachable. `wide_bbox_table` is the curated wide-feature override for Sahara/Amazon/Himalayas etc.","enum":["wide_bbox_table","country_table","admin1_table","admin2_table","admin3_table","nominatim_boundingbox","overture_division_area","centre_cell_bbox"],"type":"string"}},"type":"object"},"polygon_geojson":{"description":"True OSM/Overture boundary as GeoJSON `Polygon` or `MultiPolygon` when an admin tier resolved. Pass back to /v1/recall_polygon to mask the cell grid against the boundary.","type":"object"},"polygon_sample_cells":{"description":"Up to 64 representative cells covering the polygon — pass to /v1/recall_many or /v1/recall_polygon.","items":{"$ref":"#/components/schemas/Cell64"},"type":"array"},"via":{"description":"Layer of the seven-tier locate cascade that answered. `country`/`admin1`/`admin2`/`admin3` = GeoNames hierarchical-admin tables (in-process); `embedded` = cities1000 populated places (in-process); `pois` = curated GeoNames well-known landmarks (peaks/lakes/parks/airports/monuments, in-process); `wide_bbox_table` = curated wide regions (in-process); `cache` = sled hot cache; `photon`/`nominatim` = network fallback.","enum":["direct_latlng","wide_bbox_table","country","admin1","admin2","admin3","embedded","pois","cache","photon","nominatim"],"type":"string"}},"required":["cell64","via"],"type":"object"},"MaterializeNote":{"description":"One entry in the response's `materialize_notes[]`, recording what the lazy materializer did during this call. status:\"materialized\" means a signed fact was minted and persisted (a Primary observation OR a confirmed, evidence-backed Absence - both are signed and citeable by fact_cid). status:\"skipped\" means nothing was signed: `reason_class` says why (transient `timeout`/`upstream_error`, retryable; or structural `unknown_band`/`no_materializer`, not retryable here) and `absence` is always false, because a skip is 'unknown', never a confirmed absence.","properties":{"absence":{"description":"Always false on a skip; a confirmed absence is a signed fact with status:materialized, not a skip.","type":"boolean"},"band":{"type":"string"},"cell":{"$ref":"#/components/schemas/Cell64"},"fact_cid":{"type":"string"},"latency_ms":{"type":"number"},"ok":{"type":"boolean"},"reason":{"type":"string"},"reason_class":{"enum":["timeout","upstream_error","unknown_band","no_materializer"],"type":"string"},"retryable":{"type":"boolean"},"status":{"enum":["materialized","skipped"],"type":"string"}},"type":"object"},"PubKey":{"description":"Ed25519 32-byte public key, base32-nopad-lowercase encoded (52 chars). Returned in receipts and `/.well-known/emem.json`.","example":"777er3yihgifqmv5hmc2wwmyszgddzderzhsx6rex4yoakwomvka","type":"string"},"QueryRegionReq":{"description":"Either `geometry` (cell64 or 'cells:c1,c2,...') or `bbox` ([west, south, east, north] WGS-84 degrees) is required. When `bbox` is given the responder samples it to up to `max_cells` cells (default 256, max 1024) and runs the canonical primitive over the cell list.","properties":{"agg":{"enum":["mean","median","p90","vector_centroid"],"type":"string"},"bands":{"items":{"type":"string"},"type":"array"},"bbox":{"description":"[west, south, east, north] in WGS-84 degrees (longitude first)","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"geometry":{"description":"cell64 or `cells:c1,c2,...`","type":"string"},"max_cells":{"default":256,"description":"Cap on cells sampled from `bbox`. Ignored when `geometry` is supplied.","maximum":1024,"minimum":1,"type":"integer"}},"type":"object"},"RecallReq":{"properties":{"as_of_signed_at":{"description":"Bi-temporal transaction-time bound (RFC 3339).","format":"date-time","type":"string"},"as_of_tslot":{"description":"Bi-temporal valid-time bound.","minimum":0,"type":"integer"},"bands":{"items":{"type":"string"},"type":"array"},"cell":{"description":"cell64 string","type":"string"},"deterministic":{"description":"Sugar over `provenance`: true keeps only classes any third party can recompute from the cited raw source (direct_sensor, deterministic_index); false keeps the rest. Composable with `provenance` (intersection).","type":"boolean"},"provenance":{"description":"Tamper-provenance filter: return only facts whose band's provenance class is in this list. Applied before the receipt is signed, so the receipt covers exactly the returned facts.","items":{"enum":["direct_sensor","deterministic_index","model_output","human_curated","unclassified"],"type":"string"},"type":"array"},"scope":{"description":"Optional multi-tenant scope {user_id, agent_id, run_id, org_id}. When at least one field is set the recall is filtered to facts written under the same four-tuple and the receipt binds the scope. Omit for the global recall.","properties":{"agent_id":{"type":"string"},"org_id":{"type":"string"},"run_id":{"type":"string"},"user_id":{"type":"string"}},"type":"object"},"tslot":{"type":"integer"}},"required":["cell"],"type":"object"},"Receipt":{"description":"Ed25519-signed receipt over the canonical preimage of (request_id, served_at, primitive, cells, fact_cids). The browser-side verifier at /verify reconstructs this preimage from the receipt fields alone — no callback to the issuer. NOTE: the preimage covers ONLY those five fields. The caller's `place`/`q` string, raw `lat`/`lng`, requested `bands[]`, requested `tslot`, and `intent` are NOT signed — a wrong-place geocode produces a valid signature for the wrong cell. Branch on /v1/locate `selected.is_high_confidence` before trusting place-anchored answers. Also: `fact_cid` is per-replica (signed_at differs across responders even for byte-identical upstream pixels); cross-replica join key is the tuple (cell, band, tslot). /v1/recall_polygon emits one independently signed receipt per cell under `by_cell.<cell>.receipt` — `merged_facts[]` is convenience flattening and is NOT covered by an aggregate signature.","properties":{"cells":{"items":{"$ref":"#/components/schemas/Cell64"},"type":"array"},"cost":{"$ref":"#/components/schemas/Cost"},"fact_cids":{"items":{"$ref":"#/components/schemas/FactCid"},"type":"array"},"intent":{"description":"Optional natural-language hint. Populated when served via /v1/intent.","type":"string"},"merkle_proof":{"description":"Inclusion proof for `fact_cids[0]` when persisted. Omitted from JSON when None.","properties":{"index":{"type":"integer"},"siblings":{"items":{"type":"string"},"type":"array"}},"type":"object"},"primitive":{"description":"Namespaced wire form: `emem.recall`, `emem.find_similar`, `emem.verify`, …","type":"string"},"registry_cid":{"description":"CID of the function registry version in force.","type":"string"},"request_id":{"description":"ULID generated per request.","type":"string"},"responder":{"$ref":"#/components/schemas/PubKey"},"responder_key_epoch":{"description":"u32 rotation counter; bumps when the operator rotates keys.","type":"integer"},"responder_pubkey_b32":{"$ref":"#/components/schemas/PubKey"},"schema_cid":{"description":"CID of the active CDDL profile.","type":"string"},"served_at":{"description":"ISO 8601 UTC, second precision.","type":"string"},"signature":{"description":"Ed25519 signature, 64 bytes base32-nopad-lowercase encoded.","type":"string"},"source_versions":{"additionalProperties":{"type":"string"},"description":"Per-source freshness map.","type":"object"}},"required":["request_id","served_at","primitive","cells","fact_cids","schema_cid","responder","responder_key_epoch","responder_pubkey_b32","signature","registry_cid"],"type":"object"},"SignedResponse":{"description":"Standard recall envelope. `facts` is the array of signed facts touched by this call (subset of `bands_already_attested_at_cell` after auto-materialization). `receipt` is the responder's signature over the call. `materialize_notes` lists any lazy-materializer activity that happened to satisfy the request — empty for purely warm reads.","properties":{"bands_already_attested_at_cell":{"description":"Bands the cell already has facts for, regardless of whether they were requested. Useful for follow-up calls without a second /v1/coverage_matrix hit.","items":{"type":"string"},"type":"array"},"caveats":{"description":"Plain-language constraints the caller should fold into their answer (grid resolution, revisit cadence, sample-size warnings).","items":{"type":"string"},"type":"array"},"facts":{"items":{"$ref":"#/components/schemas/Fact"},"type":"array"},"materialize_notes":{"items":{"$ref":"#/components/schemas/MaterializeNote"},"type":"array"},"receipt":{"$ref":"#/components/schemas/Receipt"}},"required":["facts","receipt"],"type":"object"},"TrajectoryReq":{"properties":{"band":{"type":"string"},"cell":{"type":"string"},"window":{"items":{"type":"integer"},"maxItems":2,"minItems":2,"type":"array"}},"required":["cell","band","window"],"type":"object"},"Tslot":{"description":"Band-tempo-relative integer offset from the emem epoch. Each band declares its tempo (`fast` / `medium` / `slow` / `static`); tslot is the rounded count of that tempo's unit since the epoch.","minimum":0,"type":"integer"},"VerifyReceiptResp":{"description":"Response of /v1/verify_receipt. `valid` is the ed25519 signature check; `signer` is the responder pubkey the signature was checked against; `preimage_b64` is the canonical preimage that was hashed and signed, for reproduction in any other ed25519 implementation.","properties":{"errors":{"items":{"type":"string"},"type":"array"},"preimage_b64":{"type":"string"},"signer":{"$ref":"#/components/schemas/PubKey"},"valid":{"type":"boolean"}},"required":["valid","signer"],"type":"object"},"VerifyReq":{"properties":{"cell":{"type":"string"},"claim":{"$ref":"#/components/schemas/Claim"},"mode":{"enum":["fast","resolve"],"type":"string"}},"required":["claim","cell"],"type":"object"},"VerifyResp":{"description":"Response of /v1/verify. `holds` is the boolean verdict; `evidence_cids` are the fact CIDs the verifier walked to reach the verdict.","properties":{"evidence_cids":{"items":{"$ref":"#/components/schemas/FactCid"},"type":"array"},"explanation":{"type":"string"},"holds":{"type":"boolean"},"receipt":{"$ref":"#/components/schemas/Receipt"}},"required":["holds","receipt"],"type":"object"},"WaveSolveReq":{"properties":{"coastal_cell":{"description":"cell64 of the coastal destination.","type":"string"},"n_offshore_cells":{"default":8,"description":"Number of seaward cells to sample for the bathymetric profile.","maximum":64,"minimum":1,"type":"integer"},"offshore_height_m":{"description":"Offshore significant wave height H_s (m).","maximum":30,"minimum":0,"type":"number"},"period_s":{"description":"Wave period (s); 6–18 s is the typical wind-wave + swell envelope.","maximum":30,"minimum":2,"type":"number"}},"required":["coastal_cell","offshore_height_m","period_s"],"type":"object"}}},"info":{"description":"Shared, verifiable memory for AI agents: one canonical, citeable identity per place (cell64), observation (fact_cid), and object (emem:entity:), so different models reason from the same world object instead of divergent descriptions. Grounded in signed Earth observation; every read returns an ed25519 receipt. cell × band × tslot.","license":{"name":"Apache-2.0"},"title":"emem","version":"1.1.0"},"openapi":"3.1.0","paths":{"/.well-known/emem-verifier.json":{"get":{"description":"Alias of GET /v1/verifier_spec: the code-generated signing/verification specification, at a well-known path so an offline verifier can discover it without reading the OpenAPI document.","operationId":"emem_verifier_spec_well_known","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"Alias of GET /v1/verifier_spec: the code-generated signing/verification specification, at a well-known path so an offline verifier can discover it without reading the OpenAPI document.","tags":["verify"]}},"/.well-known/emem.json":{"get":{"description":"protocol discovery","operationId":"emem_well_known","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"protocol discovery"}},"/health":{"get":{"description":"liveness","operationId":"emem_health","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"liveness"}},"/mcp":{"post":{"description":"MCP JSON-RPC 2.0","operationId":"mcp_jsonrpc","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"MCP JSON-RPC 2.0"}},"/v1/agent_card":{"get":{"description":"rich tool catalog with when-to-use","operationId":"emem_agent_card","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"rich tool catalog with when-to-use"}},"/v1/agent_quickref":{"get":{"description":"agent-targeted intent map: which endpoint to call for which user intent, with usage priority + trust language","operationId":"emem_agent_quickref","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"agent-targeted intent map: which endpoint to call for which user intent, with usage priority + trust language","tags":["boring"]}},"/v1/agent_stats":{"get":{"description":"per-tool MCP latency + error counts","operationId":"emem_agent_stats","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"per-tool MCP latency + error counts"}},"/v1/air":{"get":{"description":"Recall the signed Copernicus CAMS air-quality facts (PM2.5 + NO2 + O3) at a place's cell64, attesting on a miss. Composes locate → recall → aggregate; each band carries a citeable fact_cid.\n\nWhen to use: Use when the user names a place and asks about air quality, pollution, or emissions exposure. CAMS is the European reanalysis — global coverage, ~0.4° native (resampled). For finer-grained urban PM2.5, pair with /v1/at-style stations data when available.","operationId":"emem_air_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/air?lat=&lon= — CAMS PM2.5 + NO2 + O3 bundle (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed Copernicus CAMS air-quality facts (PM2.5 + NO2 + O3) at a place's cell64, attesting on a miss. Composes locate → recall → aggregate; each band carries a citeable fact_cid.\n\nWhen to use: Use when the user names a place and asks about air quality, pollution, or emissions exposure. CAMS is the European reanalysis — global coverage, ~0.4° native (resampled). For finer-grained urban PM2.5, pair with /v1/at-style stations data when available.","operationId":"emem_air_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/air {place|lat,lng}","tags":["boring"]}},"/v1/algorithms":{"get":{"description":"Content-addressed dictionary of composition recipes — formulas that fuse attested band facts (and embeddings) into derived scores, classifications, and similarity metrics.\n\nWhen to use: Call when the user's question is COMPOSITE (flood risk, urban density, water consensus, change-since-2020) rather than a single band readout. Each entry has `kind` (solo | combined | embedding), the input `bands` (assemble one `emem_recall` body from them), the `formula` in plain math, the `output` shape, and a `citation`. The agent applies the formula in-process and quotes the algorithm key + `algorithms_cid` (from `emem_manifests`) alongside the input fact_cids — that gives the receipt enough context for any other operator to replay the same composition deterministically. Embedding entries (cosine, novelty, change, neighborhood-consistency) operate on `geotessera`; for the most common k-NN pattern the protocol-native `emem_find_similar` is faster than fetching vectors and computing locally.","operationId":"emem_algorithms","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"composition recipe registry (formulas that fuse band facts)"}},"/v1/algorithms/{key}":{"get":{"description":"Content-addressed dictionary of composition recipes — formulas that fuse attested band facts (and embeddings) into derived scores, classifications, and similarity metrics.\n\nWhen to use: Call when the user's question is COMPOSITE (flood risk, urban density, water consensus, change-since-2020) rather than a single band readout. Each entry has `kind` (solo | combined | embedding), the input `bands` (assemble one `emem_recall` body from them), the `formula` in plain math, the `output` shape, and a `citation`. The agent applies the formula in-process and quotes the algorithm key + `algorithms_cid` (from `emem_manifests`) alongside the input fact_cids — that gives the receipt enough context for any other operator to replay the same composition deterministically. Embedding entries (cosine, novelty, change, neighborhood-consistency) operate on `geotessera`; for the most common k-NN pattern the protocol-native `emem_find_similar` is faster than fetching vectors and computing locally.","operationId":"emem_algorithms_one","parameters":[{"in":"path","name":"key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"single algorithm detail"}},"/v1/artifacts/{cid}":{"get":{"description":"raw canonical grid bytes by artifact cid, Cache-Control immutable (content-addressed bytes never change). A 404 is typed and says how to rebuild: eviction is a design property (the derivation record persists and pins the scene, recipe, and geometry), never data loss.","operationId":"emem_artifact_bytes","responses":{"200":{"description":"application/x.emem-grid-f32.v1 bytes"},"404":{"description":"evicted or unknown; the derivation record pins the rebuild"}},"summary":"raw canonical grid bytes by artifact cid, Cache-Control immutable (content-addressed bytes never change). A 404 is typed and says how to rebuild: eviction is a design property (the derivation record persists and pins the scene, recipe, and geometry), never data loss."}},"/v1/ask":{"post":{"description":"Single-shot free-text answer about a real-world location, backed by signed satellite/elevation/water/built-up receipts. Forwards a place mention plus a question; runs the locate → recall → algorithm chain server-side; returns one packaged envelope.\n\nWhen to use: Use when the question concerns a specific real-world place and a packaged, citation-bearing answer is preferable to manual primitive composition. Forward the user's question verbatim as `q` plus the location as `place` (free text), `cell` (cell64), or `lat`+`lng`. The server resolves the location, classifies the question to a topic, recalls every relevant band (auto-materializing Sentinel-2 / Sentinel-1 / Cop-DEM / JRC GSW / Overture / weather on miss), surfaces the algorithm recipes that compose those bands into named scores, and returns a single envelope with `topic_routing`, `facts`, `algorithms_for_question`, an optional Sentinel-2 RGB scene URL, and a `caveats` block (grid resolution, revisit cadence). All facts are signed by the responder; the signed `receipt` (and its content-addressed `fact_cids`) is surfaced at the envelope ROOT — `response.receipt` / `response.fact_cids` — exactly like every other primitive, and is also mirrored under `facts_summary.receipt` for back-compat. Set `include_image: true` to bundle the latest cloud-free Sentinel-2 thumbnail. Out-of-scope questions return `topic_routing.matched_topic: null` plus the full inventory so the caller can route elsewhere.","operationId":"emem_ask","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"single-shot free-text answer with signed evidence"}},"/v1/at":{"get":{"description":"One-shot recall of the signed facts at a place's cell64 (or lat/lng); each band carries a citeable fact_cid. Defaults to emem's standard at-a-glance band set; pass `band` / `bands` to override. Polygon-resolved places stay at the centroid by default (`n_cells: 1`) to keep multi-band calls cheap; pass `n_cells: 2..=64` to fan out.\n\nWhen to use: Use when the user names a place and wants the standard situational readout (vegetation + elevation + landcover + recent weather) without picking bands. Polygon-aware: `place` that resolves to a polygon (park, lake, district) lands at the centroid unless `n_cells` widens it. For a single band, use the domain-specific shortcuts (emem_ndvi, emem_air, …) or emem_recall directly.","operationId":"emem_at_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}},{"in":"query","name":"band","required":false,"schema":{"description":"e.g. indices.ndvi, soilgrids.soc_0_30cm, weather.temperature_2m","type":"string"}},{"in":"query","name":"bands","required":false,"schema":{"description":"CSV of bands, applied alongside `band`","type":"string"}},{"in":"query","name":"tslot","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/at?lat=&lon=&band= — boring lat/lng lookup of any of the 101 materializable bands. Also accepts ?place=…","tags":["boring"]},"post":{"description":"One-shot recall of the signed facts at a place's cell64 (or lat/lng); each band carries a citeable fact_cid. Defaults to emem's standard at-a-glance band set; pass `band` / `bands` to override. Polygon-resolved places stay at the centroid by default (`n_cells: 1`) to keep multi-band calls cheap; pass `n_cells: 2..=64` to fan out.\n\nWhen to use: Use when the user names a place and wants the standard situational readout (vegetation + elevation + landcover + recent weather) without picking bands. Polygon-aware: `place` that resolves to a polygon (park, lake, district) lands at the centroid unless `n_cells` widens it. For a single band, use the domain-specific shortcuts (emem_ndvi, emem_air, …) or emem_recall directly.","operationId":"emem_at_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/at {place|lat,lng[,band|bands,tslot]} → multi-band at a point","tags":["boring"]}},"/v1/attest":{"post":{"description":"submit signed attestation (JSON). Body carries a batch envelope: `batch_root` (the 32-byte BLAKE3 merkle root over the per-fact CIDs, serialized as a 32-element array of byte integers — NOT a hex string), `attester`, `signature` (ed25519 over blake3(batch_root||registry_cid||schema_cid)), and `facts[]` (each is a tagged variant carrying `kind` plus cell, band, tslot, value, and per-fact metadata). The responder rejects facts that don't hash into the named batch_root, and rejects the envelope if the signature does not verify against the attester pubkey under the corresponding ed25519 key.","operationId":"emem_attest","requestBody":{"content":{"application/json":{"schema":{"properties":{"attester":{"description":"base32-nopad-lc 32-byte attester pubkey","type":"string"},"batch_root":{"description":"32-byte BLAKE3 merkle root over the per-fact CIDs, as a 32-element array of byte integers (serde [u8;32]). A hex string is NOT accepted.","items":{"maximum":255,"minimum":0,"type":"integer"},"maxItems":32,"minItems":32,"type":"array"},"facts":{"items":{"properties":{"band":{"type":"string"},"cell":{"type":"string"},"kind":{"description":"Tagged fact variant; required. `primary` = direct observation, `derivative` = deterministic function over parent facts, `absence` = signed confirmed-absence.","enum":["primary","derivative","absence"],"type":"string"},"privacy_class":{"type":"string"},"signed_at":{"type":"string"},"tslot":{"type":"integer"},"value":{}},"required":["kind","cell","band","value"],"type":"object"},"type":"array"},"signature":{"description":"base32-nopad-lc ed25519 signature over blake3(batch_root||registry_cid||schema_cid)","type":"string"}},"required":["batch_root","attester","signature","facts"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"submit signed attestation (JSON). Body carries a batch envelope: `batch_root` (the 32-byte BLAKE3 merkle root over the per-fact CIDs, serialized as a 32-element array of byte integers — NOT a hex string), `attester`, `signature` (ed25519 over blake3(batch_root||registry_cid||schema_cid)), and `facts[]` (each is a tagged variant carrying `kind` plus cell, band, tslot, value, and per-fact metadata). The responder rejects facts that don't hash into the named batch_root, and rejects the envelope if the signature does not verify against the attester pubkey under the corresponding ed25519 key."}},"/v1/attest_cbor":{"post":{"description":"submit signed attestation (canonical CBOR)","operationId":"emem_attest_cbor","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"submit signed attestation (canonical CBOR)"}},"/v1/backfill":{"post":{"description":"Materialize and sign every per-tslot fact for one (cell, band) inside a [start_unix, end_unix] window. Returns a signed list of (tslot, fact_cid, status) for each step. Slow but possible — one upstream fetch per tslot, capped by `max_facts`.\n\nWhen to use: Call when the user wants HISTORY for a fast/medium-tempo band and `emem_trajectory` returned only the latest point. The responder iterates the tslot range derived from the band's tempo, calls the per-tslot historical materializer, signs each result, and persists. After completion `emem_trajectory` over the same window returns the full series. Bands without a historical materializer (e.g. `weather.*` from met.no's nowcast) return `status: \"present_only\"` for past tslots — check `emem_coverage_matrix.history_available_from`/`history_available_to` to see how far back each band can be backfilled. Prefer this over staking an attestation when the upstream is publicly fetchable.","operationId":"emem_backfill","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackfillReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"materialize history in a window The preparer form: pass cells (up to 64) instead of cell to warm an area across the window under the partial-results contract (budget_ms, typed pending[], converged); the densification warmer loops exactly this on a schedule when the operator declares warm_priority.json and EMEM_WARM_INTERVAL_SECS."}},"/v1/band_raster":{"post":{"description":"Return a native-resolution Sentinel-2 window over a bounding box as a FIELD, not a set of points: the pixels become one content-addressed grid artifact (deterministic f32 encoding; fetch the bytes at the returned artifact url, Cache-Control immutable), and what the receipt attests is the DERIVATION, never a byte pipe. A persisted derivation record pins the chosen scene (id, asset, capture time, cloud cover), the recipe (band_raster@1), the grid georeferencing in the scene's UTM CRS, and best-effort per-cell anchors that bridge the artifact to existing signed facts; the receipt's FIELD preimage segment binds (aoi_cid, derivation_cid), reported by /v1/verify_receipt as field_bound. Bounds are refusals with the cap named: six raw S2 bands (B02/B03/B04/B08/B11/B12) and 512 px per side at native resolution (about 5.1 km at 10 m). Anchors never materialize a fact, so a cold AOI costs one scene read, nothing more. The artifact is evictable BY DESIGN: the record persists like any fact and pins everything needed to rebuild identical bytes, so eviction turns a dereference into a recompute, never a broken citation. Returns two tokens: emem:raster: (resolve with emem_raster_resolve) and the record's own emem:fact: handle. This signs and persists the derivation record.\n\nWhen to use: Call when an agent needs an area's actual field of values rather than per-cell scalars: change analysis over a scene window, input to a model that reads grids, or exporting verifiable pixels a third party can re-derive. For one cell's value use emem_recall; for an RGB visual use emem_cell_scene_rgb, which is a view, not a signed artifact; for areas beyond the 512 px cap, page the bbox.","operationId":"emem_band_raster","requestBody":{"content":{"application/json":{"schema":{"properties":{"band":{"description":"s2.B02|s2.B03|s2.B04|s2.B08|s2.B11|s2.B12","type":"string"},"bbox":{"properties":{"max_lat":{"type":"number"},"max_lng":{"type":"number"},"min_lat":{"type":"number"},"min_lng":{"type":"number"}},"required":["min_lat","min_lng","max_lat","max_lng"],"type":"object"},"observed_on":{"description":"optional target capture date YYYY-MM-DD; the chosen scene is pinned either way","type":"string"}},"required":["bbox","band"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"502":{"description":"upstream scene fetch failed; typed"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"a field as a signed derivation (docs/plans/field-tokens.md): native-resolution Sentinel-2 window over a bbox, returned as a content-addressed canonical grid artifact plus a persisted derivation record. The receipt attests the derivation, never a byte pipe: its FIELD preimage segment binds (aoi_cid, derivation_cid), the record pins the scene (id, asset, capture time), the recipe (band_raster@1), the grid georeferencing, and best-effort per-cell anchors bridging to existing signed facts. Bands: s2.B02/B03/B04/B08/B11/B12; window cap 512 px per side, refused with the cap named. The artifact is evictable (GET /v1/artifacts/{cid}); the derivation record persists and pins the rebuild."}},"/v1/bands":{"get":{"description":"Active band ontology (offsets, dims, tempo, privacy).\n\nWhen to use: Call once at session start to learn the band registry — every other primitive's `band` argument MUST come from this list.","operationId":"emem_bands","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"band ontology"}},"/v1/benchmark":{"get":{"description":"Hand-verified evaluation items for grading an agent against the responder. Returns {items[], grader_url}. Submit answers (cell64 or fact_cid per item) to POST /v1/benchmark/grade for per-item scores. Items today: elevation recall, NDVI, find_similar neighbours.\n\nWhen to use: Call once at agent-onboarding time (or in CI) to fetch the canonical task list, then have the agent answer each item using its normal tool routing, and POST the answers map to /v1/benchmark/grade for a deterministic score. Lets an operator regression-check that an agent build still hits ground truth.","operationId":"emem_benchmark","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"hand-verified evaluation items for grading an agent against the responder. Returns {items[], grader_url, _note}. Submit answers to POST /v1/benchmark/grade for per-item scores."}},"/v1/benchmark/grade":{"post":{"description":"grade an agent's submission against /v1/benchmark items. Body: {answers: {<item_id>: <fact_cid or cell64>}}. Returns per-item correctness plus an aggregate score.","operationId":"emem_benchmark_grade","requestBody":{"content":{"application/json":{"schema":{"properties":{"answers":{"additionalProperties":{"type":"string"},"type":"object"}},"required":["answers"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"grade an agent's submission against /v1/benchmark items. Body: {answers: {<item_id>: <fact_cid or cell64>}}. Returns per-item correctness plus an aggregate score."}},"/v1/burn_severity":{"post":{"description":"Compute the differenced Normalized Burn Ratio (dNBR = NBR_pre − NBR_post; Key & Benson 2006) and map it to the USGS burn-severity classes (unburned / low / moderate-low / moderate-high / high). Supply `nbr_pre` + `nbr_post` (pin the scenes bracketing the fire date) for a correct result, or omit both to use the two most-recent stored `indices.nbr` scenes (older=pre, newer=post) as a coarse estimate. The result is signed; the receipt cites the NBR fact_cids it read from the shared memory.\n\nWhen to use: Call after a wildfire to quantify how badly an area burned, or to triage post-fire severity across a region cell-by-cell. Best practice: explicitly pass `nbr_pre`/`nbr_post` from scenes that bracket the known fire date — the stored-trajectory fallback just takes the two most-recent scenes and may not bracket the fire. Surface `dnbr` and `severity_class`. For active-fire detection use `emem_hunt` with the wildfire event instead.","operationId":"emem_burn_severity","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"nbr_post":{"description":"Post-fire NBR.","type":"number"},"nbr_pre":{"description":"Pre-fire NBR.","type":"number"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Key & Benson dNBR burn severity: dNBR = nbr_pre − nbr_post, mapped to USGS severity classes. Supply `nbr_pre` + `nbr_post` (pin the scenes bracketing the fire date) or omit to use the two most-recent stored `indices.nbr` scenes."}},"/v1/capabilities":{"get":{"description":"Live capability snapshot of the responder's GPU sidecar — extensions[] (e.g. gpu, clay-v1.5, prithvi-eo2), cuda_available, models_loaded[], healthy, last_polled_unix_s. Refreshed every 30 s by a background poller; reads are constant-time.\n\nWhen to use: Call before scheduling a GPU-heavy plan (Clay / Prithvi / Galileo embeddings, foundation-anchored algorithms) so the agent knows whether the GPU tier is up *right now* without per-request /health round-trips. Pair with `emem_topics` (its `algorithm_availability` map says which algorithm keys can run given the current capabilities) and `emem_explain_algorithm` (full inference-tier metadata per algorithm). When `extensions[]` is empty the sidecar is unreachable — only CPU/scalar/cached tiers will produce facts; foundation-anchored materializers will sign Absence with `gpu_unavailable` reason.","operationId":"emem_capabilities","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"cached upstream capability snapshot (extensions[], cuda_available, models_loaded, endpoints[].trained/experimental). 30 s background poll; agents read this to filter algorithms whose inference.required_extension is missing instead of hitting /health per request."},"post":{"description":"Live capability snapshot of the responder's GPU sidecar — extensions[] (e.g. gpu, clay-v1.5, prithvi-eo2), cuda_available, models_loaded[], healthy, last_polled_unix_s. Refreshed every 30 s by a background poller; reads are constant-time.\n\nWhen to use: Call before scheduling a GPU-heavy plan (Clay / Prithvi / Galileo embeddings, foundation-anchored algorithms) so the agent knows whether the GPU tier is up *right now* without per-request /health round-trips. Pair with `emem_topics` (its `algorithm_availability` map says which algorithm keys can run given the current capabilities) and `emem_explain_algorithm` (full inference-tier metadata per algorithm). When `extensions[]` is empty the sidecar is unreachable — only CPU/scalar/cached tiers will produce facts; foundation-anchored materializers will sign Absence with `gpu_unavailable` reason.","operationId":"emem_capabilities_post","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"identical idempotent capability snapshot (accepts POST so callers that POST every /v1/* endpoint don't 405)"}},"/v1/cells/{cell64}":{"get":{"description":"recall facts at a cell","operationId":"emem_recall_cell_get","parameters":[{"in":"path","name":"cell64","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"recall facts at a cell"}},"/v1/cells/{cell64}/geojson":{"get":{"description":"Cell polygon as a native MCP EmbeddedResource (mimeType application/geo+json). Properties carry centre lat/lng, bbox, approx size in metres, and the 8-cell neighbourhood — drop straight into Mapbox / Leaflet / Deck.gl / QGIS without a GIS pipeline.\n\nWhen to use: Call when the agent (or a downstream renderer) needs the cell as geographic geometry — for map overlays, polygon-clipping ops, or feeding a styling pipeline. Pass `cell` as cell64 or place name. The result is a GeoJSON Feature with Polygon geometry; for a FeatureCollection that includes every recalled fact's value as a property, fetch /v1/cells/{cell64}/recall_geojson?bands=... over plain REST instead.","operationId":"emem_cell_geojson","parameters":[{"in":"path","name":"cell64","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"cell polygon as GeoJSON"}},"/v1/cells/{cell64}/info":{"get":{"description":"cell64 introspection (centroid, bbox, neighbors)","operationId":"emem_cell_info","parameters":[{"in":"path","name":"cell64","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"cell64 introspection (centroid, bbox, neighbors)"}},"/v1/cells/{cell64}/recall_geojson":{"get":{"description":"cell polygon as GeoJSON Feature with every recalled fact embedded as a property — paste straight into Mapbox/Leaflet/Deck.gl","operationId":"emem_cell_recall_geojson","parameters":[{"in":"path","name":"cell64","required":true,"schema":{"type":"string"}},{"in":"query","name":"bands","required":false,"schema":{"description":"comma-separated band list; default = every recallable band at this cell","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"cell polygon as GeoJSON Feature with every recalled fact embedded as a property — paste straight into Mapbox/Leaflet/Deck.gl"}},"/v1/cells/{cell64}/scene.png":{"get":{"description":"Sentinel-2 true-colour thumbnail (256×256 PNG)","operationId":"emem_cell_scene_png","parameters":[{"in":"path","name":"cell64","required":true,"schema":{"type":"string"}},{"description":"max eo:cloud_cover percent","in":"query","name":"max_cloud","required":false,"schema":{"default":20.0,"type":"number"}},{"description":"ISO-8601 date or datetime; returns the most recent S2 scene at or before this instant (window resolved server-side via EMEM_SCENE_AT_LOOKBACK_DAYS, default 7d). Ignored when `datetime` is also supplied.","in":"query","name":"at","required":false,"schema":{"example":"2024-06-15","format":"date-time","type":"string"}},{"description":"Explicit STAC datetime window `A/B`; takes precedence over `at`.","in":"query","name":"datetime","required":false,"schema":{"example":"2024-06-01T00:00:00Z/2024-06-30T00:00:00Z","type":"string"}}],"responses":{"200":{"content":{"image/png":{"schema":{"format":"binary","type":"string"}}},"description":"ok (image/png)"}},"summary":"Sentinel-2 true-colour thumbnail (256×256 PNG)"}},"/v1/cells/{cell64}/scene.rgb":{"get":{"description":"True-colour Sentinel-2 L2A RGB thumbnail centred on a cell. PNG returned as a native MCP ImageContent block (mimeType image/png). Pure-Rust pipeline: STAC search + HTTP-Range COG reads + 2-98 percentile stretch + PNG encode.\n\nWhen to use: Call when the user wants a VISUAL of a place — 'show me what this looks like', 'before/after the flood', 'is there a forest here', 'is this developed'. Returns a 256×256 px RGB image (~2.56 km × ~2.56 km at S2's 10 m native resolution), centred on the cell. Pass `cell` as a cell64 string OR a place name (auto-resolved). `max_cloud` filters scenes by `eo:cloud_cover` (default 20 %); raise it (60–80 %) for cloud-prone tropics if you keep getting 'no scene' errors. `datetime` is an RFC 3339 interval like `\"2024-01-01T00:00:00Z/2024-12-31T00:00:00Z\"` for a temporal slice (defaults to last 90 days). `structuredContent` carries the STAC item id, capture time, cloud_cover, EPSG, and per-channel reflectance percentile stretch values used — quote those alongside the image so the receipt is reproducible.","operationId":"emem_cell_scene_rgb","parameters":[{"in":"path","name":"cell64","required":true,"schema":{"type":"string"}},{"in":"query","name":"max_cloud","required":false,"schema":{"default":20.0,"type":"number"}},{"description":"ISO-8601 date or datetime; returns the most recent S2 scene at or before this instant. Ignored when `datetime` is also supplied.","in":"query","name":"at","required":false,"schema":{"format":"date-time","type":"string"}},{"description":"Explicit STAC datetime window `A/B`; takes precedence over `at`.","in":"query","name":"datetime","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"description":"raw rgb8 plane (w*h*3 bytes)"}},"summary":"Sentinel-2 true-colour thumbnail as raw 8-bit RGB pixel buffer (no PNG framing). Width/height returned via x-emem-scene-width/x-emem-scene-height headers."}},"/v1/change_attribution":{"post":{"description":"The first runnable surface of the change decomposition Δz = Δ_env + Δ_sensor + Δ_geo + Δ_encoder + ε: a per-term evidence LEDGER for the readout change at a cell, with NO numeric split. `observed` carries the Tessera year-over-year embedding change. `terms.env` carries label-free index pairs (NDVI, NBR, NDWI) with raw deltas and both fact cids, evidence a future estimator would read. `terms.sensor` records what each visit was observed through (source scheme and scene id per band) and whether that path changed. `terms.geo` is declared not estimated (no registration-residual surface exists). `terms.encoder` is pinned by construction: both vintages are slices of one signed multi-year fact under one recipe, named by fn_key. `terms.noise` reports the S2 scene-classification class per visit, so a cloud flip is visible. `split` is null and `attribution_note` says why: splitting a delta numerically needs a calibrated cross-encoder, cross-sensor stability model this responder does not have, and inventing magnitudes would fabricate the exact confusion the decomposition exists to prevent. The ledger persists: each run stores itself as a derivative fact (band change_attribution.ledger, parents = every fact read) and the response returns its own emem:fact: token under ledger_fact, so an attribution is cited and dereferenced like any reading. The receipt binds every input fact cid plus the stored ledger cid. Bands read cold may materialize, so this signs and persists facts.\n\nWhen to use: Call when a change surface (emem_diff, emem_state_diff, emem_triple_consensus, did_change) reported that a place's readout moved and the question is WHY: world, instrument, pixels, model, or noise. Read the per-term evidence and cite its fact cids; do not expect a numeric split (`split` is null by design, see `attribution_note`). Bands with fewer than two distinct tslots at the cell appear under evidence_absent with a typed reason rather than a fabricated pair. For the raw delta itself use emem_diff; for the multi-encoder change vote use emem_triple_consensus.","operationId":"emem_change_attribution","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"The attribution ledger for a readout change at a cell: per-term evidence for Δz = Δ_env + Δ_sensor + Δ_geo + Δ_encoder + ε, with NO numeric split. Reports the observed Tessera year-over-year embedding change, label-free index pairs (NDVI, NBR, NDWI) with raw deltas as environment evidence, the sources each visit was observed through (sensor record), the encoder pinning proof (one signed multi-year fact, one recipe), and the S2 scene-class per visit (noise evidence). `split` is null and `attribution_note` says why: a calibrated cross-encoder, cross-sensor stability model does not exist in this build. Each run persists as a derivative fact and returns its own emem:fact: token under ledger_fact; the receipt binds the input cids plus the stored ledger cid."}},"/v1/compare":{"post":{"description":"Compare two cells: cosine similarity over shared vector bands + per-band scalar deltas.\n\nWhen to use: Call when the user asks 'how similar is X to Y', 'compare these two places', or wants a difference vector. Returns a single cosine score and per-band deltas.","operationId":"emem_compare","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"compare two cells"}},"/v1/compare_bands":{"post":{"description":"Compare two bands at the same cell. Scalar pair → metric=delta, value=b-a. Vector pair (equal dim) → metric=cosine + per-dim delta. Returns a signed receipt naming both source fact CIDs.\n\nWhen to use: Call when the user wants cross-source consistency at one place ('does Cop-DEM agree with GMRT here?'), cross-vintage drift ('how did the embedding change between 2017 and 2024 at this cell?'), or any band-vs-band comparison within a single cell. `cell` + `a` + `b` are required. `tslot_a`/`tslot_b` are OPTIONAL: omit them to let the responder auto-pick each band's latest attested tslot — required for medium/fast-tempo bands (NDVI 30-day, MODIS 8-day, weather, CAMS) where there is no fact at tslot=0. The response carries `tslot_resolution` (echoes what was chosen and why) and `bands_with_no_history` (lists any band the cell has no attested fact for).","operationId":"emem_compare_bands","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareBandsReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompareBandsResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"per-band diff at one cell: scalar delta or vector cosine between band A and band B (optionally pinned to specific tslots), with optional consistency predicate"}},"/v1/contributors":{"get":{"description":"list of contributing pubkeys + per-band fact counts","operationId":"emem_contributors","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"list of contributing pubkeys + per-band fact counts"}},"/v1/contributors/{pubkey_b32}":{"get":{"description":"contributor profile by pubkey","operationId":"emem_contributor_one","parameters":[{"in":"path","name":"pubkey_b32","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"contributor profile by pubkey"}},"/v1/corpus_state_stats":{"get":{"description":"Signed snapshot of corpus liveness: distinct_cells, distinct_bands, facts_scanned, top per-band counts, manifest CIDs. Same payload that backs /v1/stream's corpus.state tick (signed). Use this for a one-shot poll instead of holding an SSE connection.\n\nWhen to use: Call when an agent needs a single liveness reading to surface in a dashboard, attach to a report, or decide whether to refresh local caches. Includes ed25519 signature over a deterministic preimage so the snapshot is verifiable. For a continuous feed, GET /v1/stream over Server-Sent Events instead.","operationId":"emem_corpus_state_stats","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"snapshot of corpus liveness: distinct_cells, distinct_bands, facts_scanned, per-band counts. Same payload that backs /v1/stream's corpus.state tick (signed). Use this for a one-shot poll instead of holding an SSE connection."}},"/v1/coverage":{"get":{"description":"JSON snapshot of where data lives (cells + lat/lng + counts)","operationId":"emem_coverage","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"JSON snapshot of where data lives (cells + lat/lng + counts)"}},"/v1/coverage_map.svg":{"get":{"description":"Live SVG render of the responder's corpus density, returned as a proper MCP EmbeddedResource content block (image/svg+xml) — multimodal MCP agents can render it natively.\n\nWhen to use: Call when the user asks 'where do you have data?', 'show me the coverage', or wants a visual brief of the responder's corpus footprint. Returns a 1440×720 Plate-Carrée SVG (1° × 1° bins, log-scale colour, continent envelopes for orientation) plus a structuredContent summary (cell_count, total_facts, responder pubkey, REST URL). Multi-content-block reply: an EmbeddedResource (mimeType `image/svg+xml`, with text + uri) followed by a one-line text summary so text-only clients still see the cell / fact counts. For the bare image bytes, fetch `/v1/coverage_map.svg` over plain REST.","operationId":"emem_coverage_map","responses":{"200":{"content":{"image/svg+xml":{"schema":{"format":"binary","type":"string"}}},"description":"ok (image/svg+xml)"}},"summary":"SVG render of corpus density"}},"/v1/coverage_matrix":{"get":{"description":"Per-band live status — what data is alive AND auto-materializable, with history bounds, tempo cadence, and the responder pubkey that signs the band.\n\nWhen to use: Call BEFORE `emem_recall` when you don't know which bands answer at this responder. For each band returns `has_materializer` (true → an empty recall will auto-fetch+sign, no seeding needed), `facts_count` (how many cells already cached), `last_attested_unix_s` (freshness), `tempo_seconds` (slot duration), `history_available_from` / `history_available_to` (oldest/newest Unix epoch the materializer can fetch — use these to bound an `emem_backfill` request), and `responder_pubkey_b32` (the ed25519 key whose signature attests this band — use to detect federation / multi-responder setups). Bands with `has_materializer=false AND facts_count=0` are cube placeholders without a wired connector — don't bother recalling them.","operationId":"emem_coverage_matrix","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"per-band facts_count + has_materializer + last_attested_at"}},"/v1/data_availability":{"get":{"description":"Temporal catalog: for every materializable band the upstream-of-record window the data genuinely covers, the temporal `kind` (static | annual_snapshot | annual_stack | time_series | now_only | per_release), tempo seconds, upstream wire path, and whether `emem_backfill` is meaningful.\n\nWhen to use: Call before `emem_backfill` or any historical recall to check whether a band has a meaningful past at the requested time. Each entry includes `history_available_from_unix` / `history_available_to_unix` (and ISO strings) plus `backfill_supported`. Use this to avoid trial-and-error 422s on now-only bands (`weather.*`) and to enumerate the per-year `geotessera.YYYY` vintages the responder ships. The catalog is driven by the same registry the recall path consults — so what it lists is exactly what materializes.","operationId":"emem_data_availability","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"per-band temporal coverage catalog (window + tempo + kind + upstream wire path)"}},"/v1/deforestation_alert":{"post":{"description":"Composite deforestation-alert score: `alert_score = 0.5·clamp01(ndvi_drop/0.30) + 0.5·clamp01(embedding_change/0.20)`, where `ndvi_drop = max(0, ndvi_modis_baseline − ndvi_now)` and `embedding_change = 1 − cos(tessera_latest, tessera_prev)`. Each half degrades INDEPENDENTLY and honestly: if a band is missing, that half is dropped AND the output is renamed so a half-score can never be mistaken for the full composite. If NEITHER half is computable the response is a signed `inconclusive` carrying no number. Every response also carries a machine-readable `degraded` boolean plus `degraded_reason` (closed set: `embedding_half_unavailable`, `ndvi_half_unavailable`, `no_inputs`) and `degraded_message`, so a caller gates on the flag instead of parsing prose.\n\nWhen to use: Call to flag recent forest-loss-like change at a known cell when you want a single 0..1 alert score rather than a full ensemble. Gate on `degraded`/`degraded_reason`: a half-score (`degraded:true`) must NOT be thresholded against the 0.6 alert gate. Read the renamed score field and the present/absent halves — don't treat a half-score as the full composite. For multi-cell open-world discovery use `emem_hunt` (deforestation event); for the three-encoder change ensemble use `emem_triple_consensus`; for regulatory EUDR evidence use `emem_eudr_dds`.","operationId":"emem_deforestation_alert","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"carbon.deforestation_alert_proxy: alert_score = 0.5·clamp01(ndvi_drop/0.30) + 0.5·clamp01(embedding_change/0.20). Each half degrades independently — a missing band drops its half and renames the output so a half-score can't be mistaken for the full composite; if neither half is computable the response is a signed `inconclusive`."}},"/v1/demos":{"get":{"description":"index of pre-recorded demo runs (live signed receipts)","operationId":"emem_demos","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"index of pre-recorded demo runs (live signed receipts)"}},"/v1/derive":{"post":{"description":"Register a value YOU computed from facts this responder holds, and get back a citeable `emem:fact:` token whose lineage terminates in emem-signed measurements. The registered fact names its parents by CID, so a stranger walks the DAG down to signed sensor data instead of trusting your summary. Requires an ed25519 `attester` block. What the responder signs is narrow and it says so on the response: that YOU submitted this derivation, over these parents, at this time, and it stored it. NOT that the value is true. Algebra: derive.\n\nWhen to use: Call when you have computed something from emem facts (a delta, a zone classification, a per-plot verdict, a model output) and need to hand another agent a token for it rather than a claim. Every input token must already resolve here; recall or backfill the parents first. Provenance class is model_output or human_curated; the sensor classes are refused, since this responder did not compute your value. Note the tenancy rule: a derived fact carries no canonical (cell, band, tslot) key, so it will NOT appear in anyone's emem_recall at that cell. That is the point: you are getting citation and resolution, not an injection into the shared commons. Read it back with emem_memory_token_resolve, or list your own with emem_derive_list. Idempotent per (your key, derivation body): re-registering an identical derivation returns the same token rather than a twin, so retrying a timed-out call is safe.","operationId":"emem_derive","requestBody":{"content":{"application/json":{"schema":{"properties":{"attester":{"properties":{"pubkey_b32":{"type":"string"},"sig_b32":{"type":"string"}},"required":["pubkey_b32","sig_b32"],"type":"object"},"band":{"type":"string"},"cell":{"type":"string"},"code_cid":{"description":"optional blake3 of the code that computed the value; recorded, never fetched or run","type":"string"},"confidence":{"maximum":1,"minimum":0,"type":"number"},"fn_key":{"description":"your recipe key, e.g. same_doy_ndvi_delta@1; not an entry in this responder's registry and never executed by it","type":"string"},"inputs":{"description":"parent tokens emem:fact:<cell64>:<fact_cid>; order is significant and signed","items":{"type":"string"},"minItems":1,"type":"array"},"op":{"description":"delta | mean | trend | rate | anomaly","type":"string"},"provenance_class":{"enum":["model_output","human_curated"],"type":"string"},"tslot_window":{"description":"inclusive [start, end]","items":{"type":"integer"},"maxItems":2,"minItems":2,"type":"array"},"value":{"description":"any JSON value"}},"required":["fn_key","inputs","cell","band","tslot_window","op","value","confidence","provenance_class"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"401":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"the caller's ed25519 attester binding is missing or does not verify; `details.how_to_sign` carries the exact digest to sign for this request"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"not found"},"409":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"conflict (the request contradicts a signed fact, e.g. a token whose cell does not match the fact's own cell)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Register a derivation YOU computed over facts this responder holds, and get back a citeable emem:fact: token whose lineage terminates in emem-signed measurements. Every `inputs[]` parent token must resolve here (404 derive_parent_unresolved names the one that did not) and must be cell-consistent with the fact it names (409 derive_parent_cell_mismatch). Requires an ed25519 `attester` block: sig over blake3(\"emem.memory_write|derive|/v1/derive|\"+body_hash), body_hash = blake3(the CBOR of {fn_key, inputs, cell, band, tslot_window, op, value, confidence, provenance_class, code_cid} encoded as a definite-length 10-entry map in THAT order: declaration order, deliberately NOT RFC 8949 key-sorted, with confidence as float32). Omit `attester` and the 401 returns the exact digest to sign in details.how_to_sign, along with the full byte-level rules, so no CBOR work is needed client-side. provenance_class must be model_output or human_curated; direct_sensor and deterministic_index are refused (400 derive_provenance_class_refused) because this responder did not compute the value. WHAT THE SIGNATURE ATTESTS: that this attester submitted this derivation over these parents at this time and the responder stored it, NOT that the value is true. IDEMPOTENT per (attester, body): re-posting an identical derivation returns the same token (with `deduplicated: true`) rather than minting a twin, so retrying a timed-out call is safe; two DIFFERENT attesters making the same claim each get their own token. TENANCY: the resulting derivative fact has no canonical (cell, band, tslot) key, so it is absent from recall / recall_polygon / state / query_region / memory_search / find_similar. It is reachable only by its token or via POST /v1/derived.","tags":["memory","cite","derive"]}},"/v1/derived":{"post":{"description":"List the derivations registered by one ed25519 key, optionally filtered to a cell (and then a band). The explicit opt-in read for caller-registered derivatives: they hold no canonical key, so no default read path returns them, and this is the only way to enumerate one rather than resolve it by token.\n\nWhen to use: Call to enumerate your own derivations (pass your pubkey_b32), or to inspect what a specific attester has claimed when you already have a reason to trust or audit that key. There is no all-attesters form: naming whose claims you want is the contract, not a filter you can omit.","operationId":"emem_derive_list","requestBody":{"content":{"application/json":{"schema":{"properties":{"attester_pubkey_b32":{"description":"52-char base32-nopad-lowercase ed25519 pubkey","type":"string"},"band":{"description":"only narrows when `cell` is also set","type":"string"},"cell":{"type":"string"},"limit":{"default":100,"maximum":1000,"minimum":1,"type":"integer"}},"required":["attester_pubkey_b32"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"503":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"List the derivations registered by ONE attester, optionally narrowed to a cell (and then a band). `attester_pubkey_b32` is required and there is no all-attesters form: derivative facts carry no canonical key, so this endpoint plus token resolution are the only ways to reach one, and naming whose claims you want is the contract rather than an omittable filter. Returns each derivation's token, cell, band, op, fn_key, tslot_window and signed_at, plus a receipt citing them.","tags":["memory","derive"]}},"/v1/diff":{"post":{"description":"Compute a DerivativeFact (delta) between a band's values at two tslots. Algebra: diff.\n\nWhen to use: Call when the user asks 'what changed between t1 and t2', 'give me the delta'. Returns a signed DerivativeFact + receipt — the delta itself is content-addressed and citable.","operationId":"emem_diff","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiffReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"derivative fact between two tslots (algebra: diff)"}},"/v1/discover":{"get":{"description":"machine-readable index of all surfaces","operationId":"emem_discover","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"machine-readable index of all surfaces"}},"/v1/edges":{"post":{"description":"Persist temporal knowledge-graph edges. Body is a signed Attestation envelope whose `edges[]` array carries each edge {subj, pred, obj, valid_from, valid_to?, confidence, signer, signed_at, schema_cid?, note?}. The edge leaves are folded into the merkle root so the signature commits to them. Additive: an attestation with no edges behaves exactly as /v1/attest.","operationId":"emem_edges_write","requestBody":{"content":{"application/json":{"schema":{"properties":{"edges":{"items":{"properties":{"confidence":{"type":"number"},"note":{"type":"string"},"obj":{"description":"object fact CID","type":"string"},"pred":{"type":"string"},"subj":{"description":"subject fact CID","type":"string"},"valid_from":{"type":"integer"},"valid_to":{"type":"integer"}},"required":["subj","pred","obj","valid_from","confidence","signer","signed_at"],"type":"object"},"type":"array"}},"required":["facts","edges","batch_root","attester","signature"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Persist temporal knowledge-graph edges. Body is a signed Attestation envelope whose `edges[]` array carries each edge {subj, pred, obj, valid_from, valid_to?, confidence, signer, signed_at, schema_cid?, note?}. The edge leaves are folded into the merkle root so the signature commits to them. Additive: an attestation with no edges behaves exactly as /v1/attest.","tags":["edges","knowledge-graph"]}},"/v1/edges/recall":{"post":{"description":"Read temporal knowledge-graph edges (subj --pred--> obj, valid over [valid_from, valid_to)), bi-temporally filtered, in EITHER direction. Forward (`subj`, direction=\"out\", the default): edges originating at a subject fact. Reverse (`obj`, direction=\"in\"): edges pointing AT a fact — what disagrees-with / supersedes / relates-to it. Returns a signed list of edges plus the distinct neighbour fact CIDs (`objs` for out, `subjs` for in); the receipt commits the returned edge CIDs into its signature preimage.\n\nWhen to use: Call this to read the typed CONNECTIONS of a fact — what disagrees with it, what superseded it, what relates to it — as of a point in time. A plain recall gives you the fact; this gives you how that fact links to others in the memory graph. Ask it when the user says 'what is this related to', 'what replaced this observation', 'why is this value contested', or 'what did this place's relations look like as of date X'. Pick a direction: set `subj` (direction=\"out\") to ask 'what does this fact point at'; set `obj` (direction=\"in\") to ask the REVERSE — 'what disagrees-with / supersedes / points-at this fact'. Set exactly one of subj/obj — an ambiguous or empty request errors honestly rather than returning a silent empty. Pass `as_of_tslot` to get the latest edge per neighbour whose valid interval covers that moment (newer edges shadow older — nothing is deleted); pass `pred` (e.g. `disagrees_with`, `supersedes`) to filter, or omit it (empty string) for every predicate. Tip: a quicker way to get a fact + its outbound edges in one shot is `emem_recall` with include:[\"edges\"]. Follow each edge's `obj`/`subj` with `emem_fetch` to resolve the related fact, or `emem_verify_receipt` to confirm the signature offline.","operationId":"emem_edges_recall","requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of_tslot":{"description":"valid-time bound; latest edge per neighbour whose interval covers it","type":"integer"},"direction":{"description":"out (default)=subj→objs; in=obj→subjs; inferred from which of subj/obj is set when omitted","enum":["out","in"],"type":"string"},"limit":{"default":100,"maximum":1000,"minimum":1,"type":"integer"},"obj":{"description":"object fact CID (reverse / direction=in): what points at this fact","type":"string"},"pred":{"description":"predicate filter; empty string scans all predicates","type":"string"},"subj":{"description":"subject fact CID (forward / direction=out); set exactly one of subj/obj","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Recall temporal knowledge-graph edges in either direction, bi-temporally filtered. Forward (subj, direction=\"out\", default): edges originating at a subject fact. Reverse (obj, direction=\"in\"): edges pointing AT a fact (what disagrees-with / supersedes / relates-to it). Set exactly one of subj/obj — ambiguous or empty requests are rejected with a 400, never a silent empty. With as_of_tslot set, returns the latest edge per neighbour whose [valid_from, valid_to) interval covers as_of_tslot (supersession keeps the newest). pred=\"\" scans all predicates. Response carries `direction`, `objs` (always), and `subjs` (reverse only). Receipt cites the anchor + neighbour fact CIDs and commits the returned edge CIDs into the signature preimage.","tags":["edges","knowledge-graph"]}},"/v1/elevation":{"get":{"description":"One-shot elevation answer that fuses Cop-DEM 30 m (land), GMRT (ocean topobathy), and ESA WorldCover (water mask) into a single signed scalar at a place or coordinate. Returns `elevation_m`, the source actually used, and a `coherence_note` when the two surfaces disagree at the coast.\n\nWhen to use: Use when the user asks 'how high is X' or 'what's the elevation at this lat/lng' and you want the correct answer regardless of whether the cell is land, water, or coastline — the handler picks Cop-DEM for land and GMRT for water and surfaces the choice. Pass `place` (free text), `lat`+`lng`, OR `cell`. Otherwise, prefer emem_recall with `copdem30m.elevation_mean` / `gmrt.topobathy_mean` individually.","operationId":"emem_elevation_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/elevation?lat=&lon= — boring lat/lng lookup, returns Cop-DEM elevation (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"One-shot elevation answer that fuses Cop-DEM 30 m (land), GMRT (ocean topobathy), and ESA WorldCover (water mask) into a single signed scalar at a place or coordinate. Returns `elevation_m`, the source actually used, and a `coherence_note` when the two surfaces disagree at the coast.\n\nWhen to use: Use when the user asks 'how high is X' or 'what's the elevation at this lat/lng' and you want the correct answer regardless of whether the cell is land, water, or coastline — the handler picks Cop-DEM for land and GMRT for water and surfaces the choice. Pass `place` (free text), `lat`+`lng`, OR `cell`. Otherwise, prefer emem_recall with `copdem30m.elevation_mean` / `gmrt.topobathy_mean` individually.","operationId":"emem_elevation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/elevation {place|lat,lng|cell64} → Cop-DEM elevation read-through","tags":["boring"]}},"/v1/embedding_centroid":{"post":{"description":"Mean-pool the 128-D GeoTessera embedding over a region's cells: centroid = (1/N) Σ v_i, plus the L2-normalised centroid and a content-addressed centroid_cid. The building block region_similarity composes. Region is {place} | {polygon_bbox} | {cells}. NaN dims are averaged over their finite contributors. CPU-only.\n\nWhen to use: Call when you need one representative embedding vector for an area — to feed similarity search, clustering, or a linear probe over places rather than single cells. Returns a stable centroid_cid for citation. Signed `inconclusive` when no cell in the region carried a vector.","operationId":"emem_embedding_centroid","requestBody":{"content":{"application/json":{"schema":{"properties":{"cells":{"items":{"type":"string"},"type":"array"},"max_cells":{"default":64,"maximum":256,"minimum":1,"type":"integer"},"place":{"type":"string"},"polygon_bbox":{"properties":{"max_lat":{"type":"number"},"max_lng":{"type":"number"},"min_lat":{"type":"number"},"min_lng":{"type":"number"}},"type":"object"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Mean-pooled 128-D GeoTessera centroid for a region (centroid = (1/N) Σ v_i) + the L2-normalised centroid + a content-addressed centroid_cid. Building block for region_similarity. CPU-fetched. Region is {place} | {polygon_bbox} | {cells}. Signed inconclusive when no cell carried a vector."}},"/v1/embedding_diversity":{"post":{"description":"Quantify how varied a region's landscape is: diversity = (1/(N(N-1))) Σ_{i<j} (1 − cosine(v_i, v_j)), the mean pairwise cosine distance over the region's GeoTessera embeddings. 0 = perfectly uniform; higher = more heterogeneous land cover (a determinantal-point-process / k-medoid diversity). Region is {place} | {polygon_bbox} | {cells}. CPU-only.\n\nWhen to use: Call for habitat-heterogeneity / biodiversity-proxy inputs, or to tell a monoculture from a mosaic landscape, or to rank regions by how mixed they are. Needs ≥2 embedding-covered cells, else a signed `inconclusive`. Pair with `emem_terrain` ruggedness for a fuller heterogeneity picture.","operationId":"emem_embedding_diversity","requestBody":{"content":{"application/json":{"schema":{"properties":{"cells":{"items":{"type":"string"},"type":"array"},"max_cells":{"default":64,"maximum":256,"minimum":1,"type":"integer"},"place":{"type":"string"},"polygon_bbox":{"properties":{"max_lat":{"type":"number"},"max_lng":{"type":"number"},"min_lat":{"type":"number"},"min_lng":{"type":"number"}},"type":"object"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Landscape heterogeneity over a region: diversity = (1/(N(N-1))) Σ_{i<j} (1 − cosine(v_i,v_j)), the mean pairwise cosine distance over the region's 128-D GeoTessera embeddings. 0 = uniform, higher = more varied. CPU-fetched. Region is {place} | {polygon_bbox} | {cells}. Needs ≥2 embedding-covered cells, else signed inconclusive."}},"/v1/entity":{"post":{"description":"Give a real-world object (a bridge, a farm plot, a river, a named place) a single, shared, content-addressed identity that any agent resolves the same way. Returns an `entity_token` (`emem:entity:<entity_cid>`) plus a signed receipt that attests how the reference resolved. Two agents that name the same object mint the SAME entity_cid; when a stable external id (Overture GERS / OSM) is known it dominates identity, so divergent labels for one real object still collapse to one id. This is the object-level antidote to referential drift: 'the damaged bridge near the river' becomes one canonical thing every model reasons about, not a phrase each model re-interprets.\n\nWhen to use: Call when a conversation refers to a THING and you want a stable handle to it that survives summarization and travels between agents/turns/LLMs, before it drifts into 'that infrastructure issue'. Anchor it with `place`, a `cell`, or `lat`+`lng`. Hand the returned `emem:entity:` token to any other agent; they dereference the identical object. Recall/ask at the entity's `cell64` for signed facts about it.","operationId":"emem_entity","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"type":"string"},"external_ids":{"properties":{"gers":{"type":"string"},"osm":{"type":"string"},"wikidata":{"type":"string"}},"type":"object"},"kind":{"type":"string"},"label":{"type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"parent":{"type":"string"},"place":{"type":"string"}},"required":["label"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Mint (or idempotently get) a canonical, content-addressed identity for a real-world object. Anchor with `place`, `cell`, or `lat`+`lng`; returns `entity_token` (emem:entity:<entity_cid>) + a signed receipt attesting the resolution. Identity converges on a stable external id (Overture GERS / OSM) when known, so two agents naming the same object mint the same entity_cid. The object-level antidote to referential drift.","tags":["entity","identity"]}},"/v1/entity/alias":{"post":{"description":"Record a signed equivalence: bind an alternate label or a stable external id (GERS / OSM / Wikidata) to an existing canonical object so future `emem_entity_resolve` calls on that phrasing converge to the same entity_cid. Builds the shared reference graph that keeps different agents' vocabularies pointing at one identity.\n\nWhen to use: Call when you learn that two phrasings denote the same object ('the north dam' == an existing entity), or to attach an authoritative external id to an object minted from free text.","operationId":"emem_entity_link","requestBody":{"content":{"application/json":{"schema":{"properties":{"alias":{"type":"string"},"entity_cid":{"type":"string"},"entity_token":{"type":"string"},"external_ids":{"properties":{"gers":{"type":"string"},"osm":{"type":"string"},"wikidata":{"type":"string"}},"type":"object"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Attest a signed equivalence: bind an alternate label or a stable external id (GERS/OSM/Wikidata) to an existing entity so future entity_resolve calls on that phrasing converge to the same entity_cid. Builds the shared reference graph.","tags":["entity","identity"]}},"/v1/entity/resolve":{"post":{"description":"Converge a fuzzy phrasing onto the canonical object other agents already minted, so everyone co-refers to the same identity instead of re-minting divergent ones. Pass `text` (e.g. \"the collapsed span at the ford\") to get ranked existing candidates; pass `near` to narrow to a place; or pass an `emem:entity:` `token` to dereference it directly to the signed entity body. Read-only.\n\nWhen to use: Call BEFORE minting when another agent may already have registered the object, or when you receive a `emem:entity:` token and want the object behind it. This is how two agents avoid referential drift: resolve first, mint only if nothing matches.","operationId":"emem_entity_resolve","requestBody":{"content":{"application/json":{"schema":{"properties":{"k":{"type":"integer"},"label":{"type":"string"},"near":{"type":"string"},"text":{"type":"string"},"token":{"description":"emem:entity:<entity_cid> (legacy meme: accepted) to dereference","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Resolve a fuzzy phrasing to the canonical object other agents already minted (converge, do not re-mint), or dereference an emem:entity: `token` directly to its signed body. `text` for candidates, optional `near` to narrow by place, `k` for count. Read-only.","tags":["entity","identity"]}},"/v1/entity/{id}":{"get":{"description":"Give a real-world object (a bridge, a farm plot, a river, a named place) a single, shared, content-addressed identity that any agent resolves the same way. Returns an `entity_token` (`emem:entity:<entity_cid>`) plus a signed receipt that attests how the reference resolved. Two agents that name the same object mint the SAME entity_cid; when a stable external id (Overture GERS / OSM) is known it dominates identity, so divergent labels for one real object still collapse to one id. This is the object-level antidote to referential drift: 'the damaged bridge near the river' becomes one canonical thing every model reasons about, not a phrase each model re-interprets.\n\nWhen to use: Call when a conversation refers to a THING and you want a stable handle to it that survives summarization and travels between agents/turns/LLMs, before it drifts into 'that infrastructure issue'. Anchor it with `place`, a `cell`, or `lat`+`lng`. Hand the returned `emem:entity:` token to any other agent; they dereference the identical object. Recall/ask at the entity's `cell64` for signed facts about it.","operationId":"emem_entity_get","parameters":[{"description":"entity_cid or emem:entity:<entity_cid> (legacy meme: accepted)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"not found"}},"summary":"Dereference a canonical object by entity_cid or emem:entity: token to its signed body, receipt, and recall hint. 404 with a typed code when this responder does not hold it.","tags":["entity","identity"]}},"/v1/errors":{"get":{"description":"Stable error code catalog.\n\nWhen to use: Call to enumerate the wire-stable error codes — useful when the LLM wants to programmatically branch on responses.","operationId":"emem_errors","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"error code catalog"}},"/v1/eudr_dds":{"post":{"description":"Produce a Due Diligence Statement per Regulation (EU) 2023/1115 for one or more plots. Each plot carries operator-supplied geometry (GeoJSON Polygon for >4 ha, Point for ≤4 ha non-cattle per Article 2(28)), country of production (ISO3), Combined Nomenclature code (HS-6+), and quantity in kg. The endpoint applies the regulation's 10 % canopy / 0.5 ha / 5 m height forest definition (Article 2(4)) using the EU Commission's expected JRC GFC2020 V3 baseline plus Hansen GFC v1.12 loss-year confirmation; Sims et al. 2025 driver attribution and RADD SAR fallback layer on when those connectors are wired (Absence today). The response is an Annex II-shaped envelope with per-plot verdict (pass/fail/not_in_scope/indeterminate/below_mmu), failing-cell fraction, and signed fact CIDs for every per-cell verdict — operators quote them in the company's Article 12 record. Article 9(1)(b) legality (land tenure, FPIC, country-of-origin laws) is structurally out of EO scope; the response carries an explicit `legality_disclaimer` for that reason.\n\nWhen to use: Call when a commodity supplier or EU importer needs to evidence due diligence under Regulation (EU) 2023/1115. Use the plot-level signed receipts as evidence inside the operator's company record; pair with a partner legality module before submitting the final DDS to the EU Information System (TRACES NT). For a single plot, pass one entry in `plots`. For batch supply-chain audits, pass up to a few dozen plots in one call — the endpoint fans out per plot. Surface the failing-cell fraction, the chosen forest baseline, and the legality disclaimer in the user-facing response so the operator understands what the engine claims (and does not).","operationId":"emem_eudr_dds","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EudrDdsReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"EUDR Due Diligence Statement: polygon-in, signed Annex II envelope out. Per Regulation (EU) 2023/1115 — Article 2(4) forest definition (>10% canopy, >0.5 ha, >5 m height, excluding agricultural use), Article 2(28) geolocation rule (POINT ≤4 ha non-cattle, POLYGON >4 ha or cattle), Article 9 + Annex II envelope shape. Each plot's verdict combines JRC GFC2020 V3 baseline + Hansen GFC v1.12 loss-year + (when wired) WRI Sims 2025 driver attribution + RADD SAR fallback. Set `request_visual_evidence: true` on any plot to attach a Sentinel-2 NDVI + Sentinel-1 VV-backscatter annual timeline from 2020 through the current year (+ per-cell scene.png URLs) as compliance-grade visual evidence; the EUDR budget auto-bumps to absorb the additional fan-out. Each plot also carries a `loss_year_histogram`: the per-year distribution of Hansen loss-year over the plot's sampled cells (calendar years, plus `after_cutoff_cells`), emitted as its own signed `forest_change.lossyear_histogram` derivative whose CID is folded into the receipt — so the loss-year breakdown is a verifiable figure, not an unsigned sample (weight by the plot's `sampled_polygon_fraction` to extrapolate to the full polygon). The endpoint honestly excludes Article 9(1)(b) legality (land tenure, FPIC, country-of-origin laws); the response surfaces a structured `legality_disclaimer`. Response includes an ed25519-signed `receipt` over the union of every per-cell fact_cid; verifiable offline at `/verify` (or `/v1/verify_receipt`).","tags":["eudr"]}},"/v1/facts/{cid}":{"get":{"description":"Fetch a fact by its content-address (CID). Returns the full signed Primary or Absence fact, the same body served by REST `/v1/facts/{cid}`. Closes the citation loop: any fact_cid surfaced by recall, materialize, attest, or verify can be re-resolved by another agent without REST.\n\nWhen to use: Call whenever you have a `fact_cid` (e.g. from `emem_recall`'s response, an `emem_attest` receipt, an `emem_materializers` outcome, or a citation in another agent's reply) and need the full fact body — its value, unit, sources, signer, signed_at, and derivation. Particularly useful for verifying that a citation a downstream agent gave you actually resolves on this responder. The response is byte-identical across responders for the same CID — the CID itself is the validator.","operationId":"emem_fetch","parameters":[{"in":"path","name":"cid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fact"}}},"description":"ok (immutable, ETag-tagged)"},"304":{"description":"unchanged (ETag matched If-None-Match)"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"not found"}},"summary":"fact dereference by CID (immutable, ETag-tagged)","tags":["fetch","get_fact"]}},"/v1/fetch":{"post":{"description":"Fetch a fact by its content-address (CID). Returns the full signed Primary or Absence fact, the same body served by REST `/v1/facts/{cid}`. Closes the citation loop: any fact_cid surfaced by recall, materialize, attest, or verify can be re-resolved by another agent without REST.\n\nWhen to use: Call whenever you have a `fact_cid` (e.g. from `emem_recall`'s response, an `emem_attest` receipt, an `emem_materializers` outcome, or a citation in another agent's reply) and need the full fact body — its value, unit, sources, signer, signed_at, and derivation. Particularly useful for verifying that a citation a downstream agent gave you actually resolves on this responder. The response is byte-identical across responders for the same CID — the CID itself is the validator.","operationId":"emem_fetch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Fact"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"REST mirror of MCP `emem_fetch`. Resolve a fact by `{cid}` OR materialize `{cell, band[, tslot]}` (cell may be place name).","tags":["fetch"]}},"/v1/field_boundaries":{"post":{"description":"Per-field agricultural-boundary polygons from the Fields of The World global product (~3.17B fields, 241 countries, 10 m resolution, CC-BY-4.0). Returns a GeoJSON FeatureCollection with the polygon geometries, FIBOA-compatible properties, and a planar `area_m2` per field — plus provenance (source CID, provider URL, license, attribution).\n\nWhen to use: Call when the user asks about farms, fields, parcels, croplands, plots, or agricultural boundaries inside a region — anywhere the OSM/Nominatim boundary alone is too coarse (the OSM polygon for a farm is its estate envelope; this returns the individual field polygons inside). Pass `place` (free-text) or `polygon_bbox`. For farms wider than ~10 km², split the bbox: the fetcher caps each call at 16 covering tiles. The receipt quotes `license: CC-BY-4.0` and `attribution: Fields of The World / Taylor Geospatial Institute` — surface both with any rendered map. For a one-shot \"facts at every cell inside the farm PLUS the field polygons\", call `emem_recall_polygon` with `include: [\"ftw_fields\"]` instead.","operationId":"emem_field_boundaries","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FieldBoundariesResp"}}},"description":"ok"}},"summary":"per-field agricultural-boundary polygons (Fields of The World, CC-BY-4.0)"}},"/v1/find_similar":{"post":{"description":"k-NN over the corpus by cell embedding or inline vector.\n\nWhen to use: Call when the user asks 'find places like X', 'where else looks like this', or hands an embedding to find neighbours. `key` is either a cell64 or `inline:[x,y,...]`. Default band is `geotessera` (128-D Tessera foundation embedding); pass `band: \"geotessera.multi_year\"` for the 1152-D 9-vintage (2017–2025) fusion.","operationId":"emem_find_similar","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindSimilarReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindSimilarResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"k-NN over band vectors"}},"/v1/fleet":{"get":{"description":"Per-band satellite-and-sensor fleet inventory — names the upstream platform (e.g. Sentinel-2A/B, MODIS Aqua/Terra, Landsat-8/9), revisit cadence, native resolution, and license for every materialized band. Lets an agent attribute imagery products correctly and pick the right band when revisit cadence matters.\n\nWhen to use: Call when the user asks 'which satellite is this from', 'what's the revisit time', or needs source attribution for a derived answer. Pair with emem_materializers for the wire path and emem_sources for the connector-level metadata.","operationId":"emem_fleet","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"satellite/sensor lineage feeding each band"}},"/v1/forest":{"get":{"description":"Recall the signed forest facts at a place's cell64: Hansen Global Forest Change (tree cover 2000 baseline + year-of-loss) + ESA WorldCover 2021 land class, attested on a miss; each carries a citeable fact_cid.\n\nWhen to use: Use when the user asks about deforestation, canopy cover, forest loss, or wants a forest-vs-not classification. Hansen gives year-of-loss for any cell with disturbance since 2001; WorldCover gives the current land class.","operationId":"emem_forest_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/forest?lat=&lon= — Hansen tree_cover_2000 + loss_year + ESA WorldCover class (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed forest facts at a place's cell64: Hansen Global Forest Change (tree cover 2000 baseline + year-of-loss) + ESA WorldCover 2021 land class, attested on a miss; each carries a citeable fact_cid.\n\nWhen to use: Use when the user asks about deforestation, canopy cover, forest loss, or wants a forest-vs-not classification. Hansen gives year-of-loss for any cell with disturbance since 2001; WorldCover gives the current land class.","operationId":"emem_forest_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/forest {place|lat,lng}","tags":["boring"]}},"/v1/functions":{"get":{"description":"Active function registry (derivation recipes).\n\nWhen to use: Call when you need to know which derivative ops are available for `emem_diff` or how a band is computed from upstream sources.","operationId":"emem_functions","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"function registry"}},"/v1/grid_info":{"get":{"description":"Active grid encoding: cell64 ground resolution, lat/lng axis sizes, DGGS lineage.\n\nWhen to use: Call once at session start (or when the user asks about cell resolution / 'how big is a cell'). Returns the actual ground resolution today (~9.54 m × 9.55 m square at the equator (lat 21 bits × lng 22 bits, matching Sentinel-1/Sentinel-2 native pixel pitch). The cell64 bit layout reserves a resolution-tag field for future hierarchical refinement targeting H3-equivalent res-13 (~3.4 m) cells in v0.1.) and the spec target. Useful before you reason about whether one cell is enough or whether you need `emem_recall_polygon`.","operationId":"emem_grid_info","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"declare the active spatial grid (cell64 / Hilbert / future H3)"}},"/v1/heat_solve":{"post":{"description":"Forward-step 2-D explicit finite-difference solver for the heat equation ∂u/∂t = α∇²u over a 3×3 cell stencil centred on `cell`. Reads `modis.lst_day_8day` (Land Surface Temperature) at the centre and 8 cell64 neighbours, integrates N hours ahead under a CFL-stable timestep, returns a signed forecast. Real PDE rollout, not a decay-scoring heuristic.\n\nWhen to use: Use when the user wants a short-horizon LST forecast (urban heat island, surface-temperature evolution, heatwave onset modelling) at a specific cell. Default α=1e-6 m²/s matches urban surface diffusivity (Oke 2017); pass a smaller α for water bodies or higher for vegetated surfaces. The solver caps at one-week horizons because the 8-day MODIS composite stops being a representative initial condition past that. Each call materialises 9 MODIS facts (one per neighbour) on miss — first call ~5 s cold, ~30 ms warm. Receipt cites all 9 input fact CIDs.","operationId":"emem_heat_solve","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeatSolveReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"2-D explicit-FD heat-equation solver (forecast LST N hours ahead from a 3×3 cell stencil)"}},"/v1/hunt":{"post":{"description":"Event-discovery sweep: pick an event keyword (algal_bloom, deforestation, flood_extent, wildfire, urban_heat_island, methane_plume, landslide, drought, soil_salinity, crop_stress, water_turbidity, oil_slick) plus a region (free-text name or polygon_bbox). The responder geocodes the region, fans out across up to 32 sampled cells, recalls each event's primary scalar input band, and returns the top 8 hotspots ranked by that scalar, each an attested entry in the shared memory carrying its cell64, lat/lng, the recalled value, a fact_cid for citation, and a scene.png URL. Bypass for free-text input is `emem_ask` (the classifier in /v1/ask routes \"find X in Y\" questions to the same hunter path).\n\nWhen to use: Call when the user asks an open-world discovery question (\"find oil spills in the Persian Gulf\", \"where is deforestation happening in the Amazon\", \"show me algal blooms in Lake Erie\", \"hunt wildfires across California\"). Surface 3–8 hotspots with their scene.png as image attachments and quote at least one fact_cid. For `oil_slick` the responder honestly reports `not_yet_implemented` and points at SAR-darkening + turbidity proxies — don't fabricate detections. The ranking uses the algorithm's primary scalar input only; for the full per-cell algorithm score, fetch the formula at /v1/algorithms/<key> and apply it client-side over the same recalled bands.","operationId":"emem_hunt","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HuntReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"hunter-mode event discovery: pick an event keyword (algal_bloom, deforestation, flood_extent, wildfire, urban_heat_island, methane_plume, landslide, drought, soil_salinity, crop_stress, water_turbidity, oil_slick) plus a region (free-text or polygon_bbox); returns the top 8 ranked hotspots with cell64, primary-band value, fact_cid, and scene URL. Algal-bloom and water-turbidity ranks are NDWI-gated; UHI uses a slow-band fan-out cap. Tessera embedding rerank fires when ≥3 cells have geotessera vectors, otherwise the response falls back to primary-scalar order with the reason exposed. Oil-slick is honestly not-yet-implemented; closest available physics are flood_extent_sar_threshold@1 and water_turbidity_red_band@1.","tags":["hunter"]}},"/v1/intent":{"post":{"description":"Submit a typed Intent; receive a plan or executed result.\n\nWhen to use: Call when the user asks something like 'where is X' or 'is A like B' and you don't want to pick a primitive yourself — the planner maps Intent variants to the right tool call.","operationId":"emem_intent","requestBody":{"content":{"application/json":{"schema":{"properties":{"a":{"type":"string"},"b":{"type":"string"},"band":{"type":"string"},"cell":{"type":"string"},"claim":{"$ref":"#/components/schemas/Claim"},"description":{"type":"string"},"filter":{"$ref":"#/components/schemas/Claim"},"k":{"type":"integer"},"key":{"type":"string"},"place":{"type":"string"},"type":{"enum":["where_is","what_is_here","is_like","did_change","find_like","confirm","ask"],"type":"string"},"window":{"items":{"type":"integer"},"maxItems":2,"minItems":2,"type":"array"}},"required":["type"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"typed agent intent → execution plan. Body is a tagged Intent enum: pass `{type:\"where_is\",description:...}`, `{type:\"what_is_here\",cell:...|place:...}`, `{type:\"is_like\",a:...,b:...}`, `{type:\"did_change\",cell,band,window:[u64,u64]}`, `{type:\"find_like\",key,k?,filter?}`, `{type:\"confirm\",claim,cell}`, or `{type:\"ask\",description,place?,cell?}`. New variants ship under semver."}},"/v1/jepa_predict":{"post":{"description":"Predict next-month NDVI at a cell using a constrained JEPA-pattern AR(2) seasonal predictor. Reads up to 24 past months of `indices.ndvi`, fits a closed-form predictor `y_{t+1} = α·(lag-12 NDVI or recent mean) + β·(last + slope) + γ·recent_mean`, returns the prediction clamped to NDVI's physical range. Coefficients (α=0.6, β=0.3, γ=0.1) are NOT learned, they're fixed from the agricultural-NDVI literature. For the learned multi-band dynamics head, see `emem_jepa_predict_v2` (jepa_temporal_predictor@2).\n\nWhen to use: Use when the user wants a one-month-ahead NDVI forecast at a specific cell (crop-stress monitoring, growing-season tracking, vegetation-anomaly anticipation). Lookback defaults to 6 months; if fewer monthly tslots are attested at this cell, the predictor uses what's there and surfaces the count in `lookback_months_used`. Returns 422 if no NDVI history exists at the cell — chain to `emem_backfill` first to seed history. Receipt cites every input NDVI fact CID.","operationId":"emem_jepa_predict","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JepaPredictReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"constrained JEPA-pattern AR(2) seasonal NDVI predictor (closed-form coefficients, NOT a learned MLP)"}},"/v1/jepa_predict_v2":{"post":{"description":"Predict the next-step value of 4 environmental scalars at a cell (`indices.ndvi`, `modis.lst_day_8day`, `modis.lst_night_8day`, `cams.pm25`) using a small learned dynamics MLP. Reads up to K=6 most-recent attested lags per band, runs them through an ONNX dynamics head (~200k params, CPU-fast), and returns a per-band {value, confidence, n_real_lags, via}. The receipt's `model` block carries `model_id`, `version`, `blake2b_hex` (model_cid), training/validation provenance, a top-level `skill_vs_persistence` block, and `honesty_warnings`, flagging `untrained_baseline` when the artifact is the zero-init sentinel and `NEGATIVE_SKILL` when the learned model is worse than persistence on real held-out NDVI. When the model does not beat persistence, bands with a real lag are returned from that lag tagged `via:persistence_fallback_negative_skill` (bands with no real lag fall back to labelled climatology). Distinct from v1 (`emem_jepa_predict`) which returns a single NDVI scalar via closed-form coefficients.\n\nWhen to use: Use when you want a short-horizon forecast of NDVI / land-surface temperature / PM2.5 at a cell grounded in its attested history. Returns 422 with a `/v1/backfill` hint when the cell lacks enough cached lags. Always read the receipt's `model.honesty_warnings` — `untrained_baseline` means the trivial 'predict last vintage' baseline (treat as no-op), and `NEGATIVE_SKILL` means the served values are the persistence fallback, not a learned improvement. Check each band's `via` field to see whether its value came from the learned model, persistence, or climatology.","operationId":"emem_jepa_predict_v2","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"target_month":{"description":"Month-of-year to forecast (1-12); defaults to the month after now.","maximum":12,"minimum":1,"type":"integer"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"learned multi-band-scalar dynamics head: predicts the next-step value of 4 scalars (indices.ndvi, modis.lst_day_8day, modis.lst_night_8day, cams.pm25) from up to K=6 most-recent attested lags per band. Receipt carries model_cid + training/validation provenance + a skill_vs_persistence block; honesty_warnings flags `untrained_baseline` (zero-init sentinel) and `NEGATIVE_SKILL` (worse than persistence). On negative skill, bands with a real lag are served from persistence (via=persistence_fallback_negative_skill); see each band's `via`."}},"/v1/locate":{"get":{"description":"Mint the canonical, vendor-neutral address (cell64) for a real-world place: the shared spatial identity every agent resolves to identically, so two models refer to the same ground instead of two descriptions of it. Also returns the topic-grouped inventory of bands and algorithms recallable there. For a first-class OBJECT identity (a bridge, a plot, a named place) rather than a raw cell, use emem_entity.\n\nWhen to use: Use whenever the input refers to a real-world location and the next step needs the cell64 identifier or wants to know which bands are available before recalling. The response carries `data_at_this_cell` with three sub-fields: `live_bands_by_topic` (every band recallable here, grouped by topic such as flood_water_event_window, vegetation_condition, built_up_human_geography), `algorithms_for_topic` (composition recipes that fuse those bands into named scores), and `declared_but_no_materializer_at_this_responder` (cube slots reserved without a live connector). For the single-shot path that runs the full chain server-side and returns one packaged answer, use `emem_ask` instead.","operationId":"emem_locate_get","parameters":[{"in":"query","name":"place","required":false,"schema":{"description":"Free-text place name. REQUIRED unless lat+lng are provided.","type":"string"}},{"in":"query","name":"q","required":false,"schema":{"description":"Alias for `place`.","type":"string"}},{"in":"query","name":"query","required":false,"schema":{"description":"Alias for `place`.","type":"string"}},{"in":"query","name":"name","required":false,"schema":{"description":"Alias for `place`.","type":"string"}},{"in":"query","name":"lat","required":false,"schema":{"description":"WGS-84 latitude. REQUIRED with `lng` unless `place` is provided.","type":"number"}},{"in":"query","name":"lng","required":false,"schema":{"description":"WGS-84 longitude. REQUIRED with `lat` unless `place` is provided.","type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"description":"Alias for `lng`.","type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocateResp"}}},"description":"ok"}},"summary":"resolve a place (or lat/lng) to a cell64 via query string — convenience wrapper around POST /v1/locate"},"post":{"description":"Mint the canonical, vendor-neutral address (cell64) for a real-world place: the shared spatial identity every agent resolves to identically, so two models refer to the same ground instead of two descriptions of it. Also returns the topic-grouped inventory of bands and algorithms recallable there. For a first-class OBJECT identity (a bridge, a plot, a named place) rather than a raw cell, use emem_entity.\n\nWhen to use: Use whenever the input refers to a real-world location and the next step needs the cell64 identifier or wants to know which bands are available before recalling. The response carries `data_at_this_cell` with three sub-fields: `live_bands_by_topic` (every band recallable here, grouped by topic such as flood_water_event_window, vegetation_condition, built_up_human_geography), `algorithms_for_topic` (composition recipes that fuse those bands into named scores), and `declared_but_no_materializer_at_this_responder` (cube slots reserved without a live connector). For the single-shot path that runs the full chain server-side and returns one packaged answer, use `emem_ask` instead.","operationId":"emem_locate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocateReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocateResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"resolve a place name (or lat/lng) to a cell64"}},"/v1/log/consistency":{"get":{"description":"Return an RFC 6962 consistency proof that the tree of size `first` is an append-only prefix of size `second` (defaults to the current size). This is the append-only guarantee: it catches a responder that rewrites or forks history.\n\nWhen to use: Call with `first` = the tree_size of an STH you pinned earlier. Verify the returned proof offline against that STH's root; if the first_root does not match what you pinned, the log rewrote history.","operationId":"emem_log_consistency","parameters":[{"in":"query","name":"first","required":true,"schema":{"minimum":1,"type":"integer"}},{"in":"query","name":"second","required":false,"schema":{"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"transparency log: RFC 6962 consistency proof that the tree of size `first` is an append-only prefix of size `second` (defaults to the current tree size). Verify offline with translog::verify_consistency against the first_root you pinned; a mismatch means the log rewrote history."}},"/v1/log/entries":{"get":{"description":"transparency log: RFC 6962 §4.6 get-entries. Returns the raw attestations at global indices [start, end), as {leaf_index, attestation_cbor_b32, entry_hash_b32}. This is what makes the log AUDITABLE rather than only provable: /v1/log/inclusion proves a cid you already hold is committed, while enumeration lets a third party read what else is in the tree. Entry i is the preimage of leaf i in /v1/log/sth, so blake3(attestation_cbor_b32) == entry_hash_b32 and /v1/log/inclusion proves that hash sits under the STH, with no trust in this responder. Capped at 256 per call (RFC 6962 permits returning fewer than asked); the response carries end_exclusive and truncated so you paginate on what you received, not what you requested.","operationId":"emem_log_entries","parameters":[{"description":"first global leaf index, inclusive","in":"query","name":"start","schema":{"minimum":0,"type":"integer"}},{"description":"exclusive end; defaults to start+256 and is clamped to it","in":"query","name":"end","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"501":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"transparency log: RFC 6962 §4.6 get-entries. Returns the raw attestations at global indices [start, end), as {leaf_index, attestation_cbor_b32, entry_hash_b32}. This is what makes the log AUDITABLE rather than only provable: /v1/log/inclusion proves a cid you already hold is committed, while enumeration lets a third party read what else is in the tree. Entry i is the preimage of leaf i in /v1/log/sth, so blake3(attestation_cbor_b32) == entry_hash_b32 and /v1/log/inclusion proves that hash sits under the STH, with no trust in this responder. Capped at 256 per call (RFC 6962 permits returning fewer than asked); the response carries end_exclusive and truncated so you paginate on what you received, not what you requested."}},"/v1/log/inclusion":{"get":{"description":"Return an RFC 6962 inclusion (audit) proof that a log entry is committed under the current signed tree head. Verify offline: the audit path re-derives the STH root from the entry's leaf hash.\n\nWhen to use: Call to prove a specific log entry is in the log. Pass `leaf_index` (0-based position) or `entry_hash` (base32 of the record's blake3). Returns the audit path plus the STH to check it against.","operationId":"emem_log_inclusion","parameters":[{"in":"query","name":"leaf_index","required":false,"schema":{"minimum":0,"type":"integer"}},{"in":"query","name":"entry_hash","required":false,"schema":{"description":"base32-nopad of the record's 32-byte blake3","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"transparency log: RFC 6962 inclusion (audit) proof that a log entry is committed under the current signed tree head. Pass leaf_index=<i> or entry_hash=<base32 of the record blake3>. Verify offline with translog::verify_inclusion."}},"/v1/log/sth":{"get":{"description":"Fetch the responder-signed tree head (STH) over the whole append-only attestation log: {tree_size, root_b32, signed_at, responder_pubkey_b32, signature_b32}. The signature is ed25519 over a domain-separated preimage, verifiable offline.\n\nWhen to use: Call to pin a cryptographic commitment to the log's current state. Save the STH, then later call emem_log_consistency to prove the log only grew (append-only) — a mismatch means the responder rewrote history. No arguments.","operationId":"emem_log_sth","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"transparency log: signed tree head (RFC 6962) over the append-only attestation log. {tree_size, root_b32, signed_at, responder_pubkey_b32, signature_b32}; ed25519 over PreimageV1(\"emem.translog.sth.v1\"). Pin it, then re-check /v1/log/consistency to prove the log only grew."}},"/v1/log/witness":{"post":{"description":"transparency log: submit a witness ed25519 co-signature over a (tree_size, root) tree-head claim. The responder verifies the signature AND that the root matches its own history at that size before recording it. Preimage: PreimageV1(\"emem.translog.witness.v1\"){1:u64_be tree_size, 2:root, 3:witness_pubkey}.","operationId":"emem_log_witness","requestBody":{"content":{"application/json":{"schema":{"properties":{"root_b32":{"type":"string"},"signature_b32":{"type":"string"},"tree_size":{"minimum":1,"type":"integer"},"witness_pubkey_b32":{"type":"string"}},"required":["tree_size","root_b32","witness_pubkey_b32","signature_b32"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"transparency log: submit a witness ed25519 co-signature over a (tree_size, root) tree-head claim. The responder verifies the signature AND that the root matches its own history at that size before recording it. Preimage: PreimageV1(\"emem.translog.witness.v1\"){1:u64_be tree_size, 2:root, 3:witness_pubkey}."}},"/v1/log/witnesses":{"get":{"description":"List witness co-signatures recorded for tree heads — independent parties that counter-signed a (tree_size, root) claim under their own ed25519 key. Co-signatures let a client detect split-view equivocation. Empty until witnesses submit (submission is a signed write, done off-MCP via POST /v1/log/witness).\n\nWhen to use: Call to see who has independently vouched for the log's history. For each co-signature, verify it offline, then call emem_log_consistency from that witness's tree_size to the current size to confirm the log the witness saw is a prefix of the log you see. Optional `tree_size` filter.","operationId":"emem_log_witnesses","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"transparency log: witness co-signatures recorded for the current signed tree head — independent parties that counter-signed (tree_size, root), so a client can detect split-view equivocation. Empty until witnesses submit via POST /v1/log/witness."}},"/v1/lst":{"get":{"description":"Recall the signed MODIS land surface temperature facts (day-8day + night-8day composites, 1 km native) at a place's cell64, attesting on a miss; each carries a citeable fact_cid.\n\nWhen to use: Use when the user asks about surface heat, urban heat island, thermal anomalies, or wants day/night LST. Returns both fluxes so the agent can derive day–night spread.","operationId":"emem_lst_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/lst?lat=&lon= — MODIS LST day + night 8-day composite (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed MODIS land surface temperature facts (day-8day + night-8day composites, 1 km native) at a place's cell64, attesting on a miss; each carries a citeable fact_cid.\n\nWhen to use: Use when the user asks about surface heat, urban heat island, thermal anomalies, or wants day/night LST. Returns both fluxes so the agent can derive day–night spread.","operationId":"emem_lst_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/lst {place|lat,lng}","tags":["boring"]}},"/v1/manifests":{"get":{"description":"Active manifest CIDs (bands / functions / sources / schema).\n\nWhen to use: Call to learn which exact registry versions a responder is serving. Cite these CIDs alongside any answer where reproducibility matters.","operationId":"emem_manifests","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"active manifest CIDs"}},"/v1/materializers":{"get":{"description":"Auto-fetch registry: which bands the responder will materialize on a recall miss, the upstream provider, license, value shape, and history bounds.\n\nWhen to use: Call once at session start (alongside `emem_bands` and `emem_coverage_matrix`) to learn which bands answer for ANY cell on Earth without seeding. Each entry declares `upstream_scheme`, `upstream_endpoint`, `derivation_fn_key`, `value_kind` (primary | absence | primary_or_absence), `coverage` (where the upstream has data), `unit`, `tempo`, `confidence`, and `history_available_from` / `history_available_to` (when the upstream supports historical fetch via `emem_backfill`). Use this when the user asks 'do you have flood data here', 'what providers feed this', or you need license attribution. The response also carries an `agent_hint` block explaining the trust model (responder signs, not upstream) and the absence-fact contract.","operationId":"emem_materializers","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"per-band auto-fetch registry (which bands the responder will materialize on a recall miss)"}},"/v1/memory_bundle":{"post":{"description":"Compose N (cell, band, tslot?) triples into ONE signed envelope. Each triple runs through the standard auto-materialize recall path; the resulting fact_cids are bundled into a content-addressed envelope and the responder signs over the full receipt. The composed `bundle_token` is `emem:bundle:<bundle_cid>`, a single rebindable string that cites the whole set. Algebra: merge.\n\nWhen to use: Call when the agent wants to cite multiple (place, band, vintage) facts as one handle. The bundle stays verifiable offline via /v1/verify_receipt (the receipt covers all cited fact_cids and cells). Use this instead of N separate `emem_memory_token` composers when the citation is conceptually one thing (e.g. \"the EUDR-relevant baseline for these 8 plots at 2020-12-31\").","operationId":"emem_memory_bundle","requestBody":{"content":{"application/json":{"schema":{"properties":{"purpose":{"description":"Optional free-text purpose folded into the bundle_cid, so the same triples bundled for a different purpose get a distinct id.","type":"string"},"triples":{"items":{"properties":{"band":{"type":"string"},"cell":{"type":"string"},"tslot":{"type":"integer"}},"type":"object"},"type":"array"}},"required":["triples"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Compose N (cell, band, tslot?) triples into ONE signed envelope. Each triple runs through the standard auto-materialize recall path; the resulting fact_cids are collapsed into a content-addressed bundle and the responder signs a receipt over the whole set. Returns `bundle_token` (emem:bundle:<bundle_cid>) plus per-triple citations. The memory algebra's `merge`: one handle that cites many facts.","tags":["memory","cite"]}},"/v1/memory_bundle/{token}":{"get":{"description":"Parse a `emem:bundle:<bundle_cid>` token and return the signed bundle envelope: every citation (cell, band, resolved_tslot, fact_cid, memory_token), the receipt, the responder pubkey, and the deduped flat cells[] / fact_cids[] arrays. Returns 404 with a typed code when the responder does not hold the bundle.\n\nWhen to use: Call when an agent receives an `emem:bundle:` token from another agent (or earlier turn) and wants the underlying signed citation set. The response is byte-identical to what `emem_memory_bundle` returned at the original responder.","operationId":"emem_memory_bundle_resolve","parameters":[{"description":"emem:bundle:<bundle_cid> (legacy memb: or bare cid accepted)","in":"path","name":"token","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"not found"}},"summary":"Dereference a bundle token back to its signed envelope: the citations, the fact_cids, the cells, and the receipt. Accepts emem:bundle:<bundle_cid> (legacy memb: also accepted) or a bare bundle_cid; the response always re-emits the canonical `bundle_token`. 404 when this responder did not compose the bundle (the composer is stateless, the resolver is sled-backed, so paste the token at the responder that minted it or re-compose from the original triples).","tags":["memory","cite"]}},"/v1/memory_contradictions":{"get":{"description":"Surface where the corpus DISAGREES with itself (algebra: competing evidence). When two or more independent sources signed different values for the same place + band + time, this returns that disagreement with a 0–1 severity score and citations to every disputed fact, instead of silently picking one value and hiding the conflict. The opposite of a confident single answer: it tells you when not to trust one.\n\nWhen to use: Call this when trust matters before you rely on a number — 'is there disagreement about X', 'do the sources corroborate this', 'audit this claim', or 'find contradictory observations in region Y'. Use it to decide whether a fact is well-corroborated or contested. Narrow with `cell_prefix` (e.g. \"defi.zb5\") for a region and `band` for one family; `min_severity` filters out trivial differences. Severity is per band kind: scalar = spread over the band's range, vector = 1 − mean cosine, categorical = 1 − mode share. The receipt cites every disputed CID — follow up with `emem_diff` to quantify a pair, or (with the refinement loop on) read the emitted `disagrees_with` edge via `emem_edges_recall`.","operationId":"emem_memory_contradictions_get","parameters":[{"in":"query","name":"cell_prefix","required":false,"schema":{"type":"string"}},{"in":"query","name":"band","required":false,"schema":{"type":"string"}},{"in":"query","name":"window_lo","required":false,"schema":{"type":"integer"}},{"in":"query","name":"window_hi","required":false,"schema":{"type":"integer"}},{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"in":"query","name":"min_severity","required":false,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"Same primitive as POST, exposed in query-string form for casual exploration. window_unix_s is split into window_lo + window_hi.","tags":["memory","contradiction-detection"]},"post":{"description":"Surface where the corpus DISAGREES with itself (algebra: competing evidence). When two or more independent sources signed different values for the same place + band + time, this returns that disagreement with a 0–1 severity score and citations to every disputed fact, instead of silently picking one value and hiding the conflict. The opposite of a confident single answer: it tells you when not to trust one.\n\nWhen to use: Call this when trust matters before you rely on a number — 'is there disagreement about X', 'do the sources corroborate this', 'audit this claim', or 'find contradictory observations in region Y'. Use it to decide whether a fact is well-corroborated or contested. Narrow with `cell_prefix` (e.g. \"defi.zb5\") for a region and `band` for one family; `min_severity` filters out trivial differences. Severity is per band kind: scalar = spread over the band's range, vector = 1 − mean cosine, categorical = 1 − mode share. The receipt cites every disputed CID — follow up with `emem_diff` to quantify a pair, or (with the refinement loop on) read the emitted `disagrees_with` edge via `emem_edges_recall`.","operationId":"emem_memory_contradictions","requestBody":{"content":{"application/json":{"schema":{"properties":{"band":{"description":"Band key filter (e.g. \"indices.ndvi\"). Omit to include all bands.","type":"string"},"cell_prefix":{"description":"Bytewise prefix filter on cell64 (e.g. \"defi.zb5f9\"). Omit to scan the whole corpus up to the scan cap.","type":"string"},"limit":{"default":100,"maximum":1000,"minimum":1,"type":"integer"},"min_severity":{"default":0.1,"description":"Drop contradictions whose severity falls below this floor.","maximum":1,"minimum":0,"type":"number"},"window_unix_s":{"description":"[lo, hi] inclusive Unix-seconds filter on attestations' signed_at. All disagreeing attestations must fall in the window.","items":{"minimum":0,"type":"integer"},"maxItems":2,"minItems":2,"type":"array"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"(algebra: competing evidence) Scan the multi-attester index for (cell, band, tslot) triples where two or more attesters have signed disagreeing observations. Severity is computed per band kind: scalar (max-min over band range), vector (1 - mean cosine), categorical (1 - mode share). Receipt cites every fact CID involved.","tags":["memory","contradiction-detection"]}},"/v1/memory_token":{"post":{"description":"Mint a citation handle, `emem:fact:<cell64>:<fact_cid>` (or `:<state_cid>`), that any agent or LLM resolves to the byte-identical signed object. The antidote to referential drift on the value side: hand this one string to another agent instead of re-describing the fact. Validates both components are non-empty and free of the `:` separator. Algebra: cite.\n\nWhen to use: Call when the agent wants a single rebindable string to cite a place plus an attested fact across messages, threads, agents, or tools, without re-fetching or re-describing it. Pair with `emem_verify_receipt` on the receiving end to check the signed payload. To cite an OBJECT rather than a single reading, use emem_entity's `emem:entity:` token; for many facts at once use emem_memory_bundle.","operationId":"emem_memory_token","requestBody":{"content":{"application/json":{"schema":{"properties":{"band":{"description":"Optional band key; when set the response (not the token string) carries the band's provenance block (class, deterministic, tamper_evidence, trust_rank). Required (with observed_on) to mint descriptor_token.","type":"string"},"cell":{"type":"string"},"fact_cid":{"type":"string"},"observed_on":{"description":"Optional source capture date YYYY-MM-DD, as returned by /v1/recall in sources[].captured_at. With `band`, mints descriptor_token. This is valid time (when the sensor observed), never signed_at.","type":"string"}},"required":["cell","fact_cid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"compose an emem:fact:<cell64>:<fact_cid> citation handle. Pure composer; validates shape (non-empty inputs, no ':' contamination) and returns the token, the bare-place emem:cell:<cell64> handle, plus a docs link. Pass the optional `band` to get the band's tamper-provenance block in the RESPONSE (not embedded in the token; the token is only cell + fact_cid, and provenance is attached by whichever responder later resolves it, from that responder's own band registry). Pass `band` AND `observed_on` to additionally get `descriptor_token`, the self-describing anchor emem:fact:<lat>,<lng>@<date>@<band~render>:<fact_cid>, which resolves to the identical fact. PREFER descriptor_token when handing a citation to a model: across 200 real facts, Qwen and gemma-4 segment a cell64 anchor identically 0.0% of the time (jaccard 0.6477) versus 100.0% (jaccard 1.0000) for the same cell written as 5dp coordinates, and a cell64 is a string no model has seen while 36.12010,-112.30206 is the Grand Canyon in every model's training data. Nothing is trusted: this mint never reads storage, and /v1/memory_token/resolve binds the place, band and date to the signed fact, so a descriptor minted with a wrong date yields a token that cannot resolve rather than one that misleads."}},"/v1/memory_token/resolve":{"post":{"description":"Parse a `emem:fact:<cell64>:<fact_cid>` citation handle and return the signed fact body the cid binds. Saves the agent from string-splitting the token and chaining `GET /v1/facts/<cid>` manually. Algebra: resolve.\n\nWhen to use: Call when an agent receives a memory_token from another agent (or out of a previous turn) and wants the underlying signed bytes. The response carries the parsed cell + fact_cid, the full fact body, and the stable `fact_url` an agent can hand to any other peer. 404 with a typed code if the responder doesn't hold the cid; try /v1/fetch with the cid then, or paste the token at a mirror.","operationId":"emem_memory_token_resolve","requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"description":"emem:fact:<cell64>:<fact_cid>, or emem:fact:<lat>,<lng>@<date>@<band~render>:<fact_cid> (legacy memt: accepted)","type":"string"}},"required":["token"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"not found"},"409":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"conflict (the request contradicts a signed fact, e.g. a token whose cell does not match the fact's own cell)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"single round-trip dereference of a fact token. Accepts two anchors for the same fact: emem:fact:<cell64>:<fact_cid> (legacy memt: also accepted), and the self-describing emem:fact:<lat>,<lng>@<date>@<band~render>:<fact_cid>. Fetches the signed fact body by CID and returns the canonical body, the token re-emitted in canonical grammar (canonical_token), an ed25519 receipt signed over the resolved (cell, fact_cid), and the offline-verify URL. EVERY claim in the anchor is bound to the signed body before it dereferences, so a citation can be read without a round-trip precisely because it cannot lie: the cell (or the cell the coordinates quantise to) must match the fact's own cell, the band must match the fact's band, and the date must match one of sources[].captured_at (valid time, never signed_at). Any mismatch is 409, so a real fact_cid cannot be passed off under a false place, band or date. Coordinates below 5 decimal places are refused with 400: 4dp is ~11m against a ~10m cell and would silently address a neighbouring one. A fact carrying no source capture time cannot support a date claim, so the descriptor anchor is refused for it (409) rather than accepted unbound; cite it in cell form. 404 with typed reason when the responder doesn't hold the fact."}},"/v1/memory_token/resolve_many":{"post":{"description":"batch dereference: up to 256 fact tokens in one call, resolved independently through the same pipeline as the single resolve. Partial by design: a bad or unheld token yields a typed per-item error ({ok:false, status, error}) and never fails the batch. One batch receipt binds the union of resolved (cell, fact_cid) pairs; each item carries its own receipt so any single resolution forwards on its own. Fully-resolved responses carry Cache-Control: immutable (content-addressed bodies never change and receipts never expire); a batch with failures is not cached, since a 404 can become resolvable when the fact arrives.","operationId":"emem_memory_token_resolve_many","requestBody":{"content":{"application/json":{"schema":{"properties":{"tokens":{"description":"emem:fact: tokens (legacy memt: accepted), 1 to 256 per call","items":{"type":"string"},"maxItems":256,"type":"array"}},"required":["tokens"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"batch dereference: up to 256 fact tokens in one call, resolved independently through the same pipeline as the single resolve. Partial by design: a bad or unheld token yields a typed per-item error ({ok:false, status, error}) and never fails the batch. One batch receipt binds the union of resolved (cell, fact_cid) pairs; each item carries its own receipt so any single resolution forwards on its own. Fully-resolved responses carry Cache-Control: immutable (content-addressed bodies never change and receipts never expire); a batch with failures is not cached, since a 404 can become resolvable when the fact arrives."}},"/v1/ndvi":{"get":{"description":"Recall the signed Sentinel-2 NDVI fact (indices.ndvi, 10 m native) at a place's canonical cell64, attesting it into the shared memory on a miss. Composes locate → cell64 → recall in one call; the value returns with its citeable fact_cid.\n\nWhen to use: Use when the user names a place (or lat/lng) and just wants the NDVI number. Polygon-resolved places default to a 16-cell fan-out aggregated as mean/median. Set `n_cells: 1` for point behaviour. For multi-band batches use emem_recall.","operationId":"emem_ndvi_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/ndvi?lat=&lon= — Sentinel-2 NDVI at a point (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed Sentinel-2 NDVI fact (indices.ndvi, 10 m native) at a place's canonical cell64, attesting it into the shared memory on a miss. Composes locate → cell64 → recall in one call; the value returns with its citeable fact_cid.\n\nWhen to use: Use when the user names a place (or lat/lng) and just wants the NDVI number. Polygon-resolved places default to a 16-cell fan-out aggregated as mean/median. Set `n_cells: 1` for point behaviour. For multi-band batches use emem_recall.","operationId":"emem_ndvi_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/ndvi {place|lat,lng}","tags":["boring"]}},"/v1/neighborhood_consistency":{"post":{"description":"Score how much a cell looks like its surroundings: consistency = (1/8) Σ cosine(centre, neighbour_i) over the 8 immediate cell64 neighbours, plus outlier_score = 1 − consistency. High consistency = the cell blends in (Tobler's First Law); high outlier_score = it stands out — an edge, a fresh clearing, a built patch in farmland. CPU-only GeoTessera embeddings.\n\nWhen to use: Call to flag a cell that is anomalous versus its local neighbourhood (change/edge detection, QA of a homogeneous expectation, scouting for the odd-one-out). Signed `inconclusive` when neither the centre nor any neighbour carried an embedding. For year-over-year change at one cell use `emem_state_diff` or `emem_triple_consensus`.","operationId":"emem_neighborhood_consistency","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Spatial consistency/outlier of a cell vs its 8 immediate cell64 neighbours: consistency = (1/k) Σ cosine(centre, neighbour_i) over the 128-D GeoTessera embeddings, plus outlier_score = 1 − consistency (the region_outlier_score@1 reading). High outlier = the cell stands out from its surroundings (edge, clearing, built patch). CPU-fetched. Signed inconclusive when neither centre nor any neighbour carried an embedding."}},"/v1/query_region":{"post":{"description":"Query facts over a region (single cell or list of cells), optionally aggregated per band.\n\nWhen to use: Call when the user asks 'how does region X look', 'what's the average NDVI here', or wants a region-level summary. Use `agg=mean|median|p90|vector_centroid` to fold per-band values.","operationId":"emem_query_region","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryRegionReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"query region"}},"/v1/quickstart":{"get":{"description":"6-step playbook","operationId":"emem_quickstart","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"6-step playbook"}},"/v1/raster/resolve":{"post":{"description":"Resolve emem:raster:<aoi_cid>:<band>:<tslot>:<derivation_cid> back to its signed derivation record and the artifact's status. Every claim in the token binds to the signed record before anything dereferences, the same rule fact tokens follow: the cid must be a band_raster@1 derivation and the token's aoi_cid, band, and tslot must each match the record's own body, so a real derivation_cid cannot be passed off under a false area, band, or date (mismatch is a typed 409). The response carries the full record (scene pin, grid georeferencing, anchors) and the artifact url; bytes come from GET /v1/artifacts/{cid}, immutable. An evicted artifact is not an error: the record pins the rebuild, and calling emem_band_raster with the record's own bbox, band, and capture date re-derives identical bytes. The receipt binds (aoi_cid, derivation_cid) through the FIELD preimage segment.\n\nWhen to use: Call when you receive an emem:raster: token from another agent and want the verified field behind it: first this, to get the bound record and artifact url, then fetch the bytes and re-hash them against artifact_cid for the spot-check tier of verification. For emem:fact: tokens use emem_memory_token_resolve.","operationId":"emem_raster_resolve","requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"properties":{"details":{"type":"object"},"error":{"type":"string"}},"type":"object"}}},"description":"invalid argument; `details.code` names which rule refused"},"404":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"not found"},"409":{"content":{"application/json":{"schema":{"properties":{"error":{"type":"string"}},"type":"object"}}},"description":"conflict (the request contradicts a signed fact, e.g. a token whose cell does not match the fact's own cell)"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"dereference an emem:raster:<aoi_cid>:<band>:<tslot>:<derivation_cid> token. Every claim in the token binds to the signed derivation record before anything dereferences (the fact-token rule applied to fields): the cid must be a band_raster@1 derivation and the token's aoi_cid, band, and tslot must each match the record's body, so a real derivation_cid cannot be passed off under a false area, band, or date; any mismatch is a typed 409. Returns the record and the artifact status; bytes come from GET /v1/artifacts/{cid}. An evicted artifact is a rebuild recipe, not an error. The receipt binds (aoi_cid, derivation_cid) through the FIELD preimage segment."}},"/v1/recall":{"post":{"description":"Read the signed facts at a canonical address (cell64); auto-materializes on a miss for any band with a registered materializer. A fact_cid names one signed attestation, so a recalled fact is citeable and re-verifiable rather than a paraphrase: resolving it anywhere returns those exact bytes. It is NOT a fingerprint of the observation. The digest covers the responder's key and the moment it signed, so two responders that measure the same thing mint different fact_cids and a cid resolves only at the responder that signed it; use emem_entity for identity that crosses responders. Pass `deterministic:true` (or a `provenance` class list) to keep only facts recomputable from the cited raw source, with no model or human in the loop. In the memory algebra this is ensure(cell, bands), not get: state what must exist and the responder reuses or materializes.\n\nWhen to use: Call after `emem_locate` (or with a known cell64). Returns every Primary fact stored at that (cell, band, tslot). IMPORTANT: if the cell has no fact yet for a requested band AND that band has `has_materializer=true` (per `emem_coverage_matrix` / `emem_materializers`), the responder fetches the upstream value, signs it under its identity, persists it, and returns it in the same response (slower on the first call while the upstream is fetched; fast once cached). So for any wired band you can recall ANY cell on Earth without seeding, just pass `bands: [<band>]`. The response carries `materialize_notes` listing what was just fetched. Empty result with no notes means the band has no materializer at this responder.","operationId":"emem_recall","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"recall facts (algebra: ensure; reuses what exists, materializes what is missing)"}},"/v1/recall_many":{"post":{"description":"Recall facts across a list of up to 256 cell64 strings in one signed envelope. Server fans out per-cell recalls in parallel, then aggregates the response. Auto-materializes any cell with a missing fact whose band has a registered materializer — same contract as emem_recall.\n\nWhen to use: Use after emem_find_similar (give it the neighbour cells), after emem_recall_polygon (when you want a deterministic cell list rather than a polygon), or whenever you have a precomputed set of cells (e.g. an admin-2 sample frame) and want one round-trip. Pass `cells: [c1, c2, ...]` plus the same `bands` shape as emem_recall. For more than 256 cells, batch the call.","operationId":"emem_recall_many","requestBody":{"content":{"application/json":{"schema":{"properties":{"bands":{"items":{"type":"string"},"type":"array"},"cells":{"items":{"type":"string"},"type":"array"}},"required":["cells"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"bulk recall over up to 256 cells per call Accepts budget_ms: the partial-results contract (docs/plans/partial-results.md), converged/pending[]/retry, monotone identical-request retry."}},"/v1/recall_polygon":{"post":{"description":"Recall facts across every cell inside a place's polygon (single signed envelope). Closes the place-name-drift gap for wide features (parks, lakes, regions).\n\nWhen to use: Call when the user names a wide feature (national park, river basin, country, large urban area) where one cell is too small. Pass `place` and the geocoder will fan out across the polygon — or pass `polygon_bbox` directly if you have coordinates. Returns `merged_facts`, `by_cell`, and a `polygon_bbox.source` indicator (`nominatim_boundingbox` = real polygon, `centre_cell_bbox` = fallback to one cell because the geocoder had no polygon). For *farm* queries the OSM polygon is the whole estate envelope; pass `include: [\"ftw_fields\"]` to additionally attach per-field agricultural-boundary polygons from Fields of The World (CC-BY-4.0) — or call the dedicated `emem_field_boundaries` for the pure-fetch shape.","operationId":"emem_recall_polygon","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"recall facts inside a GeoJSON polygon. Accepts budget_ms (docs/plans/partial-results.md): a soft materialization budget; on expiry the response is a first-class partial 200 with converged:false, a typed pending[] (materializing | upstream_failed, each entry stating its remedy), and a retry hint. Detached fetches persist, so the identical request retried returns strictly more from cache. Pending is unsigned and is NOT a signed absence; the receipt semantics are unchanged"}},"/v1/region_similarity":{"post":{"description":"Answer 'how alike are these two places?' Mean-pool the 128-D GeoTessera embedding across each region's cells to get a centroid, then return the cosine similarity in [-1,1] (+1 = identical landscape, 0 = unrelated). Each region is {place} | {polygon_bbox} | {cells}. CPU-fetched embeddings — no GPU sidecar needed. Surfaces how many cells in each region actually carried a vector (coverage).\n\nWhen to use: Call to compare two areas at the level of overall land character (e.g. 'is this valley like that one?', 'find me somewhere that looks like X'). Degrades to a signed `inconclusive` (no number) when a region has no embedding-covered cells. For a single cell-to-cell vector cosine use `emem_compare`; for k-NN retrieval use `emem_find_similar`.","operationId":"emem_region_similarity","requestBody":{"content":{"application/json":{"schema":{"properties":{"max_cells":{"default":64,"maximum":256,"minimum":1,"type":"integer"},"region_a":{"description":"{place} | {polygon_bbox:{min_lat,max_lat,min_lng,max_lng}} | {cells:[cell64,...]}","type":"object"},"region_b":{"description":"same shape as region_a","type":"object"}},"required":["region_a","region_b"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"How alike are two places? Mean-pool the 128-D GeoTessera embedding across each region's cells, then return cosine(centroid_a, centroid_b) in [-1,1]. CPU-fetched (no GPU). Each region is {place} | {polygon_bbox} | {cells}. Signed inconclusive when a region has no embedding-covered cell."}},"/v1/reviews":{"post":{"description":"submit task-outcome review keyed by subject. `subject_kind` must be one of: fact, cell, request_id, session, band, endpoint, other. `outcome` must be one of: success, partial, failed, noisy. Optional `agent_pubkey_b32` + `agent_signature_hex` for self-attesting reviews.","operationId":"emem_reviews_post","requestBody":{"content":{"application/json":{"schema":{"properties":{"agent_pubkey_b32":{"type":"string"},"agent_signature_hex":{"type":"string"},"comment":{"type":"string"},"outcome":{"enum":["success","partial","failed","noisy"],"type":"string"},"rating":{"maximum":255,"minimum":0,"type":"integer"},"subject_id":{"type":"string"},"subject_kind":{"enum":["fact","cell","request_id","session","band","endpoint","other"],"type":"string"},"task":{"type":"string"}},"required":["subject_kind","subject_id","task","outcome"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"submit task-outcome review keyed by subject. `subject_kind` must be one of: fact, cell, request_id, session, band, endpoint, other. `outcome` must be one of: success, partial, failed, noisy. Optional `agent_pubkey_b32` + `agent_signature_hex` for self-attesting reviews."}},"/v1/reviews/{subject_id}":{"get":{"description":"list reviews for a subject","operationId":"emem_reviews_get","parameters":[{"in":"path","name":"subject_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"list reviews for a subject"}},"/v1/rice_ch4":{"post":{"description":"Estimate seasonal CH4 emissions from rice cultivation per IPCC 2019 Refinement Eq 5.1: integrate the daily emission factor over the cultivation period with water-regime scaling (SFp pre-season, SFo organic amendment) and an optional Yan-2005 Q10 temperature modifier. `cultivation_period_days` and the regional `efc_kg_ch4_ha_day` (Table 5.11) are REQUIRED — the endpoint refuses to guess a global default because the regional EFc drives the magnitude (~30% bias if wrong). An NDWI series (supplied or read from stored `indices.ndwi`) informs the flooding-regime context.\n\nWhen to use: Call for paddy-rice GHG inventory / MRV work where the user needs kg CH4 per hectare for a cultivation season. The caller MUST pick the IPCC region's EFc row (Table 5.11) and the cultivation-period length; pass SFp/SFo when the water regime or organic amendment is known. Surface the seasonal emission, the EFc used, and the scaling factors so the inventory is auditable. For enteric/fertilizer pathways use the dedicated sustainability endpoints.","operationId":"emem_rice_ch4","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"cultivation_period_days":{"description":"Cultivation-period length in days (typically 110–150).","type":"number"},"efc_kg_ch4_ha_day":{"description":"Regional baseline EFc (kg CH4/ha/day) from IPCC 2019 Table 5.11.","type":"number"},"ndwi_series":{"description":"Optional explicit NDWI series across the cultivation period.","items":{"type":"number"},"type":"array"},"sfo":{"description":"Organic-amendment scaling factor SFo (Table 5.14); default 1.00.","type":"number"},"sfp":{"description":"Pre-season water-regime scaling factor SFp (Table 5.13); default 0.68.","type":"number"},"t_paddy_c":{"description":"Mean paddy-water temperature (°C) for the Yan-2005 Q10 modifier; omit to disable.","type":"number"}},"required":["cell","cultivation_period_days","efc_kg_ch4_ha_day"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"IPCC-2019 Tier-2 rice-cultivation CH4 (Eq 5.1): integrates the daily emission factor over the cultivation period with water-regime (SFp/SFo) and optional Yan-2005 Q10 temperature scaling. `cultivation_period_days` and the regional `efc_kg_ch4_ha_day` (Table 5.11) are REQUIRED — no defensible global default."}},"/v1/sar_forest_disturbance":{"post":{"description":"Cloud- and night-independent Sentinel-1 C-band confirmation of forest disturbance. Intact forest scatters VV strongly + stably (canopy volume scattering); clearing collapses that term so VV backscatter DROPS ~3-5 dB. Samples VV at a baseline-year July-1 anchor and the latest scene, reports `vv_drop_db = baseline − recent` and a `disturbed` flag when the drop ≥ 3 dB (Reiche et al. 2018, RSE 204:147). Both VV reads are signed Primary facts; the response cites both fact_cids. Honest `inconclusive` when either S1 vintage is unavailable. Source: Microsoft Planetary Computer sentinel-1-rtc (anonymous SAS — no requester-pays, no API key).\n\nWhen to use: Call to corroborate or scout forest clearing where cloud blocks the optical products — radar sees through cloud and at night, catching wet-season clearing the annual Hansen/JRC-TMF layers and a single cloudy Sentinel-2 pass miss (the gap RADD was meant to fill). This is an ADDITIVE scout signal, NOT a standalone legal verdict: a VV drop can also be transient (soil moisture, harvest, flood recession), so confirm with the optical consensus (`emem_eudr_dds` or `emem_deforestation_alert`) before crediting a decision.","operationId":"emem_sar_forest_disturbance","requestBody":{"content":{"application/json":{"schema":{"properties":{"baseline_year":{"description":"Baseline calendar year the VV drop is measured against (default 2020).","type":"integer"},"cell":{"description":"cell64 or place name","type":"string"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"Sentinel-1 VV backscatter-drop forest-disturbance scout (cloud- and night-independent). Samples VV at a baseline-year July-1 anchor and the latest scene; vv_drop_db = baseline − recent, disturbed when drop ≥ 3 dB (Reiche et al. 2018). Both VV reads are signed Primary facts (cited fact_cids); honest `inconclusive` when either S1 vintage is unavailable. ADDITIVE scout signal, NOT a standalone legal verdict — confirm with the optical JRC GFC2020/Hansen consensus (/v1/eudr_dds, /v1/deforestation_alert). Source: MPC sentinel-1-rtc (anonymous SAS, no requester-pays)."}},"/v1/schema":{"get":{"description":"Active CDDL/JSON schema bundle by CID.\n\nWhen to use: Rarely needed at chat time. Useful for offline verification of receipts / attestations against the exact schema version a responder used.","operationId":"emem_schema","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"active CDDL/JSON schema bundle (REST mirror of emem_schema)"}},"/v1/soil":{"get":{"description":"Recall the signed SoilGrids 250 m profile at a place's cell64 (SOC, pH, clay/sand/silt fractions, bulk density, nitrogen, all at 0–30 cm depth), attesting on a miss; each band carries a citeable fact_cid.\n\nWhen to use: Use when the user asks about soil quality, agricultural suitability, or carbon stocks at a location. Six bands returned in one envelope.","operationId":"emem_soil_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/soil?lat=&lon= — SoilGrids 2.0 0–30 cm topsoil pack (SOC, pH, clay, sand, BDOD, N) (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed SoilGrids 250 m profile at a place's cell64 (SOC, pH, clay/sand/silt fractions, bulk density, nitrogen, all at 0–30 cm depth), attesting on a miss; each band carries a citeable fact_cid.\n\nWhen to use: Use when the user asks about soil quality, agricultural suitability, or carbon stocks at a location. Six bands returned in one envelope.","operationId":"emem_soil_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/soil {place|lat,lng}","tags":["boring"]}},"/v1/sources":{"get":{"description":"Active source-connector registry (URL templates, providers, licenses).\n\nWhen to use: Call when you need to inspect which upstream EO providers are wired (Copernicus DEM, JRC GSW, ESA WorldCover, etc.) — useful for license attribution in agent answers.","operationId":"emem_sources","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"source registry"}},"/v1/spi":{"post":{"description":"Compute the Standardized Precipitation Index (McKee et al. 1993) at a cell: fit a gamma distribution to the same-window precipitation-accumulation history, then standardize the current accumulation to a z-score and map it to a drought class (extreme/severe/moderate drought … normal … wet). Supply `precip_history_mm` + `current_accumulation_mm` directly, or omit them to read the stored `weather.precipitation_mm` trajectory and build the window accumulations server-side. `window_days` selects SPI-1 (30 d), SPI-3 (90 d, default), SPI-12 (360 d), etc. The result is signed; the receipt cites the precipitation fact_cids it read from the shared memory.\n\nWhen to use: Call when the user asks 'is this place in drought', 'how dry is it relative to normal', or wants a precipitation-anomaly z-score. The response is honest: when fewer than the WMO-recommended minimum samples exist it returns verdict=`inconclusive` with `spi:null` and a `honest_note` rather than fabricating a z-score from a handful of points. Quote the `spi`, `spi_class`, and `n_samples`. For raw precipitation use `emem_weather`; SPI is the standardized anomaly.","operationId":"emem_spi","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"current_accumulation_mm":{"description":"Current-window accumulation (mm); required when precip_history_mm is supplied.","type":"number"},"precip_history_mm":{"description":"Optional explicit same-window precipitation accumulations (mm).","items":{"type":"number"},"type":"array"},"window_days":{"description":"Accumulation window (SPI-3 = 90 d default; SPI-1 = 30 d; SPI-12 = 360 d).","type":"integer"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"McKee-1993 Standardized Precipitation Index drought metric: fits a gamma to the same-window precipitation-accumulation history and standardizes the current accumulation to a z-score + drought class. Honest `inconclusive` (no z-score) when fewer than the minimum samples exist. Supply `precip_history_mm` + `current_accumulation_mm` directly, or omit to read the stored `weather.precipitation_mm` trajectory."}},"/v1/state":{"post":{"description":"Get one dense numeric fingerprint that summarises everything known about a place, ready to feed into similarity search, a classifier, or clustering. Two views: `encoder` returns a single AI-model embedding (128-D Tessera, 1024-D Clay, 1024-D Prithvi); `cube` returns the full 1792-D vector concatenated across every band, with a per-band coverage manifest.\n\nWhen to use: Call this when the user wants a machine-usable summary of a place rather than individual band readings — e.g. 'give me a feature vector for this location', 'how do I represent this place for ML', or before running similarity / linear-probe / clustering downstream. Also use it to get one rebindable handle (`memory_token` / `state_cid`) that cites the whole place. Default `view=encoder` is the cheap single-recall path; pass `view=cube` for the full attested view (its `coverage[]` lets you tell signed-zero from not-yet-materialised). Then hand the vector to `emem_find_similar` (k-NN), `emem_compare` (two-place cosine), or `emem_verify_receipt` (audit the signature).","operationId":"emem_state","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"encoder":{"default":"geotessera","description":"foundation embedding band (geotessera, clay_v1, prithvi_eo2, galileo)","type":"string"},"tslot":{"type":"integer"},"view":{"default":"encoder","enum":["encoder","cube"],"type":"string"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"dense state vector for a cell or place. view=encoder (default, 128-D single foundation embedding) or view=cube (1792-D concatenated cube). Returns {cell, view, encoder, dim, vector, l2_norm, fact_cid, memory_token, receipt}."}},"/v1/state_diff":{"post":{"description":"Vector delta between the same cell at two tslots: returns the per-element residual, its L2 norm (scalar change-magnitude), the cosine between the two source vectors (orientation drift), and both source fact CIDs so the agent can quote both attestations as evidence.\n\nWhen to use: Call when the user asks 'how much did X change between A and B' for a foundation embedding at one place. Pass `tslot_a` and `tslot_b` (must differ); default `encoder=geotessera`. For per-band scalar change (NDVI delta, elevation delta) use `emem_diff` instead.","operationId":"emem_state_diff","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"type":"string"},"encoder":{"default":"geotessera","type":"string"},"tslot_a":{"type":"integer"},"tslot_b":{"type":"integer"}},"required":["cell","tslot_a","tslot_b"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"vintage delta of one cell between two tslots. Returns the per-element residual, its L2 norm (scalar change magnitude), the cosine between the two source vectors (orientation drift), and both source fact_cids as evidence."}},"/v1/state_multi":{"post":{"description":"Get the place's fingerprint from several AI models at once (`geotessera`, `clay_v1`, `prithvi_eo2`, `galileo`) in one call, returned as a per-model map. Each model is tried independently; any that can't produce a vector here show up under `missing` with a reason instead of failing the whole request.\n\nWhen to use: Call this when the user wants a second (or third) opinion on what a place looks like — 'do the different models agree this is forest / urban / water?', 'which model has the freshest read here?', or when you want all the embeddings concatenated for a stronger downstream classifier. Use the single-model `emem_state` instead when one embedding is enough. Pass `encoders: [...]` to narrow the set.","operationId":"emem_state_multi","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"type":"string"},"encoders":{"items":{"type":"string"},"type":"array"},"tslot":{"type":"integer"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"fan-out across every wired foundation-embedding encoder (geotessera, clay_v1, prithvi_eo2, galileo). Returns per-encoder dense vectors plus a typed `missing[]` list for encoders unwired at this responder."}},"/v1/stream":{"get":{"description":"Server-Sent Events corpus stream. Emits a signed corpus.state event every `interval` seconds (default 15, clamped to [5,300]). Each tick carries a deterministic preimage and ed25519 signature so subscribers can verify without re-fetching.","operationId":"emem_stream","parameters":[{"in":"query","name":"interval","required":false,"schema":{"default":15,"maximum":300,"minimum":5,"type":"integer"}}],"responses":{"200":{"content":{"text/event-stream":{"schema":{"type":"string"}}},"description":"text/event-stream"}},"summary":"Server-Sent Events corpus stream. Emits a signed corpus.state event every `interval` seconds (default 15, clamped to [5,300]). Each tick carries a deterministic preimage and ed25519 signature so subscribers can verify without re-fetching."}},"/v1/temporal_route":{"get":{"description":"Turn a time-shaped question into a ready-to-run recall plan: it figures out WHICH bands to pull at WHICH past time windows (e.g. 'the year before the flood', 'last growing season', 'two vintages to compare') so you don't have to compute tslot offsets by hand. Returns the band + lookback + a `purpose` tag for each step. Algebra: valid(M, a): per-band validity from the physics decay kernel, cite_now versus fetch_for_intent.\n\nWhen to use: Call this first when the user's question is about CHANGE OVER TIME or a PAST EVENT and you're not sure which bands/dates to recall — 'was this flooded last year', 'what was the NDVI baseline before the fire', 'compare this place across vintages'. It hands you the recipe; then run those steps with `emem_recall`. Skip it when the user wants a single current reading. Pass `cell` plus an optional free-text `intent` hint. The plan is deterministic and the receipt cites which algorithm supplied each step.","operationId":"emem_temporal_route_get","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"PDE-based band routing for a query time + intent (also accepts POST)"},"post":{"description":"Turn a time-shaped question into a ready-to-run recall plan: it figures out WHICH bands to pull at WHICH past time windows (e.g. 'the year before the flood', 'last growing season', 'two vintages to compare') so you don't have to compute tslot offsets by hand. Returns the band + lookback + a `purpose` tag for each step. Algebra: valid(M, a): per-band validity from the physics decay kernel, cite_now versus fetch_for_intent.\n\nWhen to use: Call this first when the user's question is about CHANGE OVER TIME or a PAST EVENT and you're not sure which bands/dates to recall — 'was this flooded last year', 'what was the NDVI baseline before the fire', 'compare this place across vintages'. It hands you the recipe; then run those steps with `emem_recall`. Skip it when the user wants a single current reading. Pass `cell` plus an optional free-text `intent` hint. The plan is deterministic and the receipt cites which algorithm supplied each step.","operationId":"emem_temporal_route_post","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"PDE-based band routing for a query time + intent (algebra: valid; cite_now vs fetch_for_intent)"}},"/v1/terrain":{"post":{"description":"Compute three standard DEM terrain indices from one 3×3 Copernicus-DEM (copdem30m.elevation_mean) neighbourhood at a cell: Horn (1981) slope in degrees, Riley (1999) Terrain Ruggedness Index (TRI = sqrt(Σ(Z_centre−Z_i)²)), and Weiss (2001) Topographic Position Index (TPI = Z_centre − mean(neighbours); positive = ridge, negative = valley). The 8 neighbour cell64s are derived by perturbing the cell's lat/lng one cell pitch per axis; the east-west ground spacing is cos(lat)-corrected. Each result is signed; the receipt cites the elevation fact_cids read from the shared memory.\n\nWhen to use: Call when the user asks how steep / how rugged / ridge-or-valley a place is, for siting (solar, construction, agriculture), erosion/landslide screening, or habitat-heterogeneity inputs. Slope and TRI need the full 8-neighbour ring; TPI degrades to ≥1 neighbour. Copernicus DEM is bathymetry-free, so ocean cells return a signed `inconclusive` rather than a fabricated slope — read each index's own `verdict`. For raw elevation use `emem_elevation`.","operationId":"emem_terrain","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"step_cells":{"default":3,"description":"Stencil step in cell64 pitches (~9.55 m each). Default 3 ≈ 28.7 m matches the Cop-DEM native resolution; 1 reads flat inside one source pixel.","minimum":1,"type":"integer"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"DEM terrain triad over a 3×3 copdem30m.elevation_mean neighbourhood: Horn-1981 slope (deg), Riley-1999 ruggedness (TRI), Weiss-2001 topographic position (TPI). The 8 neighbour cell64s are derived by perturbing lat/lng step_cells pitches per axis (default 3 ≈ 28.7 m, matching the ~30 m Cop-DEM resolution). Slope/TRI need the full 8-ring; TPI degrades to ≥1 neighbour. Ocean/coast cells return a signed inconclusive (Cop-DEM is bathymetry-free), never a fabricated value."}},"/v1/tools":{"get":{"description":"The map of emem's tool surface, and the only tool you need to find the rest. Returns the working loop in the order you walk it (name a thing, ground it, cite it, resolve it, verify it, check for drift), then every other tool grouped by the question it answers, each with its one-line trigger. Pass `name` to get one tool's full input schema and a runnable example, so you can use a tool without loading all of the descriptors into context. This endpoint advertises the core loop only; the Earth-observation, search, embedding and log tools are catalogued here and remain callable by name.\n\nWhen to use: Call this FIRST when you do not know which emem tool answers the question, or when you need a capability you cannot see in your tool list. This responder advertises a small core loop by default rather than its full catalog, so a tool being absent from your list does not mean it is absent from the server. Pass `q` to search by topic (`ndvi`, `cloud`, `flood`, `verify`), `name` for one tool's exact schema, or no arguments for the whole map. If you want the full catalog registered as callable tools instead, reconnect to the /mcp/full endpoint; for a one-shot answer without picking a primitive at all, use emem_ask.","operationId":"emem_tools","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"MCP tool descriptors with schemas"}},"/v1/topics":{"get":{"description":"Topic-grouped registry of every band and algorithm at this responder, plus visual surfaces and the `declared_but_no_materializer_at_this_responder` block (cube slots reserved without a live connector). Single source of truth shared with `/v1/locate`'s `data_at_this_cell` block.\n\nWhen to use: Call when the user's question lives in a topic but they haven't named a specific band — e.g. 'is this place flood-prone' (→ flood_history_long_term + flood_water_event_window) or 'how walkable is this' (→ urban_livability). Returns three blocks: `live_bands_by_topic` (every band you can recall right now), `algorithms_for_topic` (named recipes that compose those bands into derived answers — pair with `emem_algorithms` for the formulas), and `declared_but_no_materializer_at_this_responder` (honest gaps). Browse here BEFORE inventing your own synthesis formula.","operationId":"emem_topics","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"topic-grouped band + algorithm registry (single source of truth shared with `/v1/locate`'s `data_at_this_cell` block)"}},"/v1/trajectory":{"post":{"description":"Time series for one (cell, band) over an inclusive [start, end] tslot window. Returns only what's already attested; it does NOT trigger materialization. For historical backfill use `emem_backfill`.\n\nWhen to use: Call when the user asks 'how did X change over time' for a band that already has multiple historical tslots seeded. IMPORTANT differences from `emem_recall`: (1) trajectory does NOT auto-materialize past tslots — it returns only facts that have already been attested at this responder, so for fast-tempo bands like `indices.ndwi` you'll typically see ONE point at the latest tslot until an attester seeds history. (2) tslots are non-negative `u64`; there's no negative-offset 'last 2 years' shorthand. For LONG-TERM history questions ('flooded in last 2 years', 'forest loss since 2020') prefer either (a) a static-tempo summary band that one fact answers — `surface_water.recurrence` covers 1984-2021 in a single signed value, no trajectory needed — or (b) `emem_backfill` to materialize and sign the missing tslots in one call.","operationId":"emem_trajectory","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrajectoryReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"time series"}},"/v1/triple_consensus":{"post":{"description":"Three-encoder change ensemble: compute the cosine change between the two most-recent DISTINCT vintages for each of the Clay, Prithvi, and Tessera embeddings at the cell, then vote each encoder's change against `consensus_threshold` (registry default 0.15). Returns each encoder's change magnitude, its vote, and the consensus verdict (how many of the three agree change happened). Two caveats ride every response. First, the gate is NOT calibrated per encoder: 0.15 is a threshold for spectral change, applied unchanged to cosine distances in three embedding spaces with different scales, and the deployed Prithvi checkpoint's change tops out near 0.1155, under the gate. Prithvi therefore never votes, `all_three` is arithmetically unreachable, and `two_of_three` means Clay plus Tessera; read `encoders_used[].change` per encoder instead of the vote, and see the `gate_calibration` field. Second, this tool MATERIALIZES a missing prior vintage, so despite its Read category it signs and persists facts and spends GPU time. Degrades to a signed `inconclusive` when the GPU sidecar is unreachable or a cell lacks two distinct vintages for the encoders. The response carries a machine-readable `degraded` boolean, a `degraded_reason` (closed set: `gpu_sidecar_unavailable`, `single_vintage`, `outside_coverage`, `no_finite_overlap`, `recall_failed`, `partial_consensus_N_of_3`, `insufficient_encoders`), and `degraded_message`; each `encoders_absent[]` entry also carries its own `reason_code`. A 2-of-3 result reports `degraded:true` even though it still carries a real ensemble number. This is an experiment over model outputs: each leg carries a `model_output` caution (learned representation, not a measurement), so corroborate with a deterministic band before load-bearing use.\n\nWhen to use: Call when the user wants a robust, model-agnostic 'did this place change' answer backed by three independent foundation encoders rather than one — e.g. cross-checking a single-encoder alert, or auditing change with consensus voting. Gate on `degraded`/`degraded_reason` — a `degraded:true` partial consensus is lower-confidence than a full triple. Surface the per-encoder change + the vote count. When only one encoder has two vintages the verdict is honest about the thin evidence. For a single-encoder vector delta use `emem_state_diff`; for the NDVI+embedding proxy use `emem_deforestation_alert`.","operationId":"emem_triple_consensus","requestBody":{"content":{"application/json":{"schema":{"properties":{"cell":{"description":"cell64 or place name","type":"string"},"consensus_threshold":{"description":"Override the registry gate (default 0.15), clamped to (0,1).","type":"number"}},"required":["cell"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"clay_prithvi_tessera change-ensemble: cosine change across the two most-recent distinct vintages for Clay, Prithvi, and Tessera embeddings, voted against `consensus_threshold`. The gate is not calibrated per encoder and the encoders do not share a cosine scale: the deployed Prithvi checkpoint's change caps near 0.1155 under a 0.15 gate, so it never votes and `all_three` cannot occur. Read `encoders_used[].change` rather than `agreement`; every response carries a `gate_calibration` string saying so. Materializes a missing prior vintage, so this signs and persists facts. Degrades to a signed `inconclusive` when the GPU sidecar is down or a cell lacks two distinct vintages."}},"/v1/verifier_spec":{"get":{"description":"Machine-readable specification of how this responder signs, emitted from the same compiled emem-attest tag constants the signer uses, so it cannot drift from the wire. Returns the receipt preimage v1 segment table (tag, name, scalar|list, optional) plus the domain-separation and length-prefix rules, and the segment table for every other signed family (attestation, transparency-log STH, witness co-signature, operator attestation, corpus_state_stats, stream tick). Consume once and reproduce the preimage for any signature this responder emits. Also served at /.well-known/emem-verifier.json.","operationId":"emem_verifier_spec","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"Machine-readable specification of how this responder signs, emitted from the same compiled emem-attest tag constants the signer uses, so it cannot drift from the wire. Returns the receipt preimage v1 segment table (tag, name, scalar|list, optional) plus the domain-separation and length-prefix rules, and the segment table for every other signed family (attestation, transparency-log STH, witness co-signature, operator attestation, corpus_state_stats, stream tick). Consume once and reproduce the preimage for any signature this responder emits. Also served at /.well-known/emem-verifier.json.","tags":["verify"]}},"/v1/verify":{"post":{"description":"Verify a structured claim against a cell's facts. Returns verdict + evidence CIDs + signed receipt.\n\nWhen to use: Call when the user asks a yes/no question about a cell ('is the NDVI > 0.7 here', 'has this been deforested'), or when downstream code wants citable evidence for a logical predicate.","operationId":"emem_verify","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"verify a structured claim"}},"/v1/verify_receipt":{"post":{"description":"Verify a signed receipt envelope server-side: recomputes the canonical preimage (preimage v1: tagged, length-prefixed segments; receipts without `preimage_version` verify under the legacy `request_id | served_at | primitive | cells, | fact_cids,` concatenation), runs ed25519 over the embedded pubkey + signature, and returns `{valid, reason, pubkey_b32}`. Use when the in-browser /verify path is blocked (CDN offline, agent runtime has no crypto) or when you want a server-side audit of a third-party receipt. Algebra: verify.\n\nWhen to use: Pass a receipt object exactly as returned by any read primitive (signature can be byte[] or sig_b32; pubkey can be byte[] or responder_pubkey_b32 — the verifier tolerates both shapes). Optionally override `pubkey_b32` to assert verification against a specific signer. Returns 200 with `valid: false` when the signature fails — never 4xx for a structurally-well-formed bad signature.","operationId":"emem_verify_receipt","requestBody":{"content":{"application/json":{"schema":{"properties":{"pubkey_b32":{"description":"Optional override; defaults to receipt.responder_pubkey_b32","type":"string"},"receipt":{"description":"The receipt object returned by any /v1/* response","properties":{"cells":{"items":{"type":"string"},"type":"array"},"fact_cids":{"items":{"type":"string"},"type":"array"},"primitive":{"type":"string"},"request_id":{"type":"string"},"responder_pubkey_b32":{"type":"string"},"served_at":{"type":"string"},"signature_b32":{"type":"string"}},"type":"object"}},"required":["receipt"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyReceiptResp"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"offline-verify any responder's receipt (algebra: verify): recompute preimage BLAKE3 from {request_id, served_at, primitive, cells[], fact_cids[]} and check ed25519 against the embedded responder pubkey (or the override). Works on any responder's receipt without trusting this server."}},"/v1/water":{"get":{"description":"Recall the signed surface-water facts at a place's cell64: JRC Global Surface Water recurrence (1984–2021) + Sentinel-1 SAR backscatter (current), attested on a miss and citeable by fact_cid. The pair detects standing water through clouds.\n\nWhen to use: Use when the user asks about flooding, wetlands, surface-water dynamics, or wants a robust water-presence check. JRC alone gives historical baseline; Sentinel-1 gives current flood detection.","operationId":"emem_water_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/water?lat=&lon= — JRC Global Surface Water recurrence + Sentinel-1 VV (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed surface-water facts at a place's cell64: JRC Global Surface Water recurrence (1984–2021) + Sentinel-1 SAR backscatter (current), attested on a miss and citeable by fact_cid. The pair detects standing water through clouds.\n\nWhen to use: Use when the user asks about flooding, wetlands, surface-water dynamics, or wants a robust water-presence check. JRC alone gives historical baseline; Sentinel-1 gives current flood detection.","operationId":"emem_water_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/water {place|lat,lng}","tags":["boring"]}},"/v1/wave_solve":{"post":{"description":"Forward-step 1-D explicit finite-difference solver for the shallow-water wave equation ∂²u/∂t² = c²∂²u/∂x² with c² = g·h, where depth h comes from `gmrt.topobathy_mean` along the seaward gradient. Models how an offshore swell of height H_s and period T propagates toward `coastal_cell`. Returns a signed forecast of arrival height + time + depth + phase-speed profiles, all under a CFL-stable timestep; the receipt cites the depth facts read from the shared memory.\n\nWhen to use: Use when the user wants to predict swell arrival at a coast (storm-surge planning, shoreline-impact assessment, surf forecasting). The solver walks `n_offshore_cells` cells seaward from `coastal_cell` along the bathymetric gradient (default 8 cells = 80 m of profile at the active 10 m grid), samples GMRT depth at each, and integrates the wave equation forward until the wavefront reaches the coast plus one period. Receipt cites every depth fact CID along the profile. Returns 422 with a clear message if `coastal_cell` is land-locked.","operationId":"emem_wave_solve","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaveSolveReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"1-D explicit-FD shallow-water wave-equation solver (propagate offshore swell to the coast along a bathymetric profile)"}},"/v1/weather":{"get":{"description":"Recall the signed met.no/CAMS weather facts at a place's cell64 (2 m temperature + total cloud cover + precipitation + 10 m wind speed), attesting on a miss; each value carries a citeable fact_cid.\n\nWhen to use: Use when the user names a place and asks 'what's the weather' or wants a now-cast snapshot. weather.* bands are now-only (no backfill); for climatology use terraclimate.*.","operationId":"emem_weather_get","parameters":[{"in":"query","name":"lat","required":false,"schema":{"type":"number"}},{"in":"query","name":"lon","required":false,"schema":{"type":"number"}},{"in":"query","name":"place","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"}},"summary":"GET /v1/weather?lat=&lon= — met.no nowcast (t2m + precip + wind + RH) (signed). Also accepts ?place=…","tags":["boring"]},"post":{"description":"Recall the signed met.no/CAMS weather facts at a place's cell64 (2 m temperature + total cloud cover + precipitation + 10 m wind speed), attesting on a miss; each value carries a citeable fact_cid.\n\nWhen to use: Use when the user names a place and asks 'what's the weather' or wants a now-cast snapshot. weather.* bands are now-only (no backfill); for climatology use terraclimate.*.","operationId":"emem_weather_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoringPostReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedResponse"}}},"description":"ok"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}},"description":"error — the emem.error.v1 envelope. Branch on the stable `code` (see GET /v1/errors), not the message. A malformed or missing-field request body returns `code: invalid_argument` with the offending field named in `message`."}},"summary":"POST /v1/weather {place|lat,lng}","tags":["boring"]}},"/v1/worlds":{"get":{"description":"baked 3-D gaussian splat worlds: per-preset counts, artifact sizes + sha256; artifacts at /v1/worlds/{preset}/{file} (world.ply, world.splat, world.scene.json, world.provenance.json, meta.json); viewer at /worlds","operationId":"emem_worlds","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"ok"}},"summary":"baked 3-D gaussian splat worlds: per-preset counts, artifact sizes + sha256; artifacts at /v1/worlds/{preset}/{file} (world.ply, world.splat, world.scene.json, world.provenance.json, meta.json); viewer at /worlds"}}},"servers":[{"description":"Hosted instance (HTTPS-only)","url":"https://emem.dev"}]}