Consumer discovery
Replace N client-side score calls with one ranked recommend.
Rank suitable spots in a region before a customer chooses where to go. Give the engine an activity, a region and a window, and get back the top-K ranked sub-spots: calibrated per spot, safety-gated, each with the driver that set its rank. The endpoint behind 'best kitesurf near me this weekend' inside consumer apps and booking platforms.
A worked query for a kitesurf weekend on the Cádiz coast. The engine resolves five catalog sub-spots within 25km of the region centre, scores each across the six-hour window, drops the unsafe and not-feasible ones, and returns this ranked list. Every result uses the same scoring, safety and confidence rules as /v1/score.
POST /v1/recommend-spot
{
activity: "kitesurfing",
regionCenter: { lat: 36.013, lng: -5.604 },
radiusKm: 25,
topK: 5,
window: {
from: "2026-08-28T10:00Z",
to: "2026-08-28T16:00Z"
}
}Coverage · Score works at any coordinate. Spot Discovery ranks catalogued sub-spots and is available in covered regions.
Check coverage for your regionDiscovery answers "where should we go?" in a single request. Replace N client-side score calls with one ranked recommend. Discovery ranks trusted score results; it does not invent a separate recommendation model. Every result uses the same scoring, safety and confidence rules as /v1/score.
Replace N client-side score calls with one ranked recommend.
Powers region-and-activity search result lists.
Itinerary tooling. One call instead of an analyst with five tabs.
A candidate that fails a hard gate — unsafe (lightning ≥0.85, hazardous AQI) or not_feasible (e.g. no rideable wind) — is removed before the sort, not filtered out of a ranked list afterwards. Neither an unsafe nor a not-feasible spot can ever surface as a recommendation, whatever its raw score.
When all candidates are gated, Goable returns no recommendation plus the binding constraint — a danger (lightning, wind hazard, air-quality risk) or a feasibility limit (e.g. no rideable wind anywhere in radius) — so the app can tell the user why rather than showing an empty list.
Optional personalization refines recommendations within safety and physics-defined limits. Personalization is optional, pseudonymous and deletable by design. The exact blend limits and deletion mechanics are documented in Security and the endpoint reference.
Radius and topK are tiered. Personalisation unlocks at Pro, counterfactual hints at Scale. Daily quota is separate from /v1/score: each recommend call fans out N internal scores, so the cap is independent and tighter. Starter includes 1,500 recommend calls/month, Pro 5,000; overage at €7.50/1k (Starter) and €4.00/1k (Pro).
The endpoint introduces no new physics and no new machine learning. It is orchestration of layers you already trust: spatial enumeration, per-spot scoring, safety filtering, ranking and persistence. Full detail is in the endpoint reference.
Catalog sub-spots looked up in microseconds against an in-memory R-tree built at boot.
Same physics, calibration and confidence pipeline as /v1/score, fanned out at concurrency 6 to spare upstream providers.
A verdict of unsafe (lightning ≥0.85, hazardous AQI, a safety gate) or not_feasible (e.g. no rideable wind — impossible, not dangerous) means the spot is dropped before ranking. allGated=true when every candidate fails.
Sorted descending by score; within ±0.5 the closer sub-spot wins. Top-K slice returned.
One audit row plus a recommendation.completed event (HMAC-signed, top-3 payload).
Every successful call writes one row to recommendation_runs (region centre as a PostGIS POINT, full top-K, latency, allGated flag), the primitive behind the upcoming search-heatmap analytics and the forecast-verification join.
Tenants subscribed to recommendation.completed receive an HMAC-signed webhook after each call; the payload trims to the top 3 while the full top-K stays in the audit DB. Personalization stores only a client-side hash plus model weights, and DELETE /v1/decision/user-data/:pseudonym scrubs every trace across the personalization, audit and recommendation stores in one call (GDPR Article 17).
{
"event": "recommendation.completed",
"tenantId": "tn_...",
"data": {
"activity": "kitesurfing",
"regionCenter": { "lat": 36.01, "lng": -5.6 },
"radiusKm": 50, "topK": 5,
"results": [
{ "rank": 1, "spotSlug": "kitesurfing-spot-tarifa-balneario",
"effectiveScore": 87, "verdict": "excellent" },
{ "rank": 2, "spotSlug": "kitesurfing-spot-tarifa-valdevaqueros",
"effectiveScore": 84, "verdict": "favorable" },
{ "rank": 3, "spotSlug": "kitesurfing-spot-tarifa-punta-paloma",
"effectiveScore": 79, "verdict": "favorable" }
],
"allGated": false, "latencyMs": 412
}
}One engine for two decisions: validate a chosen spot or find the best one. Score a spot you already know. Search a region when the user has not chosen a spot yet. Same scoring curves, same hard gates, same confidence model. Two workflows, one consistent decision model.
activity: "kitesurfing"location: {lat:36.013, lng:-5.604}window: {from:"…", to:"…"}
activity: "kitesurfing"regionCenter: {lat:36.01, lng:-5.6}radiusKm: 50topK: 5