# Get v2orgs chat Source: https://docs.obvlo.com/api-reference/ai-chat/get-v2orgs-chat /api-reference/openapi-preview.json get /v2/orgs/{orgId}/chat Returns the listings referenced by a guide card in the AI assistant experience. # Post v2orgs chat Source: https://docs.obvlo.com/api-reference/ai-chat/post-v2orgs-chat /api-reference/openapi-preview.json post /v2/orgs/{orgId}/chat Conversational AI endpoint for destination recommendations, itinerary generation, and itinerary email delivery. # API Changelog Source: https://docs.obvlo.com/api-reference/changelog A record of changes to the Obvlo API schema and endpoints. ## 2026-09-10 — API updates * Updated API schema metadata and documentation details. *** ## 2026-08-14 — Destination-scoped languages * Added optional query parameter `hostId` to `GET /v2/orgs/{orgId}/org-languages`, narrowing the response to one destination's target languages. * Omitting `hostId` is unchanged behaviour: callers receive the organisation-wide language list. An unknown or out-of-scope `hostId` also returns the organisation-wide list rather than an empty one. * Documented per-destination translation scope in [Translations](/product/management/translations). *** ## 2026-08-05 — API updates * Removed response property `tree[].children[].slug` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].pageId` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].guideId` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].pageType` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].segments` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].parentIds` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].locationId` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].homepageSlug` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Removed response property `tree[].children[].destinationId` from `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. *** ## 2026-07-10 — API updates * Added response property `mode` to `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Added response property `tree` to `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Added response property `siteId` to `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. * Added response property `matches` to `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug` 200 response. *** ## 2026-07-03 — Site data API (phase 1) * Added endpoint `GET /v2/orgs/{orgId}/sites/{siteId}/page-slug`. * Added support for slug tree mode (no filters) and filtered match mode (`destinationId`, `guideId`, `locationId`). * Added stable page slug mapping fields: `slug`, `segments`, `parentIds`, `homepageSlug`, and entity IDs. *** ## 2026-04-13 — API updates * Added response property `host.embeddedConfigs[].experiments` to `GET /v2/orgs/{orgId}/host-home` 200 response. * Added response property `embeddedConfigs[].experiments` to `GET /v2/orgs/{orgId}/host-profile` 200 response. *** ## 2026-03-18 — API updates * Updated API schema metadata and documentation details. *** ## 2026-03-17 — Documentation alignment * Added AI Chat preview documentation for `POST /v2/orgs/{orgId}/chat` * Marked AI Chat operations as preview in the published OpenAPI reference * Corrected endpoint pages to use the current `/v2/orgs/{orgId}/...` path format and `key` query parameter examples *** ## 2026-03-16 — API updates * Added endpoint `GET /v2/orgs/{orgId}/chat`. * Added endpoint `POST /v2/orgs/{orgId}/chat`. *** This page is updated automatically whenever the API schema changes in a deployment. Each entry links to the source commit so you can review the full diff. *** ## 2026-02-20 — Initial changelog Changelog tracking introduced. Current endpoints: * `GET /v2/orgs/{orgId}/guide-search` — Search guides with optional listings, geo-filtering, and personalisation * `GET /v2/orgs/{orgId}/listing-search` — Search listings with activity tag filtering and geo-filtering * `GET /v2/orgs/{orgId}/location-search` — Search neighbourhoods and geo areas * `GET /v2/orgs/{orgId}/host-profile` — Retrieve host branding and configuration * `GET /v2/orgs/{orgId}/host-home` — Retrieve host home content * `GET /v2/orgs/{orgId}/org-languages` — Retrieve languages configured for an organisation *** # AI Chat (Preview) Source: https://docs.obvlo.com/api-reference/endpoint/chat POST /v2/orgs/{orgId}/chat Preview conversational endpoint for recommendations, guide card listings, itinerary generation, and itinerary email delivery. This endpoint is in preview. Request and response shapes may change faster than the stable search and host endpoints. ## Overview The AI Chat endpoint powers conversational discovery in Obvlo experiences. It can: * answer chat queries with curated guide and listing cards * return guide-card listings for detail views * generate itineraries * send generated itineraries by email ## Base route ```http theme={null} POST /v2/orgs/{orgId}/chat ``` Authentication uses the standard `key` query parameter. ## Preview notes * Treat this endpoint as beta or preview in production clients. * Conversation-oriented fields such as `actions`, `hostInterests`, and itinerary payloads may evolve. * Use stable search endpoints when you need fixed, non-conversational retrieval contracts. ## POST action modes The `action` field controls how the endpoint behaves. | `action` | Purpose | | ----------------- | ---------------------------------------------------------------------- | | `chat` | Default conversational search and recommendation flow. | | `createItinerary` | Builds a day-by-day itinerary from the active profile and preferences. | | `sendEmail` | Sends a previously generated itinerary to an email address. | ## Required path and query parameters | Parameter | In | Type | Description | | --------- | ----- | ------ | ------------------------------------ | | `orgId` | path | string | Organisation ID for the request. | | `key` | query | string | Public API key for the organisation. | ## Common request fields | Field | Type | Description | | --------------------- | ------- | -------------------------------------------------------------------------------------------------- | | `message` | string | User message for the chat turn. Use `""` when triggering a non-text action. | | `profileId` | string | Optional host profile context for search and itinerary generation. Required for `createItinerary`. | | `lang` | string | Optional BCP-47 language code for response content. | | `latlng` | string | Optional `lat,lng` user location override. | | `conversationHistory` | array | Prior user and assistant messages. | | `preferences` | object | Optional guided-discovery preferences: `tripType`, `userCategory`, `interests`. | | `sessionId` | string | Optional client session identifier for tracking. | | `fingerprintId` | string | Optional end-user fingerprint identifier for tracking. | | `isInitialSearch` | boolean | Optional hint to randomise initial recommendation ranking. | ## Example requests ### Chat for recommendations ```bash theme={null} curl --request POST \ --url 'https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/chat?key=YOUR_API_KEY' \ --header 'content-type: application/json' \ --data '{ "message": "Find great cocktail bars nearby", "profileId": "YOUR_HOST_ID", "lang": "en-GB", "action": "chat" }' ``` ### Create an itinerary ```bash theme={null} curl --request POST \ --url 'https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/chat?key=YOUR_API_KEY' \ --header 'content-type: application/json' \ --data '{ "message": "", "action": "createItinerary", "profileId": "YOUR_HOST_ID", "durationDays": 3, "preferences": { "tripType": "leisure", "userCategory": "couple", "interests": ["food", "culture"] } }' ``` ### Send itinerary by email ```bash theme={null} curl --request POST \ --url 'https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/chat?key=YOUR_API_KEY' \ --header 'content-type: application/json' \ --data '{ "message": "", "action": "sendEmail", "email": "guest@example.com", "itinerary": { "name": "Weekend in London", "durationDays": 2, "days": [] } }' ``` ## Example chat response ```json theme={null} { "message": "Here are a few cocktail bars worth trying tonight.", "listings": [ { "id": "listing-1", "name": "Swift Soho", "category": "bar", "address": "12 Old Compton Street, London", "geoDistance": 420, "googleRating": 4.7, "priceLevel": 2, "openNow": true, "photo": { "url": "https://cdn.example.com/swift.jpg" }, "googleMapsUrl": "https://maps.google.com/?q=swift+soho" } ], "guides": [ { "id": "guide-1", "name": "Best Cocktail Bars", "description": "A curated shortlist of standout bars.", "coverImage": { "url": "https://cdn.example.com/guide.jpg" }, "listingCount": 8, "geoDistance": 380 } ], "toolUsed": true } ``` ## Related GET route for guide cards Guide cards in the chat UI can lazy-load their associated listings through the companion route below: ```http theme={null} GET /v2/orgs/{orgId}/chat?guideId={guideId}&lang={lang} ``` ### Guide listings example ```bash theme={null} curl --request GET \ --url 'https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/chat?key=YOUR_API_KEY&guideId=YOUR_GUIDE_ID&lang=en-GB' \ --header 'accept: application/json' ``` ### Guide listings response ```json theme={null} { "listings": [ { "id": "listing-1", "name": "Bar Termini", "category": "bar", "address": "7 Old Compton Street, London", "geoDistance": 0, "googleRating": 4.6, "priceLevel": 2, "openNow": null, "photo": { "url": "https://cdn.example.com/bar-termini.jpg" }, "googleMapsUrl": "https://maps.google.com/?q=bar+termini" } ] } ``` # Guide Search Source: https://docs.obvlo.com/api-reference/endpoint/guide-search GET /v2/orgs/{orgId}/guide-search Search curated travel guides by location, keywords, trip type, and personalisation parameters. ## Overview The `/v2/guide-search` endpoint returns a paginated list of curated travel guides from the Obvlo content engine. You can filter by geography, free-text query, and guest personalisation attributes such as trip type and interests. By default, each guide result includes its embedded listings (points of interest). You can disable this with `includeListings=false` to get a lighter response. ### Localised fields and the `lang` parameter Guide fields (`name`, `description`, `introText`, `keywords`, `attribution`, `centerLocation`, `preferences`) and all localised fields of embedded listings carry translated content. The shape of these fields depends on whether `lang` is provided: | `lang` | Field shape | Example | | ---------------------------- | --------------------------------------------------- | ------------------------------------------------------ | | Provided (e.g. `lang=en-GB`) | Plain value for the requested language. | `"name": "Top Restaurants in Shoreditch"` | | Omitted | Language map — one key per available language code. | `"name": { "en-GB": "Top Restaurants in Shoreditch" }` | When `lang` is omitted, at least the default language (`en-GB`) is always included in the map. ## Request ```http theme={null} GET /v2/orgs/{orgId}/guide-search ``` ### Required parameters | Parameter | Type | Description | | --------- | ------ | --------------------- | | `orgId` | string | Your organisation ID. | ### Optional parameters | Parameter | Type | Default | Description | | --------------------- | ------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `profileId` | string | — | Scope results to a specific host profile. | | `searchQuery` | string | — | Full-text query matched against guide names and descriptions. | | `latlng` | string | — | Geo-center for proximity ranking. Format: `latitude,longitude` (e.g. `51.5074,-0.1278`). | | `radius` | number | Profile default | Search radius in metres around `latlng`. | | `page` | integer | `1` | Page number (1-based). | | `hitsPerPage` | integer | `10` | Results per page. Maximum `20`. | | `userCategory` | string | — | Filter by user category (e.g. `couple`, `family`, `solo`). | | `tripType` | string | — | Filter by trip type (e.g. `leisure`, `business`). | | `userInterests` | string | — | Comma-separated interest tags used for personalised ranking. | | `includeListings` | boolean | `true` | Set to `false` to omit embedded listings from each guide. | | `maxListingsPerGuide` | integer | `20` | Maximum listings to embed per guide when `includeListings=true`. | | `rankBy` | string | `score` | Ranking strategy: `score` (relevance) or `distance` (nearest first, requires `latlng`). | | `lang` | string | — | BCP 47 language code (e.g. `en-GB`). When provided, localised fields in guides and their embedded listings are returned as plain values for that language instead of language maps. | ## Examples ### Basic search for an organisation ```bash Basic search theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/guide-search?key=YOUR_API_KEY" ``` ### Geo-filtered search near London ```bash Geo-filtered, near London theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/guide-search?key=YOUR_API_KEY&latlng=51.5074,-0.1278&radius=5000&hitsPerPage=5" ``` ### Personalised search for a couple on a leisure trip ```bash Personalised search theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/guide-search?key=YOUR_API_KEY&userCategory=couple&tripType=leisure&userInterests=food,culture" ``` ### Search with a specific language ```bash With lang parameter theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/guide-search?key=YOUR_API_KEY&lang=en-GB" ``` ### Search without embedded listings ```bash Without embedded listings theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/guide-search?key=YOUR_API_KEY&includeListings=false" ``` ## Response ```json theme={null} { "results": [ { "id": "guide_abc123", "contentType": "guide", "name": "Top Restaurants in Shoreditch", "description": "A curated selection of the best places to eat...", "introText": "Shoreditch is known for its vibrant food scene...", "keywords": ["restaurants", "food", "shoreditch"], "geoLocation": [51.5247, -0.0755], "featured": false, "hostIds": ["host_xyz"], "listingIds": ["listing_1", "listing_2"], "languageCodes": ["en-GB", "fr-FR"], "updatedAt": 1739800000000, "geoDistance": 1234.5, "listings": [ { "id": "listing_1", "contentType": "listing", "name": "Padella", "summary": "Acclaimed pasta restaurant in Borough Market.", "address": "6 Southwark St, London SE1 1TQ", "category": "restaurant", "geoLocation": [51.5051, -0.0906], "geoDistance": 950.0, "featured": false, "guideIds": ["guide_abc123"], "hostIds": ["host_xyz"], "score": 0.89, "searchId": "listing_1", "status": "active", "photos": [] } ] } ], "total": 42, "pages": 5 } ``` ```json theme={null} { "results": [ { "id": "guide_abc123", "contentType": "guide", "name": { "en-GB": "Top Restaurants in Shoreditch", "fr-FR": "Les meilleurs restaurants de Shoreditch" }, "description": { "en-GB": "A curated selection of the best places to eat...", "fr-FR": "Une s\u00e9lection des meilleurs endroits..." }, "introText": { "en-GB": "Shoreditch is known for its vibrant food scene...", "fr-FR": "Shoreditch est connue pour..." }, "keywords": { "en-GB": ["restaurants", "food", "shoreditch"], "fr-FR": ["restaurants", "nourriture"] }, "geoLocation": [51.5247, -0.0755], "featured": false, "hostIds": ["host_xyz"], "listingIds": ["listing_1", "listing_2"], "languageCodes": ["en-GB", "fr-FR"], "updatedAt": 1739800000000, "geoDistance": 1234.5, "listings": [ { "id": "listing_1", "contentType": "listing", "name": { "en-GB": "Padella", "fr-FR": "Padella" }, "summary": { "en-GB": "Acclaimed pasta restaurant in Borough Market.", "fr-FR": "Restaurant de p\u00e2tes reconnu au Borough Market." }, "address": { "en-GB": "6 Southwark St, London SE1 1TQ", "fr-FR": "6 Southwark St, London SE1 1TQ" }, "category": { "en-GB": "restaurant", "fr-FR": "restaurant" }, "geoLocation": [51.5051, -0.0906], "geoDistance": 950.0, "featured": false, "guideIds": ["guide_abc123"], "hostIds": ["host_xyz"], "score": 0.89, "searchId": "listing_1", "status": "active", "photos": [] } ] } ], "total": 42, "pages": 5 } ``` ### Response fields | Field | Type | Description | | --------- | ------- | ----------------------------------------------------- | | `results` | array | Array of guide objects (see below). | | `total` | integer | Total number of guides matching the query. | | `pages` | integer | Total number of pages at the requested `hitsPerPage`. | #### Guide object | Field | Type | Description | | --------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | string | Unique guide identifier. | | `contentType` | string | Always `"guide"`. | | `name` | string \| object | Guide name. Plain string when `lang` is provided; language map otherwise. | | `description` | string \| object | Guide description. Plain string when `lang` is provided; language map otherwise. | | `introText` | string \| object | Intro text. Plain string when `lang` is provided; language map otherwise. | | `keywords` | array \| object | Keywords. Array when `lang` is provided; language map of arrays otherwise. | | `geoLocation` | array | Coordinates as `[latitude, longitude]`. | | `coverImage` | object | Cover image with multiple size variants. See [Image object](#image-object). | | `featured` | boolean | Whether the guide is featured. | | `hostIds` | array | IDs of host profiles associated with this guide. | | `listingIds` | array | IDs of listings included in this guide. | | `languageCodes` | array | Language codes for which content is available. | | `updatedAt` | integer | Last updated timestamp (Unix ms). | | `geoDistance` | number | Distance in metres from the requested `latlng` (when provided). | | `listings` | array | Embedded listing objects (when `includeListings=true`). Localised fields follow the same `lang` behaviour as top-level listing search results. | #### Image object | Field | Type | Description | | -------------- | ------ | ----------------------------------------- | | `id` | string | Unique image identifier. | | `category` | string | Image category tag. | | `attributions` | array | Attribution strings for the image. | | `images` | array | Array of `{ size, url }` variant objects. | # Get Host Home Source: https://docs.obvlo.com/api-reference/endpoint/host-home GET /v2/orgs/{orgId}/host-home Single-call bootstrap for a host home screen — returns the host profile, guides with nested listings, and nearby geo areas. ## Overview The `/v2/host-home` endpoint is designed to power the initial load of a host-scoped experience. A single request returns everything needed to render the home screen: * The host **profile** (branding, search config, embedded UI config, location anchor) * The first page of **guides**, each with nested **listings** * A page of **geo areas** (neighbourhoods) scoped to the host Geo defaults and search radius are resolved from the host profile automatically. Pass `latlng` to override the geo centre (for example, to centre results on the guest's current location), or `radius` to override the search radius. Preference filters (`userCategory`, `tripType`, `userInterests`) are applied to both guide and listing ranking. ### Localised fields and the `lang` parameter Guide fields (`name`, `description`, `introText`, `keywords`, `attribution`, `centerLocation`, `preferences`) and all localised fields of embedded listings carry translated content. Geo area (location) fields (`displayName`, `headline`, `description`, `address`, `area`, `keywords`) also carry translated content. The shape of these fields depends on whether `lang` is provided: | `lang` | Field shape | Example | | ---------------------------- | --------------------------------------------------- | ------------------------------------------------------ | | Provided (e.g. `lang=en-GB`) | Plain value for the requested language. | `"name": "Top Restaurants in Shoreditch"` | | Omitted | Language map — one key per available language code. | `"name": { "en-GB": "Top Restaurants in Shoreditch" }` | When `lang` is omitted, at least the default language (`en-GB`) is always included in the map. ## Request ```http theme={null} GET /v2/orgs/{orgId}/host-home ``` ### Required parameters | Parameter | Type | Description | | ----------- | ------ | ----------------------- | | `orgId` | string | Your organisation ID. | | `profileId` | string | ID of the host profile. | ### Optional parameters | Parameter | Type | Default | Description | | --------------------- | ------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `lang` | string | — | BCP 47 language code (e.g. `en-GB`, `fr-FR`). When provided, localised fields in guides, their embedded listings, and geo areas are returned as plain values for that language instead of language maps. | | `latlng` | string | — | Geo centre override as `lat,lng` (e.g. `51.508,-0.125`). Defaults to the host's stored location. | | `radius` | integer | — | Search radius in metres from the geo centre. Defaults to the host's configured search radius. | | `guideType` | string | — | Filter guides by type. `featured` returns only featured guides; omit to return all guides. | | `userCategory` | string | — | Filter by user category (e.g. `couple`, `family`, `solo`). | | `tripType` | string | — | Filter by trip type (e.g. `leisure`, `business`). | | `userInterests` | string | — | Comma-separated interest tags for personalised ranking. | | `rankBy` | string | `score` | Ranking strategy for guides and listings: `score`, `distance`, or `relevance`. | | `guidesPage` | integer | `1` | Page number for guide results (1-based). | | `guidesPerPage` | integer | `10` | Number of guides per page. Maximum `20`. | | `maxListingsPerGuide` | integer | `20` | Maximum number of listings embedded per guide. Maximum `20`. | | `geoareasPage` | integer | `1` | Page number for geo area results (1-based). | | `geoareasPerPage` | integer | `10` | Number of geo areas per page. Maximum `20`. | ## Examples ### Basic home screen load ```bash Basic home screen load theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/host-home?key=YOUR_API_KEY&profileId=YOUR_HOST_ID" ``` ### Geo-anchored load centred on the guest's location ```bash Geo-anchored load theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/host-home?key=YOUR_API_KEY&profileId=YOUR_HOST_ID&latlng=51.508,-0.125&radius=3000" ``` ### Featured guides only, single language ```bash Featured guides only theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/host-home?key=YOUR_API_KEY&profileId=YOUR_HOST_ID&guideType=featured&lang=en-GB" ``` ### Personalised load for a couple on a leisure trip ```bash Personalised load theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/host-home?key=YOUR_API_KEY&profileId=YOUR_HOST_ID&userCategory=couple&tripType=leisure&userInterests=food,culture" ``` ### Example response ```json Example response expandable theme={null} { "host": { "id": "hotel-grand", "name": "The Grand Hotel", "orgId": "YOUR_ORG_ID", "geoLocation": [51.5074, -0.1278], "primaryLocation": { "displayName": "The Grand Hotel", "address": "1 Grand Place, London", "locationId": "loc-001", "measurement": "metric" }, "searchConfig": { "searchRadius": 4000 }, "embeddedConfigs": [ { "id": "embed-config-001", "name": "Default Widget", "createdAt": 1700000000000, "selectedLayout": "Default" } ], "currencyCode": "GBP", "currencySymbol": "£", "timeZoneName": "Europe/London" }, "guides": { "results": [ { "id": "guide-001", "name": { "en-GB": "Top Restaurants" }, "description": { "en-GB": "The best places to eat in London." }, "geoLocation": [51.508, -0.125], "geoDistance": 850, "listings": [ { "id": "listing-001", "contentType": "listing", "name": { "en-GB": "Sketch" }, "summary": { "en-GB": "An iconic multi-room restaurant and arts venue." }, "address": { "en-GB": "9 Conduit St, London W1S 2XG" }, "geoLocation": [51.511, -0.142], "geoDistance": 1200, "featured": false, "guideIds": ["guide-001"], "hostIds": ["hotel-grand"], "score": 0.91, "searchId": "listing-001", "status": "active", "photos": [] } ] } ], "total": 12, "pages": 2 }, "geoareas": { "results": [ { "id": "area-001", "displayName": { "en-GB": "Soho" }, "description": { "en-GB": "A vibrant neighbourhood known for its nightlife and culture." }, "geoLocation": [51.513, -0.131], "geoDistance": 1400 } ], "total": 5, "pages": 1 } } ``` ## Response fields ### Top-level | Field | Type | Description | | ---------- | ------ | --------------------------------------------------------------------------------------------------------------------------------- | | `host` | object | Host profile, including `embeddedConfigs`. See [Get Host Profile](/api-reference/endpoint/host-profile) for full field reference. | | `guides` | object | Paginated guide results with nested listings. | | `geoareas` | object | Paginated geo area (neighbourhood) results scoped to the host. | ### `guides` | Field | Type | Description | | --------- | --------- | --------------------------------------------------------------------------------------- | | `results` | object\[] | Array of guide objects, each containing a `listings` array of embedded listing objects. | | `total` | integer | Total number of matching guides. | | `pages` | integer | Total number of pages. | Each guide and its embedded listings follow the same shapes as the [Guide Search](/api-reference/endpoint/guide-search) endpoint. ### `geoareas` | Field | Type | Description | | --------- | --------- | ------------------------------------------- | | `results` | object\[] | Array of geo area objects (neighbourhoods). | | `total` | integer | Total number of geo areas for this host. | | `pages` | integer | Total number of pages. | Each geo area follows the same shape as the [Location Search](/api-reference/endpoint/location-search) endpoint. When `lang` is omitted, all localised fields are returned as language maps with at least the default language (`en-GB`) present. If a translation is missing for the requested `lang`, the response falls back to the host's default language. # Get Host Profile Source: https://docs.obvlo.com/api-reference/endpoint/host-profile GET /v2/orgs/{orgId}/host-profile Retrieve the public-safe profile for a host property, including branding, search configuration, and location data. ## Overview The `/v2/host-profile` endpoint returns the configuration profile for a given host. It is the authoritative source for branding, UI configuration, search defaults, and geo anchor data. Sensitive fields such as API keys, analytics tokens, and integration credentials are never included in the response. This endpoint does not localise response fields and does not support the `lang` query parameter. ## Request ```http theme={null} GET /v2/orgs/{orgId}/host-profile ``` ### Required parameters | Parameter | Type | Description | | ----------- | ------ | ----------------------------------- | | `orgId` | string | Your organisation ID. | | `profileId` | string | ID of the host profile to retrieve. | ## Examples ### Retrieve a host profile ```bash Retrieve a host profile theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/host-profile?key=YOUR_API_KEY&profileId=YOUR_HOST_ID" ``` ### Example response ```json Example response expandable theme={null} { "id": "hotel-grand", "name": "The Grand Hotel", "orgId": "YOUR_ORG_ID", "geoLocation": [51.5074, -0.1278], "primaryLocation": { "displayName": "The Grand Hotel", "address": "1 Grand Place, London", "locationId": "loc-001", "measurement": "metric", "geoLocation": { "lat": 51.5074, "lng": -0.1278 } }, "searchConfig": { "searchRadius": 4000, "guideRanking": "relevance", "listingRanking": "score" }, "embeddedConfigs": [ { "id": "embed-config-001", "name": "Default Widget", "createdAt": 1700000000000, "selectedLayout": "Default" } ], "languageCodes": ["en-GB", "fr-FR"], "defaultLanguageCode": "en-GB", "currencyCode": "GBP", "currencySymbol": "£", "timeZoneName": "Europe/London", "weather": { "icon": "02d", "temperature": 14 } } ``` ## Response fields | Field | Type | Description | | --------------------- | ----------------- | ----------------------------------------------------------------------------------------------------- | | `id` | string | Host profile ID. | | `name` | string | Display name of the host. | | `orgId` | string | Organisation ID the host belongs to. | | `geoLocation` | \[number, number] | Coordinates as `[latitude, longitude]`. | | `primaryLocation` | object | Location details for the host's primary address. | | `searchConfig` | object | Default search radius and ranking preferences. | | `languageCodes` | string\[] | Language codes supported by this host's content. | | `defaultLanguageCode` | string | Default language for content. | | `currencyCode` | string | ISO 4217 currency code (e.g. `GBP`). | | `currencySymbol` | string | Display currency symbol (e.g. `£`). | | `timeZoneName` | string | IANA time zone name (e.g. `Europe/London`). | | `uiConfig` | object | UI configuration for the host interface. | | `branding` | object | Branding colours and typography. | | `logos` | object\[] | Logo assets for the host. | | `pages` | object\[] | Custom page configurations. | | `weather` | object | Current weather snapshot (`icon`, `temperature` in Celsius). | | `perks` | object | Perks/amenities header and sections. | | `socialMedia` | object\[] | Social media links. | | `travellerArchetypes` | object\[] | Configured traveller archetypes for personalisation. | | `embeddedConfigs` | object\[] | Array of embedded UI module configuration objects. Shape determined by the host's configured modules. | | `activeIntegrations` | string\[] | List of active third-party integration identifiers. | # Listing Search Source: https://docs.obvlo.com/api-reference/endpoint/listing-search GET /v2/orgs/{orgId}/listing-search Search individual listings — restaurants, attractions, and experiences — with geo, category, and personalisation filters. ## Overview The `/v2/listing-search` endpoint returns a paginated list of individual listings from the Obvlo content engine. Listings represent single points of interest such as restaurants, hotels, attractions, or experiences. You can filter by geography, free-text query, activity tags, and guest personalisation attributes. ### Localised fields and the `lang` parameter Several listing fields (`name`, `summary`, `tagline`, `address`, `category`, `activityTags`, `keywords`, `priceLevelText`, `planVisit`, `whatToExpect`, `additionalTextAttributes`, `preferences`, `integratedCategories`) carry translated content. The shape of these fields depends on whether `lang` is provided: | `lang` | Field shape | Example | | ---------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | Provided (e.g. `lang=en-GB`) | Plain value — a single string (or array/object) for the requested language. | `"summary": "A great place to eat."` | | Omitted | Language map — one key per available language code. | `"summary": { "en-GB": "A great place to eat.", "fr-FR": "Un excellent endroit." }` | When `lang` is omitted, at least the default language (`en-GB`) is always included in the map. ## Request ```http theme={null} GET /v2/orgs/{orgId}/listing-search ``` ### Required parameters | Parameter | Type | Description | | --------- | ------ | --------------------- | | `orgId` | string | Your organisation ID. | ### Optional parameters | Parameter | Type | Default | Description | | --------------- | ------ | ------- | ---------------------------------------------------------------------------------------- | | `profileId` | string | — | Scope results to a specific host profile. | | `searchQuery` | string | — | Full-text query matched against listing names and descriptions. | | `latlng` | string | — | Geo-center for proximity ranking. Format: `latitude,longitude` (e.g. `51.5074,-0.1278`). | | `radius` | string | — | Search radius in metres around `latlng`. | | `page` | string | `"1"` | Page number (1-based). | | `hitsPerPage` | string | `"10"` | Results per page. Maximum `20`. | | `userCategory` | string | — | Filter by user category (e.g. `couple`, `family`, `solo`). | | `tripType` | string | — | Filter by trip type (e.g. `leisure`, `business`). | | `userInterests` | string | — | Comma-separated interest tags used for personalised ranking. | | `activityTags` | string | — | Comma-separated activity tags to filter results (e.g. `outdoor,family-friendly`). | | `lang` | string | — | BCP 47 language code for localised response fields (e.g. `en`, `fr`, `de`). | | `rankBy` | string | `score` | Ranking strategy: `score` (relevance) or `distance` (nearest first, requires `latlng`). | ## Examples ### Basic search for an organisation ```bash Basic search theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/listing-search?key=YOUR_API_KEY" ``` ### Geo-filtered search near Paris ```bash Geo-filtered, near Paris theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/listing-search?key=YOUR_API_KEY&latlng=48.8566,2.3522&radius=2000&hitsPerPage=10" ``` ### Filter by activity tags ```bash Filter by activity tags theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/listing-search?key=YOUR_API_KEY&activityTags=outdoor,family-friendly" ``` ### Personalised search with a specific language ```bash Personalised search theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/listing-search?key=YOUR_API_KEY&userCategory=couple&tripType=leisure&lang=en-GB" ``` ### Ranked by distance ```bash Ranked by distance theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/listing-search?key=YOUR_API_KEY&latlng=51.5074,-0.1278&rankBy=distance" ``` ## Response ```json theme={null} { "results": [ { "id": "listing_abc123", "contentType": "listing", "name": "Padella", "summary": "Acclaimed pasta restaurant in Borough Market.", "tagline": "Hand-rolled pasta made fresh daily.", "address": "6 Southwark St, London SE1 1TQ", "neighborhood": "Borough Market", "category": "restaurant", "activityTags": ["food", "casual-dining"], "geoLocation": [51.5051, -0.0906], "geoDistance": 850.2, "googleRating": 4.6, "googleMapsUrl": "https://maps.google.com/?q=Padella", "priceLevel": 2, "priceLevelText": "££", "openingHours": { "openNow": true, "periods": [ { "open": { "day": 1, "time": "1200" }, "close": { "day": 1, "time": "2145" } } ] }, "photos": [ { "id": "photo_xyz", "category": "food", "attributions": ["© Padella"], "keywords": [], "images": [ { "size": "small", "url": "https://cdn.obvlo.com/photos/small/photo_xyz.jpg" }, { "size": "large", "url": "https://cdn.obvlo.com/photos/large/photo_xyz.jpg" } ] } ], "languageCodes": ["en-GB", "fr-FR"], "featured": false, "guideIds": ["guide_abc123"], "hostIds": ["host_xyz"], "score": 0.92, "searchId": "listing_abc123", "status": "active" } ], "total": 128, "pages": 13 } ``` ```json theme={null} { "results": [ { "id": "listing_abc123", "contentType": "listing", "name": { "en-GB": "Padella", "fr-FR": "Padella" }, "summary": { "en-GB": "Acclaimed pasta restaurant in Borough Market.", "fr-FR": "Restaurant de p\u00e2tes reconnu au Borough Market." }, "tagline": { "en-GB": "Hand-rolled pasta made fresh daily.", "fr-FR": "P\u00e2tes faites \u00e0 la main chaque jour." }, "address": { "en-GB": "6 Southwark St, London SE1 1TQ", "fr-FR": "6 Southwark St, London SE1 1TQ" }, "neighborhood": "Borough Market", "category": { "en-GB": "restaurant", "fr-FR": "restaurant" }, "activityTags": { "en-GB": ["food", "casual-dining"], "fr-FR": ["food", "casual-dining"] }, "geoLocation": [51.5051, -0.0906], "geoDistance": 850.2, "googleRating": 4.6, "priceLevel": 2, "priceLevelText": { "en-GB": "\u00a3\u00a3", "fr-FR": "\u00a3\u00a3" }, "photos": [], "languageCodes": ["en-GB", "fr-FR"], "featured": false, "guideIds": ["guide_abc123"], "hostIds": ["host_xyz"], "score": 0.92, "searchId": "listing_abc123", "status": "active" } ], "total": 128, "pages": 13 } ``` ### Response fields | Field | Type | Description | | --------- | ------- | ----------------------------------------------------- | | `results` | array | Array of listing objects (see below). | | `total` | integer | Total number of listings matching the query. | | `pages` | integer | Total number of pages at the requested `hitsPerPage`. | #### Listing object | Field | Type | Description | | ---------------- | ---------------- | -------------------------------------------------------------------------------------- | | `id` | string | Unique listing identifier. | | `contentType` | string | Always `"listing"`. | | `name` | string \| object | Listing name. Plain string when `lang` is provided; language map otherwise. | | `summary` | string \| object | Short description. Plain string when `lang` is provided; language map otherwise. | | `tagline` | string \| object | One-line tagline. Plain string when `lang` is provided; language map otherwise. | | `address` | string \| object | Street address. Plain string when `lang` is provided; language map otherwise. | | `neighborhood` | string | Neighbourhood or area name (not localised). | | `category` | string \| object | Primary category. Plain string when `lang` is provided; language map otherwise. | | `activityTags` | array \| object | Activity tags. Array when `lang` is provided; language map of arrays otherwise. | | `geoLocation` | array | Coordinates as `[latitude, longitude]`. | | `geoDistance` | number | Distance in metres from the requested `latlng` (when provided). | | `googleRating` | number | Google Places rating (0–5). | | `googleMapsUrl` | string | Link to Google Maps. | | `phoneNumber` | string | Contact phone number. | | `websiteUrl` | string | Official website URL. | | `priceLevel` | integer | Numeric price level (1–4). | | `priceLevelText` | string \| object | Price label (e.g. `££`). Plain string when `lang` is provided; language map otherwise. | | `openingHours` | object | Opening hours data. See [Opening hours object](#opening-hours-object). | | `photos` | array | Array of photo objects. See [Image object](#image-object). | | `languageCodes` | array | Language codes for which content is available. | | `score` | number | Relevance score (0–1) used for ranking. | | `status` | string | Listing status (e.g. `active`). | #### Opening hours object | Field | Type | Description | | --------------- | ------- | ---------------------------------------------- | | `openNow` | boolean | Whether the listing is currently open. | | `nextCloseTime` | string | ISO 8601 datetime of next closing time. | | `isUnavailable` | boolean | `true` if opening hours data is not available. | | `periods` | array | Array of `{ open, close }` period objects. | Each period has `open` and `close` objects with: | Field | Type | Description | | ------ | ------- | ---------------------------------------------- | | `day` | integer | Day of week (0 = Sunday, 6 = Saturday). | | `time` | string | 24h time string without colon (e.g. `"0900"`). | #### Image object | Field | Type | Description | | -------------- | ------ | ----------------------------------------- | | `id` | string | Unique image identifier. | | `category` | string | Image category tag. | | `attributions` | array | Attribution strings for the image. | | `images` | array | Array of `{ size, url }` variant objects. | # Location Search Source: https://docs.obvlo.com/api-reference/endpoint/location-search GET /v2/orgs/{orgId}/location-search Search geographic locations such as neighbourhoods, districts, and areas of interest by proximity and keyword. ## Overview The `/v2/location-search` endpoint returns a paginated list of geographic locations from the Obvlo content engine. Locations represent areas of interest such as neighbourhoods, districts, or city zones. You can filter by proximity and search by keyword. ### Localised fields and the `lang` parameter The fields `displayName`, `headline`, `description`, `address`, `area`, and `keywords` carry translated content. The shape of these fields depends on whether `lang` is provided: | `lang` | Field shape | Example | | ---------------------------- | --------------------------------------------------- | ------------------------------------------ | | Provided (e.g. `lang=en-GB`) | Plain value for the requested language. | `"displayName": "Shoreditch"` | | Omitted | Language map — one key per available language code. | `"displayName": { "en-GB": "Shoreditch" }` | When `lang` is omitted, at least the default language (`en-GB`) is always included in the map. ## Request ```http theme={null} GET /v2/orgs/{orgId}/location-search ``` ### Required parameters | Parameter | Type | Description | | --------- | ------ | --------------------- | | `orgId` | string | Your organisation ID. | ### Optional parameters | Parameter | Type | Default | Description | | ------------- | ------- | --------------- | --------------------------------------------------------------------------------------------------------------------------- | | `profileId` | string | — | Scope results to a specific host profile. | | `searchQuery` | string | — | Full-text query matched against location names and descriptions. | | `latlng` | string | — | Geo-center for proximity filtering. Format: `latitude,longitude` (e.g. `51.5074,-0.1278`). | | `radius` | number | Profile default | Search radius in metres around `latlng`. | | `page` | integer | `1` | Page number (1-based). | | `hitsPerPage` | integer | `10` | Results per page. Maximum `20`. | | `rankBy` | string | `distance` | Ranking strategy: `distance` (nearest first, requires `latlng`) or `score` (relevance). | | `lang` | string | — | BCP 47 language code (e.g. `en-GB`). When provided, localised fields are returned as plain values instead of language maps. | ## Examples ### Basic search for an organisation ```bash Basic search theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/location-search?key=YOUR_API_KEY" ``` ### Geo-filtered search near London ```bash Geo-filtered, near London theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/location-search?key=YOUR_API_KEY&latlng=51.5074,-0.1278&radius=10000&hitsPerPage=5" ``` ### Text search anchored to a host profile ```bash Text search theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/location-search?key=YOUR_API_KEY&profileId=YOUR_PROFILE_ID&searchQuery=shoreditch" ``` ### Sort by relevance instead of distance ```bash Sort by relevance theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/location-search?key=YOUR_API_KEY&profileId=YOUR_PROFILE_ID&rankBy=score" ``` ### Search with a specific language ```bash With lang parameter theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/location-search?key=YOUR_API_KEY&lang=en-GB" ``` ## Response ```json theme={null} { "results": [ { "id": "location_abc123", "hostIds": ["host_xyz"], "status": "published", "displayName": "Shoreditch", "headline": "London's creative quarter", "description": "A vibrant neighbourhood in east London known for its street art and independent restaurants.", "address": "Shoreditch, London, UK", "area": "East London", "keywords": ["street art", "bars", "restaurants", "nightlife"], "geoLocation": [51.5246, -0.0771], "geoDistance": 850.3, "radius": 1200, "languageCodes": ["en-GB", "fr-FR"], "photos": [ { "id": "photo_1", "category": "exterior", "attributions": [], "keywords": [], "images": [ { "size": "lg", "url": "https://cdn.obvlo.com/photos/photo_1_lg.jpg" } ] } ] } ], "total": 12, "pages": 2 } ``` ```json theme={null} { "results": [ { "id": "location_abc123", "hostIds": ["host_xyz"], "status": "published", "displayName": { "en-GB": "Shoreditch", "fr-FR": "Shoreditch" }, "headline": { "en-GB": "London's creative quarter", "fr-FR": "Le quartier cr\u00e9atif de Londres" }, "description": { "en-GB": "A vibrant neighbourhood in east London...", "fr-FR": "Un quartier dynamique dans l'est de Londres..." }, "address": { "en-GB": "Shoreditch, London, UK", "fr-FR": "Shoreditch, Londres, Royaume-Uni" }, "area": { "en-GB": "East London", "fr-FR": "Est de Londres" }, "keywords": { "en-GB": ["street art", "bars", "restaurants", "nightlife"], "fr-FR": ["art de rue", "bars", "restaurants"] }, "geoLocation": [51.5246, -0.0771], "geoDistance": 850.3, "radius": 1200, "languageCodes": ["en-GB", "fr-FR"], "photos": [] } ], "total": 12, "pages": 2 } ``` ### Response fields | Field | Type | Description | | --------- | ------- | ----------------------------------- | | `results` | array | Array of matching location objects. | | `total` | integer | Total number of matching locations. | | `pages` | integer | Total number of pages. | ### Location object fields | Field | Type | Description | | --------------- | ----------------- | -------------------------------------------------------------------------------------------- | | `id` | string | Unique location identifier. | | `hostIds` | string\[] | Host profiles this location belongs to. | | `status` | string | Publication status (always `published` in API responses). | | `displayName` | string \| object | Location name. Plain string when `lang` is provided; language map otherwise. | | `headline` | string \| object | Short headline. Plain string when `lang` is provided; language map otherwise. | | `description` | string \| object | Description. Plain string when `lang` is provided; language map otherwise. | | `address` | string \| object | Address. Plain string when `lang` is provided; language map otherwise. | | `area` | string \| object | Area or district name. Plain string when `lang` is provided; language map otherwise. | | `keywords` | array \| object | Keywords. Array when `lang` is provided; language map of arrays otherwise. | | `geoLocation` | \[number, number] | Coordinates as `[latitude, longitude]`. | | `geoDistance` | number | Distance in metres from the `latlng` centre (present when `latlng` is provided). | | `radius` | number | Radius of the location area in metres. | | `languageCodes` | string\[] | Language codes available for this location. | | `photos` | array | Array of photo objects. Each has `id`, `category`, `attributions`, `keywords`, and `images`. | # Get Organisation Languages Source: https://docs.obvlo.com/api-reference/endpoint/org-languages GET /v2/orgs/{orgId}/org-languages Retrieve the list of languages supported by an organisation, including full locale metadata for each language code. ## Overview The `/v2/org-languages` endpoint returns the full language configuration for an organisation. Use it to discover which languages are available before making localised content requests (e.g. passing `lang` to guide-search or listing-search). When `translationEnabled` is `false`, only the default language is available and the `languages` array will be empty. ## Request ```http theme={null} GET /v2/orgs/{orgId}/org-languages ``` ### Required parameters | Parameter | Type | Description | | --------- | ------ | --------------------- | | `orgId` | string | Your organisation ID. | ### Optional parameters | Parameter | Type | Description | | --------- | ------ | ---------------------------------------------------------------------------- | | `hostId` | string | Destination ID. Narrows the response to that destination's target languages. | ## Destination-scoped languages An organisation can configure target languages **per destination** rather than one list for the whole organisation — see [Translations](/product/management/translations). Pass `hostId` when your integration operates in the context of a single destination, so a language switcher offers only the languages that destination is actually translated into. | Request | Response | | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | | No `hostId` | The organisation-wide language list — the union of every in-scope destination's languages, plus the default language. | | `hostId` of a destination with its own target languages | That destination's languages, plus the default language. | | `hostId` of a destination with no per-destination languages configured | The organisation-wide list. | | `hostId` that is unknown or outside the organisation's translation scope | The organisation-wide list. | An unrecognised `hostId` falls back to the organisation-wide list rather than returning an empty one. An empty list is indistinguishable from a mistyped parameter, and would render a language switcher with nothing in it. Omitting `hostId` is always safe: existing integrations keep receiving the organisation-wide list exactly as before. ## Examples ### Retrieve supported languages ```bash Retrieve supported languages theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/org-languages?key=YOUR_API_KEY" ``` ### Retrieve languages for one destination ```bash Retrieve languages for one destination theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/org-languages?hostId=YOUR_DESTINATION_ID&key=YOUR_API_KEY" ``` ### Example response — translations enabled ```json Example response (translations enabled) expandable theme={null} { "translationEnabled": true, "defaultLanguage": { "languageCode": "en-GB", "languageName": "English", "dialect": "British", "nativeName": "English", "isDefault": true, "isEnabled": true, "flagIconUrl": "https://cdn.obvlo.com/flags/gb.svg", "rtl": false, "currencyCode": "GBP", "currencySymbol": "£", "hour12": true, "measurement": "metric", "temperatureMetric": "celsius", "isLatinAlphabet": true }, "languages": [ { "languageCode": "en-GB", "languageName": "English", "dialect": "British", "nativeName": "English", "isDefault": true, "isEnabled": true, "flagIconUrl": "https://cdn.obvlo.com/flags/gb.svg", "rtl": false, "currencyCode": "GBP", "currencySymbol": "£", "hour12": true, "measurement": "metric", "temperatureMetric": "celsius", "isLatinAlphabet": true }, { "languageCode": "fr-FR", "languageName": "French", "dialect": "Standard", "nativeName": "Français", "isDefault": false, "isEnabled": true, "flagIconUrl": "https://cdn.obvlo.com/flags/fr.svg", "rtl": false, "currencyCode": "EUR", "currencySymbol": "€", "hour12": false, "measurement": "metric", "temperatureMetric": "celsius", "isLatinAlphabet": true } ] } ``` ### Example response — translations disabled ```json Example response (translations disabled) theme={null} { "translationEnabled": false, "defaultLanguage": { "languageCode": "en-GB", "languageName": "English", "dialect": "British", "nativeName": "English", "isDefault": true, "isEnabled": true, "flagIconUrl": "https://cdn.obvlo.com/flags/gb.svg", "rtl": false, "currencyCode": "GBP", "currencySymbol": "£", "hour12": true, "measurement": "metric", "temperatureMetric": "celsius", "isLatinAlphabet": true }, "languages": [] } ``` ## Response fields ### Top-level | Field | Type | Description | | -------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `translationEnabled` | boolean | Whether this organisation has multilingual content enabled. | | `defaultLanguage` | object | Full locale metadata for the organisation's default language. Always present. | | `languages` | array | Full locale metadata for each enabled language. Narrowed to one destination's target languages when `hostId` is supplied. Empty when `translationEnabled` is `false`. | ### Language object | Field | Type | Description | | ------------------- | ------- | --------------------------------------------------------------- | | `languageCode` | string | BCP 47 language code (e.g. `en-GB`, `fr-FR`). | | `languageName` | string | English name of the language. | | `dialect` | string | Regional dialect label (e.g. `British`, `Standard`). | | `nativeName` | string | Language name in its own script (e.g. `Français`). | | `isDefault` | boolean | `true` for the organisation's default language. | | `isEnabled` | boolean | Whether this language is enabled in the platform. | | `flagIconUrl` | string | URL to a flag icon representing the language's region. | | `rtl` | boolean | `true` if the language is right-to-left (e.g. Arabic, Hebrew). | | `currencyCode` | string | ISO 4217 currency code associated with the locale (e.g. `GBP`). | | `currencySymbol` | string | Display symbol for the currency (e.g. `£`). | | `hour12` | boolean | `true` if the locale uses a 12-hour clock. | | `measurement` | string | Unit system: `metric` or `imperial`. | | `temperatureMetric` | string | `celsius` or `fahrenheit`. | | `isLatinAlphabet` | boolean | `true` if the language uses the Latin alphabet. | # Get Site Page Slugs Source: https://docs.obvlo.com/api-reference/endpoint/site-page-slug GET /v2/orgs/{orgId}/sites/{siteId}/page-slug Resolve persisted, stable page URL slugs for a site using page hierarchy data. ## Overview The `/v2/orgs/{orgId}/sites/{siteId}/page-slug` endpoint resolves complete page URL paths from persisted site page records. Use this endpoint when you need stable links for destination, guide, and area pages without reconstructing URLs from mutable names. The API uses persisted page `slug` values and `parentId` relationships. Returned `slug` and `segments` values are microsite-relative. The resolver strips the site-root prefix (including reverse-proxy roots like `/local-guides`) so responses are not coupled to infrastructure paths. ## Request ```http theme={null} GET /v2/orgs/{orgId}/sites/{siteId}/page-slug ``` ### Required parameters | Parameter | Type | Description | | --------- | ------ | -------------------------------------- | | `orgId` | string | Your organisation ID. | | `siteId` | string | The site ID to resolve page slugs for. | ### Optional filters | Parameter | Type | Description | | --------------- | ------ | ------------------------------------------------- | | `destinationId` | string | Limit results to pages linked to a destination. | | `guideId` | string | Limit results to pages linked to a guide. | | `locationId` | string | Limit results to pages linked to a location/area. | When no filters are provided, the endpoint returns a full tree of site pages. ## Examples ### Get full site slug tree ```bash theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/sites/YOUR_SITE_ID/page-slug?key=YOUR_API_KEY" ``` ### Get matches for a specific guide ```bash theme={null} curl "https://api.obvlo.com/v2/orgs/YOUR_ORG_ID/sites/YOUR_SITE_ID/page-slug?key=YOUR_API_KEY&destinationId=DESTINATION_ID&guideId=GUIDE_ID" ``` ### Example tree response ```json theme={null} { "siteId": "site_123", "mode": "tree", "tree": [ { "pageId": "root_page", "pageType": "Root", "slug": "/london/", "homepageSlug": "/", "segments": ["london"], "parentIds": [], "destinationId": "destination_123", "guideId": null, "locationId": null, "children": [ { "pageId": "guide_page_1", "pageType": "Guide", "slug": "/london/food-guide/", "homepageSlug": null, "segments": ["london", "food-guide"], "parentIds": ["root_page"], "destinationId": "destination_123", "guideId": "guide_789", "locationId": null, "children": [] } ] } ] } ``` ### Example filtered response ```json theme={null} { "siteId": "site_123", "mode": "matches", "matches": [ { "pageId": "guide_page_1", "pageType": "Guide", "slug": "/london/food-guide/", "homepageSlug": null, "segments": ["london", "food-guide"], "parentIds": ["root_page"], "destinationId": "destination_123", "guideId": "guide_789", "locationId": null } ] } ``` ## Response notes * `mode` is `tree` when no filters are supplied. * `mode` is `matches` when one or more filters are supplied. * `slug` always includes a leading and trailing slash. * `slug` and `segments` are returned relative to the microsite root, not the reverse-proxy prefix. * Root pages include `homepageSlug: "/"`. * The endpoint returns all matches. It does not pick a single page when multiple pages match. # API Reference Source: https://docs.obvlo.com/api-reference/introduction Obvlo REST API for search, discovery, host bootstrap, and language configuration. The Obvlo API reference covers the stable content endpoints. ## Base URL ```text Base URL theme={null} https://api.obvlo.com ``` ## Authentication All requests require a valid `key` query parameter. Public endpoints use `orgId` as a path parameter in the URL, for example `/v2/orgs/{orgId}/guide-search`. Some endpoints also accept a `profileId` query parameter to scope results to a specific host profile. Contact [support@obvlo.com](mailto:support@obvlo.com) to obtain your credentials. ## Endpoints `GET /v2/orgs/{orgId}/guide-search` — Search curated travel guides. `GET /v2/orgs/{orgId}/listing-search` — Search individual listings. `GET /v2/orgs/{orgId}/location-search` — Search neighbourhoods and areas. `GET /v2/orgs/{orgId}/host-profile` — Retrieve branding and configuration for a host property. `GET /v2/orgs/{orgId}/host-home` — Bootstrap a host home screen in a single request. `GET /v2/orgs/{orgId}/org-languages` — Retrieve supported languages for an organisation. ## Pagination All search endpoints return paginated results. Use `page` (1-based) and `hitsPerPage` (max 20) to navigate pages. The response always includes `total` (total matching records) and `pages` (total page count). ## Localisation Search and discovery endpoints can return localised fields in two shapes. When `lang` is omitted, localised fields are returned as objects keyed by BCP 47 language code, for example `{ "en-GB": "Top Restaurants" }`. When `lang` is provided, the same fields are returned as resolved values for that language. This applies to guide, listing, location, and host-home responses. Preview endpoints are documented separately under the `Preview` section. # Get v2orgs guide search Source: https://docs.obvlo.com/api-reference/v2-multilingual/get-v2orgs-guide-search /api-reference/openapi-stable.json get /v2/orgs/{orgId}/guide-search Search for guides matching criteria. Returns matching guides with optional nested listings. # Get v2orgs host home Source: https://docs.obvlo.com/api-reference/v2-multilingual/get-v2orgs-host-home /api-reference/openapi-stable.json get /v2/orgs/{orgId}/host-home Single-call bootstrap for a host home screen. Returns the host profile alongside the first page of guides, each with nested listings. Preference filters (userCategory, tripType, userInterests) are applied to both guide and listing ranking. # Get v2orgs host profile Source: https://docs.obvlo.com/api-reference/v2-multilingual/get-v2orgs-host-profile /api-reference/openapi-stable.json get /v2/orgs/{orgId}/host-profile Retrieve a host profile by ID. Returns the public-safe profile fields; internal fields such as API keys and analytics configuration are excluded. # Get v2orgs listing search Source: https://docs.obvlo.com/api-reference/v2-multilingual/get-v2orgs-listing-search /api-reference/openapi-stable.json get /v2/orgs/{orgId}/listing-search Search for listings matching criteria. Returns matching listings with localized content. # Get v2orgs location search Source: https://docs.obvlo.com/api-reference/v2-multilingual/get-v2orgs-location-search /api-reference/openapi-stable.json get /v2/orgs/{orgId}/location-search Search for locations matching criteria. Returns matching locations (neighbourhoods, districts, areas) with localized content. # Get v2orgs org languages Source: https://docs.obvlo.com/api-reference/v2-multilingual/get-v2orgs-org-languages /api-reference/openapi-stable.json get /v2/orgs/{orgId}/org-languages Returns the list of languages supported by the organization. When translationEnabled is false, only the default language is returned. Pass hostId to narrow the list to a single destination when the organization configures target languages per destination; an unknown destination returns the full organization list. # Get v2orgs sites page slug Source: https://docs.obvlo.com/api-reference/v2-site-data/get-v2orgs-sites-page-slug /api-reference/openapi-stable.json get /v2/orgs/{orgId}/sites/{siteId}/page-slug Resolve complete persisted page slugs for a site. Without filters, returns a full tree. With destinationId, guideId, or locationId filters, returns matching page slug entries. # Authentication Source: https://docs.obvlo.com/developer-docs/api/authentication How to authenticate requests to the Obvlo API. ## Authentication Obvlo API requests are authenticated using the `key` query parameter. ## Example ```http theme={null} GET /v2/orgs/{orgId}/guide-search?key=YOUR_API_KEY ``` ## Related pages * [Developers overview](/developer-docs/overview) * [API overview](/developer-docs/api/overview) * [API Reference](/api-reference/introduction) # API overview Source: https://docs.obvlo.com/developer-docs/api/overview When to use the Obvlo API and where to start. ## API overview Use the API when you need direct programmatic access to Obvlo content for custom applications and integrations. ## Overview Set up secure access and token handling. Follow the implementation path from first call to production launch. Review common integration patterns for CMS, PMS, CRM/CDP, and chatbots. Review endpoint details, payloads, and responses. ## Start here * Developers overview: [/developer-docs/overview](/developer-docs/overview) * Authentication: [/developer-docs/api/authentication](/developer-docs/api/authentication) * API onboarding guide: [/onboarding-guides/api-onboarding-guide](/onboarding-guides/api-onboarding-guide) * Partner integrations: [/product/delivery/partner-integrations](/product/delivery/partner-integrations) * Endpoint details: [API Reference](/api-reference/introduction) # Zero-downtime Cloudflare migration Source: https://docs.obvlo.com/developer-docs/cloudflare-migration A professional, industry-standard workflow for migrating a live production domain from any DNS provider to Cloudflare without disrupting traffic, email, or app subdomains. This guide is provided for informational purposes only and is intended for qualified IT professionals with a thorough understanding of DNS, SSL/TLS, and domain management. Obvlo accepts no responsibility for any downtime, data loss, or service disruption that may result from following these steps. Always test in a non-production environment first and ensure you have a verified rollback plan. This document outlines the professional, industry-standard workflow for migrating a live production domain from any DNS provider to Cloudflare. Following this "Staging-to-Proxy" approach ensures that website traffic, email services, and app subdomains remain online throughout the entire transition. ## Prerequisites and verification tools Before starting, ensure you have administrative access to both your **Domain Registrar** (where you pay for the domain) and your **current DNS Host**. | Tool | Purpose | | ---------------------------------------------------------------------- | --------------------------------------------------------------------- | | [DNSViz.net](https://dnsviz.net) | Verifying DNSSEC status | | [WhatsMyDNS.net](https://whatsmydns.net) | Real-time monitoring of global nameserver propagation | | [Google Dig (Admin Toolbox)](https://toolbox.googleapps.com/apps/dig/) | Verifying individual record resolution | | AI Assistant | Sanitizing and reformatting non-standard DNS exports into BIND format | *** ## Phase 1: Pre-migration security (24–48 hours prior) The most common cause of migration failure is **DNSSEC**. If nameservers are changed while the Top-Level Domain (TLD) registry still expects cryptographic signatures from the old provider, the domain will go offline globally. ### Checklist Check your current registrar dashboard for DNSSEC/DS records. Toggle DNSSEC to **OFF** in your registrar's security settings. Enter the domain at [DNSViz.net](https://dnsviz.net). The graph must show an "Insecure" status (blue/gray boxes) with an NSEC3 proof from the registry. Do not proceed if any "Secure" (green) or "Broken" (red) paths remain. Set all critical `A`, `CNAME`, and `MX` records to **300 seconds** (5 minutes). This ensures that if a rollback is required, the change propagates in minutes rather than hours. *** ## Phase 2: The deep data audit (AI-assisted) Cloudflare's automatic scan frequently misses complex TXT records (DKIM, SPF), deeply nested subdomains, or service-specific verifications. A **manual import** is mandatory for production environments. ### Checklist Export your current DNS records as a `.csv`, `.txt`, or `.bind` file from your existing provider. Upload the raw export to an AI Assistant with the following prompt to ensure 100% compatibility: ```text theme={null} Convert these DNS records into a standard BIND zone file for Cloudflare import. Use the format: [Name] [TTL] [Class] [Type] [Content]. Wrap all TXT values in double quotes. For Webflow/Shopify/HubSpot CNAMEs, ensure trailing dots are included (e.g., cdn.webflow.com.). Convert Root A-records to CNAMEs for '@' to enable Cloudflare CNAME Flattening. ``` Manually cross-check that all Google Workspace (or other provider) MX records are present in the sanitized file. In Cloudflare **DNS > Records > Import**, upload the sanitized file. Ensure every record is set to **DNS Only** (Grey Cloud). This allows you to move DNS management without changing the underlying traffic routing or SSL handshake logic yet. *** ## Phase 3: The nameserver handover In this phase, you officially transfer "steering" control to Cloudflare. ### Checklist Navigate to Cloudflare **SSL/TLS > Overview**. Set the mode to **Full (Strict)**. Production origins (like Webflow) already have SSL. "Full (Strict)" ensures an end-to-end encrypted tunnel. In your Registrar (Squarespace/GoDaddy/etc.), replace the current nameservers with the two provided by Cloudflare. Open [WhatsMyDNS.net](https://whatsmydns.net) and check the `NS` record. You should see a majority of global nodes returning the Cloudflare nameservers. In Cloudflare **SSL/TLS > Edge Certificates**, wait until the status for your domain is **Active**. Do not enable proxying until this certificate is issued. *** ## Phase 4: Activation and proxying Now that Cloudflare owns the DNS and has a valid SSL certificate ready, you can activate the Web Application Firewall (WAF) and Workers. For detailed reverse proxy configuration (Cloudflare Workers, NGINX, Apache, IIS, and Caddy), see the [Reverse proxy guide](/microsite/reverse-proxy). ### Checklist Toggle the main `@` and `www` records to **Proxied**. Load the website and inspect the **Network** tab. Response headers must contain `server: cloudflare` and a `cf-ray` ID. Bind your Workers to specific routes (e.g. `example.com/blog/*`). Verify that subdirectories (like `/blog`) are rendering correctly and that canonical tags point to the proxied URL. *** ## Phase 5: Post-migration optimization Perform a **Purge Everything** in Cloudflare to ensure Worker logic is applied to all cached assets. After 24 hours of stability, enable DNSSEC in Cloudflare. Copy the new DS Records from Cloudflare and paste them back into your Registrar's security settings to re-establish the Chain of Trust. *** ## Incident response and rollback procedures ### Tier 1: Record-level rollback If a specific subdomain breaks, toggle that specific record back to **Grey Cloud** (DNS Only). This instantly bypasses Cloudflare for that route. ### Tier 2: The "kill switch" If the entire site behaves unexpectedly, go to the Cloudflare **Overview** page and select **Pause Cloudflare on Site**. This maintains your DNS nameservers but stops all proxying, WAF, and Worker logic. ### Tier 3: Nameserver reversal If DNS resolution fails entirely, revert the nameservers at your registrar back to the original provider's settings. This is a "hard" rollback and may take several hours to propagate. # Overview Source: https://docs.obvlo.com/developer-docs/overview Where technical teams should start when implementing Obvlo. Use this section when your engineering team is implementing Obvlo. It covers API integration, Embeddable Widget setup, Microsite implementation, and language support. ## Where do you want to start? Authentication, onboarding, and endpoint reference for direct integrations. Add the Embeddable Widget to your site and configure the core setup. Serve Microsite content on your domain and configure reverse proxy setup. View supported language codes and locale coverage for implementation. # Configuration reference Source: https://docs.obvlo.com/embed-widget/configuration-reference Complete reference for all Obvlo Embed Widget attributes and configuration options. This page documents every attribute and configuration option available on the `` custom element. For a step-by-step setup guide, see the [Quick Start](/embed-widget/quick-start). Use the [interactive test harness](https://cdn.obvlo.com/widgets/embed-test/index.html) to experiment with these options live — it generates the HTML tag for you in real time. *** ## HTML attributes These are set directly on the `` element. ### `host-id` Required **Type:** `string` The unique identifier for your host profile. Links the widget to your content in the Obvlo system. ```html theme={null} ``` In the Obvlo portal, host profiles are listed as **Destinations**. You can find the value for `host-id` in the Destinations table under the **Destination ID** column. *** ### `config-id` **Type:** `string` Select a specific embedded configuration from your host profile. When omitted, the first configuration in the profile is used. ```html theme={null} ``` *** ### `guide-id` **Type:** `string` Display a specific guide when the widget loads. Users can still navigate to other content. ```html theme={null} ``` *** ### `neighbourhood-id` **Type:** `string` Display a specific neighbourhood/geographic area when the widget loads. Only one of `guide-id` or `neighbourhood-id` should be used at a time. If both are provided, `guide-id` takes precedence. *** ### `guide-type` **Type:** `string` Filter the menu to show only guides of a specific type. | Value | Description | | --------------- | ------------------------- | | `guide` | Standard guides only | | `neighbourhood` | Neighbourhood guides only | | `featured` | Featured guides only | ```html theme={null} ``` *** ### `grid-rows` **Type:** `number` — **Default:** `1` Number of rows to display in grid views on desktop and tablet devices. ### `grid-rows-mobile` **Type:** `number` — **Default:** `3` Number of rows to display in grid views on mobile devices. ```html theme={null} ``` *** ### `lang` **Type:** `string` Sets the display language for the widget. Must be a BCP 47 language tag supported by your host profile. | Code | Language | | ------- | ------------------------ | | `en-GB` | English (UK) — *default* | | `en-US` | English (US) | | `es-ES` | Spanish (Spain) | | `fr-FR` | French (France) | | `de-DE` | German (Germany) | | `it-IT` | Italian (Italy) | | `pt-PT` | Portuguese (Portugal) | | `pt-BR` | Portuguese (Brazil) | | `zh-CN` | Chinese (Simplified) | | `ja-JP` | Japanese | | `ar-SA` | Arabic (Saudi Arabia) | See the [Language reference](/reference/language-reference) for all 63 supported languages. *** ### `uid` **Type:** `string` Custom user identifier for analytics tracking. If not provided, a random UUID is generated and persisted in browser storage. ```html theme={null} ``` *** ### `drawer` **Type:** `boolean` — **Default:** `false` Controls whether the widget appears as an overlay drawer or renders inline. This is a boolean attribute — its presence enables drawer mode. ```html theme={null} ``` ### `drawer-delay-ms` **Type:** `number` — **Default:** `0` Delay in milliseconds before automatically opening the drawer. Only applies when `drawer` is present. ```html theme={null} ``` *** ### `drawer-label` **Type:** `string` — **Default:** translated "Plan your trip" label Overrides the translated label shown on the drawer trigger button. Only applies when `drawer` is present. When omitted, the trigger falls back to the localized `drawer.plan_trip_label` translation. ```html theme={null} ``` *** ### `disable-just-for-you` **Type:** `boolean` — **Default:** `false` Hides the "Just For You" personalised guide from the guide menu. Content personalisation remains fully functional — only the menu entry is hidden. ### `hide-travel` **Type:** `boolean` — **Default:** `false` Hides travel-related content categories from the widget. *** ### `geo-location` **Type:** `string` (JSON object) — **Default:** none A JSON object containing `lat` and `lng` properties to set the user's geographic location. Used to scope content and enable proximity-based features. ```html theme={null} ``` Use single quotes around the `geo-location` value since it contains JSON with double quotes. ### `geo-search-radius` **Type:** `number` — **Default:** `10000` Search radius in metres when `geo-location` is set. Controls how far from the provided location the widget will search for content. ```html theme={null} ``` *** ### `config` **Type:** `string` (JSON object) An inline JSON string containing an `EmbeddedConfig` object. Use this attribute to pass advanced configuration options directly on the element without relying on a server-side embedded config. When using `config` in HTML, double quotes inside the JSON must be escaped as `"`. Most frameworks (React, Vue, Angular) handle this automatically when binding to an attribute. ```html theme={null} ``` See the full [EmbeddedConfig object](#embeddedconfig-object) reference below. *** ## EmbeddedConfig object The `EmbeddedConfig` object can be passed via the [`config`](#config) HTML attribute or defined in your host profile's embedded configs (managed through the Obvlo portal). All properties are optional — only include the ones you need. ```json theme={null} { "modules": ["Menu", "Grid"], "kioskMode": false, "requirePreferences": false, "disableAnimations": false, "disableSavedListings": false, "hideGuideHeader": false, "hideLanguageSelector": false, "preferredTravelMode": "WALKING", "cornerRadius": { "card": { "topLeft": 12, "topRight": 12, "bottomLeft": 12, "bottomRight": 12 }, "button": { "topLeft": 8, "topRight": 8, "bottomLeft": 8, "bottomRight": 8 } }, "containerOptions": { "maxWidth": 1240, "padding": { "top": 16, "bottom": 16, "left": 16, "right": 16 }, "map": { "width": "100%", "height": "500px" } }, "customIconUrl": "https://example.com/icon.svg" } ``` *** ### `modules` **Type:** `string[]` An ordered array of module names that controls which UI sections the widget displays and in what order. When omitted, the widget uses the default module set from your host profile. | Module | Description | | ----------------- | ------------------------------------------ | | `Banner` | Hero banner image at the top of the widget | | `Grid` | Card grid layout for browsing listings | | `List` | Vertical list layout for listings | | `Map` | Interactive map showing listing locations | | `Menu` | Guide/area navigation menu | | `ListMap` | Combined list and map split-view | | `FunctionalPanel` | Functional actions panel (search, filters) | The order of modules in the array determines their display order in the widget. ```json theme={null} { "modules": ["Menu", "Grid", "Map"] } ``` *** ### `kioskMode` **Type:** `boolean` — **Default:** `false` Enables kiosk mode, optimised for unattended displays such as hotel lobby screens or tourist information points. Typically combined with `disableAnimations` and `disableSavedListings`. *** ### `requirePreferences` **Type:** `boolean` — **Default:** `false` When enabled, the widget prompts users to set their personalisation preferences before showing content. Useful for experiences where personalised results are the primary goal. *** ### `disableAnimations` **Type:** `boolean` — **Default:** `false` Disables all transition animations in the widget. Useful for kiosk displays or accessibility requirements. *** ### `disableSavedListings` **Type:** `boolean` — **Default:** `false` Hides the saved listings feature, removing save buttons from listing cards and the saved list from the menu. *** ### `hideGuideHeader` **Type:** `boolean` — **Default:** `false` Hides the guide header section when a guide is displayed. Useful when embedding in a context that already provides its own header or title. *** ### `hideLanguageSelector` **Type:** `boolean` — **Default:** `false` Hides the language selector dropdown from the widget. Use this when the widget language is controlled externally via the [`lang`](#lang) attribute. *** ### `preferredTravelMode` **Type:** `string` — **Default:** platform default Sets the preferred travel mode for directions and distance calculations. Values are uppercase — any other value fails config validation and the setting is ignored. | Value | Description | | ----------- | --------------------------------- | | `DRIVING` | Car/vehicle travel times | | `WALKING` | Walking distances and times | | `BICYCLING` | Cycling distances and times | | `TRANSIT` | Public transport routes and times | ```json theme={null} { "preferredTravelMode": "WALKING" } ``` *** ### `cornerRadius` **Type:** `object` Customise the border radius of UI elements to match your brand. Each element type accepts an object with `topLeft`, `topRight`, `bottomLeft`, and `bottomRight` values in pixels. | Property | Description | | -------- | ------------------------------- | | `card` | Border radius for content cards | | `button` | Border radius for buttons | ```json theme={null} { "cornerRadius": { "card": { "topLeft": 16, "topRight": 16, "bottomLeft": 16, "bottomRight": 16 }, "button": { "topLeft": 24, "topRight": 24, "bottomLeft": 24, "bottomRight": 24 } } } ``` For uniform corners, set all four values to the same number. Common presets: `0` (square), `8` (small), `12` (medium), `16` (large), `24` (extra-large). *** ### `containerOptions` **Type:** `object` Controls the widget container's layout and sizing. #### `containerOptions.maxWidth` **Type:** `number` (pixels) Maximum width of the widget container. The widget will not exceed this width even if the parent element is wider. #### `containerOptions.padding` **Type:** `object` Inner padding of the widget container in pixels. | Property | Type | Description | | -------- | -------- | ------------------------ | | `top` | `number` | Top padding in pixels | | `bottom` | `number` | Bottom padding in pixels | | `left` | `number` | Left padding in pixels | | `right` | `number` | Right padding in pixels | #### `containerOptions.map` **Type:** `object` Dimensions for the map module when displayed. | Property | Type | Default | Description | | -------- | -------- | -------- | ------------------------------------------- | | `width` | `string` | `"100%"` | CSS width value (e.g. `"100%"`, `"800px"`) | | `height` | `string` | `"auto"` | CSS height value (e.g. `"500px"`, `"60vh"`) | ```json theme={null} { "containerOptions": { "maxWidth": 1240, "padding": { "top": 20, "bottom": 20, "left": 16, "right": 16 }, "map": { "width": "100%", "height": "500px" } } } ``` *** ### `customIconUrl` **Type:** `string` (URL) URL to a custom icon image (SVG recommended) displayed in the widget. Use this to brand the widget with your own logo or icon. ```json theme={null} { "customIconUrl": "https://example.com/my-icon.svg" } ``` *** ## Configuration priority When the widget initialises, it merges configuration from multiple sources. A value from a higher-priority source always wins. | Priority | Source | Description | | ----------- | --------------------------- | -------------------------------------------------------------------- | | 1 — highest | **HTML attributes** | Values set directly on the element, including the `config` attribute | | 2 | **Named embedded config** | The embedded config matching `config-id`, if provided | | 3 | **Default embedded config** | The first embedded config in your host profile | | 4 — lowest | **Attribute defaults** | Default values documented on this page | *** ## Full examples ### Modular menu with grid ```html theme={null} ``` ### Kiosk display ```html theme={null} ``` ### Geo-scoped with map ```html theme={null} ``` ### Custom-branded with rounded cards ```html theme={null} ``` ### Drawer with delayed auto-open ```html theme={null} ``` *** ## Related pages * [Developers overview](/developer-docs/overview) * [Quick Start](/embed-widget/quick-start) * [Language reference](/reference/language-reference) # Quick Start Source: https://docs.obvlo.com/embed-widget/quick-start Add the Obvlo Embed Widget to your page in minutes. ## Installation Add the widget script to your HTML ``: ```html theme={null} ``` Always load the widget script with `defer` or `async`. Both attributes prevent the script from blocking HTML parsing, keeping your page's initial render fast. Use `defer` (as shown above) if other scripts on the page depend on the widget being initialised first — it guarantees execution order. Use `async` if the widget is fully independent and you want it to execute as soon as it downloads. ## Basic usage Place the custom element anywhere in your page body: ```html theme={null} ``` That's it. The widget loads your content automatically once the script initialises. *** ## Configuration attributes When the widget initialises it resolves its configuration by merging several sources in priority order. A value set by a higher-priority source always wins over a lower-priority one. | Priority | Source | Description | | ----------- | --------------------------- | --------------------------------------------------------------------------------------------------- | | 1 — highest | **HTML attributes** | Values you set directly on the `` element. | | 2 | **Named embedded config** | The embedded config from your host profile whose ID matches the `config-id` attribute, if provided. | | 3 | **Default embedded config** | The first embedded config defined in your host profile, used when no `config-id` is supplied. | | 4 — lowest | **Attribute defaults** | The default values documented for each attribute below. | Your host profile contains the full list of embedded configs in the `embeddedConfigs` field. Use the [Get Host Profile](/api-reference/endpoint/host-profile) endpoint to inspect the configs available for a given `host-id`. *** ### Required #### `host-id` Required **Type:** `string` The unique identifier for your host profile. This links the widget to your content in the Obvlo system. In the Obvlo portal, host profiles are listed as **Destinations**. You can find the value for `host-id` in the Destinations table under the **Destination ID** column. ```html theme={null} ``` *** ### Display mode #### `drawer` **Type:** `boolean` — **Default:** `false` Controls whether the widget appears as an overlay drawer or renders inline. | Value | Behaviour | | ------------------ | -------------------------------------------------------- | | *(omitted)* | Widget displays inline within the page | | `drawer` (present) | Widget opens as an overlay drawer on top of page content | ```html theme={null} ``` #### `drawer-delay-ms` **Type:** `number` — **Default:** `0` Delay in milliseconds before automatically opening the drawer. Only applies when `drawer` is present. ```html theme={null} ``` #### `drawer-label` **Type:** `string` — **Default:** translated "Plan your trip" label Overrides the translated label shown on the drawer trigger button. Only applies when `drawer` is present. ```html theme={null} ``` *** ### Layout #### `config-id` **Type:** `string` Select a specific embedded configuration from your host profile. When omitted, the first configuration in the profile is used. ```html theme={null} ``` *** ### Content filtering #### `guide-id` **Type:** `string` Display a specific guide when the widget loads. Users can still navigate to other content. ```html theme={null} ``` #### `neighbourhood-id` **Type:** `string` Display a specific neighbourhood/geographic area when the widget loads. Only one of `guide-id` or `neighbourhood-id` should be used at a time. If both are provided, `guide-id` takes precedence. #### `guide-type` **Type:** `string` Filter the menu to show only guides of a specific type. | Value | Description | | --------------- | ------------------------- | | `guide` | Standard guides only | | `neighbourhood` | Neighbourhood guides only | | `featured` | Featured guides only | *** ### Grid display #### `grid-rows` **Type:** `number` — **Default:** `1` Number of rows to display in grid views on desktop and tablet devices. #### `grid-rows-mobile` **Type:** `number` — **Default:** `3` Number of rows to display in grid views on mobile devices. *** ### Geolocation #### `geo-location` **Type:** `string` (JSON object) A JSON object with `lat` and `lng` properties to set the user's geographic location. Used to scope content by proximity. ```html theme={null} ``` Use single quotes around the `geo-location` value since it contains JSON with double quotes. #### `geo-search-radius` **Type:** `number` — **Default:** `10000` Search radius in metres when `geo-location` is set. *** ### Feature toggles #### `disable-just-for-you` **Type:** `boolean` — **Default:** `false` Hides the "Just For You" personalised guide from the guide menu. Content personalisation remains fully functional when this attribute is set — only the menu entry is hidden. Useful for kiosk or focused experiences where the personalisation prompt would be distracting. #### `hide-travel` **Type:** `boolean` — **Default:** `false` Hides travel-related content categories from the widget. *** ### Localisation #### `lang` **Type:** `string` Sets the initial display language for the widget. Must be a full language code supported by your host profile. **Format:** `language-DIALECT` — for example `en-GB`, `es-ES`, `fr-FR` ```html theme={null} ``` Common language codes: | Code | Language | | ------- | ------------------------ | | `en-GB` | English (UK) — *default* | | `en-US` | English (US) | | `es-ES` | Spanish (Spain) | | `fr-FR` | French (France) | | `de-DE` | German (Germany) | | `it-IT` | Italian (Italy) | | `pt-PT` | Portuguese (Portugal) | | `pt-BR` | Portuguese (Brazil) | | `zh-CN` | Chinese (Simplified) | | `ja-JP` | Japanese | | `ar-SA` | Arabic (Saudi Arabia) | See the [Language reference](/reference/language-reference) for all 63 supported languages. *** ### Analytics #### `uid` **Type:** `string` Custom user identifier for analytics tracking. If not provided, a random UUID is generated automatically. ```html theme={null} ``` **Usage notes:** * Useful for tracking specific users across sessions * Should be unique per user * Automatically persisted in browser storage * Used for Google Tag Manager events *** ## Advanced configuration For more control over the widget's layout, modules, and styling, pass a JSON `config` attribute containing an `EmbeddedConfig` object: ```html theme={null} ``` The config object supports modules, kiosk mode, corner radius, container sizing, and more. See the [Configuration reference](/embed-widget/configuration-reference) for the full list of options. Use the [interactive test harness](https://cdn.obvlo.com/widgets/embed-test/index.html) to experiment with all attributes and config options — it generates the HTML tag for you in real time. *** ## Analytics events ### Using Google Analytics If your website is already using Google Analytics, the widget will automatically send events to your GA property. No additional setup is required. ### Using a different analytics provider The widget dispatches custom events that you can listen to and send to your preferred analytics provider. This functionality is designed for clients who don't use Google Analytics. To listen for analytics events, add the following JavaScript to your page: ```javascript theme={null} window.addEventListener('ob_analytics_event', (e) => { console.log(e.detail); // Send to your analytics provider }); ``` **Example implementation:** ```javascript theme={null} window.addEventListener('ob_analytics_event', (e) => { const { eventName, params, uid } = e.detail; // Send to your analytics provider yourAnalytics.track(eventName, { ...params, userId: uid }); }); ``` The event detail object contains the following properties: * `eventName` (string): The name of the analytics event (e.g., "widget\_visible", "guide\_viewed"). * `params` (object): Additional event parameters including event type ("click", "view", or "conversion"), host ID, host name, and any custom parameters specific to the event. * `uid` (string): The unique user identifier for tracking user sessions and behavior. ### Available events All events include standard parameters: `type` ('click', 'view', or 'conversion'), `host_id`, `host_name`, and `uid`. * **`widget_visible`** *(view)* — Triggered when the widget first becomes visible in the viewport. Params: `main_view` * `main_view`: widget layout type (e.g., `modular`) * **`widget_loaded`** *(view)* — Triggered when the widget finishes loading. Params: `main_view` * `main_view`: widget layout type (e.g., `modular`) * **`search`** *(click)* — Triggered when a user performs a search with a non-empty string. Params: `term` * `term`: The search query string * **`search_open`** *(click)* — Triggered when a user opens the search input field. * **`search_clear`** *(click)* — Triggered when a user clears the search input. * **`search_close`** *(click)* — Triggered when a user closes the search input field. * **`personalize_start`** *(click)* — Triggered when a user starts personalization for the first time. * **`personalize_update`** *(click)* — Triggered when a user clicks to update their existing personalization preferences. * **`personalization_complete`** *(conversion)* — Triggered when a user completes personalization for the first time. Params: all selected preference values * `tripType`: The user's selected trip type * `userCategory`: The user's selected category * `userInterests`: The user's selected interests * **`personalization_updated`** *(conversion)* — Triggered when a user updates their existing personalization preferences. Params: all selected preference values * `tripType`: The user's selected trip type * `userCategory`: The user's selected category * `userInterests`: The user's selected interests * **`listing_selected`** *(click)* — Triggered when a user selects a listing. Params: `listing_id`, `item_id`, `item_type`, `source` * `listing_id`: The selected listing's ID * `item_id`: The selected listing's ID * `item_type`: `listing` * `source`: `grid`, `list-map`, `map-component`, or `modular-menu` * **`guide_selected`** *(click)* — Triggered when a user selects a guide. Params: `guide_id`, `item_id`, `item_type`, `source` * `guide_id`: The selected guide's ID * `item_id`: The selected guide's ID * `item_type`: `guide` * `source`: Where the selection occurred * **`geoarea_selected`** *(click)* — Triggered when a user selects a geographic area. Params: `geoarea_id`, `item_id`, `item_type`, `source` * `geoarea_id`: The selected area's ID * `item_id`: The selected area's ID * `item_type`: `geoarea` * `source`: Where the selection occurred * **`page_changed`** *(click)* — Triggered when a user navigates to a different page in a paginated list. Params: `page`, `page_size`, `source` * `page`: The new page number * `page_size`: Number of items per page * `source`: Source of the page change (e.g., `explore-view`) * Additional context-specific parameters (e.g., `view`, `term`, `guide_id`/`geoarea_id`) * **`menu_scroll`** *(click)* — Triggered when a user scrolls through the guide/area menu. Params: `action` * `action`: `navigation`, `drag`, or `scroll` * **`filter_option_selected`** *(click)* — Triggered when a user selects a filter option. Params: `option` * `option`: The selected filter option * **`jfy_selected`** *(click)* — Triggered when a user selects "Just for You" (personalized recommendations). * **`drawer_open`** *(click)* — Triggered when a user opens the drawer widget. * **`language_change`** *(click)* — Triggered when a user changes the language selection. Params: `language_code` * `language_code`: BCP 47 language tag (e.g., `en-GB`, `fr-FR`) * **`saved_list_add`** *(click)* — Triggered when a user adds a listing to their saved list. Params: `listing_id`, `id`, `type` * `listing_id`: The listing's ID * `id`: The listing's ID * `type`: `listing` * **`saved_list_remove`** *(click)* — Triggered when a user removes a listing from their saved list. Params: `listing_id`, `id`, `type` * `listing_id`: The listing's ID * `id`: The listing's ID * `type`: `listing` * **`listing_view_on_map`** *(click)* — Triggered when a user clicks to view a listing's location on a map with directions. Params: `listing_id`, `listing_name` * `listing_id`: The listing's ID * `listing_name`: The listing's name * **`listing_book_now`** *(click)* — Triggered when a user clicks to make a table booking at a restaurant. Params: `listing_id`, `listing_name` * `listing_id`: The listing's ID * `listing_name`: The listing's name * **`listing_view_website`** *(click)* — Triggered when a user clicks to visit a listing's website. Params: `listing_id`, `listing_name` * `listing_id`: The listing's ID * `listing_name`: The listing's name * **`listing_see_reviews`** *(click)* — Triggered when a user clicks to view reviews on Google Maps or TripAdvisor. Params: `listing_id`, `listing_name`, `review_type`, `source` * `listing_id`: The listing's ID * `listing_name`: The listing's name * `review_type`: `google` or `tripadvisor` * `source`: `listing-detail` * **`listing_go_back`** *(click)* — Triggered when a user clicks the back button from a listing detail view. Params: `listing_id`, `listing_name` * `listing_id`: The listing's ID * `listing_name`: The listing's name * **`listing_info_click`** *(click)* — Triggered when a user clicks on listing information (map directions, phone, or website). Params: `listing_id`, `listing_name`, `info_type` * `listing_id`: The listing's ID * `listing_name`: The listing's name * `info_type`: `map`, `phone`, or `website` * **`listing_slide_changed`** *(click)* — Triggered when a user changes the image in the listing carousel. Params: `listing_id`, `listing_name`, `action`, `new_index` * `listing_id`: The listing's ID * `listing_name`: The listing's name * `action`: `navigation`, `drag`, or `pagination` * `new_index`: The index of the new slide * **`listing_see_attribution`** *(click)* — Triggered when a user clicks on a photo attribution link. Params: `listing_id`, `listing_name`, `photo_id`, `link` * `listing_id`: The listing's ID * `listing_name`: The listing's name * `photo_id`: The ID of the photo * `link`: The attribution URL * **`map_marker_click`** *(click)* — Triggered when a user clicks on a map marker. Params: `marker_type`; for listing markers: `listing_id`, `listing_name` * `marker_type`: `listing` or `home` * `listing_id`: The listing's ID *(listing markers only)* * `listing_name`: The listing's name *(listing markers only)* * **`map_popup_close`** *(click)* — Triggered when a user closes a map marker popup. Params: `marker_type`; for listing markers: `listing_id`, `listing_name` * `marker_type`: `listing` or `home` * `listing_id`: The listing's ID *(listing markers only)* * `listing_name`: The listing's name *(listing markers only)* * **`map_popup_listing_click`** *(click)* — Triggered when a user clicks a listing button within a map popup. Params: `listing_id` * `listing_id`: The listing's ID * **`map_move`** *(click)* — Triggered when a user pans/drags the map. Params: `center_lat`, `center_lng` * `center_lat`: The latitude of the new map center * `center_lng`: The longitude of the new map center * **`map_zoom`** *(click)* — Triggered when a user zooms the map. Params: `zoom_direction`, `zoom_level` * `zoom_direction`: `in` or `out` * `zoom_level`: The new zoom level * **`book_now`** *(click)* — Triggered when a user clicks a book now button. Params: `url`, `preferences` * `url`: The booking URL * `preferences`: The user's personalization preferences *** ## Examples ### Basic inline widget ```html theme={null} My Travel Site

