openapi: 3.2.0 info: title: Opplevagent Opplevelser API description: REST API for Norwegian experience and activity discovery. Experiences are harvested from curated sources and provider-verified against Brønnøysundregistrene. All read endpoints are public; no authentication required. version: 0.1.0 contact: url: https://opplevagent.no license: name: CC0 (Brreg data) url: https://creativecommons.org/publicdomain/zero/1.0/ servers: - url: https://opplevagent.no description: Production tags: - name: Opplevelser paths: /api/opplevelser/discover: get: operationId: discoverExperiences summary: Discover experiences (intent discovery) description: «Hva kan vi finne på i [sted]» — intent discovery over published experiences. All parameters are optional and combinable. Only verified experiences whose provider is brreg-active and whose confidence is medium/high are surfaced. `category=gardssalg_smaking` routes to a distinct vertical — Brreg-registered farm-sale drink producers (gårdssalg) stored separately from `experiences` — and returns `vertical:"gardssalg"` with GardssalgProducer-shaped rows instead (see `producer_type`/`booking_live` parameters and the GardssalgProducer schema below). parameters: - name: fylke in: query description: County name (e.g. «Oslo», «Troms») schema: type: string example: Troms - name: kommune in: query description: Municipality name (e.g. «Tromsø») schema: type: string example: Tromsø - name: category in: query description: Category slug (e.g. «dyreliv_safari», «natur_friluft»). «gardssalg_smaking» routes to the gårdssalg producer vertical instead of `experiences`. schema: type: string examples: experience: value: dyreliv_safari summary: Regular experience category gardssalg: value: gardssalg_smaking summary: Gårdssalg (farm-sale drink producer) vertical - name: producer_type in: query description: 'Gårdssalg-only: producer type filter (e.g. «bryggeri», «sideri», «vingård»). Ignored outside `category=gardssalg_smaking`.' schema: type: string example: sideri - name: booking_live in: query description: 'Gårdssalg-only: pass literal `true` to only return producers with live direct booking (omitted = no filter on this column, NOT «only paused»). Ignored outside `category=gardssalg_smaking`.' schema: type: boolean - name: q in: query description: 'Gårdssalg-only: free-text lookup of ONE specific producer by name and/or place (e.g. «Fjordgard Bryggeri», «Egge gård Steinkjer»). Every word must match the producer''s name, URL slug, place (poststed) or municipality; exact name matches rank first. Use the returned `id` as `provider_id` when booking. Ignored outside `category=gardssalg_smaking`.' schema: type: string maxLength: 200 example: Fjordgard Bryggeri - name: indoor_outdoor in: query description: Indoor / outdoor preference schema: type: string enum: - indoor - outdoor - both - name: weather in: query description: Weather hint — rain/snow prefer indoor & weather-independent schema: type: string enum: - rain - snow - clear - any - name: season in: query description: Season (e.g. «summer», «winter») schema: type: string example: winter - name: group_size in: query description: Number of people in the group schema: type: integer minimum: 1 - name: age in: query description: Age of the youngest participant schema: type: integer minimum: 0 - name: max_price in: query description: Maximum price (NOK) schema: type: integer minimum: 1 - name: duration_max in: query description: Maximum duration (minutes) schema: type: integer minimum: 1 - name: language in: query description: Required language (e.g. «en», «no») schema: type: string - name: lat in: query description: Origin latitude for a near-me search (decimal degrees). Must be given together with lng. schema: type: number minimum: -90 maximum: 90 example: 69.65 - name: lng in: query description: Origin longitude for a near-me search (decimal degrees). Must be given together with lat. schema: type: number minimum: -180 maximum: 180 example: 18.95 - name: radius_km in: query description: Max distance from lat/lng in kilometers. Only applies when lat/lng are given. schema: type: number exclusiveMinimum: 0 maximum: 5000 example: 50 - name: sort in: query description: '''distance'' — sort ascending by distance from lat/lng (already the default whenever lat/lng are given).' schema: type: string enum: - distance - name: limit in: query description: Max results (default 20, max 100) schema: type: integer default: 20 minimum: 1 maximum: 100 responses: '200': description: Discovery result — `vertical` and the shape of `results[]` depend on `category` (see GardssalgProducer for the gårdssalg branch). content: application/json: schema: type: object properties: vertical: type: string enum: - experiences - gardssalg query: type: object count: type: integer results: type: array items: oneOf: - $ref: '#/components/schemas/Experience' - $ref: '#/components/schemas/GardssalgProducer' examples: gardssalg: summary: category=gardssalg_smaking&fylke=Vestland value: vertical: gardssalg query: fylke: Vestland count: 1 results: - navn: Eksempel Sideri fylke: Vestland kommune: Ulvik producer_type: sideri lat: 60.57 lon: 6.9 geocode_confidence: high booking: live: false mode: paused note: Reservasjoner åpner snart; ta kontakt via profilsiden. / Bookings open soon; visit the profile page to get in touch. profile_url: https://opplevagent.no/kategori/gardssalg/produsent/eksempel-sideri--abc123 '400': description: Invalid query parameters tags: - Opplevelser /api/opplevelser/categories: get: operationId: listExperienceCategories summary: List experience categories description: Returns distinct categories with the count of published experiences in each. responses: '200': description: Array of category objects content: application/json: schema: type: object properties: categories: type: array items: type: object properties: category: type: string count: type: integer tags: - Opplevelser /api/opplevelser/{id}: get: operationId: getExperience summary: Get a single experience by ID parameters: - name: id in: path required: true description: Experience UUID schema: type: string responses: '200': description: Experience object content: application/json: schema: type: object properties: experience: $ref: '#/components/schemas/Experience' '404': description: Not found tags: - Opplevelser components: schemas: Experience: type: object properties: id: type: string description: UUID title: type: string description: Experience title category: type: string nullable: true fylke: type: string nullable: true description: County kommune: type: string nullable: true description: Municipality indoor_outdoor: type: string enum: - indoor - outdoor - both nullable: true duration_min: type: integer nullable: true description: Minimum duration (minutes) price_from: type: integer nullable: true description: Price from (NOK) price_band: type: string nullable: true booking_url: type: string nullable: true confidence: type: string enum: - high - medium - low nullable: true tags: type: array description: Derived cross-cutting filter tags (additive-only; computed from existing fields). items: type: string enum: - familievennlig - gratis - under-300 - tilgjengelig - værsikker - sesong distance_km: type: number nullable: true description: 'Distance from the caller''s lat/lng origin, in kilometers (rounded to 1 decimal). Only present when lat/lng were given in the request. Never fabricated: rows with no geocoded location are excluded from the result entirely rather than shown with a missing/guessed distance.' geo_precision: type: string enum: - address - kommune nullable: true description: How this row's location (and therefore distance_km) was derived. 'address' = geocoded from the provider's exact street address (precise). 'kommune' = a municipality centroid (approximate — do not present distance_km as exact for these rows). Only present when lat/lng were given. GardssalgProducer: type: object description: A gårdssalg (farm-sale drink producer) row — returned by `/api/opplevelser/discover?category=gardssalg_smaking` and the `discover_gardssalg` MCP tool. Stored in `experience_providers`, not `experiences` — never mix with the Experience schema. properties: id: type: string description: The producer's id — the `provider_id` for `POST /api/opplevelser/book` and the `book_gardssalg` MCP tool. navn: type: string fylke: type: string nullable: true kommune: type: string nullable: true producer_type: type: string nullable: true lat: type: number nullable: true lon: type: number nullable: true geocode_confidence: type: string enum: - high - medium - low - no_match - approximate nullable: true description: Provider-address geocode quality (`experience_providers.geocode_confidence` — distinct from the Experience schema's `geo_precision`, which uses 'address'/'kommune' instead). 'high'/'medium'/'low' come from the address-geocoding step; 'approximate' is a kommune-centroid fallback; 'no_match' means no coordinates could be resolved (lat/lon are null in that case). booking: type: object description: Honest booking status — never claims an active booking flow before the dark-launch gate opens. properties: live: type: boolean mode: type: string enum: - request - paused note: type: string profile_url: type: string nullable: true distance_km: type: number nullable: true description: Only present when lat/lng were given in the request (near-me search).