Install
openclaw skills install windsenseiCheck wind and weather conditions for wind sports (kitesurfing, wingfoiling, surfing). Get forecasts, find spots nearby, view session history, and request ne...
openclaw skills install windsenseiCheck wind conditions at your favorite spots and take action on good forecasts.
Activate when the user asks about:
WINDSENSEI_API_KEY: An API key starting with ss_. Optional — the skill works fully without it. Without it, you can still query any public spot by name, search for spots, and find spots nearby. With it, you get personalized forecasts, dashboard overview, session history, and social features.If the user wants personalized forecasts, guide them:
ss_ — set it as WINDSENSEI_API_KEY in the agent's environment configDirect link: https://windsensei.com/dashboard/profile (scroll to "API Keys" section)
spot parameterhint field suggesting signup for personalized resultsFor all authenticated endpoints, include the API key as a Bearer token:
Authorization: Bearer {WINDSENSEI_API_KEY}
The API also accepts X-API-Key: {key} header or ?api_key={key} query param.
The simplest way to check conditions. Returns a natural language summary plus structured data.
GET https://windsensei.com/api/v1/wind-report?when={when}
| Param | Values | Default | Description |
|---|---|---|---|
when | today, tomorrow, weekend, week | today | Time window |
spot | string (e.g., Cherry Beach) | — | Search public spots by name. Not needed with API key. |
locationId | string | — | Specific spot by ID |
format | text, full | full | text returns just the summary string |
Auth: Optional. With API key, returns forecasts for all the user's favorite spots automatically.
Response:
{
"success": true,
"text": "Cherry Beach: Great conditions 2-6pm — 18-22kt SW, 24°C, water 19°C. Good for Kitefoil, Twintip.",
"when": "today",
"authenticated": false,
"hint": "Personalize your forecast with your own spots and activity preferences at windsensei.com",
"locations": [
{
"locationId": "abc123",
"name": "Cherry Beach",
"timezone": "America/Toronto",
"blocks": [
{
"date": "2026-02-21",
"dateLabel": "Saturday, Feb 21",
"start": "14:00",
"end": "18:00",
"quality": "good",
"windSpeed": { "avg": 20, "gust": 25 },
"windDirection": "SW",
"windQuality": "good",
"temperature": 24,
"waterTemp": 19,
"activities": ["Kitefoil", "Twintip"]
}
]
}
]
}
When no spot specified and no API key:
{
"success": true,
"text": "Provide a spot name to check conditions. Example: ?spot=Cherry Beach",
"locations": [],
"hint": "Personalize your forecast with your own spots and activity preferences at windsensei.com"
}
Auth: Required. Single call to get current conditions for all the user's favorite spots, sorted best-first. The most efficient way to answer "how's the wind?" for authenticated users.
GET https://windsensei.com/api/v1/dashboard
Response:
{
"success": true,
"spots": [
{
"locationId": "abc123",
"name": "Cherry Beach",
"current": {
"windSpeed": 18,
"windGust": 24,
"windDirection": 225,
"temperature": 22,
"quality": "good"
},
"observed": {
"stationName": "Toronto Island",
"observationTime": "2026-02-21T14:00:00Z",
"windSpeed": 20,
"windDirection": 210,
"windGust": 26,
"temperature": 21,
"waterTemp": { "temperature": 18.5, "units": "C" }
},
"nextGoodWindow": {
"start": "2026-02-21T14:00",
"end": "2026-02-21T18:00",
"quality": "good",
"activities": ["Kitefoil", "Twintip"]
},
"modelConsensus": {
"agreement": "high",
"models": ["GFS", "ICON", "GEM"]
}
}
],
"generatedAt": "2026-02-21T12:00:00Z"
}
Key fields:
current: Forecast-based current conditionsobserved: Real weather station data (if available for the spot) — more accurate than forecastnextGoodWindow: Next rideable time window based on the user's activity preferences. Times are in the spot's local timezone.modelConsensus: Whether forecast models agree on conditionsWhen to use dashboard vs wind-report:
Find the best spots right now. Works with or without auth.
GET https://windsensei.com/api/v1/best-conditions
| Param | Values | Default | Description |
|---|---|---|---|
when | now, today, tomorrow, weekend | now | Time window. now = current hour onward. |
lat | number | — | Latitude (required if no API key) |
lng | number | — | Longitude (required if no API key) |
radius | number | 100 | Search radius in km (max 500). Only used with lat/lng. |
limit | number | 5 | Max results (max 20) |
Auth: Optional.
Response:
{
"success": true,
"when": "now",
"spots": [
{
"locationId": "abc123",
"name": "Cherry Beach",
"timezone": "America/Toronto",
"distance": 4.2,
"current": {
"windSpeed": 20,
"windGust": 26,
"windDirection": "SW",
"windQuality": "good",
"temperature": 22,
"quality": "good"
},
"waterTemp": 18.5,
"bestBlock": {
"date": "2026-02-21",
"dateLabel": "Saturday, Feb 21",
"start": "14:00",
"end": "18:00",
"quality": "good",
"windSpeed": { "avg": 20, "gust": 26 },
"windDirection": "SW",
"activities": ["Kitefoil", "Twintip"]
},
"activities": ["Kitefoil", "Twintip"]
}
],
"total": 1,
"authenticated": false,
"hint": "Get personalized results with your own spots and preferences at windsensei.com",
"generatedAt": "2026-02-21T12:00:00Z"
}
Key fields:
distance: Distance from the provided lat/lng in km (only present for nearby queries)current: Conditions right now (or first hour in the time window)bestBlock: The best consecutive window of rideable conditions in the time rangeWhen to use: "What's the best spot right now?", "Where should I go kiting near Toronto?", "Best conditions this weekend?"
Find spots by name. Works without auth.
GET https://windsensei.com/api/v1/locations/search?q={query}
| Param | Values | Default | Description |
|---|---|---|---|
q | string | — | Search query (required) |
limit | number | 10 | Max results (max 50) |
Auth: Optional. Authenticated users also see their private spots.
Response:
{
"success": true,
"query": "cherry",
"locations": [
{
"_id": "abc123",
"name": "Cherry Beach",
"latitude": 43.63,
"longitude": -79.34,
"activityTypes": ["kite"],
"windDirections": ["SW", "S", "W"],
"isFavorite": false
}
],
"total": 1
}
When to use: To resolve a spot name to a locationId for use with other endpoints, or when the user wants to browse available spots.
Find spots near a location. Works without auth.
GET https://windsensei.com/api/v1/locations/nearby?lat={lat}&lng={lng}
| Param | Values | Default | Description |
|---|---|---|---|
lat | number | — | Latitude (required) |
lng | number | — | Longitude (required) |
radius | number | 50 | Radius in km (max 500) |
limit | number | 10 | Max results (max 50) |
Auth: Optional. Authenticated users also see private spots.
Response:
{
"success": true,
"center": { "lat": 43.65, "lng": -79.38 },
"radiusKm": 50,
"locations": [
{
"id": "abc123",
"name": "Cherry Beach",
"latitude": 43.63,
"longitude": -79.34,
"distance": 4.2,
"activityTypes": ["kite"],
"windDirections": { "SW": "good", "S": "medium", "W": "good" },
"isFavorite": false
}
],
"total": 3
}
When to use: When the user says "what spots are near me?", "spots near Toronto", or "find kite spots in [city]". You'll need to geocode the city/location to lat/lng first (use the agent's geocoding capability or a known coordinate).
Full hourly forecast for a specific spot. More detailed than wind-report.
GET https://windsensei.com/api/v1/forecast/{locationId}/full
Auth: Required. Returns personalized quality ratings based on the user's activity preferences.
Response includes:
When to use: When the user wants hour-by-hour detail, or asks "when exactly is the best window?" after seeing a summary.
Get the user's favorite spots in their saved order.
GET https://windsensei.com/api/v1/favorites
Auth: Required.
Response:
{
"success": true,
"locations": [
{
"_id": "abc123",
"name": "Cherry Beach",
"latitude": 43.63,
"longitude": -79.34,
"activityTypes": ["kite"],
"windDirections": ["SW", "S", "W"],
"isFavorite": true
}
]
}
When to use: When the user asks "what are my spots?" or "list my favorites."
Get the user's logged wind sport sessions.
GET https://windsensei.com/api/v1/sessions
| Param | Values | Default | Description |
|---|---|---|---|
limit | number | 20 | Max results (max 100) |
offset | number | 0 | Pagination offset |
activityType | string | — | Filter by activity (e.g., "Kitefoil", "Wingfoil") |
locationId | string | — | Filter by spot |
Auth: Required.
Response:
{
"success": true,
"sessions": [
{
"id": "sess123",
"activityType": "Kitefoil",
"locationName": "Cherry Beach",
"startTime": "2026-02-15T14:00:00Z",
"endTime": "2026-02-15T17:30:00Z",
"duration": 12600,
"rating": 4,
"notes": "Great session, consistent SW wind",
"stats": {
"distance": 15.2,
"maxSpeed": 32.5
}
}
],
"pagination": {
"total": 47,
"limit": 20,
"offset": 0,
"hasMore": true
}
}
When to use: When the user asks "how many sessions have I had?", "show my recent sessions", "what was my last kite session?", or "how much have I ridden this month?"
See which followed users are currently out riding.
GET https://windsensei.com/api/v1/live-sessions
Auth: Required.
Response:
{
"success": true,
"data": {
"sessions": [
{
"id": "live123",
"user": {
"name": "Mike",
"nickname": "windmike"
},
"activityType": "Wingfoil",
"spotName": "Cherry Beach",
"startTime": "2026-02-21T14:00:00Z",
"lastUpdateTime": "2026-02-21T14:30:00Z",
"stats": {
"elapsedTime": 1800,
"distance": 3.2,
"maxSpeed": 22.1
},
"conditions": {
"windSpeed": 18,
"windDirection": "SW"
}
}
]
}
}
When to use: When the user asks "is anyone out right now?", "who's riding?", or "are any of my friends on the water?"
Look up any WindSensei user's public stats by their handle. No auth needed.
GET https://windsensei.com/api/public/profile/{nickname}
Auth: None.
Response includes: nickname, avatar, total session count, total duration, total distance, recent public sessions, activity types.
When to use: When the user asks about a specific rider's profile or stats.
Submit a request to add a spot that isn't in the system yet.
POST https://windsensei.com/api/v1/spot-request
Content-Type: application/json
{
"spotName": "Spot Name Here"
}
Auth: Optional. If API key is included, the request is attributed to the user.
Response:
{
"success": true,
"message": "Spot request for \"Spot Name\" submitted. We'll review it soon."
}
Duplicate requests are handled automatically — just returns success.
CRITICAL: Only report what the API data says. Do NOT:
quality and windQuality fields stateactivities array lists for each blockIf blocks is empty for a location, there are NO good conditions. Period. Don't soften this or suggest the user check anyway.
If blocks exist, they represent the API's definitive assessment — the quality, wind speed, direction, and activities are computed from the user's preferences and the spot's characteristics. Trust them exactly as returned.
timezone field (e.g., "America/Toronto"). All times in start, end, and date fields are in this timezone — not UTC, not the user's local time. When presenting times to the user, always clarify the timezone if it differs from the user's location (e.g., "2-6pm EST").observed comes from real weather stations and is more accurate. current is forecast-based. Prefer observed data when available.agreement is "high", the forecast is more reliable.false, mention the user can get better results by setting up an API key.text field first (when using wind-report) — it's already a concise, factual summary.blocks data to elaborate — but stick to what the data says.hint is present in the response, casually mention it the first time: "By the way, you can get personalized forecasts for your spots at windsensei.com"| User intent | Endpoint | Auth needed? |
|---|---|---|
| "How's the wind?" (has API key) | Dashboard | Yes |
| "How's the wind at Cherry Beach?" | Wind Report with spot= | No |
| "How's the wind this weekend?" | Wind Report with when=weekend | No |
| "What's the best spot right now?" | Best Conditions | No (lat/lng) or Yes |
| "Where should I kite near Miami?" | Best Conditions with lat/lng | No |
| "What spots are near Toronto?" | Nearby (geocode city first) | No |
| "Find spots called 'cherry'" | Search with q=cherry | No |
| "Give me hour-by-hour for Cherry Beach" | Forecast Full | Yes |
| "What are my spots?" | Favorites | Yes |
| "How many sessions have I had?" | Sessions | Yes |
| "Is anyone riding right now?" | Live Sessions | Yes |
| "Show me @windmike's profile" | Public Profile | No |
| "Add Wasaga Beach" (spot not found) | Spot Request | No |
When the user says "how's the wind at Cherry Beach":
?spot=Cherry+Beach to wind-reportFor more control, use the Search endpoint first to resolve the name to a locationId, then use that ID with other endpoints.
When a spot isn't found:
/api/v1/spot-request with the spot name.This skill does not provide or require any calendar credentials. It relies on the host agent's existing calendar tools (e.g., Google Calendar MCP, Apple Calendar, etc.). If the agent has no calendar tool available, skip calendar actions and just present the forecast data.
When the user asks to "block off", "add to calendar", or "schedule" a session based on a wind block:
Use the block data to create a calendar event via the agent's calendar tool:
Kite @ {location name} (or the relevant activity from the block)date + start timedate + end time{windSpeed.avg}kt {windDirection} (gusts {windSpeed.gust}kt), {temperature}°C{waterTemp ? ', water ' + waterTemp + '°C' : ''}. Quality: {quality}.Confirm to the user what was added.
If no calendar tool is available, offer the event details so the user can add it manually.
If there are multiple good blocks, ask which one(s) to add unless the user said "all".
First-time user, no API key:
User: "How's the wind at Cherry Beach?" → Call GET /api/v1/wind-report?spot=Cherry+Beach&when=today → Relay the
textfield → Mention: "For personalized forecasts with your own spots, check out windsensei.com"
Authenticated user, quick check:
User: "How's the wind?" → Call GET /api/v1/dashboard (with Bearer token) → Summarize: lead with the best spot, mention the rest
Best spot right now (unauthenticated):
User: "Where should I go kiting near Miami?" → Geocode Miami to lat=25.76, lng=-80.19 → Call GET /api/v1/best-conditions?lat=25.76&lng=-80.19&when=now → "Crandon Park has the best conditions right now — 18kt SE, good direction, 28°C. Best window is 2-6pm."
Best spot right now (authenticated):
User: "What's my best spot today?" → Call GET /api/v1/best-conditions?when=today (with Bearer token) → "Cherry Beach looks best today — good conditions 2-6pm with 20kt SW. Hanlan's has medium conditions 1-4pm."
Finding new spots:
User: "What kite spots are near Miami?" → Geocode Miami to lat=25.76, lng=-80.19 → Call GET /api/v1/locations/nearby?lat=25.76&lng=-80.19&radius=100 → Present the spots with distances
Session stats:
User: "How many sessions have I logged this year?" → Call GET /api/v1/sessions?limit=100 → Count and summarize by activity type
Live check:
User: "Is anyone out riding right now?" → Call GET /api/v1/live-sessions → "Your friend Mike is wingfoiling at Cherry Beach — been out for 30 minutes in 18kt SW wind"
Calendar action:
User: "Block off the good times this weekend" → Call GET /api/v1/wind-report?when=weekend → For each block, create a calendar event → "Added 2 sessions to your calendar: Kite @ Cherry Beach Sat 2-6pm, Wing @ Hanlan's Sun 10am-1pm"
Specific day:
User: "What about Saturday?" → Call GET /api/v1/wind-report?when=weekend → Relay the relevant day from the response
No conditions:
User: "Should I go kiting tomorrow?" → Call GET /api/v1/wind-report?when=tomorrow → If no blocks: "No rideable conditions tomorrow at [spot name]."
Missing spot:
User: "How's the wind at Wasaga Beach?" → Call GET /api/v1/wind-report?spot=Wasaga+Beach → Response: not found → Reply: "WindSensei doesn't have data for Wasaga Beach yet. Want me to submit a request to add it?" User: "Yeah, do it" → Call POST /api/v1/spot-request with
{"spotName": "Wasaga Beach"}→ Reply: "Done — submitted a request to add Wasaga Beach. The WindSensei team will review it."