Evidence Object v1
The planned structured contract for each claim in a Senlay response. It keeps the value, unit, source, timestamp, distance, freshness, confidence, uncertainty, and disagreement visible instead of hiding them in a generic answer.
Senlay is a spot-level verification API for SmartSurf, outdoor AI apps, IoT products, and safety applications. It is not a weather API wrapper: each response is built to explain what is happening at a coordinate, which sources support it, how fresh they are, whether they disagree, and what the decision context means.
The Physical World Model (PWM) is a real-time, multi-layered representation of physical conditions at a coordinate. One verification request can combine available data from 21 source families — including hardware weather stations, ocean buoys, tide gauges, seismometers, models, and satellites — into a unified response your AI can immediately use.
For developer workflows, the response can include a pre-formatted context_string ready to inject into an LLM context. For safety workflows such as SmartSurf, Senlay can also produce risk_event records that turn physical evidence into alertable and auditable operational context.
Measured observations vs. predictions. METAR stations, ocean buoys, and tide gauges produce measured readings. Weather models produce predicted values. When they disagree, Senlay gives hardware a higher evidence weight — while still exposing freshness, distance to the source, data quality, and uncertainty so your agent can reason about how much to trust any single value.
| What endpoint should an agent call? | Use /api/v1/try-sense for a no-key browser trial, /api/v1/sense for authenticated plain text, and /api/v1/pwm for structured JSON. |
| What parameters are required? | lat and lng are required. field and request improve domain interpretation. |
| What does the response contain? | Measured/model conditions, source evidence, freshness, confidence, disagreement, decision_context, modifiers, and context_string. |
| How do safety apps consume alerts? | Use /api/v1/risk-events and /api/v1/risk-events/recent for operational risk records suitable for SmartSurf-style station, lifeguard, and audit workflows. |
| What happens when data is missing? | Senlay returns the available context and should expose uncertainty. Agents should not invent missing measurements. |
How should an LLM use context_string? | Inject it before answering, cite source/freshness/confidence, and present decision support rather than guaranteed safety. |
Every PWM response carries source-and-freshness metadata so your agent knows how it knows. The platform is moving toward a fully structured Senlay Evidence Object per claim — this is the shape to expect:
{
"claim": "Wave height is 0.7 m",
"value": 0.7,
"unit": "m",
"evidence_type": "measured_telemetry",
"source": {
"type": "buoy",
"id": "KLIH1",
"distance_km": 18,
"timestamp": "2026-04-18T11:48:00Z"
},
"freshness": "recent",
"confidence": "medium_high",
"model_disagreement": {
"forecast_value": 0.5,
"difference": 0.2
},
"interpretation": "Small but measurable swell; suitable for low-risk coastal activity.",
"uncertainty": "Nearest buoy is 18 km away; local break conditions may vary."
}
Today this metadata is exposed across current_source, source_use, per-layer source labels, interaction/domain modifiers, decision context, and the context_string. Confidence scoring, freshness decay, and explicit disagreement flags are landing as part of Evidence Object v1.
The pre-formatted context_string reads like provenance-aware prose, ready to inject into any LLM:
Current Maui ocean state: 0.7 m waves measured by NDBC buoy KLIH1 18 km away, updated 12 minutes ago. Confidence medium-high. Forecast underestimates the live measurement by 0.2 m — treat conditions as building. Local break conditions may vary by exposure.
Production endpoints require a Bearer token. The public /api/v1/try-sense endpoint is a limited no-key trial for browser-based chat agents.
Authorization: Bearer sl_live_your_key_here
Start with pricing: senlay.world/pricing - then create a free key, no credit card.
For a chat model that can browse but cannot send POST bodies or Bearer headers, use the no-key trial URL:
https://senlay.world/api/v1/try-sense?lat=15.8801&lng=108.3380&field=kitesurfing&agentName=ChatGPT_Test_Agent
For a self-updating browser view of the same request, open the live page URL:
https://senlay.world/agents/live?lat=15.8801&lng=108.3380&field=kitesurfing&agentName=ChatGPT_Test_Agent&request=Can%20I%20kite%20safely%20now%3F
For production agents, create a free key and call the authenticated API:
curl -H "Authorization: Bearer sl_live_your_key" \ "https://senlay.world/api/v1/pwm?lat=36.01&lng=-5.60&field=drone"
For plain text output (no JSON parsing needed):
curl -H "Authorization: Bearer sl_live_your_key" \ "https://senlay.world/api/v1/sense?lat=36.01&lng=-5.60&field=drone"
Public, no-key trial endpoint for chat agents with browsing. It returns plain text, includes instructions for the model, and is rate-limited. Use this when a user wants to paste one URL into ChatGPT, Claude, Gemini, or another agent and immediately ground the conversation in live physical context.
https://senlay.world/api/v1/try-sense?lat=36.01&lng=-5.60&field=drone&agentName=MyChatAgent
| Parameter | Required | Description |
|---|---|---|
lat | Yes | Latitude (-90 to 90) |
lng | Yes | Longitude (-180 to 180) |
field | No | Reasoning mode, for example kitesurfing, drone, sailing, agriculture, running, or general |
agentName | No | Human-readable name shown in the trial response |
request | No | Agent/user task embedded into the response so the model can answer with the correct field context |
view | No | Use view=live to open the self-updating HTML page instead of the plaintext response |
The same live page is also available at /agents/live. It stores state in the URL, so parallel agents can use different agentName, field, request, and session values without overwriting each other.
Trial mode is intentionally limited: 8 requests/minute burst and 60 requests/hour per IP. The live page clamps automatic refresh to 120-600 seconds. For repeated use or automation, register an agent and use /api/v1/sense with Bearer auth.
Full Physical World Model — structured JSON with all sensor data, extended hardware readings, satellite data, and the pre-formatted context string.
| Parameter | Required | Description |
|---|---|---|
lat | Yes | Latitude (-90 to 90) |
lng | Yes | Longitude (-180 to 180) |
field | No | Optional reasoning mode used when building context_string |
{
"status": "ok",
"active_sources": 7,
"total_sensors": 3,
"pwm": {
"coordinates": { "lat": 36.01, "lng": -5.60 },
"timestamp": "2026-04-09T12:00:00Z",
"layers": {
"atmosphere": {
"current": {
"wind_speed_10m": 33, "wind_gusts_10m": 61.9,
"wind_direction_10m": 84, "temperature_2m": 16.5,
"pressure_msl": 1019.5, "humidity": 48, "cloud_cover": 3
}
},
"hydrosphere": {
"current": {
"wave_height": 1.2, "wave_period": 8.3,
"swell_wave_height": 0.8
}
},
"terrain": {
"elevation_at_point": -6, "is_ocean": true,
"depth_profile": [-4, -6, -8, -12, -6, -3, -6, -8, -10]
},
"air_quality": {
"current": {
"pm2_5": 12.3, "pm10": 18.7,
"uv_index": 6.2, "european_aqi": 42
}
}
}
},
"extended": {
"wind_sensors": [
{ "source": "metar", "name": "Rota NAS",
"distance_km": 96.8, "wind_speed_kmh": 11.1,
"is_hardware": true }
],
"wind_cross_reference": {
"sensor_count": 2, "average_kmh": 22,
"confidence": "moderate"
},
"buoys": [],
"tides": {
"station_id": "9410230",
"current_level_m": 1.2,
"next_high": { "time": "14:30", "height_m": 1.8 }
},
"earthquakes": [],
"fires": []
},
"satellite": {
"sentinel2": {
"available": true, "date": "2026-04-07",
"cloud_cover": 12
}
},
"context_string": "=== SENLAY PHYSICAL WORLD MODEL === ..."
}
| Field | Type | Description |
|---|---|---|
status | string | "ok" on success |
active_sources | number | Count of sensor sources that returned data |
pwm | object | Raw sensor data with all layers (atmosphere, hydrosphere, terrain, air quality) |
extended | object | Hardware sensor data (METAR, buoys, tides, seismic, fires, space weather) |
satellite | object | Sentinel-2 imagery metadata |
health | array | Per-source latency and status report |
context_string | string | Pre-formatted text for LLM injection — the most important field |
Simplified endpoint — returns only the context_string as plain text. Content-Type: text/plain. Ideal for agents that don't want to parse JSON.
Same as /api/v1/pwm: lat (required), lng (required), and optional field for the reasoning mode.
curl -H "Authorization: Bearer sl_live_your_key" \ "https://senlay.world/api/v1/sense?lat=36.01&lng=-5.60&field=drone"
Returns the full Physical World Model as a plain text block. No parsing needed — inject it into your agent so it can reason from current physical context.
Authenticated endpoint for operational risk events. Use it when an application needs more than context text: a station dashboard, lifeguard workflow, fleet monitor, school safety view, or IoT alert pipeline can read structured events that summarize the detected risk, severity, coordinates, sources, and recommended review path.
SmartSurf is the first proving ground: Traccar GPS movement and local triggers identify a possible rider or board problem, then Senlay adds environmental evidence before the system escalates or stays quiet. The endpoint is decision support, not a certified rescue authority.
| Endpoint | Use |
|---|---|
GET /api/v1/risk-events | Current active operational risk events. |
POST /api/v1/risk-events | Create or submit a risk event from an application workflow. |
GET /api/v1/risk-events/recent | Recent persisted events for audit, review, and station logs. |
curl -H "Authorization: Bearer sl_live_your_key" \ "https://senlay.world/api/v1/risk-events?status=active"
{
"type": "rider_stopped_offshore",
"severity": "monitor",
"status": "active",
"lat": 15.8801,
"lng": 108.3380,
"source": "smartsurf",
"evidence": {
"movement": "stopped_or_drifting",
"distance_from_shore_m": 820,
"wind_context": "offshore component possible",
"confidence": "medium"
},
"recommendation": "Ask rider to confirm OK before notifying station."
}
Dedicated registration for AI agents — no password needed. Only agentName is required. If you supply ownerEmail too, we dedupe on the (ownerEmail, agentName) pair so stateless agents that re-register each session get the SAME key back instead of piling up orphan rows.
curl -X POST https://senlay.world/api/v1/agent-register \
-H "Content-Type: application/json" \
-d '{"agentName": "MyAgent"}'
curl -X POST https://senlay.world/api/v1/agent-register \
-H "Content-Type: application/json" \
-d '{"ownerEmail": "your_real_email@domain.com", "agentName": "MyAgent"}'
| Field | Required | Description |
|---|---|---|
agentName | Yes | Agent identity, min 2 chars |
ownerEmail | No | Owner's real email. Enables dedupe on re-register. If omitted, every call creates a fresh key. |
{
"success": true,
"apiKey": "sl_live_xxxxxxxxxxxxxxxx",
"agentName": "MyAgent",
"existing": false,
"message": "Save your API key — it cannot be recovered."
}
If existing is true, we returned a previously-issued key because (ownerEmail, agentName) was already registered.
Discover what agents you've registered under an email address. Returns agent names with masked key previews only (first 12 + last 4 chars). Full keys are never returned by this endpoint. To recover the full key, call /api/v1/agent-register again with the same (ownerEmail, agentName) — dedupe returns the original key. Rate-limited 10/hour per email.
curl -X POST https://senlay.world/api/v1/agent-lookup \
-H "Content-Type: application/json" \
-d '{"ownerEmail": "your_real_email@domain.com"}'
{
"success": true,
"ownerEmail": "your_real_email@domain.com",
"count": 2,
"agents": [
{
"agentName": "MyAgent",
"keyPreview": "sl_live_4947…9109",
"tier": "free",
"requestsToday": 12,
"requestsTotal": 348,
"lastRequestDate": "2026-04-14",
"createdAt": "2026-04-10 08:22:11"
}
],
"message": "To get the full key for an agent, POST /api/v1/agent-register with the same ownerEmail + agentName."
}
Standard registration for developers (with password).
| Field | Required | Description |
|---|---|---|
email | Yes | Email address |
password | Yes | Min 8 characters |
name | No | Display name |
Response: { success, apiKey }
Login for dashboard access.
| Field | Required | Description |
|---|---|---|
email | Yes | Email address |
password | Yes | Password |
Response: { success, userId, apiKeys }
Returns all curated sensor locations. No authentication required.
Response: JSON array of spots with name, country, lat, lng, type, notes.
Returns current wind + wave data for curated spots. Cached 30 minutes. No authentication required.
Response: JSON array with name, wind_speed, wind_direction, wind_gusts, temperature, wave_height, wave_period, swell_height.
Senlay can aggregate data from 21 source families where available. Not all sources have data at every location — coastal areas have the most hardware sensors. The API returns the available context and marks gaps or uncertainty where needed.
| Source | Type | Data | Key | Update |
|---|---|---|---|---|
| Open-Meteo Forecast | MODEL | Wind, temp, pressure, humidity, clouds | No | 15 min |
| Open-Meteo Marine | MODEL | Waves, wind-wave, swell components, currents, sea level/tide model, SST | No | 15 min / model cadence |
| Open-Meteo Elevation | MODEL | Terrain, bathymetry, depth profile | No | Static |
| Open-Meteo Air Quality | MODEL | PM2.5, PM10, UV, AQI | No | 15 min |
| Copernicus Sentinel-2 | SATELLITE | Cloud-free imagery metadata | No | Days |
| METAR Aviation | HARDWARE | Wind, visibility, pressure, clouds | No | 30 min |
| NOAA NDBC Buoys + Ship Obs | HARDWARE | Ocean buoys plus moving ship reports for offshore comparison; public ship identity may be anonymized | No | Hourly / varies |
| NOAA CO-OPS Tides + Currents | HARDWARE | Tide gauges, water level, current meters, current predictions, water temp | No | 6 min / hourly |
| USGS Water Services | HARDWARE | River discharge, gage height, water temperature, turbidity, inlet/estuary context | No | 15-60 min / varies |
| WorldTides | OPTIONAL | Global tide prediction fallback where live gauges are absent | Yes | Prediction service |
| Stormglass Marine | OPTIONAL | Paid/optional marine cross-check: waves, swell, currents, water temp, wind | Yes | Provider cadence |
| Copernicus Marine | OPTIONAL | Async/cached ocean background: currents, waves, SST, in-situ, HF radar where available | Signup | Dataset cadence |
| Windy Webcams | VISUAL | Visual spot confirmation for whitecaps, shorebreak, squalls, crowding, launch state | Yes | Camera/provider cadence |
| CWOP/APRS | HARDWARE | Citizen weather stations | No | Varies |
| OpenAQ | HARDWARE | Air quality monitors | No | Varies |
| Sensor.Community | HARDWARE | Citizen air quality sensors | No | Varies |
| openSenseMap | HARDWARE | Community environmental boxes: temperature, humidity, pressure, particles, specialty sensors | No | Varies |
| USGS Earthquakes | HARDWARE | Seismic activity | No | Real-time |
| NWS Alerts | HARDWARE | Severe weather warnings (US) | No | Real-time |
| NOAA Space Weather | HARDWARE | Solar storms, geomagnetic indices | No | Hourly |
| EONET Events | SATELLITE | Natural events (storms, volcanoes) | No | Daily |
| USGS Volcano Hazards | HARDWARE | Active volcanic events | No | 30 min |
| Safecast Radiation | HARDWARE | Environmental radiation levels | No | Varies |
| Holfuy | HARDWARE | Kite/paraglide wind sensors | Optional | 1-5 min |
| WeatherLink v2 | HARDWARE | Davis WeatherLink station current observations and demo station mode | Yes | Provider cadence |
| WeatherFlow Tempest | HARDWARE | Personal weather stations plus optional rapid-wind WebSocket snapshot for configured devices | Free key | 1 min / 3 sec stream |
| Weather Underground | HARDWARE | 250K+ personal stations | Free key | 5 min |
| NASA FIRMS | SATELLITE | Active fire detection | Free key | 3 hours |
HARDWARE sources are real physical instruments measuring reality. MODEL sources are computer forecasts. SATELLITE sources are orbital observation data. When hardware and models disagree, trust HARDWARE.
Use Senlay as a function/tool in AI agent frameworks. Let the AI decide when it needs physical world data.
{
"type": "function",
"function": {
"name": "sense_physical_world",
"description": "Get real-time physical world data for a coordinate. Returns available atmosphere, ocean, terrain, air quality, live hardware sensors, earthquakes, fires, and satellite context from 21 source families where available.",
"parameters": {
"type": "object",
"properties": {
"lat": { "type": "number", "description": "Latitude (-90 to 90)" },
"lng": { "type": "number", "description": "Longitude (-180 to 180)" }
},
"required": ["lat", "lng"]
}
}
}
{
"name": "sense_physical_world",
"description": "Perceive available real-time physical conditions at a coordinate from 21 source families including hardware weather stations, ocean buoys, tide gauges, seismometers, and satellites.",
"input_schema": {
"type": "object",
"properties": {
"lat": { "type": "number", "description": "Latitude (-90 to 90)" },
"lng": { "type": "number", "description": "Longitude (-180 to 180)" }
},
"required": ["lat", "lng"]
}
}
def sense_physical_world(lat, lng):
"""Call this when your AI needs to evaluate physical conditions."""
response = requests.get(
"https://senlay.world/api/v1/sense",
params={"lat": lat, "lng": lng},
headers={"Authorization": f"Bearer {SENLAY_KEY}"}
)
return response.text # Ready to inject into context
The most important field in the response. It's a pre-formatted text block designed to be injected directly into any LLM's system prompt or user message. Contains all sensor data in natural language that AI can immediately understand and reason about. No parsing required.
Every data point in the extended response has an is_hardware boolean.
is_hardware: true — physical sensor measurement (METAR station, ocean buoy, tide gauge)is_hardware: false — computer model forecast/estimateHardware readings are measured observations; models are predictions. When they disagree, the hardware value generally carries more weight — subject to freshness, distance from the query location, station calibration, and contextual relevance. Senlay surfaces those dimensions rather than asserting ground truth.
When multiple wind sensors are available, Senlay calculates:
wind_cross_reference.average_kmh — average across all sensorswind_cross_reference.min_kmh / max_kmh — rangewind_cross_reference.confidence — "high" (3+ sensors agree) or "moderate" (2 sensors)| Code | Meaning | Response |
|---|---|---|
200 | Success | Full PWM response |
400 | Missing parameters | {"error": "lat and lng query parameters required"} |
401 | Invalid or missing API key | {"error": "API key required"} |
429 | Rate limit exceeded | {"error": "Rate limit exceeded", "limit": 100, "tier": "free"} |
500 | Server error | {"error": "description"} |
Some sensor sources may fail independently — the API still returns successfully with whatever data is available. Check active_sources to see how many sources responded.
Senlay is currently in open beta — all features are free to use. The free tier includes 100 API calls per day. Cached responses for the same location within 5 minutes don't count against your limit.
For details on future plans and pricing, see the Pricing page.
context_string ready for LLM injectionimport anthropic, requests
pwm = requests.get(
"https://senlay.world/api/v1/pwm",
params={"lat": 36.01, "lng": -5.60},
headers={"Authorization": "Bearer sl_live_your_key"}
).json()
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
system=f"Use this current spot-level verification:\n\n{pwm['context_string']}",
messages=[{"role": "user", "content": "Is it safe to fly a drone here?"}]
)
from openai import OpenAI
import requests
pwm = requests.get(
"https://senlay.world/api/v1/pwm",
params={"lat": 36.01, "lng": -5.60},
headers={"Authorization": "Bearer sl_live_your_key"}
).json()
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": pwm["context_string"]},
{"role": "user", "content": "Should I irrigate today?"}
]
)
const res = await fetch(
"https://senlay.world/api/v1/pwm?lat=36.01&lng=-5.60",
{ headers: { Authorization: "Bearer sl_live_your_key" } }
);
const pwm = await res.json();
// Inject into ANY LLM system prompt:
const systemPrompt = `Physical World Model:\n\n${pwm.context_string}`;
// Works with Claude, GPT, Gemini, Llama, Mistral, Cohere...
# Simplest possible integration — no JSON parsing
context = requests.get(
"https://senlay.world/api/v1/sense",
params={"lat": 36.01, "lng": -5.60},
headers={"Authorization": f"Bearer {SENLAY_KEY}"}
).text
# context is ready to inject directly into any LLM
These are the terms used across Senlay and SmartSurf. They are linked from the site footer and product pages so agents, riders, schools, and developers can check exactly what each word means.
The planned structured contract for each claim in a Senlay response. It keeps the value, unit, source, timestamp, distance, freshness, confidence, uncertainty, and disagreement visible instead of hiding them in a generic answer.
The trace of where a value came from: station, model, buoy, METAR, satellite, device telemetry, private sensor, or derived local rule. Provenance lets an app explain how it knows something.
How recent a measurement or model run is. A nearby stale sensor can be less useful than a fresher alternate source, so Senlay surfaces freshness instead of treating all values equally.
A decision-support signal based on source quality, distance, freshness, source agreement, and local domain fit. Confidence is not a guarantee of safety.
The visible limits of the current answer: missing sensors, stale data, disagreement, weak local coverage, or conditions changing faster than sources update.
Comparison between forecast/model values and live observations. For SmartSurf, this helps decide whether a generic forecast matches what is happening at the beach or on the water.
A structured safety workflow record. It can represent rider stopped, board separated, offshore drift, low-confidence no-go, or other alertable states for SmartSurf-style dashboards.
Senlay and SmartSurf support decisions; they do not replace instructors, local judgment, official rescue services, weather authorities, or legal safety obligations.
Plain-language context that preserves where the facts came from and how trustworthy they are. This is what an AI assistant should read before making a location-specific recommendation.
A developer/API convenience field that can be injected into an LLM prompt. It belongs in docs and integrations, not as the main marketing promise.