Explore Our Destination

``` ### Drawer mode with delay ```html theme={null} ``` *** ## Browser support * Chrome / Edge — latest 2 versions * Firefox — latest 2 versions * Safari — latest 2 versions * Mobile: iOS Safari, Chrome for Android ## Responsive behaviour The widget automatically adjusts its layout based on container width: | Breakpoint | Width | | ---------- | -------------- | | X-Large | ≥ 1200px | | Large | 992px – 1199px | | Medium | 768px – 991px | | Small | \< 768px | *** ## Troubleshooting ### Widget not appearing 1. Verify the script tag is loading correctly (check the browser console for errors). 2. Confirm `host-id` is correct and the profile is active. 3. Check that the element is not hidden by CSS. ### Content not loading 1. Verify your host profile is properly configured in the Obvlo admin portal. 2. Check the browser console for API errors. ## Related pages * [Developers overview](/developer-docs/overview) * [Configuration reference](/embed-widget/configuration-reference) * [Language reference](/reference/language-reference) *** ## Getting help * Contact your Obvlo account manager * Email [support@obvlo.com](mailto:support@obvlo.com) # Terminology Source: https://docs.obvlo.com/getting-started/key-terms Key terms teams need before onboarding and implementation. ## Terminology These are the terms most teams need first. | Term | Meaning | | -------------------------------- | ------------------------------------------------------------------------------------------ | | Destination | The location you are managing content for, such as a hotel, resort, city, or region. | | Guide | A collection of listings built around a place or theme. | | Listing | An individual recommendation inside a guide, such as a restaurant, attraction, or event. | | Portal | The management dashboard your team uses to review content, settings, and launch readiness. | | Web App (Digital Concierge) | The hosted guest-facing experience managed by Obvlo. | | Embeddable Widget (Trip Planner) | The embeddable experience placed inside your existing website. | | Microsite (HTML Pages) | Search-focused HTML pages delivered on your domain. | | API | A direct integration for sending Obvlo destination content into your own systems. | For the complete terminology list, see the [Glossary](/support/glossary). # Getting Started Source: https://docs.obvlo.com/getting-started/start-here A 5-step guide from setup to going live with your destination content. This page is for customer teams onboarding with Obvlo. Follow the steps in order, or jump to the section that matches your current stage. If any terms are unfamiliar, read [Terminology](/getting-started/key-terms) first. *** ## Step 1: Defining your content needs This first step gives Obvlo the core inputs needed to begin content creation quickly. Share your brand guidelines, tone of voice, logos, colours, and key editorial rules. Confirm which destinations are in scope for launch. Share any preferred themes or content priorities if you want to guide the initial setup. How you plan to deliver content to guests: through your website, a hosted experience, or a direct integration. This helps Obvlo set up content to match your delivery approach from the start. *** ## Step 2: Creating your content This is where Obvlo sets up your destinations and generates your content set. * Apply available brand and tone inputs * Generate guides, listings, and related content * Deliver content ready for your review and updates *** ## Step 3: Reviewing your content Your team can review and personalise content in the **Portal** before launch. * Review guides, listings, pages, and promos * Edit copy, imagery, and categorisation where needed * Approve launch-ready content when needed * Request updates from Obvlo where required For detailed guidance on reviewing and managing content in the Portal, use the [Portal guide](/onboarding-guides/portal-onboarding-guide). *** ## Step 4: Using your content This is where you activate the implementation path already agreed. * **SEO and discoverability:** [Microsite](/onboarding-guides/microsite-onboarding-guide) — search-friendly HTML pages to improve destination discoverability * **Guest experience:** [Web App (Digital Concierge)](/onboarding-guides/hosted-onboarding-guide) — a full guest-facing experience hosted by Obvlo * **Digital channel enhancement:** [Embeddable Widget (Trip Planner)](/onboarding-guides/widget-onboarding-guide) — embed Obvlo content inside your existing website * **Custom integrations:** [API](/onboarding-guides/api-onboarding-guide) — onboard API delivery, then move into developer implementation pages *** ## Step 5: Going live Make sure launch content has been reviewed, approved, and signed off. Check that your agreed delivery path is configured and connected to the right destinations. Validate branding, content display, links, language behaviour, and responsiveness. Make sure your team knows what is live, where performance is monitored, and who owns escalation. After launch, monitor engagement and feedback, refresh content where needed, and adjust rollout priorities over time. *** Before you start, confirm who on your team owns approvals and day-to-day updates. That person is your point of contact with Obvlo throughout onboarding. ## Common questions | Question | Answer | | --------------------------------------------------- | ----------------------------------------------------------------------------------- | | Where do I start? | You're here. Follow the 5 steps above in order. | | How do I choose the right delivery path? | See Step 4 above or [Help FAQs](/support/faq#help-faqs). | | What do terms like Guide, Listing, and Portal mean? | See [Terminology](/getting-started/key-terms) or the [Glossary](/support/glossary). | | Where do I find product-specific questions? | Use the [FAQ](/support/faq) — it is organised by product area. | # Overview Source: https://docs.obvlo.com/index Everything you need to get destination content live. Obvlo empowers travel businesses to sell their destinations at scale with hyper-relevant local content. This documentation is written for customer teams. Developer implementation pages are included where technical setup is required. ## Where do you want to start? Follow the 5-step journey from setup to going live. API setup, authentication, and endpoint reference. Product guides for Portal, Web App, Embeddable Widget, and Microsite. FAQ, glossary, and support references. ## Next step * If you are onboarding now, start at [Getting Started](/getting-started/start-here). * If your team is implementing code, go to [Developer docs](/developer-docs/api/overview). # Deployment modes Source: https://docs.obvlo.com/microsite/deployment-modes The four ways Obvlo content reaches your domain, what each costs you to run, and why we recommend the reverse proxy to sites.obvlo.com. Obvlo content always lives on **your** domain. What differs between modes is how it gets there, how quickly a change you publish becomes visible, and how much you have to operate yourself. Every mode is set per domain, and every mode is reversible: switching does not migrate data, change your URLs, or invalidate anything you have already published. **Start with the reverse proxy to `sites.obvlo.com`.** It is the mode we recommend for new and existing sites, and the one we support best. Pages render on request behind a cache, so publishing is live within seconds, new pages appear without a rebuild, and there is nothing to schedule or wait for. If you run WordPress, the [WordPress plugin](/microsite/wordpress-plugin) is the same thing with the configuration already done for you. ## The options **Recommended.** Whole pages from `sites.obvlo.com`, through a proxy on your own server or CDN. Publishing is live immediately. The same reverse proxy, packaged as a plugin and pre-configured for your site. Nothing to configure after activation. One guide at a time, as an HTML fragment inside a page you already render yourself. Whole pages built ahead of time and served from `content.obvlo.com`. The original mode, still fully supported. [Switch to dynamic serving →](/microsite/ssr-proxy) ## Comparison | | Reverse proxy | WordPress plugin | Embed | Prerendered | | ---------------------------------- | ---------------------------------- | ------------------- | ------------------------------ | --------------------------------------- | | **Recommended** | **Yes — default** | Yes, on WordPress | For one guide in your own page | Existing sites only | | Origin host | `sites.obvlo.com` | `sites.obvlo.com` | `sites.obvlo.com` | `content.obvlo.com` | | What Obvlo serves | Whole pages | Whole pages | A content fragment | Whole pages | | Who renders the surrounding page | Obvlo | Obvlo | You | Obvlo | | A published change is live | Within seconds | Within seconds | Within seconds | After a rebuild — 20–30 minutes or more | | New pages appear | Automatically | Automatically | You add each placement | Only after the next rebuild | | What you operate | A proxy rule on your server or CDN | A plugin | A snippet in your template | A proxy rule on your server or CDN | | Setup effort | One config file | Upload and activate | Paste one snippet | One config file | | Needs a rebuild to go live | No | No | No | Yes | | Works without a build ever running | Yes | Yes | Yes | No | ## Pros and cons Whole Obvlo pages served through a proxy rule you add to your own web server or CDN, fetching from `sites.obvlo.com`. **Pros** * Publishing is live within seconds. There is no build to wait for and no window where the site and the portal disagree. * New pages are reachable as soon as they are published — you never have to remember to rebuild. * All SEO authority accrues to your primary domain, and Obvlo renders the whole page, so structured data, canonicals and hreflang are handled for you. * Works on any stack: NGINX, Apache, IIS, Caddy, Cloudflare Workers, or most CDNs. * Fully reversible. Repointing at `content.obvlo.com` is a two-line change. **Cons** * You need access to a web server or CDN configuration. If you cannot add a proxy rule, this mode is not available to you. * The `Host` header must match the origin. It is the most common setup error, which is why you should paste the generated configuration rather than adapt an example. * If you run your own CDN in front of Obvlo, its cache is yours to purge — a publish clears Obvlo's cache, not yours. [Set up a reverse proxy →](/microsite/reverse-proxy) The same reverse proxy, delivered as a WordPress plugin with your site's origin, ids and mount path already filled in. **Pros** * No server configuration at all. Upload, activate, done. * Everything the reverse proxy gives you, including immediate publishing and new pages appearing on their own. * The download is generated for your site specifically, so there is nothing to substitute by hand and nothing to get wrong. * Deactivating the plugin hands the path straight back to WordPress. **Cons** * WordPress only. For any other stack, use the reverse proxy. * Requests pass through PHP rather than your web server's proxy module, so it is marginally slower than an NGINX or CDN rule on the same hardware. * If you change the mount path in the portal, download the plugin again — the archive carries the path it was built with. [Install the WordPress plugin →](/microsite/wordpress-plugin) A single guide returned as an HTML fragment and placed inside a page you build and render yourself. **Pros** * You keep complete control of the page: your header, footer, layout, navigation and analytics. * No proxy and no server configuration — one snippet in your template. * The server-side include variant puts the content in the HTML you send, so crawlers see it on the first request. **Cons** * One guide per placement. You add each one, so it does not scale to a large or growing set of pages the way the proxy modes do. * You own the page, so canonicals, structured data and hreflang for it are yours to get right. * Resolves against your published production content only. [Use the Embed API →](/microsite/embed-api) Whole pages built ahead of time and served as finished files from `content.obvlo.com`, through the same kind of proxy rule. **Pros** * Serves static files, so response times are extremely consistent and there is no render in the request path. * Nothing renders on demand, so a traffic spike costs nothing extra. * Well proven — this is how Obvlo microsites were originally delivered, and it remains fully supported. **Cons** * A published change is not visible until the site is rebuilt and redeployed, which takes 20–30 minutes and longer for a large site. * New pages do not exist until the next build, so publishing and going live are separate events you have to track. * The same proxy configuration burden as the reverse proxy, without the immediacy. **On this mode the deploy is the publish, so the portal shows you no publish controls.** There is no Publish button, no Preview or Production column and no publish action in the page menu, because none of them could change what a visitor receives — only a rebuild can. Deploy the site to make your saved changes live. Every other mode shows the publish controls, because on those publishing is what puts a change in front of a visitor. [Switch to dynamic serving →](/microsite/ssr-proxy) ## Where it mounts The mode decides *how* pages are produced. A second, independent choice decides *where* they appear on your domain — and it applies to the reverse proxy and prerendered modes alike. | | Path on your main domain | Dedicated subdomain | | ---------------------------------------- | ------------------------------ | ------------------------------ | | Example | `yourdomain.com/travel-guides` | `guides.yourdomain.com` | | **Recommended** | **Yes — default** | For pilots and trials | | SEO authority | Accrues to your primary domain | Accrues to the subdomain | | Touches your main site's configuration | Yes — one proxy rule | No | | Needs its own DNS record and certificate | No | Yes | | Your own pages on that hostname | Untouched | None — Obvlo serves everything | | WordPress plugin available | Yes | No | Obvlo serves one path, and every other route on the hostname stays exactly as it is. **Pros** * Link equity and page authority accrue to the domain you actually want to rank, which is the single biggest reason to run a microsite at all. * One URL structure for crawlers, one domain for analytics and attribution. * Works with every mode, including the WordPress plugin. **Cons** * You need to add a rule to the server or CDN that serves your main site. In some organisations that is the slowest part of the whole setup. A hostname of its own — `guides.yourdomain.com` — where every address is an Obvlo page. **Pros** * Nothing on your main domain changes, and no existing route moves. Nothing you already serve can break. * The fastest way to get a microsite in front of real traffic: a DNS record, a certificate, and a proxy rule on a host with nothing else on it. * Ideal for a pilot, or where you cannot get a change made to the main site's configuration. **Cons** * Search engines treat a subdomain as related to but distinct from your primary domain, so authority builds there instead of where you want it. This is the trade-off, and it is the reason the path mount is the default recommendation. * The WordPress plugin cannot do it — it proxies a path inside a WordPress site, and an install with every route proxied has no home page left of its own. * Moving to a path mount later means redirects, so it is cheaper to start on a path if you already know the pilot will become permanent. [Set up a subdomain mount →](/microsite/reverse-proxy#mounting-on-a-subdomain) ## Choosing Three rules of thumb: * **If Obvlo owns the whole page, use the reverse proxy to `sites.obvlo.com`** — or the WordPress plugin, which is the same thing pre-configured. Choose prerendered only if you have a specific reason to serve static files and can accept a rebuild before every change goes live. * **If you own the page and want one guide inside it, use the Embed API** — and use its server-side include rather than the client-side script if the guide is the reason the page should rank. * **Mount on a path of your main domain unless you are piloting.** A dedicated subdomain is faster to stand up and changes nothing you already serve, but the SEO authority accrues to the subdomain rather than to your primary domain. ## What every mode shares * **Your domain, your URLs.** Content is always served from a hostname you control, never from an Obvlo one. SEO authority accrues to your primary domain in every mode — provided you mount on a path of it rather than on a subdomain. See [Microsite overview](/microsite/overview) for why this matters. * **The same content.** Modes change delivery, never what is published. * **Multi-language support.** All configured languages are available in every mode. * **Reversibility.** Switching is a configuration change on Obvlo's side plus, for the proxy modes, two lines in your proxy configuration. ## Switching Ask your account contact to change the mode for your domain. Then: * **To the reverse proxy from prerendered** — repoint your proxy at `sites.obvlo.com` and update the `Host` header to match. [Dynamic serving](/microsite/ssr-proxy) covers the exact change and how to verify it. * **To the WordPress plugin** — download it from the portal's **Integration** tab and activate it. You can remove your old proxy rule afterwards. * **To embed** — place the snippet from the **Integration** tab where you want the guide to appear. You can leave your existing proxy in place while you try it. The portal's **Integration** tab always generates configuration for the mode your domain is currently in, so the copy you take from it is the copy that will work. ## Related pages * [Microsite overview](/microsite/overview) * [Reverse proxy](/microsite/reverse-proxy) * [WordPress plugin](/microsite/wordpress-plugin) * [Dynamic serving](/microsite/ssr-proxy) * [Embed API](/microsite/embed-api) # Embed API Source: https://docs.obvlo.com/microsite/embed-api Drop a single Obvlo guide into a page you already control, as an HTML fragment served from the Obvlo edge. The Embed API returns one Obvlo page as an **HTML fragment** — no ``, no header, no footer — so you can place it inside a page your own site already renders. Use it when the surrounding page is yours and only the guide content comes from Obvlo. If you want Obvlo to serve whole pages under your domain instead, use [Reverse proxy](/microsite/reverse-proxy) or [Dynamic serving](/microsite/ssr-proxy). Embedding is enabled per domain by Obvlo. Ask your account contact to switch your domain to embed mode — the portal's **Integration** tab then generates these snippets pre-filled with your own organisation and site ids. ## Choose client-side or server-side This is an SEO decision, not a preference. | | Client-side script | Server-side include | | ------------------- | ------------------------------------------------------ | ----------------------------------------------------------------- | | How it arrives | The browser fetches the fragment after the page loads | Your server fetches the fragment and inlines it before responding | | What a crawler sees | An empty container, unless the crawler runs JavaScript | The full guide, in the first response | | Setup | Paste two tags into a template | A few lines in your server or application code | | Use it when | The guide is supplementary content | **The guide is the reason the page should rank** | Anything you want indexed should use the server-side include. ## Client-side script Paste the container and the loader into your page template. The loader reads its configuration from the container's `data-obvlo-*` attributes. ```html theme={null}
``` | Attribute | Meaning | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `data-obvlo-org` | Your Obvlo organisation id. | | `data-obvlo-site` | The Obvlo site the page belongs to. | | `data-obvlo-path` | The page's path **within the Obvlo site** — the page hierarchy, not the URL on your domain. | | `data-obvlo-base` | Optional. A path prefix on **your** domain. Set it and links inside the fragment stay on your site; omit it and they point back at Obvlo. | | `data-obvlo-locale` | Optional. A configured language code. Defaults to the site's default language. | Anything you put inside the container is a fallback: it stays on screen until the guide loads, and stays permanently if the fetch fails. The loader only ever writes on success, so a bad network never blanks the section. ## Server-side include Your server fetches the fragment while rendering, so the guide is in the HTML the crawler receives. ```php theme={null} ['timeout' => 5, 'ignore_errors' => true], ]); $obvlo_html = @file_get_contents($obvlo_url, false, $obvlo_ctx); $obvlo_ok = isset($http_response_header[0]) && strpos($http_response_header[0], ' 200 ') !== false; echo ($obvlo_ok && $obvlo_html !== false) ? $obvlo_html : ''; ?> ``` Three properties matter more than the language you write it in, and every version Obvlo generates has all three: * **A timeout.** Obvlo must never hold up your own response. * **A status check.** A 404 body is an error page, not content — never inline it. * **An empty-string fallback.** The page renders without the guide rather than erroring. The portal also generates NGINX `ssi` and Node versions of the same include. Open the **Integration** tab for your site and pick the flavour you need. ### The endpoint ```text theme={null} https://sites.obvlo.com/api/embed/content?org=…&site=…&path=…&base=… ``` | Parameter | Required | Meaning | | --------- | -------- | ------------------------------------------------------------------------ | | `org` | Yes | Your Obvlo organisation id. | | `site` | Yes | The Obvlo site id. | | `path` | Yes | URL-encoded page path within the site, e.g. `%2Fparis`. | | `base` | No | URL-encoded path prefix on your domain, so internal links stay with you. | | `locale` | No | A configured language code. | The response is an HTML fragment with a `text/html` content type. It carries no ``, ``, `
` or `