{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Frontend setup","meta":[{"name":"robots","content":"noindex"}],"llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"frontend-setup","__idx":0},"children":["Frontend setup"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Once your backend can obtain Spotnana access tokens, configure your frontend to load the Spotnana iframe and handle the token exchange."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"query-parameter","__idx":1},"children":["Query parameter"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When loading the iframe, include the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["idp=token-exchange-auth"]}," query parameter in the URL. This instructs the Spotnana system to initiate the token exchange authentication flow."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For example, the URL containing the query parameter may look like this:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://<spotnana-embed-url>/path?idp=token-exchange-auth&orgId=<your-org-id>"]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," This query parameter clears all previous session storage and starts a fresh authentication. This is also important to capture any changes that may have happened to the user's status within your system since the last session (e.g., a user's access may have been revoked if they've left the organization)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"frontend-token-exchange-process","__idx":2},"children":["Frontend token exchange process"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When the iframe loads, Spotnana and your application exchange messages through the browser's ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://en.wikipedia.org/wiki/Web_Messaging"},"children":["postMessage"]}," API. Here's how the process works:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Spotnana sends a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOKEN_EXCHANGE_REQUEST"]}," message to your application. Here's a sample message you may receive:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"TOKEN_EXCHANGE_REQUEST\",\n  \"from\": \"spotnana-embed\"\n}\n"},"children":[]},{"$$mdtype":"Tag","name":"ol","attributes":{"start":2},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your application listens for this message. Then, it fetches the access token and refresh token from your ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"#token-exchange-request"},"children":["backend setup"]},", and responds with the following ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOKEN_EXCHANGE_RESPONSE"]},"."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"TOKEN_EXCHANGE_RESPONSE\",\n  \"payload\": {\n    \"accessToken\": \"<spotnana-access-token>\",\n    \"refreshToken\": \"<spotnana-refresh-token>\"\n  }\n}\n"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," If the token exchange fails, Spotnana sends a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["TOKEN_EXCHANGE_ERROR"]}," message as shown below:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"{\n  \"from\": \"spotnana-embed\",\n  \"type\": \"TOKEN_EXCHANGE_ERROR\",\n  \"payload\": {\n    \"errorCode\": \"INVALID_TOKEN\",\n    \"errorMessage\": \"<error description>\"\n  }\n}\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your application must be designed to listen for this event and handle authentication failures appropriately (e.g., redirecting the user to a login page or displaying an error message)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"code-sample-to-implement-frontend-logic","__idx":3},"children":["Code sample to implement frontend logic"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following HTML sample demonstrates the frontend integration."," ","This code will render the Spotnana iframe upon the click of a button"," ","(i.e., in this code, clicking the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Launch Spotnana"]}," button will initiate the rendering)."," ","This code also handles the frontend token exchange."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"html","header":{"controls":{"copy":{}}},"source":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n  <title>Spotnana Embed</title>\n  <style>\n    html, body { height: 100%; margin: 0; padding: 0 20px; }\n    .iframe-wrapper {\n      height: calc(100% - 100px);\n      width: 100%;\n      border: 1px solid #ccc;\n    }\n  </style>\n</head>\n<body>\n  <h1>Spotnana Integration</h1>\n  <button id=\"renderIframe\">Launch Spotnana</button>\n  <div class=\"iframe-wrapper\" id=\"iframeWrapper\"></div>\n\n  <script>\n    const spotnanaDomain = 'https://sboxmeta-embed-app.partners.spotnana.com';\n    const orgId = '<YOUR_ORG_ID>';\n\n    // Listen for token exchange requests from the iframe\n    window.addEventListener('message', async (event) => {\n      if (event.origin !== spotnanaDomain) return;\n      const { data, source } = event;\n\n      if (data.type === 'TOKEN_EXCHANGE_REQUEST') {\n        try {\n          // Fetch tokens from your backend\n          const response = await fetch('/api/spotnana/tokens');\n          const { accessToken, refreshToken } = await response.json();\n\n          source.postMessage({\n            type: 'TOKEN_EXCHANGE_RESPONSE',\n            payload: { accessToken, refreshToken }\n          }, spotnanaDomain);\n        } catch (error) {\n          console.error('Token fetch failed:', error);\n        }\n      }\n\n      if (data.type === 'TOKEN_EXCHANGE_ERROR') {\n        console.error('Token exchange error:', data.payload);\n        // Handle authentication failure\n      }\n    });\n\n    // Render iframe on button click\n    document.getElementById('renderIframe').addEventListener('click', () => {\n      const wrapper = document.getElementById('iframeWrapper');\n      const params = new URLSearchParams({\n        'idp': 'token-exchange-auth',\n        'orgId': orgId,\n        // Add other parameters as needed\n      });\n\n      const iframe = document.createElement('iframe');\n      iframe.src = `${spotnanaDomain}/hotels/search?${params}`;\n      iframe.width = '100%';\n      iframe.height = '100%';\n      iframe.title = 'Spotnana';\n\n      wrapper.innerHTML = '';\n      wrapper.appendChild(iframe);\n    });\n  </script>\n</body>\n</html>\n","lang":"html"},"children":[]}]},"headings":[{"value":"Frontend setup","id":"frontend-setup","depth":1},{"value":"Query parameter","id":"query-parameter","depth":2},{"value":"Frontend token exchange process","id":"frontend-token-exchange-process","depth":2},{"value":"Code sample to implement frontend logic","id":"code-sample-to-implement-frontend-logic","depth":2}],"frontmatter":{"seo":{"title":"Frontend setup"}},"lastModified":"2026-03-19T13:36:30.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/integration/iframe/iframe-frontend-setup","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}